From ec8a635f7fadf7a25b38f0be757ff5ae010bb417 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 24 Apr 2023 15:47:37 -0400 Subject: added text rule for switching to alternate text. --- src/client/views/nodes/formattedText/RichTextRules.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index e691869cc..0ca6b415f 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -5,7 +5,7 @@ import { Id } from '../../../../fields/FieldSymbols'; import { ComputedField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; import { normalizeEmail } from '../../../../fields/util'; -import { returnFalse, Utils } from '../../../../Utils'; +import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; import { FormattedTextBox } from './FormattedTextBox'; @@ -228,6 +228,12 @@ export class RichTextRules { return null; }), + // stop using active style + new InputRule(new RegExp(/#alt$/), (state, match, start, end) => { + setTimeout(() => (this.Document[this.TextBox.props.fieldKey + '-usePath'] = this.Document[this.TextBox.props.fieldKey + '-usePath'] ? undefined : 'alternate')); + return state.tr.deleteRange(start, end); + }), + // stop using active style new InputRule(new RegExp(/%%$/), (state, match, start, end) => { const tr = state.tr.deleteRange(start, end); -- cgit v1.2.3-70-g09d2 From 5f24c3f7e23808bff71ecfbc7ecca0c0f823ff4f Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 25 Apr 2023 10:46:09 -0400 Subject: fixed text footnotes to not overflow over the left edge of the box. updated a few markdown command syntaxes. --- .../nodes/formattedText/FormattedTextBox.scss | 34 ++++++++++------------ .../views/nodes/formattedText/RichTextRules.ts | 6 ++-- 2 files changed, 18 insertions(+), 22 deletions(-) (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index eb9dd1e66..b5a3c5d84 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -225,16 +225,15 @@ audiotag:hover { } footnote { - display: inline-block; + display: inline-flex; + top: -0.5em; position: relative; cursor: pointer; - - div { - padding: 0 !important; - } + height: 1em; + width: 0.5em; } -footnote::after { +footnote::before { content: counter(prosemirror-footnote); vertical-align: super; font-size: 75%; @@ -248,15 +247,14 @@ footnote::after { .footnote-tooltip { cursor: auto; font-size: 75%; - position: absolute; - left: -30px; - top: calc(100% + 10px); + position: relative; background: silver; - padding: 3px; border-radius: 2px; - max-width: 100px; - min-width: 50px; - width: max-content; + min-width: 100px; + top: 2em; + height: max-content; + left: -1em; + padding: 3px; } .prosemirror-attribution { @@ -271,8 +269,7 @@ footnote::after { border-left-color: transparent; border-right-color: transparent; position: absolute; - top: -5px; - left: 27px; + top: -0.5em; content: ' '; height: 0; width: 0; @@ -766,8 +763,8 @@ footnote::after { cursor: auto; font-size: 75%; position: absolute; - left: -30px; - top: calc(100% + 10px); + // left: -30px; + // top: calc(100% + 10px); background: silver; padding: 3px; border-radius: 2px; @@ -788,8 +785,7 @@ footnote::after { border-left-color: transparent; border-right-color: transparent; position: absolute; - top: -5px; - left: 27px; + top: -0.5em; content: ' '; height: 0; width: 0; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 0ca6b415f..23e776738 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -28,7 +28,7 @@ export class RichTextRules { emDash, // > blockquote - wrappingInputRule(/^\s*>\s$/, schema.nodes.blockquote), + wrappingInputRule(/%>\s$/, schema.nodes.blockquote), // 1. create numerical ordered list wrappingInputRule( @@ -229,7 +229,7 @@ export class RichTextRules { }), // stop using active style - new InputRule(new RegExp(/#alt$/), (state, match, start, end) => { + new InputRule(new RegExp(/%alt$/), (state, match, start, end) => { setTimeout(() => (this.Document[this.TextBox.props.fieldKey + '-usePath'] = this.Document[this.TextBox.props.fieldKey + '-usePath'] ? undefined : 'alternate')); return state.tr.deleteRange(start, end); }), @@ -302,7 +302,7 @@ export class RichTextRules { // create an inline equation node // eq:> - new InputRule(new RegExp(/:eq([a-zA-Z-0-9\(\)]*)$/), (state, match, start, end) => { + new InputRule(new RegExp(/%eq([a-zA-Z-0-9\(\)]*)$/), (state, match, start, end) => { const fieldKey = 'math' + Utils.GenerateGuid(); this.TextBox.dataDoc[fieldKey] = match[1]; const tr = state.tr.setSelection(new TextSelection(state.tr.doc.resolve(end - 3), state.tr.doc.resolve(end))).replaceSelectionWith(schema.nodes.equation.create({ fieldKey })); -- cgit v1.2.3-70-g09d2 From 3975caa4e06c8097229dcfd1d0791732cab44acb Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 25 Apr 2023 11:26:05 -0400 Subject: added shortcuts to text toolbar. changed text hyperlink shortcut to search through documents by title before linking to a new document. --- src/client/DocServer.ts | 7 +++++ src/client/util/CurrentUserUtils.ts | 10 +++---- .../views/nodes/formattedText/RichTextRules.ts | 32 ++++++++++++---------- 3 files changed, 30 insertions(+), 19 deletions(-) (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 8f79ebb03..ba64f993c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -29,6 +29,13 @@ import { GestureOverlay } from './views/GestureOverlay'; export namespace DocServer { let _cache: { [id: string]: RefField | Promise> } = {}; + export function QUERY_SERVER_CACHE(title: string) { + const foundDocId = Array.from(Object.keys(_cache)) + .filter(key => _cache[key] instanceof Doc) + .find(key => (_cache[key] as Doc).title === title); + + return foundDocId ? (_cache[foundDocId] as Doc) : undefined; + } export function UPDATE_SERVER_CACHE(print: boolean = false) { if (print) { const strings: string[] = []; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cdb12624f..4a90edd49 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -631,17 +631,17 @@ export class CurrentUserUtils { return [ { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, toolType:"font", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, - { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, - { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}}, + { title: "Size", toolTip: "Font size (%size)", btnType: ButtonType.NumberButton, width: 75, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, + { title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}}, { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} }, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, - { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Left", toolTip: "Left align (%[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, + { title: "Center", toolTip: "Center align (%^)", btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Right", toolTip: "Right align (%])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}}, { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}}, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}}, diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 23e776738..599f96bca 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -256,22 +256,26 @@ export class RichTextRules { const fieldKey = match[1]; const docId = match[3]?.replace(':', ''); const value = match[2]?.substring(1); + const linkToDoc = (target: Doc) => { + const rstate = this.TextBox.EditorView?.state; + const selection = rstate?.selection.$from.pos; + if (rstate) { + this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3)))); + } + + DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { linkRelationship: 'portal to:portal from' }); + + const fstate = this.TextBox.EditorView?.state; + if (fstate && selection) { + this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection)))); + } + }; if (!fieldKey) { if (docId) { - DocServer.GetRefField(docId).then(docx => { - const rstate = this.TextBox.EditorView?.state; - const selection = rstate?.selection.$from.pos; - if (rstate) { - this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3)))); - } - const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500 }, docId); - DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { linkRelationship: 'portal to:portal from' }); - - const fstate = this.TextBox.EditorView?.state; - if (fstate && selection) { - this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection)))); - } - }); + const target = DocServer.QUERY_SERVER_CACHE(docId); + if (target) setTimeout(() => linkToDoc(target)); + else DocServer.GetRefField(docId).then(docx => linkToDoc((docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId + '(auto)', _width: 500, _height: 500 }, docId))); + return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3); } return state.tr; -- cgit v1.2.3-70-g09d2 From 1150296b546bb3628c3dac5704150dfb4295434b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 26 Apr 2023 16:03:49 -0400 Subject: added cheatsheet and shortcut for viewing text shortcuts. changed menu organization slightly for viewability. --- src/client/util/RTFMarkup.tsx | 137 +++++++++++++++++++++ src/client/util/ServerStats.tsx | 3 +- src/client/views/MainView.tsx | 8 +- src/client/views/MainViewModal.tsx | 37 +++--- src/client/views/nodes/DocumentView.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 + .../views/nodes/formattedText/RichTextRules.ts | 7 ++ 7 files changed, 177 insertions(+), 23 deletions(-) create mode 100644 src/client/util/RTFMarkup.tsx (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx new file mode 100644 index 000000000..306d151d7 --- /dev/null +++ b/src/client/util/RTFMarkup.tsx @@ -0,0 +1,137 @@ +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { MainViewModal } from '../views/MainViewModal'; + +@observer +export class RTFMarkup extends React.Component<{}> { + static Instance: RTFMarkup; + @observable private isOpen = false; // whether the SharingManager modal is open or not + + // private get linkVisible() { + // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false; + // } + + @action + public open = () => (this.isOpen = true); + + @action + public close = () => (this.isOpen = false); + + constructor(props: {}) { + super(props); + RTFMarkup.Instance = this; + } + + @observable _stats: { [key: string]: any } | undefined; + + /** + * @returns the main interface of the SharingManager. + */ + @computed get cheatSheet() { + return ( +
+

