From 47214818a4a6f423ed65ab90d2a5876bde370dc8 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 22 Jul 2020 10:22:27 -0500 Subject: framework for properties panel --- .../collectionFreeForm/PropertiesView.scss | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/client/views/collections/collectionFreeForm/PropertiesView.scss (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss new file mode 100644 index 000000000..d4cc53bfa --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -0,0 +1,32 @@ +.propertiesView { + + background-color: rgb(168, 168, 168); + height: 100%; + z-index: 1; + + .propertiesView-title { + background-color: rgb(134, 134, 134); + border-top: 1px solid black; + border-bottom: 1px solid black; + } + + .propertiesView-name { + border-top: 1px solid black; + border-bottom: 1px solid black; + } + + .propertiesView-settings { + border-top: 1px solid black; + border-bottom: 1px solid black; + } + + .propertiesView-fields { + border-top: 1px solid black; + border-bottom: 1px solid black; + } + + .propertiesView-layout { + border-top: 1px solid black; + border-bottom: 1px solid black; + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 5b025537f60ef9222c0203db378c0298cd3dc86f Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 22 Jul 2020 12:57:05 -0500 Subject: adding fields to properties column --- src/client/util/CurrentUserUtils.ts | 8 +-- src/client/views/EditableView.tsx | 2 +- src/client/views/MainView.scss | 5 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 43 +++++++++++++--- .../collectionFreeForm/PropertiesView.tsx | 60 +++++++++++++++++----- 6 files changed, 93 insertions(+), 27 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 088e20a3e..1dd25ae30 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -660,7 +660,7 @@ export class CurrentUserUtils { CurrentUserUtils.workspaceStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], { title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; @@ -679,7 +679,7 @@ export class CurrentUserUtils { CurrentUserUtils.catalogStack = new PrefetchProxy(Docs.Create.TreeDocument([catalog], { title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; @@ -703,7 +703,7 @@ export class CurrentUserUtils { CurrentUserUtils.closedStack = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], { title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; @@ -718,7 +718,7 @@ export class CurrentUserUtils { if (doc["tabs-button-library"] === undefined) { const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({ diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 25a87ab56..259a803a2 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -192,7 +192,7 @@ export class EditableView extends React.Component { return (this.props.contents instanceof ObjectField ? (null) :
- +
; } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index d4cc53bfa..bb5b80abd 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -1,32 +1,59 @@ .propertiesView { - background-color: rgb(168, 168, 168); + background-color: rgb(205, 205, 205); height: 100%; z-index: 1; + font-family: "Noto Sans"; + cursor: auto; + + //overflow-y: scroll; .propertiesView-title { - background-color: rgb(134, 134, 134); - border-top: 1px solid black; - border-bottom: 1px solid black; + background-color: rgb(159, 159, 159); + text-align: center; + font-size: 18px; + font-weight: bold; + padding-top: 12px; + padding-bottom: 12px; } .propertiesView-name { - border-top: 1px solid black; border-bottom: 1px solid black; + padding: 8.5px; + font-size: 12.5px; } .propertiesView-settings { - border-top: 1px solid black; border-bottom: 1px solid black; + padding: 8.5px; + font-size: 12.5px; + font-weight: bold; } .propertiesView-fields { - border-top: 1px solid black; border-bottom: 1px solid black; + padding: 8.5px; + + .propertiesView-fields-title { + font-size: 12.5px; + font-weight: bold; + padding-bottom: 7px; + } + + .propertiesView-fields-content { + font-size: 10px; + margin-left: 5px; + + &:hover { + cursor: pointer; + } + } } .propertiesView-layout { - border-top: 1px solid black; border-bottom: 1px solid black; + padding: 8.5px; + font-size: 12.5px; + font-weight: bold; } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 4306453ea..bc78fccdf 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -1,20 +1,54 @@ import React = require("react"); import { observer } from "mobx-react"; import "./PropertiesView.scss"; -import { observable, action } from "mobx"; -import { Doc } from "../../../../fields/Doc"; +import { observable, action, computed } from "mobx"; +import { Doc, Field, DocListCast } from "../../../../fields/Doc"; import { DocumentView } from "../../nodes/DocumentView"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { EditableView } from "../../EditableView"; +import { KeyValueBox } from "../../nodes/KeyValueBox"; +import { Cast } from "../../../../fields/Types"; +import { listSpec } from "../../../../fields/Schema"; -// interface PropertiesViewProps { -// document: Doc; -// dataDoc: Doc; -// docView: DocumentView; -// width: number; -// } +interface PropertiesViewProps { + document: Doc; + //dataDoc: Doc; + //docView: DocumentView; +} @observer -export class PropertiesView extends React.Component<{}> { +export class PropertiesView extends React.Component { + + @computed get expandedField() { + const ids: { [key: string]: string } = {}; + const doc = this.props.document; + 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()) { + const contents = doc[key]; + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + />; + + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + return rows; + } + + @computed get layoutPreview() { + return "layout"; + } render() { return
@@ -22,16 +56,18 @@ export class PropertiesView extends React.Component<{}> { Properties
- Properties + Collection
Settings
- Fields +
Fields
+
{this.expandedField}
- Layout +
Layout
+
{this.layoutPreview}
; } -- cgit v1.2.3-70-g09d2 From a99cef9945c1c4f9e5ff07663fc6db5cda2dcd50 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 22 Jul 2020 16:27:27 -0500 Subject: added layout section --- src/client/views/collections/CollectionView.tsx | 7 +- .../collectionFreeForm/PropertiesView.scss | 15 +++- .../collectionFreeForm/PropertiesView.tsx | 82 +++++++++++++++++++--- 3 files changed, 92 insertions(+), 12 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 616e7995d..eec68f9fc 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -588,7 +588,12 @@ export class CollectionView extends Touchable - + ; } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index bb5b80abd..e81b1eeb4 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -51,9 +51,18 @@ } .propertiesView-layout { - border-bottom: 1px solid black; padding: 8.5px; - font-size: 12.5px; - font-weight: bold; + + .propertiesView-layout-title { + font-weight: bold; + font-size: 12.5px; + padding-bottom: 7px; + } + + .propertiesView-layout-content { + margin-left: 5px; + overflow: hidden; + } + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index bc78fccdf..27b0dbd9e 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -2,27 +2,58 @@ import React = require("react"); import { observer } from "mobx-react"; import "./PropertiesView.scss"; import { observable, action, computed } from "mobx"; -import { Doc, Field, DocListCast } from "../../../../fields/Doc"; +import { Doc, Field, DocListCast, WidthSym, HeightSym } from "../../../../fields/Doc"; import { DocumentView } from "../../nodes/DocumentView"; import { ComputedField } from "../../../../fields/ScriptField"; import { EditableView } from "../../EditableView"; import { KeyValueBox } from "../../nodes/KeyValueBox"; -import { Cast } from "../../../../fields/Types"; +import { Cast, StrCast, NumCast } from "../../../../fields/Types"; import { listSpec } from "../../../../fields/Schema"; +import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView"; +import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils"; +import { Id } from "../../../../fields/FieldSymbols"; +import { Transform } from "../../../util/Transform"; interface PropertiesViewProps { - document: Doc; - //dataDoc: Doc; + dataDoc: Doc; + Document: Doc; + width: number; + height: number; + renderDepth: number; + ScreenToLocalTransform: () => Transform; //docView: DocumentView; } @observer export class PropertiesView extends React.Component { + @computed get MAX_EMBED_HEIGHT() { return 200; } + + rtfWidth = () => Math.min(this.props.Document?.[WidthSym](), this.props.width - 20); + rtfHeight = () => this.rtfWidth() <= this.props.Document?.[WidthSym]() ? Math.min(this.props.Document?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + + docWidth = () => { + const layoutDoc = this.props.Document; + 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; + } + docHeight = () => { + const layoutDoc = this.props.Document; + 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.props.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; + })())); + } + @computed get expandedField() { const ids: { [key: string]: string } = {}; - const doc = this.props.document; + const doc = this.props.dataDoc; doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); const rows: JSX.Element[] = []; @@ -47,7 +78,42 @@ export class PropertiesView extends React.Component { } @computed get layoutPreview() { - return "layout"; + const layoutDoc = Doc.Layout(this.props.Document); + 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
+ "lightgrey"} + fitToBox={false} + FreezeDimensions={true} + NativeWidth={layoutDoc.type === + StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : returnZero} + NativeHeight={layoutDoc.type === + StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : returnZero} + PanelWidth={panelWidth} + PanelHeight={panelHeight} + focus={returnFalse} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + docFilters={returnEmptyFilter} + ContainingCollectionDoc={undefined} + ContainingCollectionView={undefined} + addDocument={returnFalse} + moveDocument={undefined} + removeDocument={returnFalse} + parentActive={() => false} + whenActiveChanged={emptyFunction} + addDocTab={returnFalse} + pinToPres={emptyFunction} + bringToFront={returnFalse} + ContentScaling={returnOne} + /> +
; } render() { @@ -66,8 +132,8 @@ export class PropertiesView extends React.Component {
{this.expandedField}
-
Layout
-
{this.layoutPreview}
+
Layout
+
{this.layoutPreview}
; } -- cgit v1.2.3-70-g09d2 From 66bcfb6364ca5d8b4a0fd26139966ea9d6c12f87 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 22 Jul 2020 18:07:45 -0500 Subject: adding buttons --- src/client/views/PropertiesButtons.scss | 105 ++++++++ src/client/views/PropertiesButtons.tsx | 296 +++++++++++++++++++++ src/client/views/collections/CollectionView.tsx | 2 + .../collectionFreeForm/PropertiesView.scss | 12 + .../collectionFreeForm/PropertiesView.tsx | 8 +- 5 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 src/client/views/PropertiesButtons.scss create mode 100644 src/client/views/PropertiesButtons.tsx (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss new file mode 100644 index 000000000..8dd5cec03 --- /dev/null +++ b/src/client/views/PropertiesButtons.scss @@ -0,0 +1,105 @@ +@import "globalCssVariables"; + +$linkGap : 3px; + +.propertiesButtons-linkFlyout { + grid-column: 2/4; +} + +.propertiesButtons-linkButton-empty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.propertiesButtons-linkButton-nonempty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.propertiesButtons-linkButton-empty, +.propertiesButtons-linkButton-nonempty { + height: 20px; + width: 20px; + border-radius: 50%; + opacity: 0.9; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + + &:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; + } +} + +.propertiesButtons { + margin-top: $linkGap; + grid-column: 1/4; + width: max-content; + height: auto; + display: flex; + flex-direction: row; +} + +.propertiesButtons-button { + pointer-events: auto; + padding-right: 5px; + width: 25px; +} + +.propertiesButtons-linker { + height: 20px; + width: 20px; + text-align: center; + border-radius: 50%; + pointer-events: auto; + color: $dark-color; + border: $dark-color 1px solid; + transition: 0.2s ease all; +} + +.propertiesButtons-linker:hover { + cursor: pointer; + transform: scale(1.05); +} + + +@-moz-keyframes spin { + 100% { + -moz-transform: rotate(360deg); + } +} + +@-webkit-keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes shadow-pulse { + 0% { + box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8); + } + + 100% { + box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); + } +} \ No newline at end of file diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx new file mode 100644 index 000000000..842d0320f --- /dev/null +++ b/src/client/views/PropertiesButtons.tsx @@ -0,0 +1,296 @@ +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { RichTextField } from '../../fields/RichTextField'; +import { Cast, NumCast } from "../../fields/Types"; +import { emptyFunction, setupMoveUpEvents } from "../../Utils"; +import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; +import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; +import { Docs, DocUtils } from '../documents/Documents'; +import { DragManager } from '../util/DragManager'; +import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView'; +import { ParentDocSelector } from './collections/ParentDocumentSelector'; +import './collections/ParentDocumentSelector.scss'; +import './PropertiesButtons.scss'; +import { MetadataEntryMenu } from './MetadataEntryMenu'; +import { DocumentView } from './nodes/DocumentView'; +import { GoogleRef } from "./nodes/formattedText/FormattedTextBox"; +import { TemplateMenu } from "./TemplateMenu"; +import { Template, Templates } from "./Templates"; +import React = require("react"); +import { Tooltip } from '@material-ui/core'; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +library.add(faLink); +library.add(faTag); +library.add(faTimes); +library.add(faArrowAltCircleDown); +library.add(faArrowAltCircleUp); +library.add(faArrowAltCircleRight); +library.add(faStopCircle); +library.add(faCheckCircle); +library.add(faCloudUploadAlt); +library.add(faSyncAlt); +library.add(faShare); +library.add(faPhotoVideo); + +const cloud: IconProp = "cloud-upload-alt"; +const fetch: IconProp = "sync-alt"; + +enum UtilityButtonState { + Default, + OpenRight, + OpenExternally +} + +@observer +export class PropertiesButtons extends React.Component<{ doc: Doc }, {}> { + private _dragRef = React.createRef(); + private _pullAnimating = false; + private _pushAnimating = false; + private _pullColorAnimating = false; + + @observable private pushIcon: IconProp = "arrow-alt-circle-up"; + @observable private pullIcon: IconProp = "arrow-alt-circle-down"; + @observable private pullColor: string = "white"; + @observable public isAnimatingFetch = false; + @observable public isAnimatingPulse = false; + + @observable private openHover: UtilityButtonState = UtilityButtonState.Default; + + @observable public static Instance: PropertiesButtons; + public static hasPushedHack = false; + public static hasPulledHack = false; + + public startPullOutcome = action((success: boolean) => { + if (!this._pullAnimating) { + this._pullAnimating = true; + this.pullIcon = success ? "check-circle" : "stop-circle"; + setTimeout(() => runInAction(() => { + this.pullIcon = "arrow-alt-circle-down"; + this._pullAnimating = false; + }), 1000); + } + }); + + public startPushOutcome = action((success: boolean) => { + this.isAnimatingPulse = false; + if (!this._pushAnimating) { + this._pushAnimating = true; + this.pushIcon = success ? "check-circle" : "stop-circle"; + setTimeout(() => runInAction(() => { + this.pushIcon = "arrow-alt-circle-up"; + this._pushAnimating = false; + }), 1000); + } + }); + + public setPullState = action((unchanged: boolean) => { + this.isAnimatingFetch = false; + if (!this._pullColorAnimating) { + this._pullColorAnimating = true; + this.pullColor = unchanged ? "lawngreen" : "red"; + setTimeout(this.clearPullColor, 1000); + } + }); + + private clearPullColor = action(() => { + this.pullColor = "white"; + this._pullColorAnimating = false; + }); + + //get view0() { return this.props.views()?.[0]; } + + @computed + get considerGoogleDocsPush() { + const targetDoc = this.props.doc; + const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined; + const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none"; + return !targetDoc ? (null) :
{`${published ? "Push" : "Publish"} to Google Docs`}
}> +
{ + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); + !published && runInAction(() => this.isAnimatingPulse = true); + PropertiesButtons.hasPushedHack = false; + targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1; + }}> + +
; + } + + @computed + get considerGoogleDocsPull() { + const targetDoc = this.props.doc; + const dataDoc = targetDoc && Doc.GetProto(targetDoc); + const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none"; + + const title = (() => { + switch (this.openHover) { + default: + case UtilityButtonState.Default: return `${!dataDoc?.unchanged ? "Pull from" : "Fetch"} Google Docs`; + case UtilityButtonState.OpenRight: return "Open in Right Split"; + case UtilityButtonState.OpenExternally: return "Open in new Browser Tab"; + } + })(); + + return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) :
{title}
}> +
{ + if (e.altKey) { + this.openHover = UtilityButtonState.OpenExternally; + } else if (e.shiftKey) { + this.openHover = UtilityButtonState.OpenRight; + } + })} + onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)} + onClick={async e => { + const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`; + if (e.shiftKey) { + e.preventDefault(); + let googleDoc = await Cast(dataDoc.googleDoc, Doc); + if (!googleDoc) { + const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false }; + googleDoc = Docs.Create.WebDocument(googleDocUrl, options); + dataDoc.googleDoc = googleDoc; + } + CollectionDockingView.AddRightSplit(googleDoc); + } else if (e.altKey) { + e.preventDefault(); + window.open(googleDocUrl); + } else { + this.clearPullColor(); + PropertiesButtons.hasPulledHack = false; + targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1; + dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true); + } + }}> + { + switch (this.openHover) { + default: + case UtilityButtonState.Default: return dataDoc.unchanged === false ? (this.pullIcon as any) : fetch; + case UtilityButtonState.OpenRight: return "arrow-alt-circle-right"; + case UtilityButtonState.OpenExternally: return "share"; + } + })()} + /> +
; + } + @computed + get pinButton() { + const targetDoc = this.props.doc; + const isPinned = targetDoc && Doc.isDocPinned(targetDoc); + return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}> +
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> + +
; + } + + @computed + get metadataButton() { + //const view0 = this.view0; + return
Show metadata panel
}> +
+ /* tfs: @bcz This might need to be the data document? */}> +
e.stopPropagation()} > + {} +
+
+
; + } + + // @computed + // get contextButton() { + // return { + // where === "onRight" ? CollectionDockingView.AddRightSplit(doc) : + // this.props.doc.props.addDocTab(doc, "onRight"); + // return true; + // }} />; + // } + + @observable _aliasDown = false; + onAliasButtonDown = (e: React.PointerEvent): void => { + //setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); + + } + + // onAliasButtonMoved = () => { + // if (this._dragRef.current) { + // const dragDocView = this.props.doc!; + // if (dragDocView.props){ + // const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]); + // const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + // dragData.dropAction = "alias"; + // DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { + // offsetX: dragData.offset[0], + // offsetY: dragData.offset[1], + // hideSource: false + // }); + // return true; + // } + + // } + // return false; + // } + + // @computed + // get templateButton() { + // //const view0 = this.view0; + // const templates: Map = new Map(); + // //const views = this.props.views(); + // Array.from(Object.values(Templates.TemplateList)).map(template => + // templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); + // return !this.props.doc ? (null) : + //
Tap: Customize layout. Drag: Create alias
}> + //
+ // this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} + // content={!this._aliasDown ? (null) : v).map(v => v as DocumentView)} templates={templates} />}> + //
+ // {} + //
+ //
+ //
; + // } + + render() { + if (!this.props.doc) return (null); + + const isText = this.props.doc[Doc.LayoutFieldKey(this.props.doc)] instanceof RichTextField; + const considerPull = isText && this.considerGoogleDocsPull; + const considerPush = isText && this.considerGoogleDocsPush; + return
+ {/*
+ {this.templateButton} +
*/} +
+ {this.metadataButton} +
+ {/*
+ {this.contextButton} +
*/} +
+ {this.pinButton} +
+
+ {this.considerGoogleDocsPush} +
+
+ {this.considerGoogleDocsPull} +
+
; + } +} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index eec68f9fc..853321d3c 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -50,6 +50,7 @@ import './CollectionView.scss'; import CollectionMenu from './CollectionMenu'; import { SharingPermissions } from '../../util/SharingManager'; import { PropertiesView } from './collectionFreeForm/PropertiesView'; +import { DocumentView } from '../nodes/DocumentView'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -589,6 +590,7 @@ export class CollectionView extends Touchable Transform; - //docView: DocumentView; + docView: DocumentView; } @observer @@ -125,7 +126,10 @@ export class PropertiesView extends React.Component { Collection
- Settings +
Settings
+
+ +
Fields
-- cgit v1.2.3-70-g09d2 From 171f8a5ce7ba35bb8a4045411f925a469c460774 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Thu, 23 Jul 2020 18:33:52 -0500 Subject: fixed right handle issues --- src/client/util/CurrentUserUtils.ts | 21 +- src/client/views/MainView.scss | 5 +- src/client/views/MainView.tsx | 304 +++++++++++---------- .../collectionFreeForm/PropertiesView.scss | 1 - src/client/views/nodes/DocumentView.tsx | 12 +- 5 files changed, 180 insertions(+), 163 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c89f9b8fe..d41aa4b58 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -498,14 +498,14 @@ export class CurrentUserUtils { title: string, icon: string, click: string, backgroundColor?: string, }[] { return [ - { title: "Workspace", icon: "desktop", click: 'this.selectPanel("workspace")' }, - { title: "Catalog", icon: "file", click: 'this.selectPanel("catalog")' }, - { title: "Recently Deleted", icon: "trash-alt", click: 'this.selectPanel("deleted")' }, + { title: "Workspace", icon: "desktop", click: 'MainView.selectPanel("workspace")' }, + { title: "Catalog", icon: "file", click: 'MainView.selectPanel("catalog")' }, + { title: "Recently Deleted", icon: "trash-alt", click: 'MainView.selectPanel("deleted")' }, { title: "Import", icon: "upload", click: 'this.selectPanel("upload")' }, { title: "Sharing", icon: "users", click: 'GroupManager.Instance.open()' }, - { title: "Tools", icon: "wrench", click: 'this.selectPanel("tools")' }, - { title: "Search", icon: "search", click: 'this.selectPanel("search")' }, - { title: "Help", icon: "question-circle", click: 'this.selectPanel("help")' }, + { title: "Tools", icon: "wrench", click: 'MainView.selectPanel("tools")' }, + { title: "Search", icon: "search", click: 'MainView.selectPanel("search")' }, + { title: "Help", icon: "question-circle", click: 'MainView.selectPanel("help")' }, { title: "Settings", icon: "cog", click: 'SettingsManager.Instance.open()' }, ]; } @@ -514,7 +514,7 @@ export class CurrentUserUtils { if (doc.menuStackBtns === undefined) { const buttons = CurrentUserUtils.menuBtnDescriptions(); const menuBtns = buttons.map(({ title, icon, click, backgroundColor }) => Docs.Create.FontIconDocument({ - _width: 100, _height: 100, + _width: 40, _height: 40, icon, title, onClick: click ? ScriptField.MakeScript(click) : undefined, @@ -522,18 +522,19 @@ export class CurrentUserUtils { })); doc.menuStackBtns = new PrefetchProxy(Docs.Create.MasonryDocument(menuBtns, { - _xMargin: 0, _autoHeight: true, _width: 100, _columnWidth: 60, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + _xMargin: 0, _autoHeight: true, _width: 80, _columnWidth: 50, lockedPosition: true, _chromeStatus: "disabled", })); } return doc.menuStackBtns as Doc; } static setupMenuPanel(doc: Doc) { + doc.menuStack = undefined; if (doc.menuStack === undefined) { const menuBtns = CurrentUserUtils.setupMenuButtons(doc); doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument([menuBtns], { - title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, - _width: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + _yMargin: 0, _autoHeight: true, _xMargin: 0, + _width: 80, lockedPosition: true, _chromeStatus: "disabled", })) as any as Doc; } return doc.menuStack; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index bd074adea..10e7f471b 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -137,7 +137,8 @@ right: 0; top: 0; border-left: solid 1px; - z-index: 10; + z-index: 10000; + cursor: auto; } .mainView-flyoutContainer { @@ -160,7 +161,7 @@ padding-right: 50px; overflow-y: scroll; - overflow-x: hidden; + overflow-x: clip; .mainView-menuPanel-button { width: 45px; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bc20059e8..beee9289e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -84,7 +84,7 @@ export class MainView extends React.Component { @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; - @observable public flyoutWidth: number = 250; + @observable public flyoutWidth: number = 275; private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } @computed private get userDoc() { return Doc.UserDoc(); } @@ -96,6 +96,11 @@ export class MainView extends React.Component { @observable public panelContent: string = "none"; @observable public showProperties: boolean = false; public isPointerDown = false; + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else { return undefined; } + } @observable _propertiesWidth: number = 0; propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, this._propertiesWidth)); @@ -447,148 +452,152 @@ export class MainView extends React.Component { bringToFront={emptyFunction} docFilters={returnEmptyFilter} ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> + ContainingCollectionDoc={undefined} + relative={true} + />
{this.docButtons}; } - @computed get menuPanel() { - - return
- 100} - PanelHeight={this.getContentsHeight} - renderDepth={0} - focus={emptyFunction} - backgroundColor={this.defaultBackgroundColors} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> -
; - } - // @computed get menuPanel() { + // return
- // - - // - - // - - // - - // - - // - - // - - // - - // + // 80} + // PanelHeight={this.getContentsHeight} + // renderDepth={0} + // focus={emptyFunction} + // backgroundColor={this.defaultBackgroundColors} + // parentActive={returnTrue} + // whenActiveChanged={emptyFunction} + // bringToFront={emptyFunction} + // docFilters={returnEmptyFilter} + // ContainingCollectionView={undefined} + // ContainingCollectionDoc={undefined} + // relative={true} + // /> //
; // } + @computed get menuPanel() { + return
+ + + + + + + + + + + + + + + + + +
; + } + @action selectPanel = (str: string) => { if (this.panelContent === str && this.flyoutWidth !== 0) { @@ -623,23 +632,24 @@ export class MainView extends React.Component { @computed get propertiesView() { TraceMobx(); - return this._propertiesWidth === 0 ? (null) : -
- -
; + return
+ +
; } @computed get mainContent() { const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`; + + const rightFlyout = this.selectedDocumentView ? this._propertiesWidth - 1 : this.propertiesWidth() > 10 ? 151.5 : 0; return !this.userDoc || !(this.sidebarContent instanceof Doc) ? (null) : (
+ style={{ right: rightFlyout, top: "45%" }}>
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 98eae1ac7..f64ef2211 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -2,7 +2,6 @@ background-color: rgb(205, 205, 205); height: 100%; - z-index: 1; font-family: "Noto Sans"; cursor: auto; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 998c6798e..02fe60255 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -100,6 +100,7 @@ export interface DocumentViewProps { layoutKey?: string; radialMenu?: String[]; display?: string; + relative?: boolean; } @observer @@ -1021,8 +1022,9 @@ export class DocumentView extends DocComponent(Docu } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); @computed get contents() { + const pos = this.props.relative ? "relative " : "absolute"; TraceMobx(); - return (
+ return (
(Docu } @computed get innards() { TraceMobx(); + const pos = this.props.relative ? "relative" : undefined; if (this.props.treeViewDoc && !this.props.LayoutTemplateString?.includes("LinkAnchorBox")) { // this happens when the document is a tree view label (but not an anchor dot) - return
+ return
{StrCast(this.props.Document.title)} {this.allAnchors}
; @@ -1235,7 +1241,7 @@ export class DocumentView extends DocComponent(Docu background: finalColor, opacity: finalOpacity, fontFamily: StrCast(this.Document._fontFamily, "inherit"), - fontSize: Cast(this.Document._fontSize, "string", null) + fontSize: Cast(this.Document._fontSize, "string", null), }}> {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <> {this.innards} -- cgit v1.2.3-70-g09d2 From 433ac4ffcf2eb71557ca0baff704bd67c461cadc Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Fri, 24 Jul 2020 12:16:16 -0500 Subject: implemented sharing UI in permissions --- src/client/views/MainView.tsx | 4 +- .../collectionFreeForm/PropertiesView.scss | 79 ++++++++++++++++++++++ .../collectionFreeForm/PropertiesView.tsx | 76 +++++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index beee9289e..f43eca468 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,7 +8,7 @@ import { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faDesktop, faTrashRestore, faUsers, faWrench, faCog + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faBellSlash } from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -174,7 +174,7 @@ export class MainView extends React.Component { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faMap); + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faMap, faBellSlash); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index f64ef2211..e025c7e87 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -41,6 +41,85 @@ } + .propertiesView-sharing { + border-bottom: 1px solid black; + padding: 8.5px; + + .propertiesView-sharing-title { + font-size: 12.5px; + font-weight: bold; + padding-bottom: 7px; + } + + .propertiesView-sharing-content { + font-size: 10px; + margin-left: 5px; + } + } + + .notify-button { + padding: 2px; + width: 12px; + height: 12px; + background-color: black; + border-radius: 10px; + padding-left: 2px; + padding-right: 2px; + margin-top: 2px; + margin-right: 3px; + + .notify-button-icon { + width: 6px; + height: 6.5px; + margin-left: .5px; + } + + &:hover { + background-color: rgb(158, 158, 158); + } + } + + .propertiesView-sharingTable { + + border: 1px solid black; + padding: 5px; + border-radius: 6px; + width: 170px; + background-color: #ececec; + + .propertiesView-sharingTable-item { + + display: flex; + padding: 3px; + + border-bottom: 0.5px solid grey; + + .propertiesView-sharingTable-item-name { + font-weight: bold; + width: 80px; + overflow-x: hidden; + display: inline-block; + } + + .propertiesView-sharingTable-item-permission { + + .permissions-select { + z-index: 1; + //border: 0.5px solid grey; + //border-radius: 4px; + //outline: none; + border: none; + background-color: inherit; + width: 75px; + } + } + + &:last-child { + border-bottom: none; + } + } + } + .propertiesView-fields { border-bottom: 1px solid black; padding: 8.5px; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 1954a11bf..9af4bd276 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -15,6 +15,8 @@ import { Id } from "../../../../fields/FieldSymbols"; import { Transform } from "../../../util/Transform"; import { PropertiesButtons } from "../../PropertiesButtons"; import { SelectionManager } from "../../../util/SelectionManager"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; interface PropertiesViewProps { @@ -154,6 +156,74 @@ export class PropertiesView extends React.Component { } } + @computed get permissionsSelect() { + return ; + } + + @computed get notifyIcon() { + return
{"Notify user or group of permissions change"}
}> +
+ +
+
; + } + + sharingItem(name: string, notify: boolean, editable: boolean, permission?: string) { + return
+
{name}
+ {notify ? this.notifyIcon : null} +
+ {editable ? this.permissionsSelect : permission} +
+
; + } + + @computed get sharingTable() { + return
+ {this.sharingItem("Me", false, false, "Owner")} + {this.sharingItem("Public", false, true)} + {this.sharingItem("Group 1", true, true)} + {this.sharingItem("Group 2", true, true)} + {/*
+
Me:
+
Owner
+
+
+
Public:
+
+ {this.permissionsSelect} +
+
+
+
Group 1:
+
+
+ {this.permissionsSelect} +
+
+
+
Another group:
+
+
+ {this.permissionsSelect} +
+
*/} +
; + } + render() { if (!this.selectedDoc) { @@ -176,6 +246,12 @@ export class PropertiesView extends React.Component {
+
+
Sharing {"&"} Permissions
+
+ {this.sharingTable} +
+
Fields
{this.expandedField}
-- cgit v1.2.3-70-g09d2 From 649470762c722eea1067e30d2f35aedcf84a369c Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Fri, 24 Jul 2020 13:40:24 -0500 Subject: sally UI changes --- src/client/views/DocumentDecorations.tsx | 5 +-- src/client/views/MainView.tsx | 4 +-- src/client/views/PropertiesButtons.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 13 +++++++ .../collectionFreeForm/PropertiesView.tsx | 42 +++++++++------------- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 12 ++++++- 7 files changed, 47 insertions(+), 33 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index fec4ad9e0..d30adc5a2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,5 +1,5 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause } from '@fortawesome/free-solid-svg-icons'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -44,6 +44,7 @@ library.add(faAngleDoubleRight); library.add(faAngleLeft); library.add(faAngleRight); library.add(faPause); +library.add(faExternalLinkAlt); @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -617,7 +618,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> {"_"}
}
Open Document In Tab
} placement="top">
- {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} + {SelectionManager.SelectedDocuments().length === 1 ? : "..."}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f43eca468..4a80f57b3 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,7 +8,7 @@ import { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faBellSlash + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faBellSlash, faExpandAlt } from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -174,7 +174,7 @@ export class MainView extends React.Component { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faMap, faBellSlash); + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faDesktop, faTrashRestore, faUsers, faWrench, faCog, faMap, faBellSlash, faExpandAlt); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 8488b553f..32a98b072 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -278,7 +278,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get copyButton() { const targetDoc = this.selectedDoc; - return !targetDoc ? (null) :
{"Create a copy"}
}> + return !targetDoc ? (null) :
{"Create an Alias"}
}>
{} diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index e025c7e87..e6bc3f15a 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -79,6 +79,17 @@ } } + .expansion-button-icon { + width: 11px; + height: 11px; + color: black; + margin-left: 27px; + + &:hover { + color: rgb(131, 131, 131); + } + } + .propertiesView-sharingTable { border: 1px solid black; @@ -103,6 +114,8 @@ .propertiesView-sharingTable-item-permission { + display: flex; + .permissions-select { z-index: 1; //border: 0.5px solid grey; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 9af4bd276..e911c7a9f 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -17,6 +17,7 @@ import { PropertiesButtons } from "../../PropertiesButtons"; import { SelectionManager } from "../../../util/SelectionManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; +import SharingManager from "../../../util/SharingManager"; interface PropertiesViewProps { @@ -174,19 +175,32 @@ export class PropertiesView extends React.Component { } @computed get notifyIcon() { - return
{"Notify user or group of permissions change"}
}> + return
{"Notify group of permissions change"}
}>
; } + @computed get expansionIcon() { + return
{"Show more permissions"}
}> +
{ + if (this.selectedDocumentView) { + SharingManager.Instance.open(this.selectedDocumentView); + } + }}> + +
+
; + } + sharingItem(name: string, notify: boolean, editable: boolean, permission?: string) { return
{name}
{notify ? this.notifyIcon : null}
{editable ? this.permissionsSelect : permission} + {permission === "Owner" ? this.expansionIcon : null}
; } @@ -197,30 +211,6 @@ export class PropertiesView extends React.Component { {this.sharingItem("Public", false, true)} {this.sharingItem("Group 1", true, true)} {this.sharingItem("Group 2", true, true)} - {/*
-
Me:
-
Owner
-
-
-
Public:
-
- {this.permissionsSelect} -
-
-
-
Group 1:
-
-
- {this.permissionsSelect} -
-
-
-
Another group:
-
-
- {this.permissionsSelect} -
-
*/}
; } @@ -241,7 +231,7 @@ export class PropertiesView extends React.Component { {this.selectedDoc.title}
-
Settings
+
Document Actions
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index d1c839c3b..8084d96ae 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -182,7 +182,7 @@ export class LinkMenuItem extends React.Component { switch (this.props.destinationDoc.type) { case DocumentType.IMG: destinationIcon = "image"; break; case DocumentType.COMPARISON: destinationIcon = "columns"; break; - case DocumentType.RTF: destinationIcon = "font"; break; + case DocumentType.RTF: destinationIcon = "sticky-note"; break; case DocumentType.COL: destinationIcon = "folder"; break; case DocumentType.WEB: destinationIcon = "globe-asia"; break; case DocumentType.SCREENSHOT: destinationIcon = "photo-video"; break; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 30582a287..ed3a9a4f3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -726,6 +726,14 @@ export class DocumentView extends DocComponent(Docu }); } + @action + onCopy = () => { + const copy = Doc.MakeCopy(this.props.Document, true); + copy.x = NumCast(this.props.Document.x) + NumCast(this.props.Document._width); + copy.y = NumCast(this.props.Document.y) + 30; + this.props.addDocument?.(copy); + } + @action onContextMenu = async (e: React.MouseEvent | Touch): Promise => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 @@ -787,6 +795,8 @@ export class DocumentView extends DocComponent(Docu const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; + moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" }); + moreItems.push({ description: "Create an Alias", event: () => this.onCopy(), icon: "copy" }); moreItems.push({ description: "Download document", icon: "download", event: async () => { Doc.Zip(this.props.Document); @@ -809,7 +819,7 @@ export class DocumentView extends DocComponent(Docu moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); } GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); - //moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" }); + !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); -- cgit v1.2.3-70-g09d2 From ee3d12fbf6306da958ae6546260921f5fee5553b Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Fri, 24 Jul 2020 16:32:45 -0500 Subject: changing menu layout --- src/client/views/MainView.scss | 121 ++++++++++--------- src/client/views/MainView.tsx | 132 +++++++++------------ .../collectionFreeForm/PropertiesView.scss | 8 +- 3 files changed, 123 insertions(+), 138 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 10e7f471b..51548bf58 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -156,45 +156,39 @@ .mainView-menuPanel { max-width: 95px; - background-color: #323232; - padding: 20px; - padding-right: 50px; - + background-color: black; + padding: 15px; + /* padding-right: 50px; */ overflow-y: scroll; - overflow-x: clip; + overflow-x: hidden; .mainView-menuPanel-button { - width: 45px; - height: 45px; - padding: 10px; - pointer-events: all; + width: 60px; + padding: 7.5px; touch-action: none; - border-radius: inherit; background: black; - border-radius: 100%; transform-origin: top left; - margin-bottom: 23px; + margin-bottom: 5px; margin-top: 5px; - margin-right: 25px; + border-radius: 8px; .mainView-menuPanel-button-label { - background: rgb(168, 168, 168); - color: black; - margin-left: -13px; + color: white; + margin-left: -6px; border-radius: 8px; - width: 65px; + width: 55px; position: relative; text-align: center; font-size: 9.5px; - margin-top: 4px; + margin-top: 1px; letter-spacing: normal; padding: 3px; - //margin-bottom: 23px; + background-color: inherit; } .mainView-menuPanel-button-icon { - width: 35px; + width: auto; height: 35px; padding: 5px; } @@ -203,50 +197,55 @@ width: 95% !important; height: 95%; } - } - - .mainView-menuPanel-bottomButton { - width: 50px; - height: 50px; - padding: 10px; - pointer-events: all; - touch-action: none; - border-radius: inherit; - background: #323232; - background-color: #323232; - border-radius: 100%; - transform-origin: top left; - margin-bottom: 20px; - margin-top: 5px; - - margin-right: 25px; - - .mainView-menuPanel-bottomButton-label { - background: #323232; - color: white; - margin-left: -10px; - border-radius: 8px; - width: 65px; - position: absolute; - text-align: center; - font-size: 9.5px; - margin-top: 2px; - letter-spacing: normal; - padding: 3px; - //margin-bottom: 23px; - } - .mainView-menuPanel-bottomButton-icon { - width: 50px; - height: 50px; - color: white; - } - - svg { - width: 95% !important; - height: 95%; + &:hover { + background: rgb(61, 61, 61); + cursor: pointer; } } + + // .mainView-menuPanel-bottomButton { + // width: 45px; + // height: 45px; + // padding: 10px; + // pointer-events: all; + // touch-action: none; + // //border-radius: inherit; + // background: black; + // background-color: black; + // //border-radius: 100%; + // transform-origin: top left; + // margin-bottom: 20px; + // margin-top: 5px; + + // margin-right: 25px; + + // .mainView-menuPanel-bottomButton-label { + // background: black; + // color: white; + // margin-left: -10px; + // border-radius: 8px; + // width: 65px; + // position: absolute; + // text-align: center; + // font-size: 9.5px; + // margin-top: 2px; + // letter-spacing: normal; + // padding: 3px; + // //margin-bottom: 23px; + // } + + // .mainView-menuPanel-bottomButton-icon { + // width: 50px; + // height: 50px; + // color: white; + // } + + // svg { + // width: 95% !important; + // height: 95%; + // } + // } } .mainView-searchPanel { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4a80f57b3..c08717947 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -71,6 +71,7 @@ import { PropertiesView } from './collections/collectionFreeForm/PropertiesView' import { SelectionManager } from '../util/SelectionManager'; import { PrefetchProxy } from '../../fields/Proxy'; import { DragManager } from '../util/DragManager'; +import { discovery_v1, dialogflow_v2beta1 } from 'googleapis'; @observer export class MainView extends React.Component { @@ -84,7 +85,7 @@ export class MainView extends React.Component { @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; - @observable public flyoutWidth: number = 275; + @observable public flyoutWidth: number = 0; private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } @computed private get userDoc() { return Doc.UserDoc(); } @@ -377,8 +378,11 @@ export class MainView extends React.Component { } _canClick = false; + + @action onPointerDown = (e: React.PointerEvent) => { if (this._flyoutTranslate) { + this.panelContent = "none"; this._canClick = true; this._flyoutSizeOnDown = e.clientX; document.removeEventListener("pointermove", this.onPointerMove); @@ -494,76 +498,59 @@ export class MainView extends React.Component { @computed get menuPanel() { return
- +
this.selectPanel("workspace")}> + +
Workspace
+
- +
this.selectPanel("catalog")}> + +
Catalog
+
- +
this.selectPanel("deleted")}> + +
Recently Closed
+
- +
- +
- + +
Tools
+ - + */} - +
this.selectPanel("help")} > + +
Help
+
- + onClick={() => SettingsManager.Instance.open()}> + +
Settings
+ ; } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index e6bc3f15a..b40a08086 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -5,7 +5,7 @@ font-family: "Noto Sans"; cursor: auto; - //overflow-y: scroll; + overflow-y: scroll; .propertiesView-title { background-color: rgb(159, 159, 159); @@ -76,6 +76,7 @@ &:hover { background-color: rgb(158, 158, 158); + cursor: pointer; } } @@ -87,6 +88,7 @@ &:hover { color: rgb(131, 131, 131); + cursor: pointer; } } @@ -124,6 +126,10 @@ border: none; background-color: inherit; width: 75px; + + &:hover { + cursor: pointer; + } } } -- cgit v1.2.3-70-g09d2 From 229e8ba914943da0d11dff00d14a8adae950d853 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 25 Jul 2020 10:27:27 -0500 Subject: exit button for properties --- src/client/views/MainView.tsx | 1 + .../collectionFreeForm/PropertiesView.scss | 23 ++++++++++++++++++++-- .../collectionFreeForm/PropertiesView.tsx | 6 +++++- src/client/views/nodes/DocumentLinksButton.tsx | 5 ++++- 4 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c08717947..734b45dd0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -621,6 +621,7 @@ export class MainView extends React.Component { height={this._panelHeight} renderDepth={1} ScreenToLocalTransform={Transform.Identity} + onDown={this.onDown} /> ; } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index b40a08086..7b8f116b7 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -10,10 +10,29 @@ .propertiesView-title { background-color: rgb(159, 159, 159); text-align: center; - font-size: 18px; - font-weight: bold; padding-top: 12px; padding-bottom: 12px; + display: flex; + + .propertiesView-title-name { + font-size: 18px; + font-weight: bold; + padding-left: 45px; + } + + .propertiesView-title-icon { + width: 20px; + height: 20px; + padding-left: 32px; + margin-top: -5px; + + &:hover { + color: grey; + cursor: pointer; + } + + } + } .propertiesView-name { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index e911c7a9f..a75ad13e5 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -25,6 +25,7 @@ interface PropertiesViewProps { height: number; renderDepth: number; ScreenToLocalTransform: () => Transform; + onDown: (event: any) => void; } @observer @@ -225,7 +226,10 @@ export class PropertiesView extends React.Component { return
- Properties +
Properties
+
+ +
{this.selectedDoc.title} diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index b17accfd6..fa7fe64f3 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -194,8 +194,11 @@ export class DocumentLinksButton extends React.Component + {/* {this.props.InMenu ? this.props.StartLink ? : + : links.length} */} + {this.props.InMenu ? this.props.StartLink ? : - : links.length} + link : links.length}
{DocumentLinksButton.StartLink && this.props.InMenu && !!!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
Date: Sun, 26 Jul 2020 16:19:23 -0500 Subject: overflow, open in tab bug fixes --- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.scss | 1 + src/client/views/MainView.tsx | 9 +++++---- src/client/views/PropertiesButtons.scss | 1 + src/client/views/PropertiesButtons.tsx | 23 ++++++++++++++++++++++ .../collectionFreeForm/PropertiesView.scss | 5 +++-- src/client/views/nodes/DocumentView.tsx | 2 ++ 7 files changed, 36 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 87222e942..c889557ba 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -619,7 +619,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{"_"}
} -
Open Document In Tab
} placement="top">
+
Open Document In Tab
} placement="top">
{SelectionManager.SelectedDocuments().length === 1 ? : "..."}
//
; // } @@ -519,10 +520,10 @@ export class MainView extends React.Component {
this.selectPanel("deleted")}> -
Recently Closed
+ style={{ color: this.panelContent === "deleted" ? "black" : "white" }}> Recently Used
{ // onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); } + @computed + get googlePhotosButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Export to Google Photos"}
}> +
{ + if (this.selectedDocumentView) { + GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log) + } + }}> + {} +
+
; + } + render() { if (!this.selectedDoc) return (null); @@ -428,6 +447,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isText = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField; const considerPull = isText && this.considerGoogleDocsPull; const considerPush = isText && this.considerGoogleDocsPush; + const isImage = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof ImageField; return
{this.templateButton} @@ -464,6 +484,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{this.considerGoogleDocsPull}
+
+ {this.googlePhotosButton} +
; } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 7b8f116b7..99db0022e 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -5,7 +5,8 @@ font-family: "Noto Sans"; cursor: auto; - overflow-y: scroll; + overflow-x: visible; + overflow-y: visible; .propertiesView-title { background-color: rgb(159, 159, 159); @@ -23,7 +24,7 @@ .propertiesView-title-icon { width: 20px; height: 20px; - padding-left: 32px; + padding-left: 38px; margin-top: -5px; &:hover { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0cfb12f95..6aceec90e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -101,6 +101,7 @@ export interface DocumentViewProps { radialMenu?: String[]; display?: string; relative?: boolean; + scriptContext?: any; } @observer @@ -323,6 +324,7 @@ export class DocumentView extends DocComponent(Docu const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, + scriptContext: this.props.scriptContext, thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log); if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) { -- cgit v1.2.3-70-g09d2 From e93058822d2b997b48fb711f49bb14357a61143c Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sun, 26 Jul 2020 22:54:10 -0500 Subject: layout check in properties, rich text menu to top --- src/client/views/MainView.tsx | 4 +- src/client/views/PropertiesButtons.scss | 9 ++ src/client/views/PropertiesButtons.tsx | 99 ++++++++++++++-------- src/client/views/collections/CollectionMenu.tsx | 44 +++++++--- .../collectionFreeForm/PropertiesView.scss | 21 ++++- .../collectionFreeForm/PropertiesView.tsx | 31 ++++++- src/client/views/nodes/DocumentView.tsx | 13 +++ .../views/nodes/formattedText/RichTextMenu.tsx | 47 +++++----- 8 files changed, 192 insertions(+), 76 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4f85ef333..153df7d9d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -628,7 +628,7 @@ export class MainView extends React.Component { } @computed get mainContent() { - const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); + const n = (RichTextMenu.Instance?.Pinned ? 0 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`; const rightFlyout = this.selectedDocumentView ? this._propertiesWidth - 1 : this.propertiesWidth() > 10 ? 151.5 : 0; @@ -799,7 +799,7 @@ export class MainView extends React.Component { {/* {this.search} */} - + {/* */} {LinkDescriptionPopup.descriptionPopup ? : null} {DocumentLinksButton.EditLink ? : (null)} {LinkDocPreview.LinkInfo ? { @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; } + @observable public onClick = this.selectedDoc?.onClickBehavior ? this.selectedDoc?.onClickBehavior : "nothing"; + public startPullOutcome = action((success: boolean) => { if (!this._pullAnimating) { this._pullAnimating = true; @@ -373,54 +376,80 @@ export class PropertiesButtons extends React.Component<{}, {}> { } } + @action + handleOptionChange = (e: any) => { + const value = e.target.value; + this.onClick = e.target.value; + if (value === "nothing") { + this.selectedDocumentView?.noOnClick; + } else if (value === "enterPortal") { + //this.selectedDocumentView?.noOnClick; + this.selectedDocumentView?.makeIntoPortal; + } else if (value === "toggleDetail") { + //this.selectedDocumentView?.noOnClick; + this.selectedDocumentView?.toggleDetail; + } else if (value === "linkInPlace") { + //this.selectedDocumentView?.noOnClick; + this.selectedDocumentView?.toggleFollowInPlace; + } else if (value === "linkOnRight") { + //this.selectedDocumentView?.noOnClick; + this.selectedDocumentView?.toggleFollowOnRight; + } + } + + @undoBatch @action + editOnClickScript = () => { + if (this.selectedDoc) { + DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick"); + } + } + @computed get onClickFlyout() { return
+ + Select Document +
+ + Enter Portal +
+ + Toggle Detail +
+ + Follow Link +
+ + Open Link on Right +
-
- -
-
; - - // onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); - // onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" }); - // onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); - // onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); - // onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" }); - // onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); - // onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); + +
Edit onClick Script
+
; } @computed @@ -452,9 +481,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{this.templateButton}
-
+ {/*
{this.metadataButton} -
+
*/}
{this.pinButton}
@@ -470,14 +499,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{this.deleteButton}
+
+ {this.onClickButton} +
{this.sharingButton}
-
- {this.onClickButton} -
{this.considerGoogleDocsPush}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 24be69050..5a7c98f46 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -25,6 +25,8 @@ import { SelectionManager } from "../../util/SelectionManager"; import { DocumentView } from "../nodes/DocumentView"; import { ColorState } from "react-color"; import { ObjectField } from "../../../fields/ObjectField"; +import RichTextMenu from "../nodes/formattedText/RichTextMenu"; +import { RichTextField } from "../../../fields/RichTextField"; @observer export default class CollectionMenu extends AntimodeMenu { @@ -300,6 +302,20 @@ export class CollectionFreeFormViewChrome extends React.Component { @@ -469,25 +485,27 @@ export class CollectionFreeFormViewChrome extends React.Component -
+ {!!!this.isText ?
-
-
+
: null} + {!!!this.isText ?
-
-
: null} + {!!!this.isText ?
this.Document.editing = !this.Document.editing)} > {NumCast(this.Document.currentFrame)} -
-
+
: null} + {!!!this.isText ?
-
+
: null} + + {!!!this.isText ? this.widthPicker : null} + {!!!this.isText ? this.colorPicker : null} + {!!!this.isText ? this.fillPicker : null} + {!!!this.isText ? this.drawButtons : null} + {!!!this.isText ? this.formatPane : null} + {this.isText ? : null} - {this.widthPicker} - {this.colorPicker} - {this.fillPicker} - {this.drawButtons} - {this.formatPane}
; } } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 99db0022e..8cc591875 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -164,9 +164,26 @@ padding: 8.5px; .propertiesView-fields-title { - font-size: 12.5px; - font-weight: bold; padding-bottom: 7px; + display: flex; + + .propertiesView-fields-title-name { + font-size: 12.5px; + font-weight: bold; + } + + .propertiesView-fields-title-checkbox { + float: right; + height: 20px; + margin-top: -13px; + margin-left: 115px; + + .propertiesView-fields-title-checkbox-text { + font-size: 7px; + margin-top: -10px; + margin-left: 6px; + } + } } .propertiesView-fields-content { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index a75ad13e5..3b8d782aa 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -16,7 +16,7 @@ import { Transform } from "../../../util/Transform"; import { PropertiesButtons } from "../../PropertiesButtons"; import { SelectionManager } from "../../../util/SelectionManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from "@material-ui/core"; +import { Tooltip, Checkbox } from "@material-ui/core"; import SharingManager from "../../../util/SharingManager"; @@ -41,6 +41,8 @@ export class PropertiesView extends React.Component { @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; } + @observable layoutFields: boolean = false; + @action rtfWidth = () => { if (this.selectedDoc) { @@ -88,9 +90,9 @@ export class PropertiesView extends React.Component { } @computed get expandedField() { - if (this.dataDoc) { + if (this.dataDoc && this.selectedDoc) { const ids: { [key: string]: string } = {}; - const doc = this.dataDoc; + const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); const rows: JSX.Element[] = []; @@ -215,6 +217,19 @@ export class PropertiesView extends React.Component {
; } + @computed get fieldsCheckbox() { + return ; + } + + @action + toggleCheckbox = () => { + this.layoutFields = !this.layoutFields; + } + render() { if (!this.selectedDoc) { @@ -247,7 +262,15 @@ export class PropertiesView extends React.Component {
-
Fields
+
+
+ Fields +
+
+ {this.fieldsCheckbox} +
Layout
+
+
{this.expandedField}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6aceec90e..884104f62 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -622,6 +622,18 @@ export class DocumentView extends DocComponent(Docu } } + + @undoBatch + noOnClick = (): void => { + this.Document.ignoreClick = false; + this.Document.isLinkButton = false; + } + + @undoBatch + toggleDetail = (): void => { + this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`); + } + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { @@ -673,6 +685,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action makeIntoPortal = async () => { + console.log("into portal"); const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document); if (!portalLink) { const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" }); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 47a4911b8..98c786566 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -77,7 +77,8 @@ export default class RichTextMenu extends AntimodeMenu { super(props); RichTextMenu.Instance = this; this._canFade = false; - this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + this.Pinned = true; this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, @@ -918,28 +919,34 @@ export default class RichTextMenu extends AntimodeMenu { render() { TraceMobx(); const row1 =
{[ - !this.collapsed ? this.getDragger() : (null), - !this.Pinned ? (null) :
{[ - this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), - this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), - this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), - this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), - this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), - this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), -
- ]}
, + //!this.collapsed ? this.getDragger() : (null), + // !this.Pinned ? (null) :
{[ + // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + //
+ // ]}
, + this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), this.createColorButton(), this.createHighlighterButton(), - this.createLinkButton(), - this.createBrushButton(), + //this.createLinkButton(), + //this.createBrushButton(),
, this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight), this.createButton("indent", "Inset More", undefined, this.insetParagraph), this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph), - this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph), - this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph), + //this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph), + //this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph), ]}
; const row2 =
@@ -950,21 +957,21 @@ export default class RichTextMenu extends AntimodeMenu { this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => this.activeFontFamily = val)),
, this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", action((val: string) => this.activeListType = val)), - this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), + //this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
,]}
-
- {/*
+ {/*
+ {
-
*/} +
} -
+
*/}
; return ( -- cgit v1.2.3-70-g09d2 From d4f416ef5715c8f03132244831094f8485c5e6bf Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Mon, 27 Jul 2020 16:57:51 -0500 Subject: onClick functionality and pin change --- src/client/views/PropertiesButtons.tsx | 44 +++++++++++----------- .../collectionFreeForm/PropertiesView.scss | 10 +++++ .../collectionFreeForm/PropertiesView.tsx | 13 +++++++ 3 files changed, 45 insertions(+), 22 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 1f13d0b07..336cd6ff9 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -206,7 +206,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}>
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> @@ -261,10 +261,10 @@ export class PropertiesButtons extends React.Component<{}, {}> { templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); return !docView ? (null) :
Tap: Customize layout. Drag: Create alias
}> -
+
this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} content={!this._aliasDown ? (null) : v).map(v => v as DocumentView)} templates={templates} />}> -
+
{}
@@ -284,7 +284,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { get copyButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Create an Alias"}
}> -
{}
@@ -302,7 +302,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { return !targetDoc ? (null) :
{this.selectedDoc?.lockedPosition ? "Unlock Position" : "Lock Position"}
}> -
{} @@ -315,13 +315,13 @@ export class PropertiesButtons extends React.Component<{}, {}> { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Download Document"}
}> -
{ if (this.selectedDocumentView?.props.Document) { Doc.Zip(this.selectedDocumentView?.props.Document); } }}> - {}
; @@ -332,11 +332,11 @@ export class PropertiesButtons extends React.Component<{}, {}> { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Delete Document"}
}> -
{ this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document); }}> - {}
; @@ -347,13 +347,13 @@ export class PropertiesButtons extends React.Component<{}, {}> { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Share Document"}
}> -
{ if (this.selectedDocumentView) { SharingManager.Instance.open(this.selectedDocumentView); } }}> - {}
; @@ -379,21 +379,21 @@ export class PropertiesButtons extends React.Component<{}, {}> { @action handleOptionChange = (e: any) => { const value = e.target.value; - this.selectedDoc?.onClickBehavior = e.target.value; + this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value); if (value === "nothing") { - this.selectedDocumentView?.noOnClick; + this.selectedDocumentView?.noOnClick(); } else if (value === "enterPortal") { - //this.selectedDocumentView?.noOnClick; - this.selectedDocumentView?.makeIntoPortal; + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.makeIntoPortal(); } else if (value === "toggleDetail") { - //this.selectedDocumentView?.noOnClick; - this.selectedDocumentView?.toggleDetail; + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleDetail(); } else if (value === "linkInPlace") { - //this.selectedDocumentView?.noOnClick; - this.selectedDocumentView?.toggleFollowInPlace; + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleFollowInPlace(); } else if (value === "linkOnRight") { - //this.selectedDocumentView?.noOnClick; - this.selectedDocumentView?.toggleFollowOnRight; + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleFollowOnRight(); } } @@ -457,7 +457,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Export to Google Photos"}
}> -
{ if (this.selectedDocumentView) { GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log) diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 8cc591875..0701f3b96 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -196,6 +196,16 @@ } } + .field { + display: flex; + font-size: 7px; + background-color: #e8e8e8; + padding-right: 3px; + border: 0.5px solid grey; + border-radius: 5px; + padding-left: 3px; + } + .propertiesView-layout { padding: 8.5px; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 3b8d782aa..0f59e2f79 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -113,6 +113,19 @@ export class PropertiesView extends React.Component { {contentElement}
); } + + rows.push(
+ ""} + SetValue={(value: string) => { + value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true); + return true; + }} /> +
); return rows; } } -- cgit v1.2.3-70-g09d2 From e7af5195af8a7e0cfe1bcd40d49859474568a739 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Tue, 28 Jul 2020 13:41:59 -0500 Subject: novice mode for fields and tagging and view only fields --- .../collectionFreeForm/PropertiesView.scss | 12 +++ .../collectionFreeForm/PropertiesView.tsx | 119 +++++++++++++++++---- 2 files changed, 108 insertions(+), 23 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 0701f3b96..856f8e7f2 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -170,6 +170,8 @@ .propertiesView-fields-title-name { font-size: 12.5px; font-weight: bold; + white-space: nowrap; + width: 35px; } .propertiesView-fields-title-checkbox { @@ -206,6 +208,16 @@ padding-left: 3px; } + .uneditable-field { + display: flex; + overflow-y: visible; + margin-bottom: 2px; + + &:hover { + cursor: auto; + } + } + .propertiesView-layout { padding: 8.5px; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 0f59e2f79..be024b772 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -7,7 +7,7 @@ import { DocumentView } from "../../nodes/DocumentView"; import { ComputedField } from "../../../../fields/ScriptField"; import { EditableView } from "../../EditableView"; import { KeyValueBox } from "../../nodes/KeyValueBox"; -import { Cast, StrCast, NumCast } from "../../../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../../../fields/Types"; import { listSpec } from "../../../../fields/Schema"; import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView"; import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils"; @@ -94,42 +94,113 @@ export class PropertiesView extends React.Component { const ids: { [key: string]: string } = {}; const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; 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()) { const contents = doc[key]; - let contentElement: (JSX.Element | null)[] | JSX.Element = []; - contentElement = + {key} +   +
); + } else { + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + />; + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + rows.push(
+ Field.toKeyValueString(doc, key)} - SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} - />; + GetValue={() => ""} + SetValue={this.setKeyValue} /> +
); + return rows; + } + } - rows.push(
- {key + ":"} + @computed get noviceFields() { + if (this.dataDoc && this.selectedDoc) { + const ids: { [key: string]: string } = {}; + const doc = this.dataDoc; + 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 (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { + const contents = doc[key]; + if (contents === "UNDEFINED") { + rows.push(
+ {key}   - {contentElement}
); - } + } else { + const value = Field.toString(contents as Field); + if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { + rows.push(
+ {key + ": "} +
{value}
+
); + } else { + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + />; - rows.push(
+ rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + } + } + rows.push(
""} - SetValue={(value: string) => { - value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true); - return true; - }} /> + SetValue={this.setKeyValue} />
); return rows; } } + + setKeyValue = (value: string) => { + if (this.selectedDoc && this.dataDoc) { + const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; + if (value.indexOf(":") !== -1) { + const newVal = value[0].toUpperCase() + value.substring(1, value.length); + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } else if (value[0] === "#") { + const newVal = value + ":'UNDEFINED'"; + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } + } + return false; + } + @computed get layoutPreview() { if (this.selectedDoc) { const layoutDoc = Doc.Layout(this.selectedDoc); @@ -252,6 +323,8 @@ export class PropertiesView extends React.Component {
; } + const novice = Doc.UserDoc().noviceMode; + return
Properties
@@ -277,14 +350,14 @@ export class PropertiesView extends React.Component {
- Fields + Fields {"&"} Tags
-
+ {!novice ?
{this.fieldsCheckbox}
Layout
-
+
: null}
-
{this.expandedField}
+
{novice ? this.noviceFields : this.expandedField}
Layout
-- cgit v1.2.3-70-g09d2 From f19ee5abb19ece29a0ea7a857078125307f15908 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Tue, 28 Jul 2020 16:36:15 -0500 Subject: collapsing sections --- src/client/views/DocumentDecorations.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 45 +++++++++++++ .../collectionFreeForm/PropertiesView.tsx | 73 ++++++++++++++++++---- 3 files changed, 107 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c889557ba..0a0136d87 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -577,7 +577,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> : <> {minimal ? (null) :
Show context menu
} placement="top">
- +
}
{`${this.selectionTitle}`} diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 856f8e7f2..1fda8e5a2 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -40,6 +40,10 @@ border-bottom: 1px solid black; padding: 8.5px; font-size: 12.5px; + + &:hover { + cursor: pointer; + } } .propertiesView-settings { @@ -52,6 +56,17 @@ font-weight: bold; font-size: 12.5px; padding-bottom: 7px; + display: flex; + width: 200px; + + .propertiesView-settings-title-icon { + margin-left: 2px; + margin-right: 7px; + + &:hover { + cursor: pointer; + } + } } .propertiesView-settings-content { @@ -69,6 +84,16 @@ font-size: 12.5px; font-weight: bold; padding-bottom: 7px; + display: flex; + + .propertiesView-sharing-title-icon { + margin-left: 2px; + margin-right: 7px; + + &:hover { + cursor: pointer; + } + } } .propertiesView-sharing-content { @@ -172,6 +197,16 @@ font-weight: bold; white-space: nowrap; width: 35px; + display: flex; + } + + .propertiesView-fields-title-icon { + margin-left: 2px; + margin-right: 7px; + + &:hover { + cursor: pointer; + } } .propertiesView-fields-title-checkbox { @@ -225,6 +260,16 @@ font-weight: bold; font-size: 12.5px; padding-bottom: 7px; + display: flex; + + .propertiesView-layout-title-icon { + margin-left: 2px; + margin-right: 7px; + + &:hover { + cursor: pointer; + } + } } .propertiesView-layout-content { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index be024b772..91d407eea 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { observer } from "mobx-react"; import "./PropertiesView.scss"; -import { observable, action, computed } from "mobx"; +import { observable, action, computed, runInAction } from "mobx"; import { Doc, Field, DocListCast, WidthSym, HeightSym } from "../../../../fields/Doc"; import { DocumentView } from "../../nodes/DocumentView"; import { ComputedField } from "../../../../fields/ScriptField"; @@ -43,6 +43,11 @@ export class PropertiesView extends React.Component { @observable layoutFields: boolean = false; + @observable openActions: boolean = true; + @observable openSharing: boolean = true; + @observable openFields: boolean = true; + @observable openLayout: boolean = true; + @action rtfWidth = () => { if (this.selectedDoc) { @@ -314,6 +319,25 @@ export class PropertiesView extends React.Component { this.layoutFields = !this.layoutFields; } + @computed get editableTitle() { + return StrCast(this.selectedDoc?.title)} + SetValue={this.setTitle} />; + } + + setTitle = (value: string) => { + if (this.dataDoc) { + this.selectedDoc && (this.selectedDoc.title = value); + KeyValueBox.SetField(this.dataDoc, "title", value, true); + return true; + } + return false; + } + render() { if (!this.selectedDoc) { @@ -333,35 +357,60 @@ export class PropertiesView extends React.Component {
- {this.selectedDoc.title} + {this.editableTitle}
-
Document Actions
-
- +
+
runInAction(() => { this.openActions = !this.openActions; })}> + +
+ Document Actions
+ {this.openActions ?
+ +
: null}
-
Sharing {"&"} Permissions
-
- {this.sharingTable} +
+
runInAction(() => { this.openSharing = !this.openSharing; })}> + +
+ Sharing {"&"} Permissions
+ {this.openSharing ?
+ {this.sharingTable} +
: null}
+
runInAction(() => { this.openFields = !this.openFields; })}> + +
Fields {"&"} Tags
- {!novice ?
+ {!novice && this.openFields ?
{this.fieldsCheckbox}
Layout
: null}
-
{novice ? this.noviceFields : this.expandedField}
+ {this.openFields ? +
+ {novice ? this.noviceFields : this.expandedField} +
: null}
-
Layout
-
{this.layoutPreview}
+
+
runInAction(() => { this.openLayout = !this.openLayout; })}> + +
+ Layout +
+ {this.openLayout ?
{this.layoutPreview}
: null}
; } -- cgit v1.2.3-70-g09d2 From 155407d902bac7a82a83ba2f60f05a6fddb0d6e6 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Tue, 28 Jul 2020 20:59:45 -0500 Subject: new UI for collapsing --- src/client/views/PropertiesButtons.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 66 +++++++++++++++------- .../collectionFreeForm/PropertiesView.tsx | 32 +++++------ 3 files changed, 64 insertions(+), 36 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 95d0df14e..eecf670e6 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -206,7 +206,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}>
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 1fda8e5a2..cb4b7375b 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -48,20 +48,26 @@ .propertiesView-settings { border-bottom: 1px solid black; - padding: 8.5px; + //padding: 8.5px; font-size: 12.5px; font-weight: bold; .propertiesView-settings-title { font-weight: bold; font-size: 12.5px; - padding-bottom: 7px; + padding: 4px; display: flex; width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); .propertiesView-settings-title-icon { + float: right; + right: 0; + position: absolute; margin-left: 2px; - margin-right: 7px; + margin-right: 9px; &:hover { cursor: pointer; @@ -70,25 +76,33 @@ } .propertiesView-settings-content { - margin-left: 5px; + margin-left: 12px; padding-bottom: 10px; + padding-top: 8px; } } .propertiesView-sharing { border-bottom: 1px solid black; - padding: 8.5px; + //padding: 8.5px; .propertiesView-sharing-title { - font-size: 12.5px; font-weight: bold; - padding-bottom: 7px; + font-size: 12.5px; + padding: 4px; display: flex; + width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); .propertiesView-sharing-title-icon { + float: right; + right: 0; + position: absolute; margin-left: 2px; - margin-right: 7px; + margin-right: 9px; &:hover { cursor: pointer; @@ -98,6 +112,7 @@ .propertiesView-sharing-content { font-size: 10px; + padding: 10px; margin-left: 5px; } } @@ -165,9 +180,6 @@ .permissions-select { z-index: 1; - //border: 0.5px solid grey; - //border-radius: 4px; - //outline: none; border: none; background-color: inherit; width: 75px; @@ -186,11 +198,17 @@ .propertiesView-fields { border-bottom: 1px solid black; - padding: 8.5px; + //padding: 8.5px; .propertiesView-fields-title { - padding-bottom: 7px; + font-weight: bold; + font-size: 12.5px; + padding: 4px; display: flex; + width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); .propertiesView-fields-title-name { font-size: 12.5px; @@ -201,8 +219,11 @@ } .propertiesView-fields-title-icon { + float: right; + right: 0; + position: absolute; margin-left: 2px; - margin-right: 7px; + margin-right: 9px; &:hover { cursor: pointer; @@ -225,7 +246,8 @@ .propertiesView-fields-content { font-size: 10px; - margin-left: 5px; + margin-left: 2px; + padding: 10px; &:hover { cursor: pointer; @@ -254,17 +276,23 @@ } .propertiesView-layout { - padding: 8.5px; .propertiesView-layout-title { font-weight: bold; font-size: 12.5px; - padding-bottom: 7px; + padding: 4px; display: flex; + width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); .propertiesView-layout-title-icon { + float: right; + right: 0; + position: absolute; margin-left: 2px; - margin-right: 7px; + margin-right: 9px; &:hover { cursor: pointer; @@ -273,8 +301,8 @@ } .propertiesView-layout-content { - margin-left: 5px; overflow: hidden; + padding: 10px; } } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 91d407eea..d5317efcb 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -360,55 +360,55 @@ export class PropertiesView extends React.Component { {this.editableTitle}
-
+
+ Document Actions
runInAction(() => { this.openActions = !this.openActions; })}> - +
- Document Actions
{this.openActions ?
: null}
-
+
+ Sharing {"&"} Permissions
runInAction(() => { this.openSharing = !this.openSharing; })}> - +
- Sharing {"&"} Permissions
{this.openSharing ?
{this.sharingTable}
: null}
-
+
+ Fields {"&"} Tags
runInAction(() => { this.openFields = !this.openFields; })}> - +
- Fields {"&"} Tags
- {!novice && this.openFields ?
- {this.fieldsCheckbox} -
Layout
-
: null}
+ {!novice && this.openFields ?
+ {this.fieldsCheckbox} +
Layout
+
: null} {this.openFields ?
{novice ? this.noviceFields : this.expandedField}
: null}
-
+
+ Layout
runInAction(() => { this.openLayout = !this.openLayout; })}> - +
- Layout
{this.openLayout ?
{this.layoutPreview}
: null}
-- cgit v1.2.3-70-g09d2 From 611f848ff4f7b958dc913d33dd1b760ca9474c44 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 30 Jul 2020 18:33:19 +0530 Subject: permissions from the properties panel should work now --- src/client/util/GroupManager.tsx | 2 +- src/client/util/SharingManager.tsx | 29 +++++++---- .../collectionFreeForm/PropertiesView.scss | 2 + .../collectionFreeForm/PropertiesView.tsx | 56 ++++++++++++++-------- src/fields/util.ts | 12 ++++- 5 files changed, 67 insertions(+), 34 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 72fba5c1b..dee0f105f 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -101,7 +101,7 @@ export default class GroupManager extends React.Component<{}> { */ @action open = () => { - SelectionManager.DeselectAll(); + // SelectionManager.DeselectAll(); this.isOpen = true; this.populateUsers(); this.populateGroups(); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 0d8b33fbe..9222f10c5 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -62,14 +62,12 @@ export default class SharingManager extends React.Component<{}> { @observable private groupSort: "ascending" | "descending" | "none" = "none"; private shareDocumentButtonRef: React.RefObject = React.createRef(); - - // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; // } public open = (target: DocumentView) => { - SelectionManager.DeselectAll(); + // SelectionManager.DeselectAll(); this.populateUsers().then(action(() => { this.targetDocView = target; this.targetDoc = target.props.Document; @@ -82,7 +80,7 @@ export default class SharingManager extends React.Component<{}> { public close = action(() => { this.isOpen = false; - this.users = []; + // this.users = []; this.selectedUsers = null; setTimeout(action(() => { @@ -97,7 +95,12 @@ export default class SharingManager extends React.Component<{}> { SharingManager.Instance = this; } + componentDidMount() { + this.populateUsers(); + } + populateUsers = async () => { + runInAction(() => this.users = []); const userList = await RequestPromise.get(Utils.prepend("/getUsers")); const raw = JSON.parse(userList) as User[]; const evaluating = raw.map(async user => { @@ -117,17 +120,17 @@ export default class SharingManager extends React.Component<{}> { return Promise.all(evaluating); } - setInternalGroupSharing = (group: Doc, permission: string) => { + setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - const target = this.targetDoc!; + const target = targetDoc || this.targetDoc!; const ACL = `ACL-${StrCast(group.groupName)}`; // fix this - not needed (here and setinternalsharing and removegroup) // target[ACL] = permission; // Doc.GetProto(target)[ACL] = permission; - distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!); + distributeAcls(ACL, permission as SharingPermissions, target); group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List).push(target)) : group.docsShared = new List([target]); @@ -182,14 +185,20 @@ export default class SharingManager extends React.Component<{}> { } } + shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => { + const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith)); + if (user) this.setInternalSharing(user, permission, target); + else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target); + } + // @action - setInternalSharing = (recipient: ValidatedUser, permission: string) => { + setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { const { user, notificationDoc } = recipient; - const target = this.targetDoc!; + const target = targetDoc || this.targetDoc!; const key = user.email.replace('.', '_'); const ACL = `ACL-${key}`; - distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!); + distributeAcls(ACL, permission as SharingPermissions, target); if (permission !== SharingPermissions.None) { DocListCastAsync(notificationDoc[storage]).then(resolved => { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index cb4b7375b..4ccc0950b 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -159,6 +159,8 @@ border-radius: 6px; width: 170px; background-color: #ececec; + max-height: 130px; + overflow-y: scroll; .propertiesView-sharingTable-item { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index d5317efcb..b33b37f4b 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { observer } from "mobx-react"; import "./PropertiesView.scss"; import { observable, action, computed, runInAction } from "mobx"; -import { Doc, Field, DocListCast, WidthSym, HeightSym } from "../../../../fields/Doc"; +import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin } from "../../../../fields/Doc"; import { DocumentView } from "../../nodes/DocumentView"; import { ComputedField } from "../../../../fields/ScriptField"; import { EditableView } from "../../EditableView"; @@ -18,6 +18,7 @@ import { SelectionManager } from "../../../util/SelectionManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip, Checkbox } from "@material-ui/core"; import SharingManager from "../../../util/SharingManager"; +import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util"; interface PropertiesViewProps { @@ -249,20 +250,15 @@ export class PropertiesView extends React.Component { } } - @computed get permissionsSelect() { - return SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!)}> + {Object.values(SharingPermissions).map(permission => { + return ( + ); + })} ; } @@ -286,23 +282,41 @@ export class PropertiesView extends React.Component { ; } - sharingItem(name: string, notify: boolean, editable: boolean, permission?: string) { + sharingItem(name: string, notify: boolean, effectiveAcl: symbol, permission?: string) { return
{name}
{notify ? this.notifyIcon : null}
- {editable ? this.permissionsSelect : permission} + {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission} {permission === "Owner" ? this.expansionIcon : null}
; } @computed get sharingTable() { + const AclMap = new Map([ + [AclPrivate, SharingPermissions.None], + [AclReadonly, SharingPermissions.View], + [AclAddonly, SharingPermissions.Add], + [AclEdit, SharingPermissions.Edit], + [AclAdmin, SharingPermissions.Admin] + ]); + + const effectiveAcl = GetEffectiveAcl(this.selectedDoc!); + const tableEntries = []; + + if (this.selectedDoc![AclSym]) { + for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) { + const name = key.substring(4).replace("_", "."); + if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, false, effectiveAcl, AclMap.get(value)!)); + } + } + + tableEntries.unshift(this.sharingItem("Me", false, effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]))); + if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), false, effectiveAcl, "Owner")); + return
- {this.sharingItem("Me", false, false, "Owner")} - {this.sharingItem("Public", false, true)} - {this.sharingItem("Group 1", true, true)} - {this.sharingItem("Group 2", true, true)} + {tableEntries}
; } diff --git a/src/fields/util.ts b/src/fields/util.ts index a62795e64..267da70b4 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -183,12 +183,18 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Admin", 4] ]); + let changed = false; + const dataDoc = target[DataSym]; - if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) target[key] = acl; + if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) { + target[key] = acl; + changed = true; + } if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { dataDoc[key] = acl; + changed = true; DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { @@ -214,6 +220,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc } }); } + + changed && fetchProto(target); } const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", -- cgit v1.2.3-70-g09d2 From abb2140eb098b29e7b42b54d8e3173149c8a1123 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Thu, 30 Jul 2020 09:50:33 -0500 Subject: framework for ink in properties --- .../collectionFreeForm/FormatShapePane.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 68 ++++++++++++++++++++++ .../collectionFreeForm/PropertiesView.tsx | 40 +++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx index ddc282e57..eb6097acf 100644 --- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx +++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx @@ -395,12 +395,12 @@ export default class FormatShapePane extends AntimodeMenu { @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); } @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } - @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); } @computed get controlPoints() { return this.controlPointsButton(); } @computed get lockRatio() { return this.lockRatioButton(); } @computed get rotate90() { return this.rotate90Button(); } + @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get propertyGroupItems() { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index cb4b7375b..f70e5f837 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -117,6 +117,74 @@ } } + .propertiesView-appearance { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-appearance-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-appearance-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-appearance-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .propertiesView-transform { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-transform-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + width: 200px; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-transform-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-transform-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + .notify-button { padding: 2px; width: 12px; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index d5317efcb..7c8e7cdc3 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -18,6 +18,8 @@ import { SelectionManager } from "../../../util/SelectionManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip, Checkbox } from "@material-ui/core"; import SharingManager from "../../../util/SharingManager"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import FormatShapePane from "./FormatShapePane"; interface PropertiesViewProps { @@ -47,6 +49,10 @@ export class PropertiesView extends React.Component { @observable openSharing: boolean = true; @observable openFields: boolean = true; @observable openLayout: boolean = true; + @observable openAppearance: boolean = true; + @observable openTransform: boolean = true; + + @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; } @action rtfWidth = () => { @@ -383,6 +389,40 @@ export class PropertiesView extends React.Component { {this.sharingTable}
: null}
+ + + + + {this.isInk ?
+
+ Appearance +
runInAction(() => { this.openAppearance = !this.openAppearance; })}> + +
+
+ {this.openAppearance ?
+ +
: null} +
: null} + + {this.isInk ?
+
+ Transform +
runInAction(() => { this.openTransform = !this.openTransform; })}> + +
+
+ {this.openTransform ?
+ transform +
: null} +
: null} + + + + +
-- cgit v1.2.3-70-g09d2 From a61c116e9028c2778b5c48cd596f9cb00d80d0f6 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 30 Jul 2020 23:29:37 +0530 Subject: ui and other minor changes --- src/client/util/GroupMemberView.scss | 2 +- .../collections/collectionFreeForm/PropertiesView.scss | 15 ++++++++++++--- .../collections/collectionFreeForm/PropertiesView.tsx | 14 +++++++------- 3 files changed, 20 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss index c609c5c7b..e8a4bc0c1 100644 --- a/src/client/util/GroupMemberView.scss +++ b/src/client/util/GroupMemberView.scss @@ -43,7 +43,7 @@ text-overflow: ellipsis; &:hover { - text-overflow: visible; + text-overflow: unset; overflow-x: auto; } } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 4ccc0950b..4917b7822 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -126,7 +126,7 @@ padding-left: 2px; padding-right: 2px; margin-top: 2px; - margin-right: 3px; + margin-left: 3px; .notify-button-icon { width: 6px; @@ -166,18 +166,25 @@ display: flex; padding: 3px; - + align-items: center; border-bottom: 0.5px solid grey; + &:hover .propertiesView-sharingTable-item-name { + overflow-x: unset; + white-space: unset; + overflow-wrap: break-word; + } + .propertiesView-sharingTable-item-name { font-weight: bold; width: 80px; overflow-x: hidden; display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; } .propertiesView-sharingTable-item-permission { - display: flex; .permissions-select { @@ -185,6 +192,8 @@ border: none; background-color: inherit; width: 75px; + text-align: justify; // for Edge + text-align-last: end; &:hover { cursor: pointer; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index b33b37f4b..bd720ac82 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -263,7 +263,7 @@ export class PropertiesView extends React.Component { } @computed get notifyIcon() { - return
{"Notify group of permissions change"}
}> + return
Notify with message
}>
@@ -282,10 +282,10 @@ export class PropertiesView extends React.Component {
; } - sharingItem(name: string, notify: boolean, effectiveAcl: symbol, permission?: string) { + sharingItem(name: string, effectiveAcl: symbol, permission?: string) { return
-
{name}
- {notify ? this.notifyIcon : null} +
{name}
+ {name !== "Me" ? this.notifyIcon : null}
{effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission} {permission === "Owner" ? this.expansionIcon : null} @@ -308,12 +308,12 @@ export class PropertiesView extends React.Component { if (this.selectedDoc![AclSym]) { for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) { const name = key.substring(4).replace("_", "."); - if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, false, effectiveAcl, AclMap.get(value)!)); + if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!)); } } - tableEntries.unshift(this.sharingItem("Me", false, effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]))); - if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), false, effectiveAcl, "Owner")); + tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]))); + if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), effectiveAcl, "Owner")); return
{tableEntries} -- cgit v1.2.3-70-g09d2 From 1ea51992bd418c319a253ed41c50497a6b13de25 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Thu, 30 Jul 2020 15:31:34 -0500 Subject: fixing layouts UI --- src/client/util/CurrentUserUtils.ts | 6 +- src/client/views/MainView.scss | 49 +----- src/client/views/MainView.tsx | 173 ++++++++------------- .../collectionFreeForm/PropertiesView.scss | 27 ++-- .../collectionFreeForm/PropertiesView.tsx | 14 +- 5 files changed, 89 insertions(+), 180 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 725be882e..3d8bd6bd5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -508,12 +508,12 @@ export class CurrentUserUtils { return [ { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu("Workspace")' }, { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu("Catalog")' }, - { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu("deleted")' }, + { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu("Archive")' }, { title: "Import", icon: "upload", click: 'scriptContext.selectMenu("Import")' }, - { title: "Sharing", icon: "users", click: 'scriptContext.groupManager.open()' }, + { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu("Sharing")' }, { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu("Tools")' }, { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu("Help")' }, - { title: "Settings", icon: "cog", click: 'SettingsManager.Instance.open()' }, + { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu("Settings")' }, ]; } diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index fe01c27a3..054a409df 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -124,14 +124,13 @@ } &:hover { - cursor: pointer; + cursor: grab; } } .mainiView-propertiesView { display: flex; flex-direction: column; - width: 200px; height: 100%; position: absolute; right: 0; @@ -212,50 +211,6 @@ } } -// .mainView-menuPanel-bottomButton { -// width: 45px; -// height: 45px; -// padding: 10px; -// pointer-events: all; -// touch-action: none; -// //border-radius: inherit; -// background: black; -// background-color: black; -// //border-radius: 100%; -// transform-origin: top left; -// margin-bottom: 20px; -// margin-top: 5px; - -// margin-right: 25px; - -// .mainView-menuPanel-bottomButton-label { -// background: black; -// color: white; -// margin-left: -10px; -// border-radius: 8px; -// width: 65px; -// position: absolute; -// text-align: center; -// font-size: 9.5px; -// margin-top: 2px; -// letter-spacing: normal; -// padding: 3px; -// //margin-bottom: 23px; -// } - -// .mainView-menuPanel-bottomButton-icon { -// width: 50px; -// height: 50px; -// color: white; -// } - -// svg { -// width: 95% !important; -// height: 95%; -// } -// } - - .mainView-searchPanel { width: 100%; height: 33px; @@ -342,7 +297,7 @@ position: absolute; z-index: 2; touch-action: none; - cursor: grab; + cursor: ew-resize; .mainView-libraryHandle-icon { width: 10px; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ec0bff8a7..825a75afa 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -101,6 +101,7 @@ export class MainView extends React.Component { return "chevron-right"; } } + @observable propertiesDownX: number | undefined; componentDidMount() { DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's @@ -233,7 +234,7 @@ export class MainView extends React.Component { const freeformOptions: DocumentOptions = { x: 0, y: 400, - _width: this._panelWidth * .7 - this._propertiesWidth, + _width: this._panelWidth * .7 - this.propertiesWidth(), _height: this._panelHeight, title: "Collection " + workspaceCount, }; @@ -299,12 +300,12 @@ export class MainView extends React.Component { @action onResize = (r: any) => { - this._panelWidth = r.offset.width - this._propertiesWidth; + this._panelWidth = r.offset.width - this.propertiesWidth(); this._panelHeight = r.offset.height; } @action - getPWidth = () => this._panelWidth - this._propertiesWidth; + getPWidth = () => this._panelWidth - this.propertiesWidth(); getPHeight = () => this._panelHeight; getContentsHeight = () => this._panelHeight - this._buttonBarHeight; @@ -401,20 +402,67 @@ export class MainView extends React.Component { } } + @action + onDown = (e: React.PointerEvent) => { + this.propertiesDownX = e.screenX; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + // setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { + // this._propertiesWidth = this._panelWidth - Math.max(Transform.Identity().transformPoint(e.clientX, 0)[0], 0); + // return false; + // }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 200) : 0), false); + } + @action onPointerMove = (e: PointerEvent) => { - this.flyoutWidth = Math.max(e.clientX, 0); - Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false); - this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30; + if (this.propertiesDownX) { + this._propertiesWidth = this._propertiesWidth + (this.propertiesDownX - e.screenX) * .5; + if (this._propertiesWidth < 150) { + this._propertiesWidth = 0; + this.propertiesDownX = undefined; + } else if (this._propertiesWidth > 400) { + this._propertiesWidth = 400; + this.propertiesDownX = undefined; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } else { + this.flyoutWidth = Math.max(e.clientX, 0); + Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false); + this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30; + } } @action onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) { - this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; - this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30); + if (this.propertiesDownX) { + if (Math.abs(this.propertiesDownX - e.screenX) < 3) { + if (this._propertiesWidth < 10) { + this._propertiesWidth = 200; + } else { + this._propertiesWidth = 0; + } + } else { + this._propertiesWidth = this._propertiesWidth + (this.propertiesDownX - e.screenX) * .5; + if (this._propertiesWidth < 150) { + this._propertiesWidth = 0; + } else if (this._propertiesWidth > 400) { + this._propertiesWidth = 400; + } + } + this.propertiesDownX = undefined; + } else { + if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) { + this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; + this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30); + } } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); + } flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { @@ -501,91 +549,6 @@ export class MainView extends React.Component {
; } - // @computed get menuPanel() { - // return
- //
- //
this.selectPanel("workspace")}> - // - //
Workspace
- //
- //
- - //
- //
this.selectPanel("catalog")}> - // - //
Catalog
- //
- //
- - //
- //
this.selectPanel("deleted")}> - // - //
Recently Used
- //
- //
- - //
- //
this.selectPanel("upload")}> - // - //
Import
- //
- //
- - //
- //
this.selectPanel("sharing")} - // onClick={() => GroupManager.Instance.open()}> - // - //
Sharing
- //
- //
- - //
- //
this.selectPanel("tools")} - // style={{ - // backgroundColor: this.panelContent === "tools" ? "lightgrey" : "", - // }}> - // - //
Tools
- //
- //
- - //
- //
this.selectPanel("help")} > - // - //
Help
- //
- //
- - //
- //
this.selectPanel("settings")} - // onClick={() => SettingsManager.Instance.open()}> - // - //
Settings
- //
- //
- //
; - // } - @action @undoBatch closeFlyout = () => { @@ -606,8 +569,9 @@ export class MainView extends React.Component { case "Tools": this.sidebarContent.proto = CurrentUserUtils.toolsStack; break; case "Workspace": this.sidebarContent.proto = CurrentUserUtils.workspaceStack; break; case "Catalog": this.sidebarContent.proto = CurrentUserUtils.catalogStack; break; - case "deleted": this.sidebarContent.proto = CurrentUserUtils.closedStack; break; - case "Search": this.sidebarContent.proto = CurrentUserUtils.searchStack; break; + case "Archive": this.sidebarContent.proto = CurrentUserUtils.closedStack; break; + case "Settings": this.sidebarContent.proto = SettingsManager.Instance.open(); break; + case "Sharing": this.sidebarContent.proto = GroupManager.Instance.open(); break; } MainView.expandFlyout(); } @@ -615,25 +579,21 @@ export class MainView extends React.Component { } @action @undoBatch - onDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - this._propertiesWidth = this._panelWidth - Math.max(Transform.Identity().transformPoint(e.clientX, 0)[0], 0); - return false; - }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 200) : 0), false); + closeProperties = () => { + this._propertiesWidth = 0; } @computed get propertiesView() { TraceMobx(); return
; } @@ -643,7 +603,7 @@ export class MainView extends React.Component { const n = (CollectionMenu.Instance?.Pinned ? 1 : 0); const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`; - const rightFlyout = this.selectedDocumentView ? this._propertiesWidth - 1 : this.propertiesWidth() > 10 ? 151.5 : 0; + const rightFlyout = this.propertiesWidth() - 1; return !this.userDoc ? (null) : (
} - {this.propertiesWidth() < 10 ? (null) : this.propertiesView} + {this.propertiesWidth() < 10 ? (null) : +
{this.propertiesView}
}
); } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 83e0fc0d5..76c934551 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -57,7 +57,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); @@ -92,7 +91,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); @@ -126,7 +124,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); @@ -160,7 +157,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); @@ -275,7 +271,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); @@ -299,18 +294,17 @@ cursor: pointer; } } + } - .propertiesView-fields-title-checkbox { - float: right; - height: 20px; - margin-top: -13px; - margin-left: 115px; - - .propertiesView-fields-title-checkbox-text { - font-size: 7px; - margin-top: -10px; - margin-left: 6px; - } + .propertiesView-fields-checkbox { + float: right; + height: 20px; + margin-top: -9px; + + .propertiesView-fields-checkbox-text { + font-size: 7px; + margin-top: -10px; + margin-left: 6px; } } @@ -352,7 +346,6 @@ font-size: 12.5px; padding: 4px; display: flex; - width: 200px; color: white; padding-left: 8px; background-color: rgb(51, 51, 51); diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index c4291f2a4..976e52fda 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -361,16 +361,16 @@ export class PropertiesView extends React.Component { render() { if (!this.selectedDoc) { - return
-
+ return
+
No Document Selected
; } const novice = Doc.UserDoc().noviceMode; - return
-
+ return
+
Properties
@@ -381,7 +381,7 @@ export class PropertiesView extends React.Component {
- Document Actions + Actions
runInAction(() => { this.openActions = !this.openActions; })}> @@ -447,9 +447,9 @@ export class PropertiesView extends React.Component {
- {!novice && this.openFields ?
+ {!novice && this.openFields ?
{this.fieldsCheckbox} -
Layout
+
Layout
: null} {this.openFields ?
-- cgit v1.2.3-70-g09d2 From 57122492fc344d254c507422aace7932b785276c Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Thu, 30 Jul 2020 19:46:55 -0500 Subject: started merging with ink, transform buttons done --- src/client/documents/Documents.ts | 8 +- src/client/views/MainView.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 55 ++++++++++ .../collectionFreeForm/PropertiesView.tsx | 118 ++++++++++++++++++++- 4 files changed, 175 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 32f46bd1c..88c25567e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -304,10 +304,10 @@ export namespace Docs { [DocumentType.MENUICON, { layout: { view: MenuIconBox, dataField: defaultDataKey }, }], - [DocumentType.RECOMMENDATION, { - layout: { view: RecommendationsBox, dataField: defaultDataKey }, - options: { _width: 200, _height: 200 }, - }], + // [DocumentType.RECOMMENDATION, { + // layout: { view: RecommendationsBox, dataField: defaultDataKey }, + // options: { _width: 200, _height: 200 }, + // }], [DocumentType.WEBCAM, { layout: { view: DashWebRTCVideo, dataField: defaultDataKey } }], diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0e2362866..8e4851b6e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -172,7 +172,7 @@ export class MainView extends React.Component { fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper, fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, - fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand); + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 30598adfb..f44445066 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -378,4 +378,59 @@ } } +} + +.inking-button { + + display: flex; + + .inking-button-points { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + margin-left: 5px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-lock { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-rotate { + background-color: #333333; + padding: 7px; + border-radius: 7px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 57f31ca3d..90a55ed2d 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { observer } from "mobx-react"; import "./PropertiesView.scss"; import { observable, action, computed, runInAction } from "mobx"; -import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin } from "../../../../fields/Doc"; +import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt } from "../../../../fields/Doc"; import { DocumentView } from "../../nodes/DocumentView"; import { ComputedField } from "../../../../fields/ScriptField"; import { EditableView } from "../../EditableView"; @@ -21,6 +21,11 @@ import SharingManager from "../../../util/SharingManager"; import { DocumentType } from "../../../documents/DocumentTypes"; import FormatShapePane from "./FormatShapePane"; import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util"; +import { InkField } from "../../../../fields/InkField"; +import { undoBatch } from "../../../util/UndoManager"; +import { ColorState, SketchPicker } from "react-color"; +import AntimodeMenu from "../../AntimodeMenu"; +import "./FormatShapePane.scss"; interface PropertiesViewProps { @@ -349,6 +354,7 @@ export class PropertiesView extends React.Component { SetValue={this.setTitle} />; } + @action setTitle = (value: string) => { if (this.dataDoc) { this.selectedDoc && (this.selectedDoc.title = value); @@ -358,6 +364,112 @@ export class PropertiesView extends React.Component { return false; } + + + + + @undoBatch + @action + rotate = (angle: number) => { + const _centerPoints: { X: number, Y: number }[] = []; + if (this.selectedDoc) { + const doc = this.selectedDoc; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + _centerPoints.push({ X: left, Y: top }); + } + } + + var index = 0; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + doc.rotation = Number(doc.rotation) + Number(angle); + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + + const newPoints: { X: number, Y: number }[] = []; + for (var i = 0; i < ink.length; i++) { + const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + const xs = newPoints.map(p => p.X); + const ys = newPoints.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + + doc._height = (bottom - top); + doc._width = (right - left); + } + index++; + } + } + } + + @observable _controlBtn: boolean = false; + @observable _lock: boolean = false; + + @computed + get controlPointsButton() { + return
+
{"Edit points"}
}> +
this._controlBtn = !this._controlBtn)} style={{ backgroundColor: this._controlBtn ? "black" : "" }}> + +
+
+
{this._lock ? "Unlock points" : "Lock points"}
}> +
this._lock = !this._lock)} > + +
+
+
{"Rotate 90˚"}
}> +
this.rotate(Math.PI / 2))}> + +
+
+
; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @computed get appearanceEditor() { return "appearance" }; + + @computed get transformEditor() { return this.controlPointsButton }; + render() { if (!this.selectedDoc) { @@ -416,7 +528,7 @@ export class PropertiesView extends React.Component {
{this.openAppearance ?
- + {this.appearanceEditor}
: null}
: null} @@ -429,7 +541,7 @@ export class PropertiesView extends React.Component {
{this.openTransform ?
- transform + {this.transformEditor}
: null}
: null} -- cgit v1.2.3-70-g09d2 From 7778393f3d920623f4281fa08eec6c4d8f9d3cf3 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Fri, 31 Jul 2020 12:17:35 -0500 Subject: transform in ink properties done --- src/client/views/collections/CollectionMenu.tsx | 4 +- .../collectionFreeForm/PropertiesView.scss | 64 ++++++++ .../collectionFreeForm/PropertiesView.tsx | 182 +++++++++++++++++---- 3 files changed, 215 insertions(+), 35 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 1ac8798bd..e70a16da9 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -332,11 +332,11 @@ export class CollectionViewBaseChrome extends React.Component + {/*
-
+
*/} {this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) :
; } + inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => { + return
+
{title}
+ setter(e.target.value)} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { + return
+ {this.inputBox(key, value, setter, title1)} + {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)} +
; + } + @action + upDownButtons = (dirs: string, field: string) => { + switch (field) { + case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; + // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; + case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break; + case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break; + case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; + case "wid": + const oldWidth = NumCast(this.selectedDoc?._width); + const oldHeight = NumCast(this.selectedDoc?._height); + const oldX = NumCast(this.selectedDoc?.x); + const oldY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10)); + this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); + const doc = this.selectedDoc; + if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { + console.log(doc.x, doc.y, doc._height, doc._width); + const ink = Cast(doc.data, InkField)?.inkData; + console.log(ink); + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth; + const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + } + } + break; + case "hgt": + const oWidth = NumCast(this.selectedDoc?._width); + const oHeight = NumCast(this.selectedDoc?._height); + const oX = NumCast(this.selectedDoc?.x); + const oY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10)); + this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); + const docu = this.selectedDoc; + if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { + console.log(docu.x, docu.y, docu._height, docu._width); + const ink = Cast(docu.data, InkField)?.inkData; + console.log(ink); + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth; + const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight; + newPoints.push({ X: newX, Y: newY }); + } + docu.data = new InkField(newPoints); + } + } + break; + } + } + getField(key: string) { + //if (this.selectedDoc) { + return Field.toString(this.selectedDoc[key] as Field); + // } else { + // return undefined as Opt; + // } + } + @computed get shapeXps() { return this.getField("x"); } + @computed get shapeYps() { return this.getField("y"); } + @computed get shapeRot() { return this.getField("rotation"); } + @computed get shapeHgt() { return this.getField("_height"); } + @computed get shapeWid() { return this.getField("_width"); } + set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); } + set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); } + set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); } + set shapeWid(value) { + const oldWidth = NumCast(this.selectedDoc?._width); + this.selectedDoc && (this.selectedDoc._width = Number(value)); + this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); + } + set shapeHgt(value) { + const oldHeight = NumCast(this.selectedDoc?._height); + this.selectedDoc && (this.selectedDoc._height = Number(value)); + this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); + } - - - - - - - - - - - - - - - - + @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); } + @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } + @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } @@ -468,7 +570,14 @@ export class PropertiesView extends React.Component { @computed get appearanceEditor() { return "appearance" }; - @computed get transformEditor() { return this.controlPointsButton }; + @computed get transformEditor() { + return
+ {this.controlPointsButton} + {this.hgtInput} + {this.XpsInput} + {this.rotInput} +
; + }; render() { @@ -492,10 +601,11 @@ export class PropertiesView extends React.Component { {this.editableTitle}
-
+
runInAction(() => { this.openActions = !this.openActions; })} + style={{ backgroundColor: this.openActions ? "black" : "" }}> Actions -
runInAction(() => { this.openActions = !this.openActions; })}> +
@@ -504,10 +614,11 @@ export class PropertiesView extends React.Component {
: null}
-
+
runInAction(() => { this.openSharing = !this.openSharing; })} + style={{ backgroundColor: this.openSharing ? "black" : "" }}> Sharing {"&"} Permissions -
runInAction(() => { this.openSharing = !this.openSharing; })}> +
@@ -520,10 +631,11 @@ export class PropertiesView extends React.Component { {this.isInk ?
-
+
runInAction(() => { this.openAppearance = !this.openAppearance; })} + style={{ backgroundColor: this.openAppearance ? "black" : "" }}> Appearance -
runInAction(() => { this.openAppearance = !this.openAppearance; })}> +
@@ -533,10 +645,11 @@ export class PropertiesView extends React.Component {
: null} {this.isInk ?
-
+
runInAction(() => { this.openTransform = !this.openTransform; })} + style={{ backgroundColor: this.openTransform ? "black" : "" }}> Transform -
runInAction(() => { this.openTransform = !this.openTransform; })}> +
@@ -550,11 +663,12 @@ export class PropertiesView extends React.Component {
-
+
runInAction(() => { this.openFields = !this.openFields; })} + style={{ backgroundColor: this.openFields ? "black" : "" }}>
Fields {"&"} Tags -
runInAction(() => { this.openFields = !this.openFields; })}> +
@@ -569,7 +683,9 @@ export class PropertiesView extends React.Component {
: null}
-
+
runInAction(() => { this.openLayout = !this.openLayout; })} + style={{ backgroundColor: this.openLayout ? "black" : "" }}> Layout
runInAction(() => { this.openLayout = !this.openLayout; })}> -- cgit v1.2.3-70-g09d2 From 3504f35028dcd7df3cd65e8fdfa53e6b6876e09a Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Fri, 31 Jul 2020 14:56:30 -0500 Subject: starting appearance --- .../collectionFreeForm/PropertiesView.scss | 65 ++++++++-- .../collectionFreeForm/PropertiesView.tsx | 142 ++++++++++++++++++++- 2 files changed, 192 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 4cbf02107..0b23d2325 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -466,12 +466,12 @@ .inputBox-button-up { background-color: #333333; - height: 9px; - padding-left: 3px; - padding-right: 3px; - padding-top: 1px; - padding-bottom: 1px; - border-radius: 1.5px; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; &:hover { background: rgb(131, 131, 131); @@ -482,12 +482,12 @@ .inputBox-button-down { background-color: #333333; - height: 9px; - padding-left: 3px; - padding-right: 3px; - padding-top: 1px; - padding-bottom: 1px; - border-radius: 1.5px; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; &:hover { background: rgb(131, 131, 131); @@ -497,4 +497,45 @@ } } +} + +.color-palette { + width: 160px; + height: 360; +} + +.strokeAndFill { + + display: flex; + + .fill { + margin-right: 30px; + display: flex; + padding-bottom: 7px; + margin-left: 20px; + + .fill-title { + font-size: 12px; + margin-right: 2px; + } + + .fill-button { + padding-top: 2px; + margin-top: -1px; + } + } + + .stroke { + display: flex; + + .stroke-title { + font-size: 12px; + } + + .stroke-button { + padding-top: 2px; + margin-left: 2px; + margin-top: -1px; + } + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 24521a6f2..013b4be34 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -16,7 +16,7 @@ import { Transform } from "../../../util/Transform"; import { PropertiesButtons } from "../../PropertiesButtons"; import { SelectionManager } from "../../../util/SelectionManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip, Checkbox } from "@material-ui/core"; +import { Tooltip, Checkbox, Divider } from "@material-ui/core"; import SharingManager from "../../../util/SharingManager"; import { DocumentType } from "../../../documents/DocumentTypes"; import FormatShapePane from "./FormatShapePane"; @@ -563,12 +563,148 @@ export class PropertiesView extends React.Component { @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } + @observable private _fillBtn = false; + @observable private _lineBtn = false; + + private _lastFill = "#D0021B"; + private _lastLine = "#D0021B"; + private _lastDash = "2"; + + @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } + @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } + set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); } + set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); } + + colorButton(value: string, setter: () => {}) { + return
setter()))}> +
+ {value === "" || value === "transparent" ?

: ""} +
; + } + + @undoBatch + @action + switchStk = (color: ColorState) => { + const val = String(color.hex); + this.colorStk = val; + return true; + } + @undoBatch + @action + switchFil = (color: ColorState) => { + const val = String(color.hex); + this.colorFil = val; + return true; + } + + colorPicker(setter: (color: string) => {}, type: string) { + return ; + } + + @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } + @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } + + @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } + @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } + + @computed get strokeAndFill() { + return
+
+
+
Fill:
+
{this.fillButton}
+
+
+
Stroke:
+
{this.lineButton}
+
+
+ {this._fillBtn ? this.fillPicker : ""} + {this._lineBtn ? this.linePicker : ""} +
; + } + + @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; } + @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } + @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } + @computed get widthStk() { return this.getField("strokeWidth") || "1"; } + @computed get markHead() { return this.getField("strokeStartMarker") || ""; } + @computed get markTail() { return this.getField("strokeEndMarker") || ""; } + set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } + set dashdStk(value) { + value && (this._lastDash = value) && (this.unStrokd = false); + this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined); + } + set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); } + set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } + set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); } + set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); } + @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); } + regInput = (key: string, value: any, setter: (val: string) => {}) => { + return
+ setter(e.target.value)} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + + @computed get widthAndDash() { + return
{(this.solidStk || this.dashdStk) ? "Width" : ""} + {(this.solidStk || this.dashdStk) ? this.stkInput : ""} + + + {(this.solidStk || this.dashdStk) ? this.widthStk = e.target.value))} /> : (null)} +
+ {(this.solidStk || this.dashdStk) ? <> +

Arrow Head

+ this.markHead = this.markHead ? "" : "arrow"))} + style={{ position: "absolute", right: 110, width: 20 }} /> +

Arrow End

+ this.markTail = this.markTail ? "" : "arrow"))} + style={{ position: "absolute", right: 0, width: 20 }} /> +
+ : ""} + Dash: + this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} + style={{ position: "absolute", right: 110, width: 20 }} /> +
; + } - @computed get appearanceEditor() { return "appearance" }; + @computed get appearanceEditor() { + return
+ {this.widthAndDash} + {this.strokeAndFill} +
; + } @computed get transformEditor() { return
@@ -577,7 +713,7 @@ export class PropertiesView extends React.Component { {this.XpsInput} {this.rotInput}
; - }; + } render() { -- cgit v1.2.3-70-g09d2 From 034e05b5bb587e361e9ab87160c9f3abddc9845b Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 1 Aug 2020 13:34:36 -0500 Subject: finished ink menu merge --- src/client/views/MainView.tsx | 4 +- src/client/views/PropertiesButtons.scss | 12 +-- src/client/views/collections/CollectionMenu.tsx | 11 +-- .../collectionFreeForm/PropertiesView.scss | 90 ++++++++++++++++++++-- .../collectionFreeForm/PropertiesView.tsx | 88 +++++++++++++-------- 5 files changed, 153 insertions(+), 52 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 74be36bda..993c6f4b6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -378,7 +378,7 @@ export class MainView extends React.Component { setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { this._propertiesWidth = this._panelWidth - e.clientX; return false; - }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 200) : 0), false); + }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false); } @action @@ -464,7 +464,7 @@ export class MainView extends React.Component { onClick={undefined} ScreenToLocalTransform={this.mainContainerXf} ContentScaling={returnOne} - PanelWidth={() => 60} + PanelWidth={() => 70} PanelHeight={this.getContentsHeight} renderDepth={0} focus={emptyFunction} diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss index 11265b413..e099122c4 100644 --- a/src/client/views/PropertiesButtons.scss +++ b/src/client/views/PropertiesButtons.scss @@ -20,8 +20,8 @@ $linkGap : 3px; .propertiesButtons-linkButton-empty, .propertiesButtons-linkButton-nonempty { - height: 25px; - width: 25px; + height: 30px; + width: 30px; border-radius: 5px; opacity: 0.9; pointer-events: auto; @@ -35,7 +35,7 @@ $linkGap : 3px; display: flex; justify-content: center; align-items: center; - margin-right: 5px; + margin-right: 10px; &:hover { background: $main-accent; @@ -68,12 +68,12 @@ $linkGap : 3px; padding-right: 5px; width: 25px; border-radius: 5px; - margin-right: 8px; + margin-right: 18px; } .propertiesButtons-linker { - height: 25px; - width: 25px; + height: 30px; + width: 30px; text-align: center; border-radius: 5px; pointer-events: auto; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 01374cfbb..498030b71 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -554,11 +554,12 @@ export class CollectionFreeFormViewChrome extends React.Component FormatShapePane.Instance.Pinned = !FormatShapePane.Instance.Pinned)} - style={{ backgroundColor: this._fillBtn ? "121212" : "" }}> - - ; + // return ; + return null; } render() { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 0b23d2325..8847fe89d 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -221,7 +221,8 @@ border: 1px solid black; padding: 5px; border-radius: 6px; - width: 170px; + /* width: 170px; */ + margin-right: 10px; background-color: #ececec; max-height: 130px; overflow-y: scroll; @@ -250,6 +251,9 @@ .propertiesView-sharingTable-item-permission { display: flex; + right: 34; + float: right; + position: absolute; .permissions-select { z-index: 1; @@ -392,7 +396,7 @@ width: 32; height: 32; padding-top: 9px; - margin-left: 5px; + margin-left: 18px; &:hover { background: rgb(131, 131, 131); @@ -505,14 +509,14 @@ } .strokeAndFill { - display: flex; + margin-top: 10px; .fill { - margin-right: 30px; + margin-right: 40px; display: flex; padding-bottom: 7px; - margin-left: 20px; + margin-left: 35px; .fill-title { font-size: 12px; @@ -538,4 +542,80 @@ margin-top: -1px; } } +} + +.widthAndDash { + + .width { + .width-top { + display: flex; + + .width-title { + font-size: 12; + margin-right: 20px; + margin-left: 35px; + text-align: center; + } + + .width-input { + margin-right: 30px; + margin-top: -10px; + } + } + + .width-range { + margin-right: 1px; + margin-bottom: 6; + } + } + + .arrows { + + display: flex; + margin-bottom: 3px; + + .arrows-head { + + display: flex; + margin-right: 35px; + + .arrows-head-title { + font-size: 12; + } + + .arrows-head-input { + margin-left: 6px; + margin-top: 2px; + } + } + + .arrows-tail { + display: flex; + + .arrows-tail-title { + font-size: 12; + } + + .arrows-tail-input { + margin-left: 6px; + margin-top: 2px; + } + } + } + + .dashed { + + display: flex; + margin-left: 74px; + margin-bottom: 6px; + + .dashed-title { + font-size: 12; + } + + .dashed-input { + margin-left: 6px; + margin-top: 2px; + } + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index ca81bf131..f39a75b81 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -26,6 +26,7 @@ import { undoBatch } from "../../../util/UndoManager"; import { ColorState, SketchPicker } from "react-color"; import AntimodeMenu from "../../AntimodeMenu"; import "./FormatShapePane.scss"; +import { discovery_v1 } from "googleapis"; interface PropertiesViewProps { @@ -295,7 +296,7 @@ export class PropertiesView extends React.Component { } sharingItem(name: string, effectiveAcl: symbol, permission?: string) { - return
+ return
{name}
{name !== "Me" ? this.notifyIcon : null}
@@ -399,11 +400,11 @@ export class PropertiesView extends React.Component { if (ink) { const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; - const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; + for (var i = 0; i < ink.length; i++) { + const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y; newPoints.push({ X: newX, Y: newY }); - }); + } doc.data = new InkField(newPoints); const xs = newPoints.map(p => p.X); const ys = newPoints.map(p => p.Y); @@ -534,7 +535,11 @@ export class PropertiesView extends React.Component { } getField(key: string) { - return Field.toString(this.selectedDoc?.[key] as Field); + //if (this.selectedDoc) { + return Field.toString(this.selectedDoc[key] as Field); + // } else { + // return undefined as Opt; + // } } @computed get shapeXps() { return this.getField("x"); } @@ -565,7 +570,7 @@ export class PropertiesView extends React.Component { private _lastFill = "#D0021B"; private _lastLine = "#D0021B"; - private _lastDash = "2"; + private _lastDash: any = "2"; @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } @@ -629,7 +634,7 @@ export class PropertiesView extends React.Component { } @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; } - @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } + @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; } @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } @computed get widthStk() { return this.getField("strokeWidth") || "1"; } @computed get markHead() { return this.getField("strokeStartMarker") || ""; } @@ -667,35 +672,50 @@ export class PropertiesView extends React.Component { } @computed get widthAndDash() { - return
{(this.solidStk || this.dashdStk) ? "Width" : ""} - {(this.solidStk || this.dashdStk) ? this.stkInput : ""} - - - {(this.solidStk || this.dashdStk) ? this.widthStk = e.target.value))} /> : (null)} -
- {(this.solidStk || this.dashdStk) ? <> -

Arrow Head

- this.markHead = this.markHead ? "" : "arrow"))} - style={{ position: "absolute", right: 110, width: 20 }} /> -

Arrow End

- this.markTail = this.markTail ? "" : "arrow"))} - style={{ position: "absolute", right: 0, width: 20 }} /> -
- : ""} - Dash: - this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} - style={{ position: "absolute", right: 110, width: 20 }} /> + return
+
+
+
Width:
+
{this.stkInput}
+
+ this.widthStk = e.target.value))} /> +
+ +
+
+
Arrow Head:
+ this.markHead = this.markHead ? "" : "arrow"))} /> +
+
+
Arrow End:
+ this.markTail = this.markTail ? "" : "arrow"))} /> +
+
+
+
Dash:
+ +
; } + @undoBatch @action + changeDash = () => { + const dash = this.dashdStk; + if (dash === "2") { + this.dashdStk = "0"; + } else { + this.dashdStk = "2"; + } + } + @computed get appearanceEditor() { return
{this.widthAndDash} -- cgit v1.2.3-70-g09d2 From b317845a0aa01319ba0b6d45a0f61d8fa2075e3f Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 1 Aug 2020 15:39:15 -0500 Subject: UI changes --- src/client/views/MainView.tsx | 2 +- src/client/views/collections/CollectionDockingView.scss | 14 +++++++++++--- src/client/views/collections/CollectionMenu.tsx | 1 + .../collections/collectionFreeForm/PropertiesView.scss | 17 ++++++++--------- .../collections/collectionFreeForm/PropertiesView.tsx | 8 ++++---- 5 files changed, 25 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 993c6f4b6..d095a9836 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -172,7 +172,7 @@ export class MainView extends React.Component { fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper, fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, - fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo); + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 6d894b56a..27d66f796 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -12,12 +12,14 @@ width: 100%; height: 100%; position: absolute; + .miniThumb { background: #25252525; position: absolute; } } } + .lm_title { margin-top: 3px; border-radius: 5px; @@ -27,6 +29,7 @@ transform: translate(0px, -3px); cursor: grab; } + .lm_title.focus-visible { cursor: text; } @@ -35,8 +38,9 @@ overflow: hidden; height: 19px; margin-top: -2px; - display:inline-block; + display: inline-block; } + .lm_active .lm_title { border: solid 1px lightgray; } @@ -47,11 +51,11 @@ } .lm_header .lm_tab { - padding-right : 20px; + padding-right: 20px; } .lm_popout { - display:none; + display: none; } .messageCounter { @@ -74,6 +78,7 @@ position: absolute; top: 0; left: 0; + // overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu) .collectionDockingView-gear { padding-left: 5px; @@ -81,7 +86,10 @@ width: 18px; display: inline-block; margin: auto; + + display: none; } + .collectionDockingView-dragAsDocument { touch-action: none; position: absolute; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 498030b71..fdd1b4e81 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -415,6 +415,7 @@ export class CollectionFreeFormViewChrome extends React.Component { this.document.hideMinimap = !this.document.hideMinimap; } + private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""]; private _width = ["1", "5", "10", "100"]; // private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"]; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 8847fe89d..74f32275a 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -14,18 +14,17 @@ padding-top: 12px; padding-bottom: 12px; display: flex; - - .propertiesView-title-name { - font-size: 18px; - font-weight: bold; - padding-left: 45px; - } + font-size: 18px; + font-weight: bold; + justify-content: center; .propertiesView-title-icon { width: 20px; height: 20px; padding-left: 38px; margin-top: -5px; + right: 19; + position: absolute; &:hover { color: grey; @@ -580,7 +579,7 @@ margin-right: 35px; .arrows-head-title { - font-size: 12; + font-size: 10; } .arrows-head-input { @@ -593,7 +592,7 @@ display: flex; .arrows-tail-title { - font-size: 12; + font-size: 10; } .arrows-tail-input { @@ -610,7 +609,7 @@ margin-bottom: 6px; .dashed-title { - font-size: 12; + font-size: 10; } .dashed-input { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index f39a75b81..3eb3ef8ab 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -115,7 +115,7 @@ export class PropertiesView extends React.Component { const rows: JSX.Element[] = []; for (const key of Object.keys(ids).slice().sort()) { const contents = doc[key]; - if (contents === "UNDEFINED") { + if (key[0] === "#") { rows.push(
{key}   @@ -158,7 +158,7 @@ export class PropertiesView extends React.Component { for (const key of Object.keys(ids).slice().sort()) { if (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { const contents = doc[key]; - if (contents === "UNDEFINED") { + if (key[0] === "#") { rows.push(
{key}   @@ -211,7 +211,7 @@ export class PropertiesView extends React.Component { KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); return true; } else if (value[0] === "#") { - const newVal = value + ":'UNDEFINED'"; + const newVal = value + `:'${value}'`; KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); return true; } @@ -745,7 +745,7 @@ export class PropertiesView extends React.Component { return
-
Properties
+ Properties
-- cgit v1.2.3-70-g09d2 From 5bbfed1556e47546eb6768681a86859b011a4c18 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sun, 2 Aug 2020 13:55:38 -0500 Subject: sharing fix --- src/client/util/SharingManager.tsx | 4 ++-- .../views/collections/collectionFreeForm/PropertiesView.scss | 6 +++--- .../views/collections/collectionFreeForm/PropertiesView.tsx | 9 ++++++--- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index eda12a5e9..7a0fe4140 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -142,7 +142,7 @@ export default class SharingManager extends React.Component<{}> { * @param group * @param permission */ - setInternalGroupSharing = (group: Doc, permission: string) => { + setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -224,7 +224,7 @@ export default class SharingManager extends React.Component<{}> { } } - setInternalSharing = (recipient: ValidatedUser, permission: string) => { + setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { const { user, notificationDoc } = recipient; const target = targetDoc || this.targetDoc!; const key = user.email.replace('.', '_'); diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 74f32275a..a96716659 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -241,7 +241,7 @@ .propertiesView-sharingTable-item-name { font-weight: bold; - width: 80px; + width: 95px; overflow-x: hidden; display: inline-block; text-overflow: ellipsis; @@ -259,8 +259,8 @@ border: none; background-color: inherit; width: 75px; - text-align: justify; // for Edge - text-align-last: end; + //text-align: justify; // for Edge + //text-align-last: end; &:hover { cursor: pointer; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 3eb3ef8ab..73f0f1bdd 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -156,7 +156,10 @@ export class PropertiesView extends React.Component { 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 (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { + if ((key[0] === key[0].toUpperCase() && key.substring(0, 3) !== "ACL") + || key[0] === "#" || key === "author" || + key === "creationDate" || key.indexOf("lastModified") !== -1) { + const contents = doc[key]; if (key[0] === "#") { rows.push(
@@ -297,8 +300,8 @@ export class PropertiesView extends React.Component { sharingItem(name: string, effectiveAcl: symbol, permission?: string) { return
-
{name}
- {name !== "Me" ? this.notifyIcon : null} +
{name}
+ {/* {name !== "Me" ? this.notifyIcon : null} */}
{effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission} {permission === "Owner" ? this.expansionIcon : null} -- cgit v1.2.3-70-g09d2 From 8303cd6389fe9e3c861d7b429bb4e32d3417a895 Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Mon, 3 Aug 2020 02:58:31 +0800 Subject: Merge remote-tracking branch 'origin/menu_restructure' into presentation_updates --- package.json | 6 +- src/Utils.ts | 4 +- src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 12 + src/client/util/CurrentUserUtils.ts | 116 ++- src/client/util/GroupMemberView.scss | 2 +- src/client/util/SelectionManager.ts | 3 + src/client/util/SharingManager.tsx | 6 +- src/client/views/ContextMenuItem.tsx | 4 +- src/client/views/DocumentButtonBar.tsx | 8 +- src/client/views/DocumentDecorations.tsx | 19 +- src/client/views/EditableView.tsx | 2 +- src/client/views/MainView.scss | 135 +++- src/client/views/MainView.tsx | 393 ++++++---- src/client/views/PreviewCursor.tsx | 6 +- src/client/views/PropertiesButtons.scss | 119 +++ src/client/views/PropertiesButtons.tsx | 598 ++++++++++++++ .../views/collections/CollectionDockingView.scss | 16 +- .../views/collections/CollectionDockingView.tsx | 20 +- src/client/views/collections/CollectionMenu.tsx | 79 +- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 1 + .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 7 +- src/client/views/collections/CollectionView.scss | 1 + src/client/views/collections/CollectionView.tsx | 11 +- .../views/collections/ParentDocumentSelector.tsx | 6 +- src/client/views/collections/SchemaTable.tsx | 8 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 +- .../collectionFreeForm/FormatShapePane.tsx | 27 +- .../collectionFreeForm/PropertiesView.scss | 620 +++++++++++++++ .../collectionFreeForm/PropertiesView.tsx | 858 +++++++++++++++++++++ src/client/views/linking/LinkMenuItem.tsx | 3 +- .../views/nodes/ContentFittingDocumentView.tsx | 3 + src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentLinksButton.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 62 +- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/FontIconBox.scss | 2 +- src/client/views/nodes/MenuIconBox.scss | 49 ++ src/client/views/nodes/MenuIconBox.tsx | 33 + src/client/views/nodes/PresBox.tsx | 10 +- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- .../views/nodes/formattedText/RichTextMenu.tsx | 41 +- src/client/views/nodes/formattedText/nodes_rts.ts | 8 +- src/fields/Doc.ts | 6 +- src/server/ActionUtilities.ts | 10 +- src/server/ApiManagers/UtilManager.ts | 1 - src/server/DashUploadUtils.ts | 27 +- src/server/GarbageCollector.ts | 6 +- src/server/MemoryDatabase.ts | 5 +- src/server/Message.ts | 2 +- src/server/ProcessFactory.ts | 6 +- src/server/Recommender.ts | 133 ---- src/server/RouteManager.ts | 8 +- src/server/Search.ts | 2 +- src/server/database.ts | 8 +- src/server/downsize.ts | 2 +- src/server/index.ts | 34 +- src/server/server_Initialization.ts | 38 +- src/server/websocket.ts | 27 +- 63 files changed, 3126 insertions(+), 521 deletions(-) create mode 100644 src/client/views/PropertiesButtons.scss create mode 100644 src/client/views/PropertiesButtons.tsx create mode 100644 src/client/views/collections/collectionFreeForm/PropertiesView.scss create mode 100644 src/client/views/collections/collectionFreeForm/PropertiesView.tsx create mode 100644 src/client/views/nodes/MenuIconBox.scss create mode 100644 src/client/views/nodes/MenuIconBox.tsx delete mode 100644 src/server/Recommender.ts (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/package.json b/package.json index 11af6b2c6..420765e7c 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ }, "scripts": { "start-release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", - "start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --transpile-only -- src/server/index.ts", - "oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", + "start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts", + "oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts", "debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts", "build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production", "test": "mocha -r ts-node/register test/**/*.ts", @@ -260,4 +260,4 @@ "xoauth2": "^1.2.0", "xregexp": "^4.3.0" } -} +} \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index a01a94134..0b057dc23 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,8 +1,8 @@ import v4 = require('uuid/v4'); import v5 = require("uuid/v5"); -import { Socket, Room } from 'socket.io'; -import { Message } from './server/Message'; import { ColorState } from 'react-color'; +import { Socket } from 'socket.io'; +import { Message } from './server/Message'; export namespace Utils { export let DRAG_THRESHOLD = 4; diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 985fcce11..659f78970 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -13,6 +13,7 @@ export enum DocumentType { INK = "ink", // ink stroke SCREENSHOT = "screenshot", // view of a desktop application FONTICON = "fonticonbox", // font icon + MENUICON = "menuiconbox", QUERY = "query", // search query LABEL = "label", // simple text label BUTTON = "button", // onClick button diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0da93aa7a..01cdb1eee 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -31,6 +31,7 @@ import { ColorBox } from "../views/nodes/ColorBox"; import { ComparisonBox } from "../views/nodes/ComparisonBox"; import { DocHolderBox } from "../views/nodes/DocHolderBox"; import { FontIconBox } from "../views/nodes/FontIconBox"; +import { MenuIconBox } from "../views/nodes/MenuIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { ImageBox } from "../views/nodes/ImageBox"; import { KeyValueBox } from "../views/nodes/KeyValueBox"; @@ -305,6 +306,13 @@ export namespace Docs { layout: { view: FontIconBox, dataField: defaultDataKey }, options: { _width: 40, _height: 40, borderRounding: "100%" }, }], + [DocumentType.MENUICON, { + layout: { view: MenuIconBox, dataField: defaultDataKey }, + }], + // [DocumentType.RECOMMENDATION, { + // layout: { view: RecommendationsBox, dataField: defaultDataKey }, + // options: { _width: 200, _height: 200 }, + // }], [DocumentType.WEBCAM, { layout: { view: DashWebRTCVideo, dataField: defaultDataKey } }], @@ -791,6 +799,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) }); } + export function MenuIconDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.MENUICON), undefined, { hideLinkButton: true, ...(options || {}) }); + } + export function PresElementBoxDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6d752832a..b37c91c56 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -38,6 +38,16 @@ export class CurrentUserUtils { @observable public static GuestWorkspace: Doc | undefined; @observable public static GuestMobile: Doc | undefined; + @observable public static toolsBtn: any | undefined; + @observable public static libraryBtn: any | undefined; + @observable public static searchBtn: any | undefined; + + @observable public static toolsStack: any | undefined; + @observable public static workspaceStack: any | undefined; + @observable public static catalogStack: any | undefined; + @observable public static closedStack: any | undefined; + @observable public static searchStack: any | undefined; + // sets up the default User Templates - slideView, queryView, descriptionView static setupUserTemplateButtons(doc: Doc) { if (doc["template-button-query"] === undefined) { @@ -492,6 +502,46 @@ export class CurrentUserUtils { return doc.myItemCreators as Doc; } + static menuBtnDescriptions(): { + title: string, icon: string, click: string, + }[] { + return [ + { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu("Workspace")' }, + { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu("Catalog")' }, + { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu("Archive")' }, + { title: "Import", icon: "upload", click: 'scriptContext.selectMenu("Import")' }, + { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu("Sharing")' }, + { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu("Tools")' }, + { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu("Help")' }, + { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu("Settings")' }, + ]; + } + + static setupMenuPanel(doc: Doc) { + if (doc.menuStack === undefined) { + const buttons = CurrentUserUtils.menuBtnDescriptions(); + const menuBtns = buttons.map(({ title, icon, click }) => Docs.Create.MenuIconDocument({ + icon, + title, + _backgroundColor: "black", + stayInCollection: true, + _width: 60, + _height: 60, + onClick: ScriptField.MakeScript(click, { scriptContext: "any" }), + })); + + doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { + title: "menuItemPanel", + _backgroundColor: "black", + _gridGap: 0, + _yMargin: 0, + _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, lockedPosition: true, _chromeStatus: "disabled", + })); + } + return doc.menuStack as Doc; + } + + // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu static setupActiveMobileMenu(doc: Doc) { if (doc.activeMobileMenu === undefined) { @@ -601,6 +651,8 @@ export class CurrentUserUtils { const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + doc["tabs-button-tools"] = undefined; + if (doc.myCreators === undefined) { doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, @@ -617,8 +669,11 @@ export class CurrentUserUtils { if (doc["tabs-button-tools"] === undefined) { const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true + _width: 500, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, title: "tools stack", forceActive: true })) as any as Doc; + + CurrentUserUtils.toolsStack = toolsStack; + doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({ _width: 35, _height: 25, title: "Tools", _fontSize: "10pt", letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", @@ -627,41 +682,63 @@ export class CurrentUserUtils { dragFactory: toolsStack, removeDropProperties: new List(["lockedPosition"]), stayInCollection: true, + hideFilterView: true, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), })); } - (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel - return doc["tabs-button-tools"] as Doc; + (doc["tabs-button-tools"] as any as Doc).sourcePanel; // prefetch sourcePanel + + return doc["tabs-button-tools"] as any as Doc; } static setupWorkspaces(doc: Doc) { // setup workspaces library item + doc.myWorkspaces === undefined; if (doc.myWorkspaces === undefined) { doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, + title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, })); } const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`); (doc.myWorkspaces as Doc).contextMenuScripts = new List([newWorkspace!]); (doc.myWorkspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); + const workspaces = doc.myWorkspaces as Doc; + + CurrentUserUtils.workspaceStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], { + title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true, + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" + })) as any as Doc; + return doc.myWorkspaces as Doc; } static setupCatalog(doc: Doc) { + doc.myCatalog === undefined; if (doc.myCatalog === undefined) { doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], { title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, - childDropAction: "alias", targetDropAction: "same", stayInCollection: true, + childDropAction: "alias", targetDropAction: "same", stayInCollection: true, treeViewOpen: true, })); } + + const catalog = doc.myCatalog as Doc; + + CurrentUserUtils.catalogStack = new PrefetchProxy(Docs.Create.TreeDocument([catalog], { + title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true, + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" + })) as any as Doc; + return doc.myCatalog as Doc; } static setupRecentlyClosed(doc: Doc) { // setup Recently Closed library item + doc.myRecentlyClosed === undefined; if (doc.myRecentlyClosed === undefined) { doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, stayInCollection: true, + title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, stayInCollection: true, })); } // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready @@ -670,6 +747,14 @@ export class CurrentUserUtils { (doc.myRecentlyClosed as Doc).contextMenuScripts = new List([clearAll!]); (doc.myRecentlyClosed as Doc).contextMenuLabels = new List(["Clear All"]); + const recentlyClosed = doc.myRecentlyClosed as Doc; + + CurrentUserUtils.closedStack = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], { + title: " ", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true, + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" + })) as any as Doc; + return doc.myRecentlyClosed as Doc; } // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views @@ -681,7 +766,7 @@ export class CurrentUserUtils { if (doc["tabs-button-library"] === undefined) { const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({ @@ -701,6 +786,7 @@ export class CurrentUserUtils { // setup the Search button which will display the search panel. static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) { + doc["tabs-button-search"] = undefined; if (doc["tabs-button-search"] === undefined) { doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({ _width: 50, _height: 25, title: "Search", _fontSize: "10pt", @@ -711,8 +797,9 @@ export class CurrentUserUtils { lockedPosition: true, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel") })); + CurrentUserUtils.searchStack = new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc; } - return doc["tabs-button-search"] as Doc; + return doc["tabs-button-search"] as any as Doc; } static setupSidebarContainer(doc: Doc) { @@ -728,17 +815,17 @@ export class CurrentUserUtils { // setup the list of sidebar mode buttons which determine what is displayed in the sidebar static async setupSidebarButtons(doc: Doc) { const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc); - const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer); - const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer); - const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer); + CurrentUserUtils.toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer); + CurrentUserUtils.libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer); + CurrentUserUtils.searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer); // Finally, setup the list of buttons to display in the sidebar if (doc["tabs-buttons"] === undefined) { - doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], { + doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([CurrentUserUtils.libraryBtn, CurrentUserUtils.searchBtn, CurrentUserUtils.toolsBtn], { _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode", title: "sidebar btn row stack", backgroundColor: "dimGray", })); - (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn }); + (CurrentUserUtils.toolsBtn.onClick as ScriptField).script.run({ this: CurrentUserUtils.toolsBtn }); } } @@ -749,7 +836,7 @@ export class CurrentUserUtils { })) as any as Doc static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, dropAction: "alias", removeDropProperties: new List(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100 + ...opts, dropAction: "alias", removeDropProperties: new List(["dropAction"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40 })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window @@ -855,6 +942,7 @@ export class CurrentUserUtils { this.setupDocTemplates(doc); // sets up the template menu of templates this.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile + this.setupMenuPanel(doc); this.setupOverlays(doc); // documents in overlay layer this.setupDockedButtons(doc); // the bottom bar of font icons this.setupDefaultPresentation(doc); // presentation that's initially triggered diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss index 2fc27ed03..2eb164988 100644 --- a/src/client/util/GroupMemberView.scss +++ b/src/client/util/GroupMemberView.scss @@ -44,7 +44,7 @@ background: none; &:hover { - text-overflow: visible; + text-overflow: unset; overflow-x: auto; } } diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 20d881961..05ba00331 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -12,6 +12,7 @@ export namespace SelectionManager { @observable IsDragging: boolean = false; SelectedDocuments: ObservableMap = new ObservableMap(); + @action SelectDoc(docView: DocumentView, ctrlPressed: boolean): void { @@ -32,6 +33,7 @@ export namespace SelectionManager { } @action DeselectDoc(docView: DocumentView): void { + if (manager.SelectedDocuments.get(docView)) { manager.SelectedDocuments.delete(docView); docView.props.whenActiveChanged(false); @@ -40,6 +42,7 @@ export namespace SelectionManager { } @action DeselectAll(): void { + Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments.clear(); Doc.UserDoc().activeSelection = new List([]); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 5896bdf92..000b3e46c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -72,8 +72,6 @@ export default class SharingManager extends React.Component<{}> { @observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component) @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component) - - // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; // } @@ -148,7 +146,7 @@ export default class SharingManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - const target = this.targetDoc!; + const target = targetDoc || this.targetDoc!; const ACL = `ACL-${StrCast(group.groupName)}`; target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target); @@ -222,7 +220,7 @@ export default class SharingManager extends React.Component<{}> { setInternalSharing = (recipient: ValidatedUser, permission: string) => { const { user, notificationDoc } = recipient; - const target = this.targetDoc!; + const target = targetDoc || this.targetDoc!; const key = user.email.replace('.', '_'); const ACL = `ACL-${key}`; diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 81432968d..7e233ec04 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -80,7 +80,7 @@ export class ContextMenuItem extends React.Component +
{this.props.icon ? ( @@ -95,7 +95,7 @@ export class ContextMenuItem extends React.Component window.innerHeight * 2 / 3 ? "flex-end" : "center"; const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : ""; const submenu = !this.overItem ? (null) : -
+
{this._items.map(prop => )}
; if (!("noexpand" in this.props)) { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 6b85616c2..23a3e1684 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -281,7 +281,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV {DocumentLinksButton.StartLink ?
: null} -
+ {/*
{this.templateButton}
@@ -289,16 +289,16 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
{this.contextButton} -
+
*/}
{this.pinButton}
-
+ {/*
{this.considerGoogleDocsPush}
{this.considerGoogleDocsPull} -
+
*/}
; } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7fc4a5c99..dd5303904 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,5 +1,5 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause } from '@fortawesome/free-solid-svg-icons'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction, get } from "mobx"; import { observer } from "mobx-react"; @@ -47,6 +47,7 @@ library.add(faAngleDoubleRight); library.add(faAngleLeft); library.add(faAngleRight); library.add(faPause); +library.add(faExternalLinkAlt); @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -533,12 +534,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; - for (var i = 0; i < ink.length; i++) { + ink.forEach(i => { // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = (doc.x - this._inkDocs[index].x) + (ink[i].X * doc._width) / this._inkDocs[index].width; - const newY = (doc.y - this._inkDocs[index].y) + (ink[i].Y * doc._height) / this._inkDocs[index].height; + const newX = ((doc.x || 0) - this._inkDocs[index].x) + (i.X * (doc._width || 0)) / this._inkDocs[index].width; + const newY = ((doc.y || 0) - this._inkDocs[index].y) + (i.Y * (doc._height || 0)) / this._inkDocs[index].height; newPoints.push({ X: newX, Y: newY }); - } + }); doc.data = new InkField(newPoints); } @@ -617,9 +618,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
} : <> - {minimal ? (null) :
Show context menu
} placement="top">
- -
} + {/* {minimal ? (null) :
Show context menu
} placement="top">
+ +
} */}
{`${this.selectionTitle}`}
@@ -666,7 +667,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> {"_"}
}
Open Document In Tab
} placement="top">
- {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} + {SelectionManager.SelectedDocuments().length === 1 ? : "..."}
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index c7b149842..f9d060681 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -191,7 +191,7 @@ export class EditableView extends React.Component { return (this.props.contents instanceof ObjectField ? (null) :
{ return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; } + @observable public sidebarContent: any = this.userDoc?.["tabs-panelContainer"]; + @observable public panelContent: string = "none"; + @observable public showProperties: boolean = false; public isPointerDown = false; + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else { return undefined; } + } + + @observable _propertiesWidth: number = 0; + propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, this._propertiesWidth)); + + @computed get propertiesIcon() { + if (this.propertiesWidth() < 10) { + return "chevron-left"; + } else { + return "chevron-right"; + } + } + @observable propertiesDownX: number | undefined; componentDidMount() { DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's @@ -148,7 +171,10 @@ export class MainView extends React.Component { fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper, - fa.faBezierCurve, fa.faCircle, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, + fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH); + //Pres trail icons (just for the sake of merging) + library.add(fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer); this.initEventListeners(); this.initAuthenticationRouters(); @@ -211,7 +237,7 @@ export class MainView extends React.Component { const freeformOptions: DocumentOptions = { x: 0, y: 400, - _width: this._panelWidth * .7, + _width: this._panelWidth * .7 - this.propertiesWidth(), _height: this._panelHeight, title: "Collection " + workspaceCount, }; @@ -277,16 +303,21 @@ export class MainView extends React.Component { @action onResize = (r: any) => { - this._panelWidth = r.offset.width; + this._panelWidth = r.offset.width;// - this.propertiesWidth(); this._panelHeight = r.offset.height; } - getPWidth = () => this._panelWidth; + + @action + getPWidth = () => this._panelWidth - this.propertiesWidth() + getPHeight = () => this._panelHeight; getContentsHeight = () => this._panelHeight - this._buttonBarHeight; - defaultBackgroundColors = (doc: Doc) => { + defaultBackgroundColors = (doc: Opt) => { + if (this.panelContent === doc?.title) return "lightgrey"; if (this.darkScheme) { switch (doc?.type) { + case DocumentType.MENUICON: return "white"; case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: return "#2d2d2d"; case DocumentType.LINK: case DocumentType.COL: { @@ -296,6 +327,7 @@ export class MainView extends React.Component { } } else { switch (doc?.type) { + case DocumentType.MENUICON: return "black"; case DocumentType.RTF: return "#f1efeb"; case DocumentType.BUTTON: case DocumentType.LABEL: return "lightgray"; @@ -308,7 +340,8 @@ export class MainView extends React.Component { } } @computed get mainDocView() { - return ; } @computed get dockingContent() { TraceMobx(); const mainContainer = this.mainContainer; const width = this.flyoutWidth; - return - {({ measureRef }) => -
- {!mainContainer ? (null) : this.mainDocView} -
- } -
; - } - - _canClick = false; - onPointerDown = (e: React.PointerEvent) => { - if (this._flyoutTranslate) { - this._canClick = true; - this._flyoutSizeOnDown = e.clientX; - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointerup", this.onPointerUp); - e.stopPropagation(); - e.preventDefault(); - } + return
+ {!mainContainer ? (null) : this.mainDocView} +
; } @action - pointerLeaveDragger = () => { - if (!this._flyoutTranslate) { - this.flyoutWidth = 0; - this._flyoutTranslate = true; - } + onPropertiesPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { + this._propertiesWidth = this._panelWidth - e.clientX; + return false; + }), returnFalse, action(() => this._propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false); } @action - onPointerMove = (e: PointerEvent) => { - this.flyoutWidth = Math.max(e.clientX, 0); - Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false); - this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30; - } - @action - onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) { - this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; - this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30); + onFlyoutPointerDown = (e: React.PointerEvent) => { + this.panelContent = "none"; + if (this._flyoutTranslate) { + setupMoveUpEvents(this, e, action((e: PointerEvent) => { + this.flyoutWidth = Math.max(e.clientX, 0); + this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30; + return false; + }), emptyFunction, action(() => { + this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; + this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30); + })); } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); } + flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : doc.dockingConfig ? this.openWorkspace(doc) : CollectionDockingView.AddRightSplit(doc, libraryPath); } - sidebarScreenToLocal = () => new Transform(0, (RichTextMenu.Instance.Pinned ? -35 : 0) + (CollectionMenu.Instance.Pinned ? -35 : 0), 1); + sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0), 1); + //sidebarScreenToLocal = () => new Transform(0, (RichTextMenu.Instance.Pinned ? -35 : 0) + (CollectionMenu.Instance.Pinned ? -35 : 0), 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight); + @computed get closePosition() { return 55 + this.flyoutWidth; } @computed get flyout() { - const sidebarContent = this.userDoc?.["tabs-panelContainer"]; - if (!(sidebarContent instanceof Doc)) { - return (null); - } - return
-
- -
-
+ if (!this.sidebarContent) return null; + return
+
+ {this.flyoutWidth > 0 ?
+ +
: null} + -
- -
+ ContainingCollectionDoc={undefined} + relative={true} + />
- {this.docButtons} + {this.docButtons}
; + } + + @computed get menuPanel() { + + return
+ 70} + PanelHeight={this.getContentsHeight} + renderDepth={0} + focus={emptyFunction} + backgroundColor={this.defaultBackgroundColors} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={returnEmptyFilter} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + relative={true} + scriptContext={this} + /> +
; + } + + + @action @undoBatch + closeFlyout = () => { + this.panelContent = "none"; + this.flyoutWidth = 0; + } + + get groupManager() { return GroupManager.Instance; } + + @action @undoBatch + selectMenu = (str: string) => { + if (this.panelContent === str && this.flyoutWidth !== 0) { + this.panelContent = "none"; + this.flyoutWidth = 0; + } else { + this.panelContent = str; + switch (this.panelContent) { + case "Tools": this.sidebarContent.proto = CurrentUserUtils.toolsStack; break; + case "Workspace": this.sidebarContent.proto = CurrentUserUtils.workspaceStack; break; + case "Catalog": this.sidebarContent.proto = CurrentUserUtils.catalogStack; break; + case "Archive": this.sidebarContent.proto = CurrentUserUtils.closedStack; break; + case "Settings": this.sidebarContent.proto = SettingsManager.Instance.open(); break; + case "Sharing": this.sidebarContent.proto = GroupManager.Instance.open(); break; + } + if (str === "Settings" || str === "Sharing" || str === "Help") { + this.panelContent = "none"; + this.flyoutWidth = 0; + } else { + MainView.expandFlyout(); + } + } + return true; + } + + @action @undoBatch + closeProperties = () => { + this._propertiesWidth = 0; + } + + @computed get propertiesView() { + TraceMobx(); + return
+
; } + @computed get mainInnerContent() { + const rightFlyout = this.propertiesWidth() - 1; + return <> + {this.menuPanel} +
+
+ {this.flyoutWidth !== 0 ?
+ +
+
+
: null} +
+ {this.flyout} + {this.expandButton} +
+
+ {this.dockingContent} + {this.showProperties ? (null) : +
+
+
+
+ } + {this.propertiesWidth() < 10 ? (null) : +
{this.propertiesView}
} +
+ ; + } + @computed get mainContent() { - const sidebar = this.userDoc?.["tabs-panelContainer"]; - const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); + //const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); + const n = (CollectionMenu.Instance?.Pinned ? 1 : 0); const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`; - return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( -
-
-
-
- -
-
- {this.flyout} - {this.expandButton} -
+ const pinned = FormatShapePane.Instance?.Pinned; + const innerContent = this.mainInnerContent; + return !this.userDoc ? (null) : ( + + {({ measureRef }) => +
+ {innerContent}
- {this.dockingContent} -
-
); + } + ); } public static expandFlyout = action(() => { @@ -506,7 +608,7 @@ export class MainView extends React.Component { }); @computed get expandButton() { - return !this._flyoutTranslate ? (
) : (null); + return !this._flyoutTranslate ? (
) : (null); } addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); @@ -600,8 +702,16 @@ export class MainView extends React.Component { ; } + @computed get search() { + return
+
{Doc.CurrentUserEmail}
+
SEARCH GOES HERE
+
; + } + render() { return (
+ {this.inkResources} @@ -610,9 +720,10 @@ export class MainView extends React.Component { + {/* {this.search} */} - +
{LinkDescriptionPopup.descriptionPopup ? : null} {DocumentLinksButton.EditLink ? : (null)} {LinkDocPreview.LinkInfo ? { } else if (e.clipboardData.items.length) { const batch = UndoManager.StartBatch("collection view drop"); const files: File[] = []; - for (let i = 0; i < e.clipboardData.items.length; i++) { - const file = e.clipboardData.items[i].getAsFile(); + Array.from(e.clipboardData.items).forEach(item => { + const file = item.getAsFile(); file && files.push(file); - } + }); const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] }); generatedDocuments.forEach(PreviewCursor._addDocument); batch.end(); diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss new file mode 100644 index 000000000..e099122c4 --- /dev/null +++ b/src/client/views/PropertiesButtons.scss @@ -0,0 +1,119 @@ +@import "globalCssVariables"; + +$linkGap : 3px; + +.propertiesButtons-linkFlyout { + grid-column: 2/4; +} + +.propertiesButtons-linkButton-empty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.propertiesButtons-linkButton-nonempty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.propertiesButtons-linkButton-empty, +.propertiesButtons-linkButton-nonempty { + height: 30px; + width: 30px; + border-radius: 5px; + opacity: 0.9; + pointer-events: auto; + background-color: #121721; + color: #fcfbf7; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + margin-right: 10px; + + &:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; + } +} + +.propertiesButtons { + margin-top: $linkGap; + grid-column: 1/4; + width: max-content; + height: auto; + display: flex; + flex-direction: row; + overflow-x: visible; +} + +.onClickFlyout-editScript { + text-align: center; + border: 0.5px solid grey; + background-color: rgb(230, 230, 230); + border-radius: 9px; + padding: 4px; +} + + +.propertiesButtons-button { + pointer-events: auto; + padding-right: 5px; + width: 25px; + border-radius: 5px; + margin-right: 18px; +} + +.propertiesButtons-linker { + height: 30px; + width: 30px; + text-align: center; + border-radius: 5px; + pointer-events: auto; + color: $dark-color; + border: $dark-color 1px solid; + transition: 0.2s ease all; + margin-right: 5px; +} + +.propertiesButtons-linker:hover { + cursor: pointer; + transform: scale(1.05); +} + + +@-moz-keyframes spin { + 100% { + -moz-transform: rotate(360deg); + } +} + +@-webkit-keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes shadow-pulse { + 0% { + box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8); + } + + 100% { + box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); + } +} \ No newline at end of file diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx new file mode 100644 index 000000000..59e7cc7c8 --- /dev/null +++ b/src/client/views/PropertiesButtons.tsx @@ -0,0 +1,598 @@ +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { RichTextField } from '../../fields/RichTextField'; +import { Cast, NumCast, BoolCast } from "../../fields/Types"; +import { emptyFunction, setupMoveUpEvents, Utils } from "../../Utils"; +import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; +import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; +import { Docs, DocUtils } from '../documents/Documents'; +import { DragManager } from '../util/DragManager'; +import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView'; +import { ParentDocSelector } from './collections/ParentDocumentSelector'; +import './collections/ParentDocumentSelector.scss'; +import './PropertiesButtons.scss'; +import { MetadataEntryMenu } from './MetadataEntryMenu'; +import { DocumentView } from './nodes/DocumentView'; +import { GoogleRef } from "./nodes/formattedText/FormattedTextBox"; +import { TemplateMenu } from "./TemplateMenu"; +import { Template, Templates } from "./Templates"; +import React = require("react"); +import { Tooltip } from '@material-ui/core'; +import { SelectionManager } from '../util/SelectionManager'; +import SharingManager from '../util/SharingManager'; +import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils'; +import { ImageField } from '../../fields/URLField'; +import { undoBatch, UndoManager } from '../util/UndoManager'; +import { DocumentType } from '../documents/DocumentTypes'; +import { CollectionFreeFormView } from './collections/collectionFreeForm/CollectionFreeFormView'; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +library.add(faLink); +library.add(faTag); +library.add(faTimes); +library.add(faArrowAltCircleDown); +library.add(faArrowAltCircleUp); +library.add(faArrowAltCircleRight); +library.add(faStopCircle); +library.add(faCheckCircle); +library.add(faCloudUploadAlt); +library.add(faSyncAlt); +library.add(faShare); +library.add(faPhotoVideo); + +const cloud: IconProp = "cloud-upload-alt"; +const fetch: IconProp = "sync-alt"; + +enum UtilityButtonState { + Default, + OpenRight, + OpenExternally +} + +@observer +export class PropertiesButtons extends React.Component<{}, {}> { + private _dragRef = React.createRef(); + private _pullAnimating = false; + private _pushAnimating = false; + private _pullColorAnimating = false; + + @observable private pushIcon: IconProp = "arrow-alt-circle-up"; + @observable private pullIcon: IconProp = "arrow-alt-circle-down"; + @observable private pullColor: string = "white"; + @observable public isAnimatingFetch = false; + @observable public isAnimatingPulse = false; + + @observable private openHover: UtilityButtonState = UtilityButtonState.Default; + + @observable public static Instance: PropertiesButtons; + public static hasPushedHack = false; + public static hasPulledHack = false; + + + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else { return undefined; } + } + @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } + @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; } + + @computed get onClick() { return this.selectedDoc?.onClickBehavior ? this.selectedDoc?.onClickBehavior : "nothing"; } + + public startPullOutcome = action((success: boolean) => { + if (!this._pullAnimating) { + this._pullAnimating = true; + this.pullIcon = success ? "check-circle" : "stop-circle"; + setTimeout(() => runInAction(() => { + this.pullIcon = "arrow-alt-circle-down"; + this._pullAnimating = false; + }), 1000); + } + }); + + public startPushOutcome = action((success: boolean) => { + this.isAnimatingPulse = false; + if (!this._pushAnimating) { + this._pushAnimating = true; + this.pushIcon = success ? "check-circle" : "stop-circle"; + setTimeout(() => runInAction(() => { + this.pushIcon = "arrow-alt-circle-up"; + this._pushAnimating = false; + }), 1000); + } + }); + + public setPullState = action((unchanged: boolean) => { + this.isAnimatingFetch = false; + if (!this._pullColorAnimating) { + this._pullColorAnimating = true; + this.pullColor = unchanged ? "lawngreen" : "red"; + setTimeout(this.clearPullColor, 1000); + } + }); + + private clearPullColor = action(() => { + this.pullColor = "white"; + this._pullColorAnimating = false; + }); + + @computed + get considerGoogleDocsPush() { + const targetDoc = this.selectedDoc; + const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined; + const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none"; + return !targetDoc ? (null) :
{`${published ? "Push" : "Publish"} to Google Docs`}
}> +
{ + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); + !published && runInAction(() => this.isAnimatingPulse = true); + PropertiesButtons.hasPushedHack = false; + targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1; + }}> + +
; + } + + @computed + get considerGoogleDocsPull() { + const targetDoc = this.selectedDoc; + const dataDoc = targetDoc && Doc.GetProto(targetDoc); + const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none"; + + const title = (() => { + switch (this.openHover) { + default: + case UtilityButtonState.Default: return `${!dataDoc?.unchanged ? "Pull from" : "Fetch"} Google Docs`; + case UtilityButtonState.OpenRight: return "Open in Right Split"; + case UtilityButtonState.OpenExternally: return "Open in new Browser Tab"; + } + })(); + + return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) :
{title}
}> +
{ + if (e.altKey) { + this.openHover = UtilityButtonState.OpenExternally; + } else if (e.shiftKey) { + this.openHover = UtilityButtonState.OpenRight; + } + })} + onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)} + onClick={async e => { + const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`; + if (e.shiftKey) { + e.preventDefault(); + let googleDoc = await Cast(dataDoc.googleDoc, Doc); + if (!googleDoc) { + const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false }; + googleDoc = Docs.Create.WebDocument(googleDocUrl, options); + dataDoc.googleDoc = googleDoc; + } + CollectionDockingView.AddRightSplit(googleDoc); + } else if (e.altKey) { + e.preventDefault(); + window.open(googleDocUrl); + } else { + this.clearPullColor(); + PropertiesButtons.hasPulledHack = false; + targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1; + dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true); + } + }}> + { + switch (this.openHover) { + default: + case UtilityButtonState.Default: return dataDoc.unchanged === false ? (this.pullIcon as any) : fetch; + case UtilityButtonState.OpenRight: return "arrow-alt-circle-right"; + case UtilityButtonState.OpenExternally: return "share"; + } + })()} + /> +
; + } + @computed + get pinButton() { + const targetDoc = this.selectedDoc; + const isPinned = targetDoc && Doc.isDocPinned(targetDoc); + return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}> +
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> + +
; + } + + @computed + get metadataButton() { + //const view0 = this.view0; + if (this.selectedDoc) { + return
Show metadata panel
}> +
+ /* tfs: @bcz This might need to be the data document? */}> +
e.stopPropagation()} > + {} +
+
+
; + } else { + return null; + } + + } + + @observable _aliasDown = false; + onAliasButtonDown = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); + } + onAliasButtonMoved = () => { + if (this._dragRef.current) { + const dragDocView = this.selectedDocumentView!; + const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]); + const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + dragData.dropAction = "alias"; + DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { + offsetX: dragData.offset[0], + offsetY: dragData.offset[1], + hideSource: false + }); + return true; + } + return false; + } + + @computed + get templateButton() { + const docView = this.selectedDocumentView; + const templates: Map = new Map(); + const views = [this.selectedDocumentView]; + Array.from(Object.values(Templates.TemplateList)).map(template => + templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); + return !docView ? (null) : +
Customize layout
}> +
+ this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} + content={ v).map(v => v as DocumentView)} templates={templates} />}> +
+ {} +
+
+
; + } + + onCopy = () => { + if (this.selectedDoc && this.selectedDocumentView) { + const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); + copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); + copy.y = NumCast(this.selectedDoc.y) + 30; + this.selectedDocumentView.props.addDocument?.(copy); + } + } + + @computed + get copyButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Tap or Drag to create an alias"}
}> +
+ {} +
+
; + } + + @action + onLock = () => { + this.selectedDocumentView?.toggleLockPosition(); + } + + @computed + get lockButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{this.selectedDoc?.lockedPosition ? + "Unlock Position" : "Lock Position"}
}> +
+ {} +
+
; + } + + @computed + get downloadButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Download Document"}
}> +
{ + if (this.selectedDocumentView?.props.Document) { + Doc.Zip(this.selectedDocumentView?.props.Document); + } + }}> + {} +
+
; + } + + @computed + get deleteButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Delete Document"}
}> +
{ + this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document); + }}> + {} +
+
; + } + + @computed + get sharingButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Share Document"}
}> +
{ + if (this.selectedDocumentView) { + SharingManager.Instance.open(this.selectedDocumentView); + } + }}> + {} +
+
; + } + + @computed + get onClickButton() { + if (this.selectedDoc) { + return
Choose onClick behavior
}> +
+ +
e.stopPropagation()} > + {} +
+
+
; + } else { + return null; + } + } + + @action + handleOptionChange = (e: any) => { + const value = e.target.value; + this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value); + if (value === "nothing") { + this.selectedDocumentView?.noOnClick(); + } else if (value === "enterPortal") { + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.makeIntoPortal(); + } else if (value === "toggleDetail") { + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleDetail(); + } else if (value === "linkInPlace") { + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleFollowLink("inPlace", true, false); + } else if (value === "linkOnRight") { + this.selectedDocumentView?.noOnClick(); + this.selectedDocumentView?.toggleFollowLink("onRight", false, false); + } + } + + @undoBatch @action + editOnClickScript = () => { + if (this.selectedDoc) { + DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick"); + } + } + + @computed + get onClickFlyout() { + return
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
Edit onClick Script
+
; + } + + @computed + get googlePhotosButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{"Export to Google Photos"}
}> +
{ + if (this.selectedDocumentView) { + GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log); + } + }}> + {} +
+
; + } + + @computed + get clustersButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{this.selectedDoc?.useClusters ? "Stop Showing Clusters" : "Show Clusters"}
}> +
+ {} +
+
; + } + + @action @undoBatch + changeFitToBox = () => { + this.selectedDoc && (this.selectedDoc._fitToBox = !this.selectedDoc._fitToBox); + } + + @action @undoBatch + changeClusters = () => { + this.selectedDoc && (this.selectedDoc.useClusters = !this.selectedDoc.useClusters); + } + + @computed + get fitContentButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) :
{this.selectedDoc?._fitToBox ? "Stop Fitting Content" : "Fit Content"}
}> +
+ {} +
+
; + } + + // @computed + // get importButton() { + // const targetDoc = this.selectedDoc; + // return !targetDoc ? (null) :
{"Import a Document"}
}> + //
{ + // if (this.selectedDocumentView) { + // CollectionFreeFormView.importDocument(100, 100); + // } + // }}> + // {} + //
+ //
; + // } + + + render() { + if (!this.selectedDoc) return (null); + + const isText = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField; + const considerPull = isText && this.considerGoogleDocsPull; + const considerPush = isText && this.considerGoogleDocsPush; + const isImage = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof ImageField; + const isCollection = this.selectedDoc.type === DocumentType.COL ? true : false; + const isFreeForm = this.selectedDoc._viewType === "freeform" ? true : false; + + return
+
+ {this.templateButton} +
+ {/*
+ {this.metadataButton} +
*/} +
+ {this.pinButton} +
+
+ {this.copyButton} +
+
+ {this.lockButton} +
+
+ {this.downloadButton} +
+
+
+
+ {this.deleteButton} +
+
+ {this.onClickButton} +
+
+ {this.sharingButton} +
+
+ {this.considerGoogleDocsPush} +
+
+ {this.considerGoogleDocsPull} +
+
+ {this.googlePhotosButton} +
+ {/*
+ {this.importButton} +
*/} + +
+ {this.clustersButton} +
+ +
+ {this.fitContentButton} +
+ +
+
; + } +} diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 9b14df760..6d93b2ba8 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -12,6 +12,7 @@ width: 100%; height: 100%; position: absolute; + .miniThumb { background: #25252525; position: absolute; @@ -89,6 +90,7 @@ transform: translate(0px, -3px); cursor: grab; } + .lm_title.focus-visible { cursor: text; } @@ -96,23 +98,25 @@ .lm_title_wrap { overflow: hidden; height: 19px; - margin-top: -3px; - display:inline-block; + margin-top: -2px; + display: inline-block; } + .lm_active .lm_title { border: solid 1px lightgray; } + .lm_header .lm_tab .lm_close_tab { position: absolute; text-align: center; } .lm_header .lm_tab { - padding-right : 20px; + padding-right: 20px; } .lm_popout { - display:none; + display: none; } .messageCounter { @@ -135,6 +139,7 @@ position: absolute; top: 0; left: 0; + // overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu) .collectionDockingView-gear { padding-left: 5px; @@ -142,7 +147,10 @@ width: 18px; display: inline-block; margin: auto; + + display: none; } + .collectionDockingView-dragAsDocument { touch-action: none; position: absolute; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f658e9816..d685fac4e 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,9 +1,8 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx"; +import { action, computed, Lambda, observable, reaction, runInAction, trace, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; -import Measure from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; import { DateField } from '../../../fields/DateField'; import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc"; @@ -507,7 +506,6 @@ export class CollectionDockingView extends React.Component`; tab.titleElement[0].onclick = (e: any) => tab.titleElement[0].focus(); tab.titleElement[0].onchange = (e: any) => { tab.titleElement[0].size = e.currentTarget.value.length + 1; @@ -522,6 +520,10 @@ export class CollectionDockingView extends React.Component { + const view = DocumentManager.Instance.getDocumentView(doc); + view && SelectionManager.SelectDoc(view, false); + }; // shifts the focus to this tab when another tab is dragged over it tab.element[0].onmouseenter = (e: any) => { if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return; @@ -676,10 +678,15 @@ export class DockedFrameRenderer extends React.Component { @observable private _panelHeight = 0; @observable private _document: Opt; @observable private _isActive: boolean = false; + _tabReaction: IReactionDisposer | undefined; get _stack(): any { return (this.props as any).glContainer.parent.parent; } + get _tab(): any { + const tab = (this.props as any).glContainer.tab.element[0] as HTMLElement; + return tab.getElementsByClassName("lm_title")?.[0]; + } constructor(props: any) { super(props); DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); @@ -740,9 +747,16 @@ export class DockedFrameRenderer extends React.Component { this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.on("tab", this.onActiveContentItemChanged); this.onActiveContentItemChanged(); + this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, "white") }), + (data) => { + const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document)); + this._tab.style.backgroundColor = selected ? data.color : ""; + } + ); } componentWillUnmount() { + this._tabReaction?.(); this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.off("tab", this.onActiveContentItemChanged); } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 0ca86172f..fdd1b4e81 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -25,6 +25,8 @@ import { SelectionManager } from "../../util/SelectionManager"; import { DocumentView } from "../nodes/DocumentView"; import { ColorState } from "react-color"; import { ObjectField } from "../../../fields/ObjectField"; +import RichTextMenu from "../nodes/formattedText/RichTextMenu"; +import { RichTextField } from "../../../fields/RichTextField"; import { ScriptField } from "../../../fields/ScriptField"; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { DocUtils } from "../../documents/Documents"; @@ -47,7 +49,7 @@ export default class CollectionMenu extends AntimodeMenu { componentDidMount() { reaction(() => SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0], - (doc) => doc && this.SetSelection(doc)) + (doc) => doc && this.SetSelection(doc)); } @action @@ -160,7 +162,8 @@ export class CollectionViewBaseChrome extends React.Component { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; }, }; - _freeform_commands = [this._viewCommand, this._saveFilterCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; + @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } + _stacking_commands = [this._contentCommand, this._templateCommand]; _masonry_commands = [this._contentCommand, this._templateCommand]; _schema_commands = [this._templateCommand, this._narrativeCommand]; @@ -308,18 +311,32 @@ export class CollectionViewBaseChrome extends React.Component; } + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else { return undefined; } + } + @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } + @computed get isText() { + if (this.selectedDoc) { + return this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField; + } + else return false; + } + render() { return (
- {this.props.type === CollectionViewType.Invalid || this.props.type === CollectionViewType.Docking ? (null) : this.viewModes} - {this.props.type === CollectionViewType.Docking ? (null) : this.templateChrome} -
+ {this.props.type === CollectionViewType.Invalid || + this.props.type === CollectionViewType.Docking || this.isText ? (null) : this.viewModes} + {this.props.type === CollectionViewType.Docking || this.isText ? (null) : this.templateChrome} + {/*
-
+
*/} {this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) : ; + // return ; + return null; } render() { return !this.props.docView.layoutDoc ? (null) :
- {this.props.docView.props.renderDepth !== 0 ? (null) : + {this.props.docView.props.renderDepth !== 0 || this.isText ? (null) :
} -
+ {!!!this.isText ?
-
-
: null} + {!!!this.isText ?
this.document.editing = !this.document.editing)} > {NumCast(this.document.currentFrame)} -
-
+
: null} + {!!!this.isText ?
-
+
: null} - {!this.props.isOverlay || this.document.type !== DocumentType.WEB ? (null) : + {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText ? (null) : } - {!this.props.isOverlay || this.props.docView.layoutDoc.isAnnotating ? + {(!this.props.isOverlay || this.props.docView.layoutDoc.isAnnotating) && !this.isText ? <> {this.drawButtons} {this.widthPicker} @@ -566,6 +602,7 @@ export class CollectionFreeFormViewChrome extends React.Component : (null) } + {this.isText ? : null}
; } } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 5553bbbb7..f67e049fd 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -253,7 +253,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.typesDropdownChange(!this._openTypes)}> - +
{this._openTypes ? allColumnTypes : justColType}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 425dc90e4..a104ac011 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -227,6 +227,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) addDocTab={this.addDocTab} bringToFront={returnFalse} ContentScaling={returnOne} + scriptContext={this.props.scriptContext} pinToPres={this.props.pinToPres} />; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 26d2ee8a3..68c233a16 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -351,7 +351,7 @@ export class CollectionStackingViewFieldColumn extends React.Component +
(schemaCtor: (doc: Doc) => T, moreProps?: const reg = new RegExp(Utils.prepend(""), "g"); const modHtml = srcUrl ? html.replace(reg, srcUrl) : html; const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 }); - Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc)["text"] = text; + Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text; this.props.addDocument(htmlDoc); if (srcWeb) { const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")[0].contentDocument?.getSelection()?.focusNode as any); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b8996c178..e5c4b9187 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -90,7 +90,10 @@ class TreeView extends React.Component { get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } @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; } + set treeViewOpen(c: boolean) { + if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; + else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; + } @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.defaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @@ -101,7 +104,7 @@ class TreeView extends React.Component { const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields - DocListCast(this.doc[field])) as Doc[]; // otherwise use the document's data field + DocListCast(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"); } diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss index b630f9cf8..a5aef86de 100644 --- a/src/client/views/collections/CollectionView.scss +++ b/src/client/views/collections/CollectionView.scss @@ -24,6 +24,7 @@ border-right: unset; z-index: 2; } + .collectionTimeView-treeView { display: flex; flex-direction: column; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7e7ea6786..62e8dc26a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -78,6 +78,7 @@ export interface CollectionViewCustomProps { childLayoutTemplate?: () => Opt; // specify a layout Doc template to use for children of the collection childLayoutString?: string; // specify a layout string to use for children of the collection childOpacity?: () => number; + hideFilter?: true; } export interface CollectionRenderProps { @@ -309,7 +310,7 @@ export class CollectionView extends Touchable this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); + !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null; if (this.props.Document.childLayout instanceof Doc) { optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" }); } @@ -368,7 +369,7 @@ export class CollectionView extends Touchable this.props.PanelWidth() - this.facetWidth(); + bodyPanelWidth = () => this.props.PanelWidth(); facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth)); @computed get dataDoc() { @@ -490,6 +491,7 @@ export class CollectionView extends Touchable this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false); } + filterBackground = () => "rgba(105, 105, 105, 0.432)"; get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves) @computed get scriptField() { @@ -559,6 +561,7 @@ export class CollectionView extends Touchable
; } + childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString); @@ -588,11 +591,11 @@ export class CollectionView extends Touchable } - {this.facetWidth() < 10 ? (null) : this.filterView} + {this.facetWidth() < 10 ? (null) : this.filterView} */}
); } } diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 8c0b8de9d..532dd6abc 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -42,14 +42,14 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)); const containerProtoSets = await Promise.all(aliases.map(async alias => - await Promise.all((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs))); + ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs))); const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => { - return (await SearchUtil.GetAliasesOfDocument(container)); + return (SearchUtil.GetAliasesOfDocument(container)); })); const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => { - return (await SearchUtil.GetAliasesOfDocument(dp)); + return (SearchUtil.GetAliasesOfDocument(dp)); })); const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()).keys()); runInAction(() => { diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index cde795098..7e2840c2c 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -148,7 +148,7 @@ export class SchemaTable extends React.Component { } @action - changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown; + changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column[] { @@ -208,7 +208,7 @@ export class SchemaTable extends React.Component { }}> {col.heading}
; - const sortIcon = col.desc === undefined ? "circle" : col.desc === true ? "caret-down" : "caret-up"; + const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up"; const header =
{ {keysDropdown}
this.changeSorting(col)} - style={{ paddingRight: "6px", display: "inline" }}> + style={{ paddingRight: "6px", marginLeft: "4px", display: "inline" }}>
this.props.openHeader(col, e.clientX, e.clientY)} style={{ float: "right", paddingRight: "6px" }}> - +
; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 151acb64d..109808956 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1145,7 +1145,7 @@ export class CollectionFreeFormView extends CollectionSubView { TraceMobx(); return this.doLayoutComputation }, + this._layoutComputeReaction = reaction(() => this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); @@ -1240,13 +1240,15 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); - appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); + !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); const viewctrls = ContextMenu.Instance.findByDescription("UI Controls..."); const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : []; - viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }); - viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); + + + !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; + !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); const options = ContextMenu.Instance.findByDescription("Options..."); @@ -1291,7 +1293,7 @@ export class CollectionFreeFormView extends CollectionSubView { SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { docs.docs.forEach(d => LinkManager.Instance.addLink(d)); - }) + }); }, 2000); // need to give solr some time to update so that this query will find any link docs we've added. } } diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx index ddc282e57..6263be261 100644 --- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx +++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx @@ -13,7 +13,6 @@ import AntimodeMenu from "../../AntimodeMenu"; import "./FormatShapePane.scss"; import { undoBatch } from "../../../util/UndoManager"; import { ColorState, SketchPicker } from 'react-color'; -import { DocumentView } from "../../../views/nodes/DocumentView" @observer export default class FormatShapePane extends AntimodeMenu { @@ -124,12 +123,12 @@ export default class FormatShapePane extends AntimodeMenu { console.log(ink); if (ink) { const newPoints: { X: number, Y: number }[] = []; - for (var j = 0; j < ink.length; j++) { + ink.forEach(i => { // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth; - const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight; + const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; + const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; newPoints.push({ X: newX, Y: newY }); - } + }); doc.data = new InkField(newPoints); } } @@ -148,12 +147,12 @@ export default class FormatShapePane extends AntimodeMenu { console.log(ink); if (ink) { const newPoints: { X: number, Y: number }[] = []; - for (var j = 0; j < ink.length; j++) { + ink.forEach(i => { // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth; - const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight; + const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; + const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; newPoints.push({ X: newX, Y: newY }); - } + }); doc.data = new InkField(newPoints); } } @@ -191,11 +190,11 @@ export default class FormatShapePane extends AntimodeMenu { if (ink) { const newPoints: { X: number, Y: number }[] = []; - for (var i = 0; i < ink.length; i++) { - const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X; - const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y; + ink.forEach(i => { + const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; newPoints.push({ X: newX, Y: newY }); - } + }); doc.data = new InkField(newPoints); const xs = newPoints.map(p => p.X); const ys = newPoints.map(p => p.Y); @@ -395,12 +394,12 @@ export default class FormatShapePane extends AntimodeMenu { @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); } @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } - @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); } @computed get controlPoints() { return this.controlPointsButton(); } @computed get lockRatio() { return this.lockRatioButton(); } @computed get rotate90() { return this.rotate90Button(); } + @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get propertyGroupItems() { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss new file mode 100644 index 000000000..74f32275a --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -0,0 +1,620 @@ +.propertiesView { + + background-color: rgb(205, 205, 205); + height: 100%; + font-family: "Noto Sans"; + cursor: auto; + + overflow-x: visible; + overflow-y: visible; + + .propertiesView-title { + background-color: rgb(159, 159, 159); + text-align: center; + padding-top: 12px; + padding-bottom: 12px; + display: flex; + font-size: 18px; + font-weight: bold; + justify-content: center; + + .propertiesView-title-icon { + width: 20px; + height: 20px; + padding-left: 38px; + margin-top: -5px; + right: 19; + position: absolute; + + &:hover { + color: grey; + cursor: pointer; + } + + } + + } + + .propertiesView-name { + border-bottom: 1px solid black; + padding: 8.5px; + font-size: 12.5px; + + &:hover { + cursor: pointer; + } + } + + .propertiesView-settings { + border-bottom: 1px solid black; + //padding: 8.5px; + font-size: 12.5px; + font-weight: bold; + + .propertiesView-settings-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-settings-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-settings-content { + margin-left: 12px; + padding-bottom: 10px; + padding-top: 8px; + } + + } + + .propertiesView-sharing { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-sharing-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-sharing-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-sharing-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .propertiesView-appearance { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-appearance-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-appearance-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-appearance-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .propertiesView-transform { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-transform-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-transform-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-transform-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .notify-button { + padding: 2px; + width: 12px; + height: 12px; + background-color: black; + border-radius: 10px; + padding-left: 2px; + padding-right: 2px; + margin-top: 2px; + margin-left: 3px; + + .notify-button-icon { + width: 6px; + height: 6.5px; + margin-left: .5px; + } + + &:hover { + background-color: rgb(158, 158, 158); + cursor: pointer; + } + } + + .expansion-button-icon { + width: 11px; + height: 11px; + color: black; + margin-left: 27px; + + &:hover { + color: rgb(131, 131, 131); + cursor: pointer; + } + } + + .propertiesView-sharingTable { + + border: 1px solid black; + padding: 5px; + border-radius: 6px; + /* width: 170px; */ + margin-right: 10px; + background-color: #ececec; + max-height: 130px; + overflow-y: scroll; + + .propertiesView-sharingTable-item { + + display: flex; + padding: 3px; + align-items: center; + border-bottom: 0.5px solid grey; + + &:hover .propertiesView-sharingTable-item-name { + overflow-x: unset; + white-space: unset; + overflow-wrap: break-word; + } + + .propertiesView-sharingTable-item-name { + font-weight: bold; + width: 80px; + overflow-x: hidden; + display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; + } + + .propertiesView-sharingTable-item-permission { + display: flex; + right: 34; + float: right; + position: absolute; + + .permissions-select { + z-index: 1; + border: none; + background-color: inherit; + width: 75px; + text-align: justify; // for Edge + text-align-last: end; + + &:hover { + cursor: pointer; + } + } + } + + &:last-child { + border-bottom: none; + } + } + } + + .propertiesView-fields { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-fields-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-fields-title-name { + font-size: 12.5px; + font-weight: bold; + white-space: nowrap; + width: 35px; + display: flex; + } + + .propertiesView-fields-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-fields-checkbox { + float: right; + height: 20px; + margin-top: -9px; + + .propertiesView-fields-checkbox-text { + font-size: 7px; + margin-top: -10px; + margin-left: 6px; + } + } + + .propertiesView-fields-content { + font-size: 10px; + margin-left: 2px; + padding: 10px; + + &:hover { + cursor: pointer; + } + } + } + + .field { + display: flex; + font-size: 7px; + background-color: #e8e8e8; + padding-right: 3px; + border: 0.5px solid grey; + border-radius: 5px; + padding-left: 3px; + } + + .uneditable-field { + display: flex; + overflow-y: visible; + margin-bottom: 2px; + + &:hover { + cursor: auto; + } + } + + .propertiesView-layout { + + .propertiesView-layout-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + .propertiesView-layout-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-layout-content { + overflow: hidden; + padding: 10px; + } + + } +} + +.inking-button { + + display: flex; + + .inking-button-points { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + margin-left: 18px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-lock { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-rotate { + background-color: #333333; + padding: 7px; + border-radius: 7px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } +} + +.inputBox-duo { + display: flex; +} + +.inputBox { + + margin-top: 10px; + display: flex; + height: 19px; + margin-right: 15px; + + .inputBox-title { + font-size: 12px; + padding-right: 5px; + } + + .inputBox-input { + font-size: 10px; + width: 50px; + margin-right: 1px; + border-radius: 3px; + + &:hover { + cursor: pointer; + } + } + + .inputBox-button { + + .inputBox-button-up { + background-color: #333333; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inputBox-button-down { + background-color: #333333; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + } +} + +.color-palette { + width: 160px; + height: 360; +} + +.strokeAndFill { + display: flex; + margin-top: 10px; + + .fill { + margin-right: 40px; + display: flex; + padding-bottom: 7px; + margin-left: 35px; + + .fill-title { + font-size: 12px; + margin-right: 2px; + } + + .fill-button { + padding-top: 2px; + margin-top: -1px; + } + } + + .stroke { + display: flex; + + .stroke-title { + font-size: 12px; + } + + .stroke-button { + padding-top: 2px; + margin-left: 2px; + margin-top: -1px; + } + } +} + +.widthAndDash { + + .width { + .width-top { + display: flex; + + .width-title { + font-size: 12; + margin-right: 20px; + margin-left: 35px; + text-align: center; + } + + .width-input { + margin-right: 30px; + margin-top: -10px; + } + } + + .width-range { + margin-right: 1px; + margin-bottom: 6; + } + } + + .arrows { + + display: flex; + margin-bottom: 3px; + + .arrows-head { + + display: flex; + margin-right: 35px; + + .arrows-head-title { + font-size: 10; + } + + .arrows-head-input { + margin-left: 6px; + margin-top: 2px; + } + } + + .arrows-tail { + display: flex; + + .arrows-tail-title { + font-size: 10; + } + + .arrows-tail-input { + margin-left: 6px; + margin-top: 2px; + } + } + } + + .dashed { + + display: flex; + margin-left: 74px; + margin-bottom: 6px; + + .dashed-title { + font-size: 10; + } + + .dashed-input { + margin-left: 6px; + margin-top: 2px; + } + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx new file mode 100644 index 000000000..35c7fd425 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -0,0 +1,858 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import "./PropertiesView.scss"; +import { observable, action, computed, runInAction } from "mobx"; +import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt } from "../../../../fields/Doc"; +import { DocumentView } from "../../nodes/DocumentView"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { EditableView } from "../../EditableView"; +import { KeyValueBox } from "../../nodes/KeyValueBox"; +import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { listSpec } from "../../../../fields/Schema"; +import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView"; +import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils"; +import { Id } from "../../../../fields/FieldSymbols"; +import { Transform } from "../../../util/Transform"; +import { PropertiesButtons } from "../../PropertiesButtons"; +import { SelectionManager } from "../../../util/SelectionManager"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip, Checkbox, Divider } from "@material-ui/core"; +import SharingManager from "../../../util/SharingManager"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import FormatShapePane from "./FormatShapePane"; +import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util"; +import { InkField } from "../../../../fields/InkField"; +import { undoBatch } from "../../../util/UndoManager"; +import { ColorState, SketchPicker } from "react-color"; +import AntimodeMenu from "../../AntimodeMenu"; +import "./FormatShapePane.scss"; +import { discovery_v1 } from "googleapis"; +import { PresBox } from "../../nodes/PresBox"; +import { DocumentManager } from "../../../util/DocumentManager"; + + +interface PropertiesViewProps { + width: number; + height: number; + renderDepth: number; + ScreenToLocalTransform: () => Transform; + onDown: (event: any) => void; +} + +@observer +export class PropertiesView extends React.Component { + + @computed get MAX_EMBED_HEIGHT() { return 200; } + + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else if (PresBox.Instance) { + const presSlide = PresBox.Instance._selectedArray[0]; + const view = DocumentManager.Instance.getDocumentView(presSlide); + if (view) return view; + } else { return undefined; } + } + @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } + @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; } + + @observable layoutFields: boolean = false; + + @observable openActions: boolean = true; + @observable openSharing: boolean = true; + @observable openFields: boolean = true; + @observable openLayout: boolean = true; + @observable openAppearance: boolean = true; + @observable openTransform: boolean = true; + + @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; } + + @action + rtfWidth = () => { + if (this.selectedDoc) { + return Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20); + } else { + return 0; + } + } + @action + rtfHeight = () => { + if (this.selectedDoc) { + return this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + } else { + return 0; + } + } + + @action + 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; + } else { + return 0; + } + } + + @action + 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; + } + } + + @computed get expandedField() { + if (this.dataDoc && this.selectedDoc) { + const ids: { [key: string]: string } = {}; + const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; + 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()) { + const contents = doc[key]; + if (key[0] === "#") { + rows.push(
+ {key} +   +
); + } else { + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + />; + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + rows.push(
+ ""} + SetValue={this.setKeyValue} /> +
); + return rows; + } + } + + @computed get noviceFields() { + if (this.dataDoc && this.selectedDoc) { + const ids: { [key: string]: string } = {}; + const doc = this.dataDoc; + 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 (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { + const contents = doc[key]; + if (key[0] === "#") { + rows.push(
+ {key} +   +
); + } else { + const value = Field.toString(contents as Field); + if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { + rows.push(
+ {key + ": "} +
{value}
+
); + } else { + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} + />; + + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + } + } + rows.push(
+ ""} + SetValue={this.setKeyValue} /> +
); + return rows; + } + } + + + setKeyValue = (value: string) => { + if (this.selectedDoc && this.dataDoc) { + const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; + if (value.indexOf(":") !== -1) { + const newVal = value[0].toUpperCase() + value.substring(1, value.length); + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } else if (value[0] === "#") { + const newVal = value + `:'${value}'`; + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } + } + return false; + } + + @computed get layoutPreview() { + if (this.selectedDoc) { + const layoutDoc = Doc.Layout(this.selectedDoc); + 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
+ "lightgrey"} + fitToBox={false} + FreezeDimensions={true} + NativeWidth={layoutDoc.type === + StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : returnZero} + NativeHeight={layoutDoc.type === + StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : returnZero} + PanelWidth={panelWidth} + PanelHeight={panelHeight} + focus={returnFalse} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + docFilters={returnEmptyFilter} + ContainingCollectionDoc={undefined} + ContainingCollectionView={undefined} + addDocument={returnFalse} + moveDocument={undefined} + removeDocument={returnFalse} + parentActive={() => false} + whenActiveChanged={emptyFunction} + addDocTab={returnFalse} + pinToPres={emptyFunction} + bringToFront={returnFalse} + ContentScaling={returnOne} + dontRegisterView={true} + /> +
; + } else { + return null; + } + } + + getPermissionsSelect(user: string) { + return ; + } + + @computed get notifyIcon() { + return
Notify with message
}> +
+ +
+
; + } + + @computed get expansionIcon() { + return
{"Show more permissions"}
}> +
{ + if (this.selectedDocumentView) { + SharingManager.Instance.open(this.selectedDocumentView); + } + }}> + +
+
; + } + + sharingItem(name: string, effectiveAcl: symbol, permission?: string) { + return
+
{name}
+ {name !== "Me" ? this.notifyIcon : null} +
+ {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission} + {permission === "Owner" ? this.expansionIcon : null} +
+
; + } + + @computed get sharingTable() { + const AclMap = new Map([ + [AclPrivate, SharingPermissions.None], + [AclReadonly, SharingPermissions.View], + [AclAddonly, SharingPermissions.Add], + [AclEdit, SharingPermissions.Edit], + [AclAdmin, SharingPermissions.Admin] + ]); + + const effectiveAcl = GetEffectiveAcl(this.selectedDoc!); + const tableEntries = []; + + if (this.selectedDoc![AclSym]) { + for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) { + const name = key.substring(4).replace("_", "."); + if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!)); + } + } + + tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]))); + if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), effectiveAcl, "Owner")); + + return
+ {tableEntries} +
; + } + + @computed get fieldsCheckbox() { + return ; + } + + @action + toggleCheckbox = () => { + this.layoutFields = !this.layoutFields; + } + + @computed get editableTitle() { + return StrCast(this.selectedDoc?.title)} + SetValue={this.setTitle} />; + } + + @action + setTitle = (value: string) => { + if (this.dataDoc) { + this.selectedDoc && (this.selectedDoc.title = value); + KeyValueBox.SetField(this.dataDoc, "title", value, true); + return true; + } + return false; + } + + + + + + + + + + @undoBatch + @action + rotate = (angle: number) => { + const _centerPoints: { X: number, Y: number }[] = []; + if (this.selectedDoc) { + const doc = this.selectedDoc; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + _centerPoints.push({ X: left, Y: top }); + } + } + + var index = 0; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + doc.rotation = Number(doc.rotation) + Number(angle); + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + + const newPoints: { X: number, Y: number }[] = []; + for (var i = 0; i < ink.length; i++) { + const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + const xs = newPoints.map(p => p.X); + const ys = newPoints.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + + doc._height = (bottom - top); + doc._width = (right - left); + } + index++; + } + } + } + + @observable _controlBtn: boolean = false; + @observable _lock: boolean = false; + + @computed + get controlPointsButton() { + return
+
{"Edit points"}
}> +
this._controlBtn = !this._controlBtn)} style={{ backgroundColor: this._controlBtn ? "black" : "" }}> + +
+
+
{this._lock ? "Unlock points" : "Lock points"}
}> +
this._lock = !this._lock)} > + +
+
+
{"Rotate 90˚"}
}> +
this.rotate(Math.PI / 2))}> + +
+
+
; + } + + inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => { + return
+
{title}
+ setter(e.target.value)} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + + inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { + return
+ {this.inputBox(key, value, setter, title1)} + {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)} +
; + } + + @action + upDownButtons = (dirs: string, field: string) => { + switch (field) { + case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; + // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; + case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break; + case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break; + case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; + case "wid": + const oldWidth = NumCast(this.selectedDoc?._width); + const oldHeight = NumCast(this.selectedDoc?._height); + const oldX = NumCast(this.selectedDoc?.x); + const oldY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10)); + this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); + const doc = this.selectedDoc; + if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { + console.log(doc.x, doc.y, doc._height, doc._width); + const ink = Cast(doc.data, InkField)?.inkData; + console.log(ink); + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth; + const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + } + } + break; + case "hgt": + const oWidth = NumCast(this.selectedDoc?._width); + const oHeight = NumCast(this.selectedDoc?._height); + const oX = NumCast(this.selectedDoc?.x); + const oY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10)); + this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); + const docu = this.selectedDoc; + if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { + console.log(docu.x, docu.y, docu._height, docu._width); + const ink = Cast(docu.data, InkField)?.inkData; + console.log(ink); + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth; + const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight; + newPoints.push({ X: newX, Y: newY }); + } + docu.data = new InkField(newPoints); + } + } + break; + } + } + + getField(key: string) { + //if (this.selectedDoc) { + return Field.toString(this.selectedDoc[key] as Field); + // } else { + // return undefined as Opt; + // } + } + + @computed get shapeXps() { return this.getField("x"); } + @computed get shapeYps() { return this.getField("y"); } + @computed get shapeRot() { return this.getField("rotation"); } + @computed get shapeHgt() { return this.getField("_height"); } + @computed get shapeWid() { return this.getField("_width"); } + set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); } + set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); } + set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); } + set shapeWid(value) { + const oldWidth = NumCast(this.selectedDoc?._width); + this.selectedDoc && (this.selectedDoc._width = Number(value)); + this._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); + } + set shapeHgt(value) { + const oldHeight = NumCast(this.selectedDoc?._height); + this.selectedDoc && (this.selectedDoc._height = Number(value)); + this._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); + } + + @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); } + @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } + @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } + + @observable private _fillBtn = false; + @observable private _lineBtn = false; + + private _lastFill = "#D0021B"; + private _lastLine = "#D0021B"; + private _lastDash: any = "2"; + + @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } + @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } + set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); } + set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); } + + colorButton(value: string, setter: () => {}) { + return
setter()))}> +
+ {value === "" || value === "transparent" ?

: ""} +
; + } + + @undoBatch + @action + switchStk = (color: ColorState) => { + const val = String(color.hex); + this.colorStk = val; + return true; + } + @undoBatch + @action + switchFil = (color: ColorState) => { + const val = String(color.hex); + this.colorFil = val; + return true; + } + + colorPicker(setter: (color: string) => {}, type: string) { + return ; + } + + @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } + @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } + + @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } + @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } + + @computed get strokeAndFill() { + return
+
+
+
Fill:
+
{this.fillButton}
+
+
+
Stroke:
+
{this.lineButton}
+
+
+ {this._fillBtn ? this.fillPicker : ""} + {this._lineBtn ? this.linePicker : ""} +
; + } + + @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; } + @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; } + @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } + @computed get widthStk() { return this.getField("strokeWidth") || "1"; } + @computed get markHead() { return this.getField("strokeStartMarker") || ""; } + @computed get markTail() { return this.getField("strokeEndMarker") || ""; } + set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } + set dashdStk(value) { + value && (this._lastDash = value) && (this.unStrokd = false); + this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined); + } + set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); } + set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } + set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); } + set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); } + + + @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); } + + + regInput = (key: string, value: any, setter: (val: string) => {}) => { + return
+ setter(e.target.value)} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + + @computed get widthAndDash() { + return
+
+
+
Width:
+
{this.stkInput}
+
+ this.widthStk = e.target.value))} /> +
+ +
+
+
Arrow Head:
+ this.markHead = this.markHead ? "" : "arrow"))} /> +
+
+
Arrow End:
+ this.markTail = this.markTail ? "" : "arrow"))} /> +
+
+
+
Dash:
+ +
+
; + } + + @undoBatch @action + changeDash = () => { + const dash = this.dashdStk; + if (dash === "2") { + this.dashdStk = "0"; + } else { + this.dashdStk = "2"; + } + } + + @computed get appearanceEditor() { + return
+ {this.widthAndDash} + {this.strokeAndFill} +
; + } + + @computed get transformEditor() { + return
+ {this.controlPointsButton} + {this.hgtInput} + {this.XpsInput} + {this.rotInput} +
; + } + + render() { + + if (!this.selectedDoc) { + return
+
+ No Document Selected +
; + } + + const novice = Doc.UserDoc().noviceMode; + + return
+
+ Properties +
+ +
+
+
+ {this.editableTitle} +
+
+
runInAction(() => { this.openActions = !this.openActions; })} + style={{ backgroundColor: this.openActions ? "black" : "" }}> + Actions +
+ +
+
+ {this.openActions ?
+ +
: null} +
+
+
runInAction(() => { this.openSharing = !this.openSharing; })} + style={{ backgroundColor: this.openSharing ? "black" : "" }}> + Sharing {"&"} Permissions +
+ +
+
+ {this.openSharing ?
+ {this.sharingTable} +
: null} +
+ + + + + {this.isInk ?
+
runInAction(() => { this.openAppearance = !this.openAppearance; })} + style={{ backgroundColor: this.openAppearance ? "black" : "" }}> + Appearance +
+ +
+
+ {this.openAppearance ?
+ {this.appearanceEditor} +
: null} +
: null} + + {this.isInk ?
+
runInAction(() => { this.openTransform = !this.openTransform; })} + style={{ backgroundColor: this.openTransform ? "black" : "" }}> + Transform +
+ +
+
+ {this.openTransform ?
+ {this.transformEditor} +
: null} +
: null} + + + + + +
+
runInAction(() => { this.openFields = !this.openFields; })} + style={{ backgroundColor: this.openFields ? "black" : "" }}> +
+ Fields {"&"} Tags +
+ +
+
+
+ {!novice && this.openFields ?
+ {this.fieldsCheckbox} +
Layout
+
: null} + {this.openFields ? +
+ {novice ? this.noviceFields : this.expandedField} +
: null} +
+
+
runInAction(() => { this.openLayout = !this.openLayout; })} + style={{ backgroundColor: this.openLayout ? "black" : "" }}> + Layout +
runInAction(() => { this.openLayout = !this.openLayout; })}> + +
+
+ {this.openLayout ?
{this.layoutPreview}
: null} +
+
; + } +} \ No newline at end of file diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index d1c839c3b..0aabf5319 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -182,7 +182,7 @@ export class LinkMenuItem extends React.Component { switch (this.props.destinationDoc.type) { case DocumentType.IMG: destinationIcon = "image"; break; case DocumentType.COMPARISON: destinationIcon = "columns"; break; - case DocumentType.RTF: destinationIcon = "font"; break; + case DocumentType.RTF: destinationIcon = "sticky-note"; break; case DocumentType.COL: destinationIcon = "folder"; break; case DocumentType.WEB: destinationIcon = "globe-asia"; break; case DocumentType.SCREENSHOT: destinationIcon = "photo-video"; break; @@ -196,6 +196,7 @@ export class LinkMenuItem extends React.Component { case DocumentType.DOCHOLDER: destinationIcon = "expand"; break; case DocumentType.VID: destinationIcon = "video"; break; case DocumentType.INK: destinationIcon = "pen-nib"; break; + case DocumentType.PDF: destinationIcon = "file"; break; default: destinationIcon = "question"; break; } diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 6081def5d..f7e253f42 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -78,6 +78,9 @@ export class ContentFittingDocumentView extends React.Component 0.001 ? "auto" : this.props.PanelWidth(), height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(), diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e8173d103..e3f258b8d 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -18,6 +18,7 @@ import { DocHolderBox } from "./DocHolderBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import { FontIconBox } from "./FontIconBox"; +import { MenuIconBox } from "./MenuIconBox"; import { FieldView, FieldViewProps } from "./FieldView"; import { FormattedTextBox } from "./formattedText/FormattedTextBox"; import { ImageBox } from "./ImageBox"; @@ -189,7 +190,7 @@ export class DocumentContentsView extends React.Component + {/* {this.props.InMenu ? this.props.StartLink ? : + : links.length} */} + {this.props.InMenu ? this.props.StartLink ? : - : links.length} + link : links.length}
{DocumentLinksButton.StartLink && this.props.InMenu && !!!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
(Docu } onClick = action((e: React.MouseEvent | React.PointerEvent) => { - if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && + if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { let stopPropagate = true; let preventDefault = true; @@ -319,10 +321,11 @@ export class DocumentView extends DocComponent(Docu const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, + scriptContext: this.props.scriptContext, thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log); - if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) { + if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) { UndoManager.RunInBatch(func, "on click"); } else func(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself @@ -584,6 +587,18 @@ export class DocumentView extends DocComponent(Docu } } + + @undoBatch + noOnClick = (): void => { + this.Document.ignoreClick = false; + this.Document.isLinkButton = false; + } + + @undoBatch + toggleDetail = (): void => { + this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`); + } + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { if (this.props.Document === Doc.UserDoc().activeWorkspace) { @@ -660,6 +675,14 @@ export class DocumentView extends DocComponent(Docu } } + @action + onCopy = () => { + const copy = Doc.MakeCopy(this.props.Document, true); + copy.x = NumCast(this.props.Document.x) + NumCast(this.props.Document._width); + copy.y = NumCast(this.props.Document.y) + 30; + this.props.addDocument?.(copy); + } + @action onContextMenu = async (e: React.MouseEvent | Touch): Promise => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 @@ -704,14 +727,14 @@ export class DocumentView extends DocComponent(Docu const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); - onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" }); + onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "concierge-bell" }); - !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "concierge-bell" }); - onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "concierge-bell" }); - onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "snowflake" }); - onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); - !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "hand-point-right" }); + onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" }); + onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" }); + onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" }); + onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" }); + !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" }); const funcs: ContextMenuProps[] = []; if (this.layoutDoc.onDragStart) { @@ -724,6 +747,9 @@ export class DocumentView extends DocComponent(Docu const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) }); + moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" }); + //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); + moreItems.push({ description: "Create an Alias", event: () => this.onCopy(), icon: "copy" }); if (!Doc.UserDoc().noviceMode) { moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); @@ -735,16 +761,18 @@ export class DocumentView extends DocComponent(Docu } moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); } + //GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); + const effectiveAcl = GetEffectiveAcl(this.props.Document); (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); - moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" }); + !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); const help = cm.findByDescription("Help..."); const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : []; + !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" }); - helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); @@ -786,8 +814,9 @@ export class DocumentView extends DocComponent(Docu childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); @computed.struct get linkOffset() { return [-15, 0]; } @computed get contents() { + const pos = this.props.relative ? "relative " : "absolute"; TraceMobx(); - return (
+ return (
(Docu ChromeHeight={this.chromeHeight} isSelected={this.isSelected} select={this.select} + scriptContext={this.props.scriptContext} onClick={this.onClickFunc} layoutKey={this.finalLayoutKey} /> {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} @@ -877,8 +907,12 @@ export class DocumentView extends DocComponent(Docu } @computed get innards() { TraceMobx(); + const pos = this.props.relative ? "relative" : undefined; if (this.props.treeViewDoc && !this.props.LayoutTemplateString?.includes("LinkAnchorBox")) { // this happens when the document is a tree view label (but not an anchor dot) - return
+ return
{StrCast(this.props.Document.title)} {this.allAnchors}
; @@ -1003,7 +1037,7 @@ export class DocumentView extends DocComponent(Docu background: finalColor, opacity: finalOpacity, fontFamily: StrCast(this.Document._fontFamily, "inherit"), - fontSize: Cast(this.Document._fontSize, "string", null) + fontSize: Cast(this.Document._fontSize, "string", null), }}> {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <> {this.innards} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 5d5bc1d73..1b4151bd8 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -61,6 +61,7 @@ export interface FieldViewProps { color?: string; xMargin?: number; yMargin?: number; + scriptContext?: any; } @observer diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss index 5b85d8b0b..69c835318 100644 --- a/src/client/views/nodes/FontIconBox.scss +++ b/src/client/views/nodes/FontIconBox.scss @@ -16,7 +16,7 @@ position: absolute; text-align: center; font-size: 8px; - margin-top:4px; + //margin-top:4px; letter-spacing: normal; left: 0; overflow: hidden; diff --git a/src/client/views/nodes/MenuIconBox.scss b/src/client/views/nodes/MenuIconBox.scss new file mode 100644 index 000000000..1b72f5a8f --- /dev/null +++ b/src/client/views/nodes/MenuIconBox.scss @@ -0,0 +1,49 @@ +.menuButton { + //padding: 7px; + padding-left: 7px; + width: 100%; + width: 60px; + height: 70px; + + .menuButton-wrap { + width: 45px; + /* padding: 5px; */ + touch-action: none; + background: black; + transform-origin: top left; + /* margin-bottom: 5px; */ + margin-top: 5px; + margin-right: 25px; + border-radius: 8px; + + &:hover { + background: rgb(61, 61, 61); + cursor: pointer; + } + } + + .menuButton-label { + color: white; + margin-right: 4px; + border-radius: 8px; + width: 42px; + position: relative; + text-align: center; + font-size: 8px; + margin-top: 1px; + letter-spacing: normal; + padding: 3px; + background-color: inherit; + } + + .menuButton-icon { + width: auto; + height: 35px; + padding: 5px; + } + + svg { + width: 95% !important; + height: 95%; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx new file mode 100644 index 000000000..0aa7b327e --- /dev/null +++ b/src/client/views/nodes/MenuIconBox.tsx @@ -0,0 +1,33 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { createSchema, makeInterface } from '../../../fields/Schema'; +import { StrCast } from '../../../fields/Types'; +import { DocComponent } from '../DocComponent'; +import { FieldView, FieldViewProps } from './FieldView'; +import './MenuIconBox.scss'; +const MenuIconSchema = createSchema({ + icon: "string" +}); + +type MenuIconDocument = makeInterface<[typeof MenuIconSchema]>; +const MenuIconDocument = makeInterface(MenuIconSchema); +@observer +export class MenuIconBox extends DocComponent(MenuIconDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MenuIconBox, fieldKey); } + _ref: React.RefObject = React.createRef(); + + render() { + + const color = this.props.backgroundColor?.(this.props.Document) === "lightgrey" ? "black" : "white"; + const menuBTN =
+
+ +
{this.dataDoc.title}
+
+
; + + return menuBTN; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index c04c6c409..97f223c02 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -32,6 +32,7 @@ import { conformsTo } from "lodash"; import { translate } from "googleapis/build/src/apis/translate"; import { DragManager, dropActionType } from "../../util/DragManager"; import { actionAsync } from "mobx-utils"; +import { SelectionManager } from "../../util/SelectionManager"; type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @@ -260,7 +261,7 @@ export class PresBox extends ViewBoxBaseComponent const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); const srcContext = await DocCastAsync(targetDoc.context); const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); - const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined; + const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined; if (this.itemIndex >= 0) { if (targetDoc) { if (srcContext) this.layoutDoc.presCollection = srcContext; @@ -496,6 +497,8 @@ export class PresBox extends ViewBoxBaseComponent selectElement = (doc: Doc) => { // this._selectedArray = []; this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex)); + const view = DocumentManager.Instance.getDocumentView(doc); + view && SelectionManager.SelectDoc(view, false); // this._selectedArray.push(this.childDocs[this.childDocs.indexOf(doc)]); // console.log(this._selectedArray); } @@ -504,8 +507,9 @@ export class PresBox extends ViewBoxBaseComponent @action multiSelect = (doc: Doc, ref: HTMLElement) => { this._selectedArray.push(this.childDocs[this.childDocs.indexOf(doc)]); - const ele = ref.getElementsByClassName("stacking"); this._eleArray.push(ref); + const view = DocumentManager.Instance.getDocumentView(doc); + view && SelectionManager.SelectDoc(view, true); } //Shift click @@ -517,6 +521,8 @@ export class PresBox extends ViewBoxBaseComponent for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) { this._selectedArray.push(this.childDocs[i]); this._eleArray.push(ref); + const view = DocumentManager.Instance.getDocumentView(doc); + view && SelectionManager.SelectDoc(view, true); } } } diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 958a37568..8ae71c035 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -190,7 +190,7 @@ export class DashFieldViewInternal extends React.Component c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb")); list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb")); - alias._pivotField = this._fieldKey; + alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey; this.props.tbox.props.addDocTab(alias, "onRight"); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 627c6e363..fc65f34eb 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1267,7 +1267,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.doLinkOnDeselect(); // move the richtextmenu offscreen - if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide(); + //if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide(); } _lastTimedMark: Mark | undefined = undefined; @@ -1346,7 +1346,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } render() { TraceMobx(); - const scale = this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1); + const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground; setTimeout(() => this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props), this.props.isSelected() ? 10 : 0); // need to make sure that we update a text box that is selected after updating the one that was deselected diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 7ccbfa051..f76707a79 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -77,7 +77,8 @@ export default class RichTextMenu extends AntimodeMenu { super(props); RichTextMenu.Instance = this; this._canFade = false; - this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + this.Pinned = true; this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, @@ -529,7 +530,7 @@ export default class RichTextMenu extends AntimodeMenu { indentParagraph(state: EditorState, dispatch: any) { var tr = state.tr; - let headin = false; + const heading = false; state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) { const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined; @@ -539,7 +540,7 @@ export default class RichTextMenu extends AntimodeMenu { } return true; }); - !headin && dispatch?.(tr); + !heading && dispatch?.(tr); return true; } @@ -920,16 +921,22 @@ export default class RichTextMenu extends AntimodeMenu { render() { TraceMobx(); const row1 =
{[ - !this.collapsed ? this.getDragger() : (null), - !this.Pinned ? (null) :
{[ - this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), - this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), - this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), - this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), - this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), - this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), -
- ]}
, + //!this.collapsed ? this.getDragger() : (null), + // !this.Pinned ? (null) :
{[ + // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + //
+ // ]}
, + this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), this.createColorButton(), this.createHighlighterButton(), this.createLinkButton(), @@ -957,16 +964,16 @@ export default class RichTextMenu extends AntimodeMenu { this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
,]}
-
- {/*
+ {/*
+ {
-
*/} +
} -
+
*/}
; return ( diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 0eca6d753..1616500f6 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -79,14 +79,14 @@ export const nodes: { [index: string]: NodeSpec } = { { tag: "h5", attrs: { level: 5 } }, { tag: "h6", attrs: { level: 6 } }], toDOM(node) { - var dom = toParagraphDOM(node) as any; - var level = node.attrs.level || 1; + const dom = toParagraphDOM(node) as any; + const level = node.attrs.level || 1; dom[0] = 'h' + level; return dom; }, getAttrs(dom: any) { - var attrs = getParagraphNodeAttrs(dom) as any; - var level = Number(dom.nodeName.substring(1)) || 1; + const attrs = getParagraphNodeAttrs(dom) as any; + const level = Number(dom.nodeName.substring(1)) || 1; attrs.level = level; return attrs; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 43e74ff61..85fc63074 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -528,10 +528,10 @@ export namespace Doc { const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); const field = ProxyField.WithoutProxy(() => doc[key]); const copyObjectField = async (field: ObjectField) => { - const list = await Cast(doc[key], listSpec(Doc)); + const list = Cast(doc[key], listSpec(Doc)); const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc); if (docs !== undefined && docs.length) { - const clones = await Promise.all(docs.map(async d => await Doc.makeClone(d as Doc, cloneMap, rtfs, exclusions, dontCreate))); + const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, rtfs, exclusions, dontCreate))); !dontCreate && assignKey(new List(clones)); } else if (doc[key] instanceof Doc) { assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions, dontCreate)); // reference documents except copy documents that are expanded teplate fields @@ -625,7 +625,7 @@ export namespace Doc { Array.from(map.entries()).forEach(f => docs[f[0]] = f[1]); const docString = JSON.stringify({ id: doc[Id], docs }, replacer); - var zip = new JSZip(); + const zip = new JSZip(); zip.file(doc.title + ".json", docString); diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts index 60f66c878..fd9bc0c83 100644 --- a/src/server/ActionUtilities.ts +++ b/src/server/ActionUtilities.ts @@ -1,11 +1,11 @@ -import { readFile, writeFile, exists, mkdir, unlink, createWriteStream } from 'fs'; -import { ExecOptions } from 'shelljs'; import { exec } from 'child_process'; -import * as path from 'path'; -import * as rimraf from "rimraf"; -import { yellow, Color } from 'colors'; +import { Color, yellow } from 'colors'; +import { createWriteStream, exists, mkdir, readFile, unlink, writeFile } from 'fs'; import * as nodemailer from "nodemailer"; import { MailOptions } from "nodemailer/lib/json-transport"; +import * as path from 'path'; +import * as rimraf from "rimraf"; +import { ExecOptions } from 'shelljs'; import Mail = require('nodemailer/lib/mailer'); const projectRoot = path.resolve(__dirname, "../../"); diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index e2cd88726..e657866ce 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -6,7 +6,6 @@ import { exec } from 'child_process'; // const recommender = new Recommender(); // recommender.testModel(); -import executeImport from "../../scraping/buxton/final/BuxtonImporter"; export default class UtilManager extends ApiManager { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 2bf4c1956..890fb6f6d 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -1,23 +1,22 @@ -import { unlinkSync, createWriteStream, readFileSync, rename, writeFile, existsSync } from 'fs'; -import { Utils } from '../Utils'; -import * as path from 'path'; -import * as sharp from 'sharp'; -import request = require('request-promise'); +import { red } from 'colors'; import { ExifImage } from 'exif'; -import { Opt } from '../fields/Doc'; -import { AcceptibleMedia, Upload } from './SharedMediaTypes'; -import { filesDirectory, publicDirectory } from '.'; import { File } from 'formidable'; +import { createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs'; +import * as path from 'path'; import { basename } from "path"; -import { createIfNotExists } from './ActionUtilities'; +import * as sharp from 'sharp'; +import { Stream } from 'stream'; +import { filesDirectory, publicDirectory } from '.'; +import { Opt } from '../fields/Doc'; import { ParsedPDF } from "../server/PdfTypes"; +import { Utils } from '../Utils'; +import { createIfNotExists } from './ActionUtilities'; +import { clientPathToFile, Directory, pathToDirectory, serverPathToFile } from './ApiManagers/UploadManager'; +import { resolvedServerUrl } from "./server_Initialization"; +import { AcceptibleMedia, Upload } from './SharedMediaTypes'; +import request = require('request-promise'); const parse = require('pdf-parse'); -import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './ApiManagers/UploadManager'; -import { red } from 'colors'; -import { Stream } from 'stream'; -import { resolvedPorts } from './server_Initialization'; const requestImageSize = require("../client/util/request-image-size"); -import { resolvedServerUrl } from "./server_Initialization"; export enum SizeSuffix { Small = "_s", diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts index 24745cbb4..a9a3b0481 100644 --- a/src/server/GarbageCollector.ts +++ b/src/server/GarbageCollector.ts @@ -1,9 +1,9 @@ -import { Database } from './database'; - -import * as path from 'path'; import * as fs from 'fs'; +import * as path from 'path'; +import { Database } from './database'; import { Search } from './Search'; + function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { for (const key in doc) { if (!doc.hasOwnProperty(key)) { diff --git a/src/server/MemoryDatabase.ts b/src/server/MemoryDatabase.ts index 1f1d702d9..7f477327e 100644 --- a/src/server/MemoryDatabase.ts +++ b/src/server/MemoryDatabase.ts @@ -1,6 +1,7 @@ -import { IDatabase, DocumentsCollection } from './IDatabase'; -import { Transferable } from './Message'; +import { DH_CHECK_P_NOT_SAFE_PRIME } from 'constants'; import * as mongodb from 'mongodb'; +import { DocumentsCollection, IDatabase } from './IDatabase'; +import { Transferable } from './Message'; export class MemoryDatabase implements IDatabase { diff --git a/src/server/Message.ts b/src/server/Message.ts index ff0381fd3..59b24cd82 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,6 +1,6 @@ -import { Utils } from "../Utils"; import { Point } from "../pen-gestures/ndollar"; import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter"; +import { Utils } from "../Utils"; export class Message { private _name: string; diff --git a/src/server/ProcessFactory.ts b/src/server/ProcessFactory.ts index acb8b3a99..63682368f 100644 --- a/src/server/ProcessFactory.ts +++ b/src/server/ProcessFactory.ts @@ -1,8 +1,8 @@ -import { existsSync, mkdirSync } from "fs"; -import { pathFromRoot, fileDescriptorFromStream } from './ActionUtilities'; -import rimraf = require("rimraf"); import { ChildProcess, spawn, StdioOptions } from "child_process"; +import { existsSync, mkdirSync } from "fs"; import { Stream } from "stream"; +import { fileDescriptorFromStream, pathFromRoot } from './ActionUtilities'; +import rimraf = require("rimraf"); export namespace ProcessFactory { diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts deleted file mode 100644 index 935ec3871..000000000 --- a/src/server/Recommender.ts +++ /dev/null @@ -1,133 +0,0 @@ -// //import { Doc } from "../fields/Doc"; -// //import { StrCast } from "../fields/Types"; -// //import { List } from "../fields/List"; -// //import { CognitiveServices } from "../client/cognitive_services/CognitiveServices"; - -// // var w2v = require('word2vec'); -// var assert = require('assert'); -// var arxivapi = require('arxiv-api-node'); -// import requestPromise = require("request-promise"); -// import * as use from '@tensorflow-models/universal-sentence-encoder'; -// import { Tensor } from "@tensorflow/tfjs-core/dist/tensor"; -// require('@tensorflow/tfjs-node'); - -// //http://gnuwin32.sourceforge.net/packages/make.htm - -// export class Recommender { - -// private _model: any; -// static Instance: Recommender; -// private dimension: number = 0; -// private choice: string = ""; // Tensorflow or Word2Vec - -// constructor() { -// Recommender.Instance = this; -// } - -// /*** -// * Loads pre-trained model from TF -// */ - -// public async loadTFModel() { -// let self = this; -// return new Promise(res => { -// use.load().then(model => { -// self.choice = "TF"; -// self._model = model; -// self.dimension = 512; -// res(model); -// }); -// } - -// ); -// } - -// /*** -// * Loads pre-trained model from word2vec -// */ - -// // private loadModel(): Promise { -// // let self = this; -// // return new Promise(res => { -// // w2v.loadModel("./node_modules/word2vec/examples/fixtures/vectors.txt", function (err: any, model: any) { -// // self.choice = "WV"; -// // self._model = model; -// // self.dimension = model.size; -// // res(model); -// // }); -// // }); -// // } - -// /*** -// * Testing -// */ - -// public async testModel() { -// if (!this._model) { -// await this.loadTFModel(); -// } -// if (this._model) { -// if (this.choice === "WV") { -// let similarity = this._model.similarity('father', 'mother'); -// } -// else if (this.choice === "TF") { -// const model = this._model as use.UniversalSentenceEncoder; -// // Embed an array of sentences. -// const sentences = [ -// 'Hello.', -// 'How are you?' -// ]; -// const embeddings = await this.vectorize(sentences); -// if (embeddings) embeddings.print(true /*verbose*/); -// // model.embed(sentences).then(embeddings => { -// // // `embeddings` is a 2D tensor consisting of the 512-dimensional embeddings for each sentence. -// // // So in this example `embeddings` has the shape [2, 512]. -// // embeddings.print(true /* verbose */); -// // }); -// } -// } -// else { -// console.log("model not found :("); -// } -// } - -// /*** -// * Uses model to convert words to vectors -// */ - -// public async vectorize(text: string[]): Promise { -// if (!this._model) { -// await this.loadTFModel(); -// } -// if (this._model) { -// if (this.choice === "WV") { -// let word_vecs = this._model.getVectors(text); -// return word_vecs; -// } -// else if (this.choice === "TF") { -// const model = this._model as use.UniversalSentenceEncoder; -// return new Promise(res => { -// model.embed(text).then(embeddings => { -// res(embeddings); -// }); -// }); - -// } -// } -// } - -// // public async trainModel() { -// // w2v.word2vec("./node_modules/word2vec/examples/eng_news-typical_2016_1M-sentences.txt", './node_modules/word2vec/examples/my_phrases.txt', { -// // cbow: 1, -// // size: 200, -// // window: 8, -// // negative: 25, -// // hs: 0, -// // sample: 1e-4, -// // threads: 20, -// // iter: 200, -// // minCount: 2 -// // }); -// // } - -// } diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts index 1a2340afc..78b75d6be 100644 --- a/src/server/RouteManager.ts +++ b/src/server/RouteManager.ts @@ -1,8 +1,8 @@ -import RouteSubscriber from "./RouteSubscriber"; -import { DashUserModel } from "./authentication/DashUserModel"; -import { Request, Response, Express } from 'express'; -import { cyan, red, green } from 'colors'; +import { cyan, green, red } from 'colors'; +import { Express, Request, Response } from 'express'; import { AdminPriviliges } from "."; +import { DashUserModel } from "./authentication/DashUserModel"; +import RouteSubscriber from "./RouteSubscriber"; export enum Method { GET, diff --git a/src/server/Search.ts b/src/server/Search.ts index 21064e520..decd1f5b1 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -1,5 +1,5 @@ -import * as rp from 'request-promise'; import { red } from 'colors'; +import * as rp from 'request-promise'; const pathTo = (relative: string) => `http://localhost:8983/solr/dash/${relative}`; diff --git a/src/server/database.ts b/src/server/database.ts index 2372cbcf2..b7aa77f5d 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -1,11 +1,11 @@ import * as mongodb from 'mongodb'; -import { Transferable } from './Message'; +import * as mongoose from 'mongoose'; import { Opt } from '../fields/Doc'; -import { Utils, emptyFunction } from '../Utils'; +import { emptyFunction, Utils } from '../Utils'; import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils'; -import { IDatabase, DocumentsCollection } from './IDatabase'; +import { DocumentsCollection, IDatabase } from './IDatabase'; import { MemoryDatabase } from './MemoryDatabase'; -import * as mongoose from 'mongoose'; +import { Transferable } from './Message'; import { Upload } from './SharedMediaTypes'; export namespace Database { diff --git a/src/server/downsize.ts b/src/server/downsize.ts index 5cd709fa3..382994e2d 100644 --- a/src/server/downsize.ts +++ b/src/server/downsize.ts @@ -1,5 +1,5 @@ -import * as sharp from 'sharp'; import * as fs from 'fs'; +import * as sharp from 'sharp'; const folder = "./src/server/public/files/"; const pngTypes = ["png", "PNG"]; diff --git a/src/server/index.ts b/src/server/index.ts index 9af4b00bc..9185e3c5e 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,29 +1,29 @@ require('dotenv').config(); -import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; +import { yellow } from "colors"; import * as mobileDetect from 'mobile-detect'; import * as path from 'path'; -import { Database } from './database'; -import { DashUploadUtils } from './DashUploadUtils'; -import RouteSubscriber from './RouteSubscriber'; -import initializeServer, { resolvedPorts } from './server_Initialization'; -import RouteManager, { Method, _success, _permission_denied, _error, _invalid, PublicHandler } from './RouteManager'; import * as qs from 'query-string'; -import UtilManager from './ApiManagers/UtilManager'; -import { SearchManager } from './ApiManagers/SearchManager'; -import UserManager from './ApiManagers/UserManager'; -import DownloadManager from './ApiManagers/DownloadManager'; -import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader'; -import DeleteManager from "./ApiManagers/DeleteManager"; -import PDFManager from "./ApiManagers/PDFManager"; -import UploadManager from "./ApiManagers/UploadManager"; import { log_execution } from "./ActionUtilities"; +import DeleteManager from "./ApiManagers/DeleteManager"; +import DownloadManager from './ApiManagers/DownloadManager'; import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager"; -import HypothesisManager from "./ApiManagers/HypothesisManager"; import GooglePhotosManager from "./ApiManagers/GooglePhotosManager"; -import { Logger } from "./ProcessFactory"; -import { yellow } from "colors"; +import HypothesisManager from "./ApiManagers/HypothesisManager"; +import PDFManager from "./ApiManagers/PDFManager"; +import { SearchManager } from './ApiManagers/SearchManager'; import SessionManager from "./ApiManagers/SessionManager"; +import UploadManager from "./ApiManagers/UploadManager"; +import UserManager from './ApiManagers/UserManager'; +import UtilManager from './ApiManagers/UtilManager'; +import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader'; +import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent"; +import { DashUploadUtils } from './DashUploadUtils'; +import { Database } from './database'; +import { Logger } from "./ProcessFactory"; +import RouteManager, { Method, PublicHandler } from './RouteManager'; +import RouteSubscriber from './RouteSubscriber'; +import initializeServer, { resolvedPorts } from './server_Initialization'; export const AdminPriviliges: Map = new Map(); export const onWindows = process.platform === "win32"; diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 744d4547b..e40f2b8e5 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -1,31 +1,31 @@ +import * as bodyParser from 'body-parser'; +import { blue, yellow } from 'colors'; +import * as cookieParser from 'cookie-parser'; +import * as cors from "cors"; import * as express from 'express'; -import * as expressValidator from 'express-validator'; import * as session from 'express-session'; +import * as expressValidator from 'express-validator'; +import * as fs from 'fs'; +import { Server as HttpServer } from "http"; +import { createServer, Server as HttpsServer } from "https"; import * as passport from 'passport'; -import * as bodyParser from 'body-parser'; -import * as cookieParser from 'cookie-parser'; -import expressFlash = require('express-flash'); -import flash = require('connect-flash'); -import { Database } from './database'; -import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager'; -const MongoStore = require('connect-mongo')(session); -import RouteManager from './RouteManager'; -import { WebSocket } from './websocket'; +import * as request from 'request'; import * as webpack from 'webpack'; -const config = require('../../webpack.config'); -const compiler = webpack(config); import * as wdm from 'webpack-dev-middleware'; import * as whm from 'webpack-hot-middleware'; -import * as fs from 'fs'; -import * as request from 'request'; -import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; import { logPort } from './ActionUtilities'; -import { blue, yellow } from 'colors'; -import * as cors from "cors"; -import { createServer, Server as HttpsServer } from "https"; -import { Server as HttpServer } from "http"; import { SSL } from './apis/google/CredentialsLoader'; +import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager'; +import { Database } from './database'; +import RouteManager from './RouteManager'; +import RouteSubscriber from './RouteSubscriber'; +import { WebSocket } from './websocket'; +import expressFlash = require('express-flash'); +import flash = require('connect-flash'); +const MongoStore = require('connect-mongo')(session); +const config = require('../../webpack.config'); +const compiler = webpack(config); /* RouteSetter is a wrapper around the server that prevents the server from being exposed. */ diff --git a/src/server/websocket.ts b/src/server/websocket.ts index f63a35e43..d5f89a750 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -1,21 +1,20 @@ -import * as fs from 'fs'; -import { logPort } from './ActionUtilities'; -import { Utils } from "../Utils"; -import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent, RoomMessage } from "./Message"; -import { Client } from "./Client"; -import { Socket } from "socket.io"; -import { Database } from "./database"; -import { Search } from "./Search"; -import * as sio from 'socket.io'; -import YoutubeApi from "./apis/youtube/youtubeApiSample"; -import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader"; -import { timeMap } from "./ApiManagers/UserManager"; import { green } from "colors"; +import * as express from "express"; +import { createServer, Server } from "https"; import { networkInterfaces } from "os"; +import * as sio from 'socket.io'; +import { Socket } from "socket.io"; import executeImport from "../scraping/buxton/final/BuxtonImporter"; +import { Utils } from "../Utils"; +import { logPort } from './ActionUtilities'; +import { timeMap } from "./ApiManagers/UserManager"; +import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader"; +import YoutubeApi from "./apis/youtube/youtubeApiSample"; +import { Client } from "./Client"; +import { Database } from "./database"; import { DocumentsCollection } from "./IDatabase"; -import { createServer, Server } from "https"; -import * as express from "express"; +import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from "./Message"; +import { Search } from "./Search"; import { resolvedPorts } from './server_Initialization'; export namespace WebSocket { -- cgit v1.2.3-70-g09d2 From ac177d7b9a01075fbf1ad1c327c7625d95569afb Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sun, 2 Aug 2020 18:20:59 -0500 Subject: bug fixes found with sally --- src/client/views/PropertiesButtons.scss | 13 +++++++-- src/client/views/PropertiesButtons.tsx | 29 +++++++++++++------ src/client/views/collections/CollectionMenu.scss | 19 ++++++++----- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/PropertiesView.scss | 24 ++++++++++++++++ .../collectionFreeForm/PropertiesView.tsx | 33 ++++++++++------------ src/client/views/nodes/DocumentView.tsx | 8 +++--- src/client/views/nodes/FontIconBox.scss | 2 +- src/client/views/nodes/FontIconBox.tsx | 23 ++++++++------- src/client/views/nodes/LabelBox.tsx | 2 +- 11 files changed, 103 insertions(+), 54 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss index e099122c4..b3a317a6b 100644 --- a/src/client/views/PropertiesButtons.scss +++ b/src/client/views/PropertiesButtons.scss @@ -77,10 +77,19 @@ $linkGap : 3px; text-align: center; border-radius: 5px; pointer-events: auto; - color: $dark-color; - border: $dark-color 1px solid; + // color: $dark-color; + // border: $dark-color 1px solid; + background-color: #252b33; + color: #fcfbf7; transition: 0.2s ease all; margin-right: 5px; + padding-top: 5px; + + &:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; + } } .propertiesButtons-linker:hover { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 59e7cc7c8..d3f056a34 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -208,7 +208,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}>
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> @@ -238,6 +238,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { onAliasButtonDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); } + @undoBatch onAliasButtonMoved = () => { if (this._dragRef.current) { const dragDocView = this.selectedDocumentView!; @@ -273,12 +274,17 @@ export class PropertiesButtons extends React.Component<{}, {}> {
; } + @undoBatch onCopy = () => { if (this.selectedDoc && this.selectedDocumentView) { - const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); - copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); - copy.y = NumCast(this.selectedDoc.y) + 30; - this.selectedDocumentView.props.addDocument?.(copy); + // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); + // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); + // copy.y = NumCast(this.selectedDoc.y) + 30; + // this.selectedDocumentView.props.addDocument?.(copy); + const alias = Doc.MakeAlias(this.selectedDoc); + alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); + alias.y = NumCast(this.selectedDoc.y) + 30; + this.selectedDocumentView.props.addDocument?.(alias); } } @@ -295,7 +301,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { ; } - @action + @action @undoBatch onLock = () => { this.selectedDocumentView?.toggleLockPosition(); } @@ -337,15 +343,19 @@ export class PropertiesButtons extends React.Component<{}, {}> { return !targetDoc ? (null) :
{"Delete Document"}
}>
{ - this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document); - }}> + onPointerDown={this.deleteDocument}> {}
; } + @undoBatch + @action + deleteDocument = () => { + this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document); + } + @computed get sharingButton() { const targetDoc = this.selectedDoc; @@ -380,6 +390,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { } } + @undoBatch @action handleOptionChange = (e: any) => { const value = e.target.value; diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index 9da204787..0a316317f 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -2,8 +2,8 @@ .collectionMenu-cont { - position:relative; - display:inline-flex; + position: relative; + display: inline-flex; width: 100%; opacity: 0.9; z-index: 9001; @@ -12,14 +12,15 @@ color: white; transform-origin: top left; top: 0; - width:100%; + width: 100%; .antimodeMenu-button { padding: 0; width: 30px; display: flex; + svg { - margin:auto; + margin: auto; } } @@ -108,6 +109,7 @@ margin-top: auto; margin-bottom: auto; } + .collectionViewBaseChrome-viewSpecs { margin-left: 5px; display: grid; @@ -318,12 +320,14 @@ text-align: center; display: block; } + .color-previewI { width: 80%; height: 20%; bottom: 0; position: absolute; } + .color-previewII { width: 80%; height: 80%; @@ -336,7 +340,7 @@ margin: auto; /* Make the buttons appear below each other */ } - + .btn-draw { display: inline-flex; margin: auto; @@ -374,6 +378,7 @@ display: block; margin: auto; } + border-right: solid gray 1px; } } @@ -398,14 +403,14 @@ .collectionSchemaViewChrome-toggler { width: 100px; - height: 41px; + height: 35px; background-color: black; position: relative; } .collectionSchemaViewChrome-togglerButton { width: 47px; - height: 35px; + height: 30px; background-color: $light-color-secondary; // position: absolute; transition: all 0.5s ease; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7787a8b38..3c7471d7c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -785,7 +785,7 @@ export class CollectionTreeView extends CollectionSubView UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" }); - !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "hand-point-right" }); + !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); } outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {}); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index c2b55d38e..89034a0c0 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -339,7 +339,7 @@ export class CollectionView extends Touchable Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), })); - !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "hand-point-right" }); + !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); if (!Doc.UserDoc().noviceMode) { const more = cm.findByDescription("More..."); diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index a96716659..7df56115f 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -60,6 +60,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-settings-title-icon { float: right; right: 0; @@ -94,6 +98,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-sharing-title-icon { float: right; right: 0; @@ -127,6 +135,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-appearance-title-icon { float: right; right: 0; @@ -160,6 +172,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-transform-title-icon { float: right; right: 0; @@ -287,6 +303,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-fields-title-name { font-size: 12.5px; font-weight: bold; @@ -362,6 +382,10 @@ padding-left: 8px; background-color: rgb(51, 51, 51); + &:hover { + cursor: pointer; + } + .propertiesView-layout-title-icon { float: right; right: 0; diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 73f0f1bdd..0168c825f 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -205,7 +205,7 @@ export class PropertiesView extends React.Component { } } - + @undoBatch setKeyValue = (value: string) => { if (this.selectedDoc && this.dataDoc) { const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc; @@ -266,9 +266,14 @@ export class PropertiesView extends React.Component { } } + @undoBatch + changePermissions = (e: any, user: string) => { + SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!); + } + getPermissionsSelect(user: string) { return this.changePermissions(e, user)}> {Object.values(SharingPermissions).map(permission => { return ( @@ -318,12 +319,15 @@ export class PropertiesView extends React.Component { /** * @returns a row of the permissions panel */ - sharingItem(name: string, effectiveAcl: symbol, permission?: string) { - return
+ sharingItem(name: string, effectiveAcl: symbol, permission: string) { + return
this.selectedUser = this.selectedUser === name ? "" : name)} + >
{name}
{/* {name !== "Me" ? this.notifyIcon : null} */}
- {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission} + {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission} {permission === "Owner" ? this.expansionIcon : null}
; @@ -344,13 +348,21 @@ export class PropertiesView extends React.Component { const effectiveAcl = GetEffectiveAcl(this.selectedDoc!); const tableEntries = []; + // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { if (this.selectedDoc![AclSym]) { for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) { const name = key.substring(4).replace("_", "."); - if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!)); + if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author/* && sidebarUsersDisplayed![name] !== false*/) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!)); } } + // 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)); + // } + // } + // }) + // shifts the current user and the owner to the top of the doc. tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]))); if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), effectiveAcl, "Owner")); @@ -558,7 +570,7 @@ export class PropertiesView extends React.Component { getField(key: string) { //if (this.selectedDoc) { - return Field.toString(this.selectedDoc[key] as Field); + return Field.toString(this.selectedDoc![key] as Field); // } else { // return undefined as Opt; // } @@ -749,8 +761,18 @@ export class PropertiesView extends React.Component {
; } - render() { + /** + * Handles adding and removing members from the sharing panel + */ + // handleUserChange = (selectedUser: string, add: boolean) => { + // if (!Doc.UserDoc().sidebarUsersDisplayed) Doc.UserDoc().sidebarUsersDisplayed = new Doc; + // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { + // sidebarUsersDisplayed![`display-${selectedUser}`] = add; + // !add && runInAction(() => this.selectedUser = ""); + // }); + // } + render() { if (!this.selectedDoc) { return
@@ -800,6 +822,36 @@ export class PropertiesView extends React.Component { {!this.openSharing ? (null) :
{this.sharingTable} + {/*
+ + + + {this.addButtonPressed ? + // : + : + null} +
*/}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 60075d5cf..46eabf15a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -571,6 +571,7 @@ export class DocumentView extends DocComponent(Docu alert("Can't delete the active workspace"); } else { SelectionManager.DeselectAll(); + this.props.Document.deleted = true; this.props.removeDocument?.(this.props.Document); } } -- cgit v1.2.3-70-g09d2 From 33487aa34fd455acf5216cfee7913d6e36f390ed Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Fri, 7 Aug 2020 01:35:46 +0800 Subject: presentaiton options slightly reformatted :pear: --- src/client/views/PropertiesButtons.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../collectionFreeForm/PropertiesView.scss | 37 +++++++++ .../collectionFreeForm/PropertiesView.tsx | 64 ++++++++-------- src/client/views/nodes/PresBox.scss | 8 +- src/client/views/nodes/PresBox.tsx | 89 ++++++++++------------ .../views/nodes/formattedText/FormattedTextBox.tsx | 19 ++--- .../views/presentationview/PresElementBox.scss | 4 +- .../views/presentationview/PresElementBox.tsx | 3 +- 9 files changed, 128 insertions(+), 102 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/PropertiesView.scss') diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 55eb6c028..5c584d270 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -221,8 +221,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> - +
{ return (
document.addEventListener("keydown", PresBox.Instance.minimizeEvents, false)} > - {
document.addEventListener("keydown", PresBox.Instance.minimizeEvents, false)}> + {
PresBox.Instance.startAutoPres(PresBox.Instance.itemIndex)}>
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 5b41db90e..3ae94efb7 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -405,6 +405,43 @@ } } + + .propertiesView-presTrails { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-presTrails-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-presTrails-title-icon { + float: right; + right: 0; + position: absolute; + margin-left: 2px; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-presTrails-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } } .inking-button { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index 89f48fc65..cef2241c9 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -71,11 +71,10 @@ export class PropertiesView extends React.Component { @observable openAppearance: boolean = true; @observable openTransform: boolean = true; //Pres Trails booleans: - @observable openAddSlide: boolean = true; - @observable openPresentationTools: boolean = true; - @observable openPresTransitions: boolean = true; - @observable openPresProgressivize: boolean = true; - @observable openSlideOptions: boolean = true; + @observable openPresTransitions: boolean = false; + @observable openPresProgressivize: boolean = false; + @observable openAddSlide: boolean = false; + @observable openSlideOptions: boolean = false; @observable inActions: boolean = false; @observable _controlBtn: boolean = false; @@ -874,6 +873,7 @@ export class PropertiesView extends React.Component {
; } if (this.isPres) { + const selectedItem: boolean = PresBox.Instance._selectedArray.length > 0; return
Presentation @@ -890,57 +890,57 @@ export class PropertiesView extends React.Component {
-
-
runInAction(() => { this.openAddSlide = !this.openAddSlide; })} - style={{ backgroundColor: this.openAddSlide ? "black" : "" }}> -     Add new slide -
- -
-
- {this.openAddSlide ?
- {PresBox.Instance.newDocumentDropdown} -
: null} -
-
-
+
runInAction(() => { this.openPresTransitions = !this.openPresTransitions; })} style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>     Transitions -
+
- {this.openPresTransitions ?
+ {this.openPresTransitions ?
{PresBox.Instance.transitionDropdown}
: null} -
-
-
} + {!selectedItem ? (null) :
+
runInAction(() => { this.openPresProgressivize = !this.openPresProgressivize; })} style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>     Progressivize -
+
- {this.openPresProgressivize ?
+ {this.openPresProgressivize ?
{PresBox.Instance.progressivizeDropdown}
: null} -
-
-
} + {!selectedItem ? (null) :
+
runInAction(() => { this.openSlideOptions = !this.openSlideOptions; })} style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>     {PresBox.Instance.stringType} options -
+
- {this.openSlideOptions ?
+ {this.openSlideOptions ?
{PresBox.Instance.optionsDropdown}
: null} +
} +
+
runInAction(() => { this.openAddSlide = !this.openAddSlide; })} + style={{ backgroundColor: this.openAddSlide ? "black" : "" }}> +     Add new slide +
+ +
+
+ {this.openAddSlide ?
+ {PresBox.Instance.newDocumentDropdown} +
: null}
if (srcContext.miniPres) { srcContext.miniPres = false; CollectionDockingView.AddRightSplit(this.rootDoc); - document.removeEventListener("keydown", this.minimizeEvents, false); } else { srcContext.miniPres = true; this.props.addDocTab?.(this.rootDoc, "close"); - document.addEventListener("keydown", this.minimizeEvents, false); } } } @@ -506,20 +504,6 @@ export class PresBox extends ViewBoxBaseComponent } } - // Key events when the minimized player is active - @action - minimizeEvents = (e: KeyboardEvent) => { - e.stopPropagation(); - e.preventDefault(); - if (e.keyCode === 27) { // Escape key - this.layoutDoc.presStatus = "edit"; - } if (e.keyCode === 37) { // left(37) / a(65) / up(38) to go back - this.back(); - } if (e.keyCode === 39) { // right (39) / d(68) / down(40) to go to next - this.next(); - } - } - // Key for when the presentaiton is active (according to Selection Manager) @action keyEvents = (e: KeyboardEvent) => { @@ -531,10 +515,10 @@ export class PresBox extends ViewBoxBaseComponent else this.layoutDoc.presStatus = "edit"; } if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all if (this.layoutDoc.presStatus === "edit") this._selectedArray = this.childDocs; - } if (e.keyCode === 37) { // left(37) / a(65) / up(38) to go back - if (this.layoutDoc.presStatus !== "edit") this.back(); - } if (e.keyCode === 39) { // right (39) / d(68) / down(40) to go to next - if (this.layoutDoc.presStatus !== "edit") this.next(); + } if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back + this.back(); + } if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next + this.next(); } if (e.keyCode === 32) { // spacebar to 'present' or autoplay if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0); else this.layoutDoc.presStatus = "manual"; @@ -720,7 +704,7 @@ export class PresBox extends ViewBoxBaseComponent
{duration} s
) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} /> -
+
Short
Medium
Long
@@ -814,6 +798,20 @@ export class PresBox extends ViewBoxBaseComponent
activeItem.openDocument = !activeItem.openDocument}>Open document
+
+
{ + activeItem.presPinView = !activeItem.presPinView; + if (activeItem.presPinView) { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeItem.presPinViewX = x; + activeItem.presPinViewY = y; + activeItem.presPinViewScale = scale; + } + }}>Presentation pin view
+
Store original website
@@ -1592,35 +1590,26 @@ export class PresBox extends ViewBoxBaseComponent @computed get toolbar() { const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); - if (activeItem) { - return ( -
-
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> - - -
-
-
{"View paths"}
}>
- 1 ? this.viewPaths : undefined} /> -
-
{this.expandBoolean ? "Minimize all" : "Expand all"}
}> -
{ this.toggleExpand(); this.childDocs.forEach((doc, ind) => { if (this.expandBoolean) doc.presExpandInlineButton = true; else doc.presExpandInlineButton = false; }); }}> - -
-
-
-
- ); - } else { - return ( -
-
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> - - -
-
- ); - } + return ( +
+
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> + + +
+
+
{"View paths"}
}> +
1 ? 1 : 0.3 }} className={`toolbar-button ${this.pathBoolean ? "active" : ""}`} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}> + +
+
+
{this.expandBoolean ? "Minimize all" : "Expand all"}
}> +
0 ? 1 : 0.3 }} className={`toolbar-button ${this.expandBoolean ? "active" : ""}`} onClick={() => { if (this.childDocs.length > 0) this.toggleExpand(); this.childDocs.forEach((doc, ind) => { if (this.expandBoolean) doc.presExpandInlineButton = true; else doc.presExpandInlineButton = false; }); }}> + +
+
+
+
+ ); } /** diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index af4bd77c7..cc37cf586 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -577,7 +577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const mainBulletText: string[] = []; const mainBulletList: Doc[] = []; if (list) { - const newBullets: Doc[] = this.recursiveProgressivize(1, list); + const newBullets: Doc[] = this.recursiveProgressivize(1, list)[0]; mainBulletList.push.apply(mainBulletList, newBullets); } console.log(mainBulletList.length); @@ -591,7 +591,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.props.addDocument?.(doc); } - recursiveProgressivize = (nestDepth: number, list: HTMLCollectionOf, d?: number, y?: number, before?: string): Doc[] => { + recursiveProgressivize = (nestDepth: number, list: HTMLCollectionOf, d?: number, y?: number, before?: string): [Doc[], number] => { const mainBulletList: Doc[] = []; let b = d ? d : 0; let yLoc = y ? y : 0; @@ -607,29 +607,30 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (listItem.childElementCount > 1) { b++; nestCount++; - count = before ? count + nestCount + "." : nestCount + "."; yLoc += height; + count = before ? count + nestCount + "." : nestCount + "."; const text = listItem.getElementsByTagName("p")[0].innerText; const length = text.length; - const bullet1 = Docs.Create.TextDocument(count + " " + text, { title: "Slide text", _width: width, _height: height, x: xLoc, y: 10 + (yLoc), _fontSize: fontSize, backgroundColor: "rgba(0,0,0,0)", appearFrame: d ? d : b }); + const bullet1 = Docs.Create.TextDocument(count + " " + text, { title: "Slide text", _width: width, _autoHeight: true, x: xLoc, y: (yLoc), _fontSize: fontSize, backgroundColor: "rgba(0,0,0,0)", appearFrame: d ? d : b }); + // yLoc += NumCast(bullet1._height); mainBulletList.push(bullet1); const newList = this.recursiveProgressivize(nestDepth + 1, listItem.getElementsByTagName("li"), b, yLoc, count); - mainBulletList.push.apply(mainBulletList, newList); - b += newList.length; + mainBulletList.push.apply(mainBulletList, newList[0]); yLoc += newList.length * (55 - ((nestDepth + 1) * 5)); } else { b++; nestCount++; - count = before ? count + nestCount + "." : nestCount + "."; yLoc += height; + count = before ? count + nestCount + "." : nestCount + "."; const text = listItem.innerText; const length = text.length; - const bullet1 = Docs.Create.TextDocument(count + " " + text, { title: "Slide text", _width: width, _height: height, x: xLoc, y: 10 + (yLoc), _fontSize: fontSize, backgroundColor: "rgba(0,0,0,0)", appearFrame: d ? d : b }); + const bullet1 = Docs.Create.TextDocument(count + " " + text, { title: "Slide text", _width: width, _autoHeight: true, x: xLoc, y: (yLoc), _fontSize: fontSize, backgroundColor: "rgba(0,0,0,0)", appearFrame: d ? d : b }); + // yLoc += NumCast(bullet1._height); mainBulletList.push(bullet1); } } }); - return mainBulletList; + return [mainBulletList, yLoc]; } recordDictation = () => { diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss index 3d730d7ac..1e776384a 100644 --- a/src/client/views/presentationview/PresElementBox.scss +++ b/src/client/views/presentationview/PresElementBox.scss @@ -4,7 +4,7 @@ $light-background: #ececec; .presElementBox-item { display: grid; - grid-template-columns: max-content max-content max-content; + grid-template-columns: max-content max-content max-content max-content; background-color: #d5dce2; font-family: Roboto; letter-spacing: normal; @@ -122,7 +122,7 @@ $light-background: #ececec; padding-left: 10px; padding-right: 10px; letter-spacing: normal; - max-width: max-content; + width: max-content; text-overflow: ellipsis; overflow: hidden; white-space: pre; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 816577847..a6dbb76ef 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -326,11 +326,12 @@ export class PresElementBox extends ViewBoxBaseComponent {`${this.indexInPres + 1}.`}
-
+
{`${this.targetDoc?.title}`}
{"Movement speed"}
}>
300 ? "block" : "none" }}>{this.transition}
{"Duration"}
}>
300 ? "block" : "none" }}>{this.duration}
+
{"Presentation pin view"}
}>
300 ? "block" : "none" }}>V
{"Remove from presentation"}
}>
{ -- cgit v1.2.3-70-g09d2