aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/PropertiesView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/PropertiesView.tsx')
-rw-r--r--src/client/views/PropertiesView.tsx180
1 files changed, 166 insertions, 14 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 024db82a4..ac2625f32 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -5,7 +5,7 @@ import { IconLookup } 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';
@@ -14,13 +14,13 @@ import { ColorResult, SketchPicker } from 'react-color';
import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
-import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } 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';
@@ -43,6 +43,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';
const _global = (window /* browser */ || global) /* node */ as any;
@@ -117,6 +121,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 } = {};
@@ -794,7 +802,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
};
getField(key: string) {
- return Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
+ return this.containsInkDoc ? Field.toString(this.inkDoc?.[DocData][key] as FieldType) : Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
}
@computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore
@@ -805,8 +813,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(
@@ -843,10 +860,32 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
private _lastDash: any = '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 (
@@ -917,10 +956,69 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
);
}
- @computed get dashdStk() { return this.selectedDoc?.stroke_dash || ''; } // prettier-ignore
+ @computed get smoothAndColor() {
+ const targetDoc = this.selectedLayoutDoc;
+ return (
+ <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>
+ {!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>
+ );
+ }
+
+ @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) {
@@ -930,13 +1028,31 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
set markScal(value) {
this.selectedDoc && (this.selectedDoc[DocData].stroke_markerScale = Number(value));
}
+ @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '1'); } // 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) {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
+ if (this.containsInkDoc) {
+ const childDocs = DocListCast(this.selectedDoc[DocData].data);
+ childDocs.forEach(doc => {
+ doc[DocData].stroke_startMarker = value;
+ });
+ } else {
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
+ }
}
@computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore
set markTail(value) {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
+ if (this.containsInkDoc) {
+ const childDocs = DocListCast(this.selectedDoc[DocData].data);
+ childDocs.forEach(doc => {
+ doc[DocData].stroke_endMarker = value;
+ });
+ } else {
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
+ }
}
regInput = (key: string, value: any, setter: (val: string) => {}) => (
@@ -1036,6 +1152,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>
);
}
@@ -1164,6 +1290,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
}
@computed get inkSubMenu() {
+ this.containsInkDoc = false;
return (
// prettier-ignore
<>
@@ -1177,6 +1304,30 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
);
}
+ 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
@@ -1737,6 +1888,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}