diff options
Diffstat (limited to 'src/client/views/PropertiesView.tsx')
-rw-r--r-- | src/client/views/PropertiesView.tsx | 182 |
1 files changed, 171 insertions, 11 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index daa8e1720..6cf16825a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -2,7 +2,7 @@ import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@mui/material'; -import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components'; +import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Toggle, ToggleType, Type } from 'browndash-components'; import { concat } from 'lodash'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -12,13 +12,13 @@ import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmark import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; import { ComputedField } from '../../fields/ScriptField'; -import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { GroupManager } from '../util/GroupManager'; @@ -41,6 +41,10 @@ import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; +import { InkingStroke } from './InkingStroke'; +import { SettingsManager } from '../util/SettingsManager'; +import { MarqueeOptionsMenu } from './collections/collectionFreeForm'; +import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; interface PropertiesViewProps { width: number; @@ -113,6 +117,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @observable openAddSlide: boolean = false; @observable openSlideOptions: boolean = false; + // For ink groups + @observable containsInkDoc: boolean = false; + @observable inkDoc: Doc | undefined = undefined; + @observable _controlButton: boolean = false; private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -813,8 +821,17 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps set shapeWid(value) { this.selectedDoc && (this.selectedDoc._width = Math.round(value * 100) / 100); } // prettier-ignore @computed get shapeHgt() { return NumCast(this.selectedDoc?._height); } // prettier-ignore set shapeHgt(value) { this.selectedDoc && (this.selectedDoc._height = Math.round(value * 100) / 100); } // prettier-ignore - @computed get strokeThk(){ return NumCast(this.selectedDoc?.[DocData].stroke_width); } // prettier-ignore - set strokeThk(value) { this.selectedDoc && (this.selectedDoc[DocData].stroke_width = Math.round(value * 100) / 100); } // prettier-ignore + @computed get strokeThk(){ return this.containsInkDoc ? NumCast(this.inkDoc?.[DocData].stroke_width) : NumCast(this.selectedDoc?.[DocData].stroke_width); } // prettier-ignore + set strokeThk(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_width = Math.round(value * 100) / 100; + }) + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_width = Math.round(value * 100) / 100); + } + } // prettier-ignore @computed get hgtInput() { return this.inputBoxDuo( @@ -851,10 +868,32 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps private _lastDash: string = '2'; - @computed get colorFil() { return StrCast(this.selectedDoc?.[DocData].fillColor); } // prettier-ignore - set colorFil(value) { this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); } // prettier-ignore - @computed get colorStk() { return StrCast(this.selectedDoc?.[DocData].color); } // prettier-ignore - set colorStk(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore + @computed get colorFil() { return this.containsInkDoc ? StrCast(this.inkDoc?.[DocData].fillColor) : StrCast(this.selectedDoc?.[DocData].fillColor); } // prettier-ignore + set colorFil(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + const inkStroke = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; + const { inkData } = inkStroke.inkScaledData(); + if (InkingStroke.IsClosed(inkData)) { + doc[DocData].fillColor = value || undefined; + } + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); + } + } + @computed get colorStk() { return this.containsInkDoc ? StrCast(this.inkDoc?.[DocData].color) : StrCast(this.selectedDoc?.[DocData].color); } // prettier-ignore + set colorStk(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].color = value || undefined; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); + } + } colorButton(value: string, type: string, setter: () => void) { return ( @@ -925,10 +964,69 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps ); } - @computed get dashdStk() { return StrCast(this.selectedDoc?.stroke_dash); } // prettier-ignore + @computed get smoothAndColor() { + const targetDoc = this.selectedLayoutDoc; + return ( + <div> + {!targetDoc.layout_isSvg && ( + <div className="color"> + <Toggle + text={'Color with GPT'} + color={SettingsManager.userColor} + icon={<FontAwesomeIcon icon="fill-drip" />} + iconPlacement="left" + align="flex-start" + fillWidth + toggleType={ToggleType.BUTTON} + onClick={undoable(() => { + SmartDrawHandler.Instance.colorWithGPT(targetDoc); + }, 'smoothStrokes')} + /> + </div> + )} + <div className="smooth"> + <Toggle + text={'Smooth Ink Strokes'} + color={SettingsManager.userColor} + icon={<FontAwesomeIcon icon="bezier-curve" />} + iconPlacement="left" + align="flex-start" + fillWidth + toggleType={ToggleType.BUTTON} + onClick={undoable(() => { + InkStrokeProperties.Instance.smoothInkStrokes(this.containsInkDoc ? DocListCast(targetDoc.data) : [targetDoc], this.smoothAmt); + }, 'smoothStrokes')} + /> + </div> + <div className="smooth-slider"> + {this.getNumber( + 'Smooth Amount', + '', + 1, + Math.max(20, this.smoothAmt), + this.smoothAmt, + (val: number) => { + !isNaN(val) && (this.smoothAmt = val); + }, + 20, + 1 + )} + </div> + </div> + ); + } + + @computed get dashdStk() { return this.containsInkDoc? this.inkDoc?.stroke_dash || '' : this.selectedDoc?.stroke_dash || ''; } // prettier-ignore set dashdStk(value) { value && (this._lastDash = value); - this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined); + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_dash = value ? this._lastDash : undefined; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined); + } } @computed get widthStk() { return this.getField('stroke_width') || '1'; } // prettier-ignore set widthStk(value) { @@ -936,14 +1034,36 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } @computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore set markScal(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_markerScale = Number(value); + }); + } this.selectedDoc && (this.selectedDoc[DocData].stroke_markerScale = Number(value)); } + @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '10'); } // prettier-ignore + set smoothAmt(value) { + this.selectedDoc && (this.selectedDoc[DocData].stroke_smoothAmount = Number(value)); + } @computed get markHead() { return this.getField('stroke_startMarker') || ''; } // prettier-ignore set markHead(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_startMarker = value; + }); + } this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value); } @computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore set markTail(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_endMarker = value; + }); + } this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value); } @@ -1056,6 +1176,16 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="appearance-editor"> {this.widthAndDash} {this.strokeAndFill} + {this.smoothAndColor} + </div> + ); + } + + @computed get inkEditor() { + return ( + <div className="ink-editor"> + {this.widthAndDash} + {this.strokeAndFill} </div> ); } @@ -1185,6 +1315,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } @computed get inkSubMenu() { + this.containsInkDoc = false; return ( // prettier-ignore <> @@ -1198,6 +1329,34 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps ); } + /** + * Determines if a selected collection/group document contains any ink strokes to allow users to edit groups + * of ink strokes in the properties menu. + */ + containsInk = (selectedDoc: Doc) => { + const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data); + for (var i = 0; i < childDocs.length; i++) { + if (DocumentView.getDocumentView(childDocs[i])?.layoutDoc?.layout_isSvg) { + this.inkDoc = childDocs[i]; + this.containsInkDoc = true; + return true; + } + } + this.containsInkDoc = false; + return false; + }; + + @computed get inkCollectionSubMenu() { + return ( + // prettier-ignore + <> + <PropertiesSection title="Ink Appearance" isOpen={this.openAppearance} setIsOpen={bool => { this.openAppearance = bool; }} onDoubleClick={this.CloseAll}> + {this.isGroup && this.containsInk(this.selectedDoc) ? this.appearanceEditor : null} + </PropertiesSection> + </> + ); + } + @computed get fieldsSubMenu() { return ( <PropertiesSection @@ -1749,6 +1908,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps {this.linksSubMenu} {!this.selectedLink || !this.openLinks ? null : this.linkProperties} {this.inkSubMenu} + {this.inkCollectionSubMenu} {this.contextsSubMenu} {isNovice ? null : this.sharingSubMenu} {this.filtersSubMenu} |