From cb59842fcb79af7ef72443e08c6a479c3f0af343 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 25 Apr 2022 20:20:48 -0400 Subject: changed freeformview's with engines to not allow children to be edited using setHeight prop since engine layouts are temporary and shouldn't edit the doc. fixed tags to work with pivot view. cleaned up pile views to be less hacky.. --- src/client/views/MainView.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a67cb3014..68b4710ed 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -297,6 +297,7 @@ export class MainView extends React.Component { searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} + suppressSetHeight={true} renderDepth={-1} />; } -- cgit v1.2.3-70-g09d2 From 08217336445cf2b3cd3efbe97e3c83525f02bf1b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 26 Apr 2022 22:34:21 -0400 Subject: added image cropping. made treeView icons show up for file system and dashboard, and made them persist invisibly to fix triggering on hover. changed tree view filesystem and dashboard to show expanded icons on hover. --- src/client/views/MainView.tsx | 2 +- src/client/views/MarqueeAnnotator.tsx | 27 ++++++++++++- src/client/views/PropertiesView.tsx | 4 +- src/client/views/StyleProvider.scss | 2 +- src/client/views/collections/TreeView.scss | 23 +++++++++-- src/client/views/collections/TreeView.tsx | 16 ++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++++---- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 45 ++++++++++++++++++++++ .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- src/client/views/pdf/AnchorMenu.tsx | 16 +++++++- 11 files changed, 132 insertions(+), 27 deletions(-) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 68b4710ed..b73074899 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -362,7 +362,7 @@ export class MainView extends React.Component { pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} layerProvider={undefined} - styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards ? DashboardStyleProvider : DefaultStyleProvider} + styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider} rootSelected={returnTrue} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 563261dec..a6b012bd6 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -5,7 +5,7 @@ import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { NumCast } from "../../fields/Types"; import { GetEffectiveAcl } from "../../fields/util"; -import { Utils } from "../../Utils"; +import { unimplementedFunction, Utils } from "../../Utils"; import { Docs } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; @@ -33,6 +33,7 @@ export interface MarqueeAnnotatorProps { getPageFromScroll?: (top: number) => number; finishMarquee: (x?: number, y?: number, PointerEvent?: PointerEvent) => void; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); + anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined; } @observer export class MarqueeAnnotator extends React.Component { @@ -63,6 +64,7 @@ export class MarqueeAnnotator extends React.Component { doc.addEventListener("pointermove", this.onSelectMove); doc.addEventListener("pointerup", this.onSelectEnd); + AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight("rgba(173, 216, 230, 0.75)", true), true); AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true)); AnchorMenu.Instance.Highlight = this.highlight; AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations); @@ -93,6 +95,29 @@ export class MarqueeAnnotator extends React.Component { } }); }); + /** + * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation. + * It also initiates a Drag/Drop interaction to place the text annotation. + */ + AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop ? unimplementedFunction : action((e: PointerEvent, ele: HTMLElement) => { + e.preventDefault(); + e.stopPropagation(); + var cropRegion: Doc | undefined; + const sourceAnchorCreator = () => { + cropRegion = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color + cropRegion && this.props.addDocument(cropRegion); + return cropRegion; + }; + const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!; + DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { + dragComplete: e => { + if (!e.aborted && e.linkDocument) { + Doc.GetProto(e.linkDocument).linkRelationship = "cropped image"; + Doc.GetProto(e.linkDocument).title = "crop: " + this.props.docView.rootDoc.title; + } + } + }); + }); } componentWillUnmount() { const doc = (this.props.iframe?.()?.contentDocument ?? document); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 91cf83619..b63395c76 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1360,12 +1360,12 @@ export class PropertiesView extends React.Component { {this.optionsSubMenu} + {this.fieldsSubMenu} + {this.sharingSubMenu} {isNovice ? null : this.filtersSubMenu} - {isNovice ? null : this.fieldsSubMenu} - {isNovice ? null : this.layoutSubMenu} ; } diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss index f26ed1f2d..8929954c8 100644 --- a/src/client/views/StyleProvider.scss +++ b/src/client/views/StyleProvider.scss @@ -25,5 +25,5 @@ } .styleProvider-treeView-icon { - display: none; + opacity: 0; } \ No newline at end of file diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index 7c75810d3..4707ebb80 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -21,7 +21,9 @@ } .treeView-bulletIcons { - width: $TREE_BULLET_WIDTH; + // width: $TREE_BULLET_WIDTH; + width: 100%; + height: 100%; .treeView-expandIcon { display: none; @@ -37,10 +39,17 @@ &:hover { .treeView-expandIcon { - display: unset; + display: unset; } } } + .treeView-bulletIcons:hover img { + left: 14px; + position: absolute; + transform-origin: center left; + transform: scale(6); + pointer-events:none; + } .bullet { position: relative; @@ -60,6 +69,10 @@ pointer-events: all; } +.bullet:hover { + z-index: 100; +} + .treeView-openRight { display: none; height: 17px; @@ -123,7 +136,9 @@ } >svg { - display: none; + //display: none; + opacity: 0; + pointer-events: none; } } } @@ -150,6 +165,8 @@ >svg, .styleProvider-treeView-icon { display: inherit; + opacity: unset; + pointer-events: unset; } } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index b9710b3f5..9108acfcf 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -101,8 +101,9 @@ export class TreeView extends React.Component { get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive get defaultExpandedView() { return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : - this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "layout") : - this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); + this.props.treeView.dashboardMode ? this.fieldKey : + this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : + this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); } @computed get doc() { return this.props.document; } @@ -491,15 +492,12 @@ export class TreeView extends React.Component { } @computed get validExpandViewTypes() { - if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) { - return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"]; - } - const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; - const links = () => DocListCast(this.doc.links).length ? "links" : ""; - const data = () => this.childDocs ? this.fieldKey : ""; + const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : ""; + const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : ""; + const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ""; const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; + const layout = (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bdae5a4f8..f0b3d70a0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -936,8 +936,8 @@ export class CollectionFreeFormView extends CollectionSubView 20) { deltaScale = 20 / invTransform.Scale; } - if (deltaScale * invTransform.Scale < 1 && this.isAnnotationOverlay) { - deltaScale = 1 / invTransform.Scale; + if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) { + deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale; } const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y); @@ -989,8 +989,13 @@ export class CollectionFreeFormView extends CollectionSubView { + if (!region) return; + const cropping = Doc.MakeCopy(region, true); + Doc.GetProto(region).lockedPosition = true; + Doc.GetProto(region).title = "region:" + this.rootDoc.title; + Doc.GetProto(region).isPushpin = true; + this.addDocument(region); + const anchx = NumCast(cropping.x); + const anchy = NumCast(cropping.y); + const anchw = NumCast(cropping._width); + const anchh = NumCast(cropping._height); + const viewScale = NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]) / anchw; + cropping.title = "crop: " + this.rootDoc.title; + cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); + cropping.y = NumCast(this.rootDoc.y); + cropping._width = anchw * (this.props.scaling?.() || 1); + cropping._height = anchh * (this.props.scaling?.() || 1); + cropping.isLinkButton = undefined; + const croppingProto = Doc.GetProto(cropping); + croppingProto.annotationOn = undefined; + croppingProto.isPrototype = true; + croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.type = DocumentType.IMG; + croppingProto.layout = ImageBox.LayoutString("data") + croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField); + croppingProto["data-nativeWidth"] = anchw; + croppingProto["data-nativeHeight"] = anchh; + croppingProto.viewScale = viewScale; + croppingProto.viewScaleMin = viewScale; + croppingProto.panX = anchx / viewScale; + croppingProto.panY = anchy / viewScale; + croppingProto.panXMin = (anchx) / viewScale; + croppingProto.panXMax = (anchw) / viewScale; + croppingProto.panYMin = (anchy) / viewScale; + croppingProto.panYMax = (anchh) / viewScale; + if (addCrop) { + DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", ""); + } + this.props.bringToFront(cropping); + return cropping; + } + specificContextMenu = (e: React.MouseEvent): void => { const field = Cast(this.dataDoc[this.fieldKey], ImageField); if (field) { @@ -367,6 +411,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent} ); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 710270be4..41e2fc266 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -19,7 +19,7 @@ import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from '../../../documents/Documents'; @@ -219,6 +219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return undefined; }); AnchorMenu.Instance.onMakeAnchor = this.getAnchor; + AnchorMenu.Instance.StartCropDrag = unimplementedFunction; /** * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. * It also initiates a Drag/Drop interaction to place the text annotation. diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index ad3afb775..29d068817 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -46,8 +46,10 @@ export class AnchorMenu extends AntimodeMenu { public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search + public OnCrop: (e: PointerEvent) => void = unimplementedFunction; public OnClick: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; + public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; public GetAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; public Delete: () => void = unimplementedFunction; @@ -79,6 +81,13 @@ export class AnchorMenu extends AntimodeMenu { }, returnFalse, e => this.OnClick?.(e)); } + cropDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, (e: PointerEvent) => { + this.StartCropDrag(e, this._commentCont.current!); + return true; + }, returnFalse, e => this.OnCrop?.(e)); + } + @action highlightClicked = (e: React.MouseEvent) => { if (!this.Highlight(this.highlightColor, false) && this.Pinned) { @@ -161,7 +170,12 @@ export class AnchorMenu extends AntimodeMenu { , - + , + AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? <> : {"Click/Drag to create cropped image"}}> + + , ] : [ {"Remove Link Anchor"}}>