diff options
-rw-r--r-- | src/client/util/SettingsManager.tsx | 5 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 8 | ||||
-rw-r--r-- | src/client/views/MainView.scss | 4 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 176 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 6 |
8 files changed, 119 insertions, 94 deletions
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index ff0b22381..e20434461 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -8,6 +8,8 @@ import { SelectionManager } from "./SelectionManager"; import "./SettingsManager.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Networking } from "../Network"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { Utils } from "../../Utils"; library.add(fa.faWindowClose); @@ -90,6 +92,9 @@ export default class SettingsManager extends React.Component<{}> { <div className="settings-type"> <button onClick={this.onClick} value="password">reset password</button> <button onClick={this.onClick} value="data">reset data</button> + <button onClick={() => window.location.assign(Utils.prepend("/logout"))}> + {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"} + </button> </div> {this.settingsContent === "password" ? <div className="settings-content"> diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9669c21a9..a3c476125 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -237,6 +237,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return false; } + _initialAutoHeight = false; @action onPointerDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { }); @@ -251,6 +252,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } this._snapX = e.pageX; this._snapY = e.pageY; + this._initialAutoHeight = true; } onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { @@ -353,7 +355,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { dW && (doc._width = actualdW); dH && (doc._height = actualdH); - dH && doc._autoHeight && (doc._autoHeight = false); + dH && this._initialAutoHeight && (doc._autoHeight = this._initialAutoHeight = false); } } })); @@ -362,6 +364,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onPointerUp = (e: PointerEvent): void => { + SelectionManager.SelectedDocuments().map(dv => { + dv.layoutDoc._delayAutoHeight && (dv.layoutDoc._autoHeight = true); + dv.layoutDoc._delayAutoHeight = undefined; + }); this._resizeHdlId = ""; this.Interacting = false; (e.button === 0) && this._resizeUndo?.end(); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 753ba700c..dca2a1e3e 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -107,7 +107,9 @@ position: absolute; left: 0; bottom: 0; - font-size: 8px; + border-radius: 25%; + margin-left: -5px; + background: darkblue; } .mainView-settings:hover { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 560dd5d11..62496b01f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -493,10 +493,7 @@ export class MainView extends React.Component { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> <button className="mainView-settings" key="settings" onClick={() => SettingsManager.Instance.open()}> - Settings - </button> - <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend("/logout"))}> - {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"} + <FontAwesomeIcon icon="cog" size="lg" /> </button> </div> {this.docButtons} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6c9ade83f..5cccec776 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1084,7 +1084,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu <DocumentContentsView {...OmitKeys(this.props, ['children']).omit} hideOnLeave={true} LayoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`} - ContentScaling={this.childScaling} + ContentScaling={returnOne} ChromeHeight={this.chromeHeight} isSelected={this.isSelected} select={this.select} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index d5a28fd14..3c6841f08 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -79,11 +79,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna this._reactionDisposer?.(); } + multiValueDelimeter = ";"; + // set the display of the field's value (checkbox for booleans, span of text for strings) @computed get fieldValueContent() { if (this._dashDoc) { const dashVal = this._dashDoc[this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : ""); - const fval = StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal; + const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal; const boolVal = Cast(fval, "boolean", null); const strVal = Field.toString(fval as Field) || ""; @@ -153,8 +155,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna } else if (nodeText.startsWith("=:=")) { Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3)); } else { + const splits = newText.split(this.multiValueDelimeter); if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) { - this._dashDoc![this._fieldKey] = newText; + this._dashDoc![this._fieldKey] = splits.length > 1 ? new List<string>(splits) : newText; } } }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2c51397eb..3a586ff66 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -658,11 +658,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp ); this._disposers.autoHeight = reaction( () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight], - () => this.tryUpdateHeight() + () => setTimeout(() => this.tryUpdateHeight(), 0) ); this._disposers.height = reaction( () => this.layoutDoc[HeightSym](), - height => height <= 20 && (this.layoutDoc._autoHeight = true) + action(height => { + if (height <= 20) { + if (this.layoutDoc._nativeWidth || this.layoutDoc._nativeHeight) { + Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight()); + } + this.layoutDoc._delayAutoHeight = true; + } + }) ); this.setupEditor(this.config, this.props.fieldKey); @@ -1184,8 +1191,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @action tryUpdateHeight(limitHeight?: number) { let scrollHeight = this._ref.current?.scrollHeight; - if (this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && - getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + if (this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + scrollHeight = scrollHeight * NumCast(this.layoutDoc.scale, 1); if (limitHeight && scrollHeight > limitHeight) { scrollHeight = limitHeight; this.layoutDoc.limitHeight = undefined; @@ -1234,87 +1241,90 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBoxComment.Hide(); } return ( - - <div className={`formattedTextBox-cont`} ref={this._ref} - style={{ - height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : `calc(100% - ${this.props.ChromeHeight?.() || 0}px`, - background: this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""), - opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1, - color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"), - pointerEvents: interactive ? "none" : undefined, - fontSize: Cast(this.layoutDoc._fontSize, "number", null), - fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"), - ...style - }} - onContextMenu={this.specificContextMenu} - onKeyDown={this.onKeyPress} - onFocus={this.onFocused} - onClick={this.onClick} - onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)} - onBlur={this.onBlur} - onPointerUp={this.onPointerUp} - onPointerDown={this.onPointerDown} - onMouseUp={this.onMouseUp} - onWheel={this.onPointerWheel} - onPointerEnter={action(() => this._entered = true)} - onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => { - this._entered = false; - const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); - for (let child: any = target; child; child = child?.parentElement) { - if (child === this._ref.current!) { - this._entered = true; + <div className={"formattedTextBox-cont"} style={{ + transform: `scale(${scale})`, + transformOrigin: "top left", + width: `${100 / scale}%`, + height: `${100 / scale}%`, + }}> + <div className={`formattedTextBox-cont`} ref={this._ref} + style={{ + width: "100%", + height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : `calc(100% - ${this.props.ChromeHeight?.() || 0}px`, + background: this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""), + opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1, + color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"), + pointerEvents: interactive ? "none" : undefined, + fontSize: Cast(this.layoutDoc._fontSize, "number", null), + fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"), + ...style + }} + onContextMenu={this.specificContextMenu} + onKeyDown={this.onKeyPress} + onFocus={this.onFocused} + onClick={this.onClick} + onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)} + onBlur={this.onBlur} + onPointerUp={this.onPointerUp} + onPointerDown={this.onPointerDown} + onMouseUp={this.onMouseUp} + onWheel={this.onPointerWheel} + onPointerEnter={action(() => this._entered = true)} + onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => { + this._entered = false; + const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); + for (let child: any = target; child; child = child?.parentElement) { + if (child === this._ref.current!) { + this._entered = true; + } } - } - })} - > - <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} onScroll={this.onscrolled} ref={this._scrollRef}> - <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} - style={{ - transform: `scale(${scale})`, - transformOrigin: "top left", - width: `${100 / scale}%`, - height: `${100 / scale}%`, - padding: `${NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0)}px`, - pointerEvents: ((this.layoutDoc.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined - }} /> + })} + > + <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} onScroll={this.onscrolled} ref={this._scrollRef}> + <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} + style={{ + padding: `${NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0)}px`, + pointerEvents: ((this.layoutDoc.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined + }} /> + </div> + {!this.layoutDoc._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ? + <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> : + <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")} + style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> + <CollectionFreeFormView {...this.props} + PanelHeight={this.props.PanelHeight} + PanelWidth={this.sidebarWidth} + NativeHeight={returnZero} + NativeWidth={returnZero} + annotationsKey={this.annotationKey} + isAnnotationOverlay={false} + focus={this.props.focus} + isSelected={this.props.isSelected} + select={emptyFunction} + active={this.annotationsActive} + ContentScaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + CollectionView={undefined} + ScreenToLocalTransform={this.sidebarScreenToLocal} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionDoc}> + </CollectionFreeFormView> + <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> + </div>} + {!this.layoutDoc._showAudio ? (null) : + <div className="formattedTextBox-dictation" + onPointerDown={e => { + runInAction(() => this._recording = !this._recording); + setTimeout(() => this._editorView!.focus(), 500); + e.stopPropagation(); + }} > + <FontAwesomeIcon className="formattedTExtBox-audioFont" + style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" /> + </div>} </div> - {!this.layoutDoc._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ? - <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> : - <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")} - style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> - <CollectionFreeFormView {...this.props} - PanelHeight={this.props.PanelHeight} - PanelWidth={this.sidebarWidth} - NativeHeight={returnZero} - NativeWidth={returnZero} - annotationsKey={this.annotationKey} - isAnnotationOverlay={false} - focus={this.props.focus} - isSelected={this.props.isSelected} - select={emptyFunction} - active={this.annotationsActive} - ContentScaling={returnOne} - whenActiveChanged={this.whenActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocument} - CollectionView={undefined} - ScreenToLocalTransform={this.sidebarScreenToLocal} - renderDepth={this.props.renderDepth + 1} - ContainingCollectionDoc={this.props.ContainingCollectionDoc}> - </CollectionFreeFormView> - <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> - </div>} - {!this.layoutDoc._showAudio ? (null) : - <div className="formattedTextBox-dictation" - onPointerDown={e => { - runInAction(() => this._recording = !this._recording); - setTimeout(() => this._editorView!.focus(), 500); - e.stopPropagation(); - }} > - <FontAwesomeIcon className="formattedTExtBox-audioFont" - style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" /> - </div>} </div> ); } diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index d619bc4a0..0ba591fec 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -11,6 +11,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import { wrappingInputRule } from "./prosemirrorPatches"; import RichTextMenu from "./RichTextMenu"; import { schema } from "./schema_rts"; +import { List } from "../../../../new_fields/List"; export class RichTextRules { public Document: Doc; @@ -64,11 +65,12 @@ export class RichTextRules { // create an inline view of a tag stored under the '#' field new InputRule( - new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), + new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_;\-0-9]*)\s$/), (state, match, start, end) => { const tag = match[1]; if (!tag) return state.tr; - this.Document[DataSym]["#"] = tag; + const multiple = tag.split(";"); + this.Document[DataSym]["#"] = multiple.length > 1 ? new List(multiple) : tag; const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" }); return state.tr.deleteRange(start, end).insert(start, fieldView); }), |