+ {`wiki:phrase`} + {` display wikipedia page for entered text (terminate with carriage return)`} +

+

+ {`#tag `} + {` add hashtag metadata to document. e.g, #idea`} +

+

+ {`#, ## ... ###### `} + {` set heading style based on number of '#'s between 1 and 6`} +

+

+ {`#tag `} + {` add hashtag metadata to document. e.g, #idea`} +

+

+ {`>> `} + {` add a sidebar text document inline`} +

+

+ {`\`\` `} + {` create a code snippet block`} +

+

+ {`%% `} + {` restore default styling`} +

+

+ {`%color `} + {` changes text color styling. e.g., %green.`} +

+

+ {`%num `} + {` set font size. e.g., %10 for 10pt font`} +

+

+ {`%eq `} + {` creates an equation block for typeset math`} +

+

+ {`%alt `} + {` switch between primary and alternate text (see bottom right Button for hover options).`} +

+

+ {`%f `} + {` create an inline footnote`} +

+

+ {`%> `} + {` create a bockquote section. Terminate with 2 carriage returns`} +

+

+ {`%( `} + {` start a section of inline elidable text. Terminate the inline text with %)`} +

+

+ {`%q `} + {` start a quoted block of text that’s indented on the left and right. Terminate with %q`} +

+

+ {`%d `} + {` start a block text where the first line is indented`} +

+

+ {`%h `} + {` start a block of text that begins with a hanging indent`} +

+

+ {`%[ `} + {` left justify text`} +

+

+ {`%^ `} + {` center text`} +

+

+ {`%] `} + {` right justify text`} +

+

+ {`[:doctitle]] `} + {` hyperlink to document specified by it’s title`} +

+

+ {`[[fieldname]] `} + {` display value of fieldname`} +

+

+ {`[[fieldname=value]] `} + {` assign value to fieldname of document and display it`} +

+

+ {`[[fieldname:doctitle]] `} + {` show value of fieldname from doc specified by it’s title`} +

+
+ ); + } + + render() { + return ; + } +} diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx index 0ab411bff..f84ad8598 100644 --- a/src/client/util/ServerStats.tsx +++ b/src/client/util/ServerStats.tsx @@ -1,8 +1,7 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { MainViewModal } from '../views/MainViewModal'; -import { DocumentView } from '../views/nodes/DocumentView'; import './SharingManager.scss'; @observer diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 43f56dd8c..5af9a9f9a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -14,7 +14,7 @@ import { StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { CaptureManager } from '../util/CaptureManager'; import { DocumentManager } from '../util/DocumentManager'; @@ -22,11 +22,12 @@ import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; import { ReportManager } from '../util/ReportManager'; +import { RTFMarkup } from '../util/RTFMarkup'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { SelectionManager } from '../util/SelectionManager'; +import { ServerStats } from '../util/ServerStats'; import { ColorScheme, SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; -import { ServerStats } from '../util/ServerStats'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; @@ -41,7 +42,7 @@ import { DashboardView } from './DashboardView'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; -import { TOPBAR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss'; +import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss'; import { Colors } from './global/globalEnums'; import { KeyManager } from './GlobalKeyHandler'; import { InkTranscription } from './InkTranscription'; @@ -971,6 +972,7 @@ export class MainView extends React.Component { + diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 55dee005d..32997a944 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import "./MainViewModal.scss"; +import './MainViewModal.scss'; import { observer } from 'mobx-react'; export interface MainViewOverlayProps { @@ -15,31 +15,38 @@ export interface MainViewOverlayProps { @observer export class MainViewModal extends React.Component { - render() { const p = this.props; const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; const overlayOpacity = p.overlayDisplayedOpacity || 0.4; - return !p.isDisplayed ? (null) : ( -
-
+ return !p.isDisplayed ? null : ( +
+
{p.contents}
-
); } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 38aa8db55..d58a4c199 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -767,6 +767,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' }); constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() }); (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); @@ -774,11 +775,10 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' }); !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' }); !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' }); !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DataSym]), icon: 'hand-point-right' }); @@ -825,7 +825,7 @@ export class DocumentViewInternal extends DocComponent, dataDoc: Doc) => void; @@ -858,6 +859,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' }); + optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); this._downX = this._downY = Number.NaN; }; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 599f96bca..20ce929ad 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -8,6 +8,7 @@ import { normalizeEmail } from '../../../../fields/util'; import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; +import { RTFMarkup } from '../../../util/RTFMarkup'; import { FormattedTextBox } from './FormattedTextBox'; import { wrappingInputRule } from './prosemirrorPatches'; import { RichTextMenu } from './RichTextMenu'; @@ -228,6 +229,12 @@ export class RichTextRules { return null; }), + // show markup help + new InputRule(new RegExp(/%\?$/), (state, match, start, end) => { + setTimeout(RTFMarkup.Instance.open); + return state.tr.deleteRange(start, end); + }), + // stop using active style new InputRule(new RegExp(/%alt$/), (state, match, start, end) => { setTimeout(() => (this.Document[this.TextBox.props.fieldKey + '-usePath'] = this.Document[this.TextBox.props.fieldKey + '-usePath'] ? undefined : 'alternate')); -- cgit v1.2.3-70-g09d2 From 7342eb81f241ce0b2a5a33ddfb0c865eab6a492f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 09:10:36 -0400 Subject: added proper scaling of multi-selections and groups. fixed pinViewport to work again. --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/DocumentButtonBar.tsx | 4 - src/client/views/DocumentDecorations.tsx | 112 ++++++++++++--------- src/client/views/collections/TabDocView.tsx | 1 + .../collections/collectionFreeForm/MarqueeView.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 10 +- src/client/views/nodes/button/FontIconBox.tsx | 6 +- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/formattedText/marks_rts.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 1 + 10 files changed, 80 insertions(+), 65 deletions(-) (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5475873a9..479fcd011 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -621,8 +621,8 @@ export class CurrentUserUtils { } static viewTools(): Button[] { return [ - { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Snap\xA0Lines", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "View\xA0All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Arrange",icon: "arrow-down-short-wide",toolTip: "Toggle Auto Arrange",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 8c2ced55a..30e41b06c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -303,10 +303,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV onPointerEnter={action(e => (this.subEndLink = (pinLayout ? 'Layout' : '') + (pinLayout && pinContent ? ' &' : '') + (pinContent ? ' Content' : '')))} onPointerLeave={action(e => (this.subEndLink = ''))} onClick={e => { - const docs = this.props - .views() - .filter(v => v) - .map(dv => dv!.rootDoc); this.view0 && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, { pinDocLayout: pinLayout, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 042e8f6d0..2811c96eb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -12,7 +12,7 @@ import { InkField } from '../../fields/InkField'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; -import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; +import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; @@ -34,6 +34,7 @@ import React = require('react'); import { RichTextField } from '../../fields/RichTextField'; import { LinkFollower } from '../util/LinkFollower'; import _ = require('lodash'); +import { DocumentManager } from '../util/DocumentManager'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -509,73 +510,86 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P dragRight = false, dragBotRight = false, dragTop = false; - let dX = 0, - dY = 0, - dW = 0, - dH = 0; + let dXin = 0, + dYin = 0, + dWin = 0, + dHin = 0; switch (this._resizeHdlId.split(' ')[0]) { case '': break; case 'documentDecorations-topLeftResizer': - dX = -1; - dY = -1; - dW = -move[0]; - dH = -move[1]; + dXin = -1; + dYin = -1; + dWin = -move[0]; + dHin = -move[1]; break; case 'documentDecorations-topRightResizer': - dW = move[0]; - dY = -1; - dH = -move[1]; + dWin = move[0]; + dYin = -1; + dHin = -move[1]; break; case 'documentDecorations-topResizer': - dY = -1; - dH = -move[1]; + dYin = -1; + dHin = -move[1]; dragTop = true; break; case 'documentDecorations-bottomLeftResizer': - dX = -1; - dW = -move[0]; - dH = move[1]; + dXin = -1; + dWin = -move[0]; + dHin = move[1]; break; case 'documentDecorations-bottomRightResizer': - dW = move[0]; - dH = move[1]; + dWin = move[0]; + dHin = move[1]; dragBotRight = true; break; case 'documentDecorations-bottomResizer': - dH = move[1]; + dHin = move[1]; dragBottom = true; break; case 'documentDecorations-leftResizer': - dX = -1; - dW = -move[0]; + dXin = -1; + dWin = -move[0]; break; case 'documentDecorations-rightResizer': - dW = move[0]; + dWin = move[0]; dragRight = true; break; } - SelectionManager.Views().forEach( + const isGroup = first.rootDoc._isGroup ? first.rootDoc : undefined; + const scaleViews = isGroup ? DocListCast(isGroup.data).map(doc => DocumentManager.Instance.getFirstDocumentView(doc)!) : SelectionManager.Views(); + const aggBounds = aggregateBounds(scaleViews.map(view => view.rootDoc) as any, 0, 0); + const refWidth = aggBounds.r - aggBounds.x; + const refHeight = aggBounds.b - aggBounds.y; + const scaleRefPt = first.props + .ScreenToLocalTransform() + .inverse() + .transformPoint( + NumCast(isGroup?._xPadding) + (dXin ? refWidth : 0), // + NumCast(isGroup?._yPadding) + (dYin ? refHeight : 0) + ); + scaleViews.forEach( action((docView: DocumentView) => { if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions(); - if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { - const doc = Document(docView.rootDoc); + if (dXin !== 0 || dYin !== 0 || dWin !== 0 || dHin !== 0) { + const doc = docView.rootDoc; + const refCent = docView.props.ScreenToLocalTransform().transformPoint(scaleRefPt[0], scaleRefPt[1]); + if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight)) { doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth; } const nwidth = docView.nativeWidth; const nheight = docView.nativeHeight; - let docheight = doc._height || 0; - let docwidth = doc._width || 0; - const width = docwidth; - let height = docheight || (nheight / nwidth) * width; - height = !height || isNaN(height) ? 20 : height; + const docwidth = NumCast(doc._width); + let docheight = (hgt => (!hgt || isNaN(hgt) ? 20 : hgt))(NumCast(doc._height) || (nheight / nwidth) * docwidth); + let dW = docwidth * (dWin / refWidth); + let dH = docheight * (dHin / refHeight); const scale = docView.props.ScreenToLocalTransform().Scale; const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen); if (nwidth && nheight) { - if (nwidth / nheight !== width / height && !dragBottom && !dragTop) { - height = (nheight / nwidth) * width; + if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) { + docheight = (nheight / nwidth) * docwidth; } if (modifyNativeDim && !dragBottom && !dragTop) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction @@ -583,21 +597,25 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P else dW = (dH * nwidth) / nheight; } } - let actualdW = Math.max(width + dW * scale, 20); - let actualdH = Math.max(height + dH * scale, 20); + let actualdW = Math.max(docwidth + dW * scale, 20); + let actualdH = Math.max(docheight + dH * scale, 20); + let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth)); + let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight)); const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false; const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { if (Doc.NativeWidth(doc)) { - doc._nativeWidth = (actualdW / (doc._width || 1)) * Doc.NativeWidth(doc); + doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc); } } else { if (!doc._fitWidth || preserveNativeDim) { actualdH = (nheight / nwidth) * actualdW; doc._height = actualdH; - } else if (!modifyNativeDim || dragBotRight) doc._height = actualdH; + } else if (!modifyNativeDim || dragBotRight) { + doc._height = actualdH; + } } doc._width = actualdW; } else { @@ -605,21 +623,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) - doc._nativeHeight = (actualdH / (doc._height || 1)) * Doc.NativeHeight(doc); + doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc); doc._autoHeight = false; } else { if (!doc._fitWidth || preserveNativeDim) { actualdW = (nwidth / nheight) * actualdH; doc._width = actualdW; - } else if (!modifyNativeDim || dragBotRight) doc._width = actualdW; + } else if (!modifyNativeDim || dragBotRight) { + doc._width = actualdW; + } } if (!modifyNativeDim) { - actualdH = Math.min((nheight / nwidth) * NumCast(doc._width), actualdH); - doc._height = actualdH; - } else doc._height = actualdH; + actualdH = Math.min((nheight / nwidth) * docwidth, actualdH); + } + doc._height = actualdH; } } else { - const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2]; + const rotCtr = [docwidth / 2, docheight / 2]; const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI); const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); @@ -632,8 +652,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P doc.x = NumCast(doc.x) + tlRotated.x + rotCtr[0] - (tlRotated2.x + rotCtr2[0]); // doc shifts by amount topleft moves because rotation is about center of doc doc.y = NumCast(doc.y) + tlRotated.y + rotCtr[1] - (tlRotated2.y + rotCtr2[1]); } - doc.x = (doc.x || 0) + dX * (actualdW - docwidth); - doc.y = (doc.y || 0) + (dragBottom ? 0 : dY * (actualdH - docheight)); + doc.x = NumCast(doc.x) + dX; + doc.y = NumCast(doc.y) + dY; doc._lastModified = new DateField(); } const val = this._dragHeights.get(docView.layoutDoc); @@ -726,7 +746,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } // hide the decorations if the parent chooses to hide it or if the document itself hides it const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; - const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating; + const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || this._isRounding || this._isRotating; const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating; const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 4bbc3bb44..45604c1bf 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -266,6 +266,7 @@ export class TabDocView extends React.Component { pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); + if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc); if (!pinProps?.audioRange && duration !== undefined) { pinDoc.mediaStart = 'manual'; pinDoc.mediaStop = 'manual'; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index eaeb5f933..e5f47823c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -425,9 +425,8 @@ export class MarqueeView extends React.Component { - const doc = this.props.Document; - TabDocView.PinDoc(doc, { pinViewport: this.Bounds }); + pinWithView = () => { + TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds }); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c957bc778..a25e5c42d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -765,9 +765,8 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' }); if (!Doc.IsSystem(this.rootDoc)) { - constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' }); constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() }); (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); @@ -775,8 +774,9 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' }); @@ -819,9 +819,7 @@ export class DocumentViewInternal extends DocComponent { - window.open(documentationLink, '_blank'); - }, + event: () => window.open(documentationLink, '_blank'), icon: 'book', }); } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 1a75a7e76..4f570b5fc 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -610,16 +610,16 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); }); -ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snap lines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { +ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snapline' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); // prettier-ignore - const map: Map<'grid' | 'snap lines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ + const map: Map<'grid' | 'snapline' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ ['grid', { undo: false, checkResult: (doc:Doc) => doc._backgroundGridShow, setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow, }], - ['snap lines', { + ['snapline', { undo: false, checkResult: (doc:Doc) => doc.showSnapLines, setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines, diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 20ce929ad..e5943f257 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -391,7 +391,7 @@ export class RichTextRules { const content = selected.selection.content(); const replaced = node ? selected.replaceRangeWith(start, end, schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })) : state.tr; - return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))).setStoredMarks([...node.marks, ...sm]); + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end))).setStoredMarks([...node.marks, ...sm]); }), new InputRule(new RegExp(/%\)/), (state, match, start, end) => { diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 3898490d3..5b47e8a70 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -349,7 +349,7 @@ export const marks: { [index: string]: MarkSpec } = { group: 'inline', toDOM(node: any) { const uid = node.attrs.userid.replace('.', '').replace('@', ''); - const min = Math.round(node.attrs.modified / 12); + const min = Math.round(node.attrs.modified / 60); const hr = Math.round(min / 60); const day = Math.round(hr / 60 / 24); const remote = node.attrs.userid !== Doc.CurrentUserEmail ? ' UM-remote' : ''; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index f8c47aafe..0b780f589 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -567,6 +567,7 @@ export class PresBox extends ViewBoxBaseComponent() { bestTarget._panY = viewport.panY; const dv = DocumentManager.Instance.getDocumentView(bestTarget); if (dv) { + changed = true; const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height); activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale); dv.ComponentView?.brushView?.(viewport); -- cgit v1.2.3-70-g09d2 From 66e5fe4d8c4c6fae768305e31b45735f563b7500 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 11:10:56 -0400 Subject: updated some text shortcuts and enabled selecting regions to make footnoes/elided text. --- src/client/util/CurrentUserUtils.ts | 6 +- src/client/util/RTFMarkup.tsx | 40 +++++------ .../views/nodes/formattedText/FootnoteView.tsx | 2 - .../formattedText/ProsemirrorExampleTransfer.ts | 78 ++++++++++++++++++++++ .../views/nodes/formattedText/RichTextRules.ts | 24 +------ 5 files changed, 102 insertions(+), 48 deletions(-) (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 479fcd011..ca23e8f53 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -641,9 +641,9 @@ export class CurrentUserUtils { { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Left", toolTip: "Left align (%[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, - { title: "Center", toolTip: "Center align (%^)", btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Right", toolTip: "Right align (%])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, + { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, { title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}}, { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}}, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}}, diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index 306d151d7..69f62fc3f 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -55,6 +55,26 @@ export class RTFMarkup extends React.Component<{}> { {`\`\` `} {` create a code snippet block`}

+

+ {`cmd-f `} + {` collapse to an inline footnote)`} +

+

+ {`cmd-e `} + {` collapse to elided text`} +

+

+ {`cmd-[ `} + {` left justify text`} +

+

+ {`cmd-\\ `} + {` center text`} +

+

+ {`cmd-] `} + {` right justify text`} +

{`%% `} {` restore default styling`} @@ -75,18 +95,10 @@ export class RTFMarkup extends React.Component<{}> { {`%alt `} {` switch between primary and alternate text (see bottom right Button for hover options).`}

-

- {`%f `} - {` create an inline footnote`} -

{`%> `} {` create a bockquote section. Terminate with 2 carriage returns`}

-

- {`%( `} - {` start a section of inline elidable text. Terminate the inline text with %)`} -

{`%q `} {` start a quoted block of text that’s indented on the left and right. Terminate with %q`} @@ -99,18 +111,6 @@ export class RTFMarkup extends React.Component<{}> { {`%h `} {` start a block of text that begins with a hanging indent`}

-

- {`%[ `} - {` left justify text`} -

-

- {`%^ `} - {` center text`} -

-

- {`%] `} - {` right justify text`} -

{`[:doctitle]] `} {` hyperlink to document specified by it’s title`} diff --git a/src/client/views/nodes/formattedText/FootnoteView.tsx b/src/client/views/nodes/formattedText/FootnoteView.tsx index 531a60297..cf48e1250 100644 --- a/src/client/views/nodes/formattedText/FootnoteView.tsx +++ b/src/client/views/nodes/formattedText/FootnoteView.tsx @@ -83,13 +83,11 @@ export class FootnoteView { }; toggle = () => { - console.log('TOGGLE'); if (this.innerView) this.close(); else this.open(); }; close() { - console.log('CLOSE'); this.innerView?.destroy(); this.innerView = null; this.dom.textContent = ''; diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 68b0488a2..4dfe07b24 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -8,6 +8,7 @@ import { AclAugment, AclSelfEdit, Doc } from '../../../../fields/Doc'; import { GetEffectiveAcl } from '../../../../fields/util'; import { Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; +import { RTFMarkup } from '../../../util/RTFMarkup'; import { SelectionManager } from '../../../util/SelectionManager'; import { OpenWhere } from '../DocumentView'; import { liftListItem, sinkListItem } from './prosemirrorPatches.js'; @@ -178,6 +179,83 @@ export function buildKeymap>(schema: S, props: any, mapKey dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1)))); return true; }); + bind('Cmd-?', (state: EditorState, dispatch: (tx: Transaction) => void) => { + RTFMarkup.Instance.open(); + return true; + }); + bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => { + if (!state.selection.empty) { + const mark = state.schema.marks.summarizeInclusive.create(); + const tr = state.tr.addMark(state.selection.$from.pos, state.selection.$to.pos, mark); + const content = tr.selection.content(); + tr.selection.replaceWith(tr, schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() })); + dispatch(tr); + } + return true; + }); + bind('Cmd-]', (state: EditorState, dispatch: (tx: Transaction) => void) => { + const resolved = state.doc.resolve(state.selection.from) as any; + const tr = state.tr; + if (resolved?.parent.type.name === 'paragraph') { + tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks); + } else { + const node = resolved.nodeAfter; + const sm = state.storedMarks || undefined; + if (node) { + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + } + } + dispatch(tr); + return true; + }); + bind('Cmd-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => { + const resolved = state.doc.resolve(state.selection.from) as any; + const tr = state.tr; + if (resolved?.parent.type.name === 'paragraph') { + tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks); + } else { + const node = resolved.nodeAfter; + const sm = state.storedMarks || undefined; + if (node) { + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + } + } + dispatch(tr); + return true; + }); + bind('Cmd-[', (state: EditorState, dispatch: (tx: Transaction) => void) => { + const resolved = state.doc.resolve(state.selection.from) as any; + const tr = state.tr; + if (resolved?.parent.type.name === 'paragraph') { + tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks); + } else { + const node = resolved.nodeAfter; + const sm = state.storedMarks || undefined; + if (node) { + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + } + } + dispatch(tr); + return true; + }); + + bind('Cmd-f', (state: EditorState, dispatch: (tx: Transaction) => void) => { + const content = state.tr.selection.empty ? undefined : state.tr.selection.content().content.textBetween(0, state.tr.selection.content().size + 1); + const newNode = schema.nodes.footnote.create({}, content ? state.schema.text(content) : undefined); + const tr = state.tr; + tr.replaceSelectionWith(newNode); // replace insertion with a footnote. + dispatch( + tr.setSelection( + new NodeSelection( // select the footnote node to open its display + tr.doc.resolve( + // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node) + tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0) + ) + ) + ) + ); + return true; + }); bind('Ctrl-a', (state: EditorState, dispatch: (tx: Transaction) => void) => { dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1)))); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index e5943f257..cc19d12bd 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -8,7 +8,6 @@ import { normalizeEmail } from '../../../../fields/util'; import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; -import { RTFMarkup } from '../../../util/RTFMarkup'; import { FormattedTextBox } from './FormattedTextBox'; import { wrappingInputRule } from './prosemirrorPatches'; import { RichTextMenu } from './RichTextMenu'; @@ -29,7 +28,7 @@ export class RichTextRules { emDash, // > blockquote - wrappingInputRule(/%>\s$/, schema.nodes.blockquote), + wrappingInputRule(/%>$/, schema.nodes.blockquote), // 1. create numerical ordered list wrappingInputRule( @@ -191,21 +190,6 @@ export class RichTextRules { } }), - // %f create footnote - new InputRule(new RegExp(/%f$/), (state, match, start, end) => { - const newNode = schema.nodes.footnote.create({}); - const tr = state.tr; - tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote. - return tr.setSelection( - new NodeSelection( // select the footnote node to open its display - tr.doc.resolve( - // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node) - tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0) - ) - ) - ); - }), - // activate a style by name using prefix '%' new InputRule(new RegExp(/%[a-z]+$/), (state, match, start, end) => { const color = match[0].substring(1, match[0].length); @@ -229,12 +213,6 @@ export class RichTextRules { return null; }), - // show markup help - new InputRule(new RegExp(/%\?$/), (state, match, start, end) => { - setTimeout(RTFMarkup.Instance.open); - return state.tr.deleteRange(start, end); - }), - // stop using active style new InputRule(new RegExp(/%alt$/), (state, match, start, end) => { setTimeout(() => (this.Document[this.TextBox.props.fieldKey + '-usePath'] = this.Document[this.TextBox.props.fieldKey + '-usePath'] ? undefined : 'alternate')); -- cgit v1.2.3-70-g09d2