diff options
-rw-r--r-- | package-lock.json | 24 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/client/documents/DocumentTypes.ts | 1 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 23 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 4 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/EquationBox.scss | 0 | ||||
-rw-r--r-- | src/client/views/nodes/EquationBox.tsx | 66 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 4 | ||||
-rw-r--r-- | webpack.config.js | 6 |
12 files changed, 130 insertions, 5 deletions
diff --git a/package-lock.json b/package-lock.json index 464fdfb51..933c5037c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5526,6 +5526,15 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, + "equation-editor-react": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/equation-editor-react/-/equation-editor-react-0.0.6.tgz", + "integrity": "sha512-7Zsx04yh4DsirpMzMVnEaDwMxrpcjiAt6NOvSj+DEOa56VfKGXsmouWFOCtSVixahDSVEDs53LbhnPqFh4cVqw==", + "requires": { + "jquery": "^3.4.1", + "mathquill": "^0.10.1-a" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -8813,6 +8822,21 @@ } } }, + "mathquill": { + "version": "0.10.1-a", + "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz", + "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=", + "requires": { + "jquery": "^1.12.3" + }, + "dependencies": { + "jquery": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz", + "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" + } + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", diff --git a/package.json b/package.json index a18b11967..28d77aa38 100644 --- a/package.json +++ b/package.json @@ -153,6 +153,7 @@ "cookie-session": "^2.0.0-rc.1", "cors": "^2.8.5", "depcheck": "^0.9.2", + "equation-editor-react": "0.0.6", "exif": "^0.6.0", "express": "^4.16.4", "express-flash": "0.0.2", diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 080657fd8..20dbc6f25 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -21,6 +21,7 @@ export enum DocumentType { PDFANNO = "pdfanno", // pdf text selection (could be just a collection?) DATE = "date", // calendar view of a date SCRIPTING = "script", // script editor + EQUATION = "equation", // equation edtior // special purpose wrappers that either take no data or are compositions of lower level types LINK = "link", // link (view of a document that acts as a link) diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3f03d39da..e270d2ac8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -57,6 +57,7 @@ import { PresElementBox } from "../views/presentationview/PresElementBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; +import { EquationBox } from "../views/nodes/EquationBox"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -241,6 +242,7 @@ export class DocumentOptions { sidebarColor?: string; // background color of text sidebar sidebarViewType?: string; // collection type of text sidebar docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents + text?: string; textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse"; @@ -374,6 +376,9 @@ export namespace Docs { [DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, }], + [DocumentType.EQUATION, { + layout: { view: EquationBox, dataField: defaultDataKey }, + }], [DocumentType.BUTTON, { layout: { view: LabelBox, dataField: "onClick" }, }], @@ -890,6 +895,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.LABEL), undefined, { ...(options || {}) }); } + export function EquationDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) }); + } + export function ButtonDocument(options?: DocumentOptions) { // const btn = InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" }); // btn.layoutKey = "layout_onClick"; @@ -1220,6 +1229,20 @@ export namespace DocUtils { icon: "eye" }); ContextMenu.Instance.addItem({ + description: ":math", event: () => { + const created = Docs.Create.EquationDocument(); + if (created) { + created.author = Doc.CurrentUserEmail; + created.x = x; + created.y = y; + created.width = 300; + created.height = 35; + EquationBox.SelectOnLoad = created[Id]; + docAdder?.(created); + } + }, icon: "compress-arrows-alt" + }); + ContextMenu.Instance.addItem({ description: "Add Template Doc ...", subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title), diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 357bff36d..5b7394c42 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -611,8 +611,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> {closeIcon} - {titleArea} - {seldoc.props.hideResizeHandles ? (null) : + {seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea} + {seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : <> {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : <Tooltip key="i" title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top"> diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5a2fb285b..2352aa22a 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -110,6 +110,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | case DocumentType.FILTER: docColor = docColor || (darkScheme() ? "#2d2d2d" : "rgba(105, 105, 105, 0.432)"); break; case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break; case DocumentType.SLIDER: break; + case DocumentType.EQUATION: docColor = docColor || "transparent"; break; case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break; case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break; case DocumentType.LINK: return "transparent"; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index c0af1e08a..02c112745 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,6 +11,7 @@ import { CollectionView } from "../collections/CollectionView"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; import { AudioBox } from "./AudioBox"; import { LabelBox } from "./LabelBox"; +import { EquationBox } from "./EquationBox"; import { SliderBox } from "./SliderBox"; import { LinkBox } from "./LinkBox"; import { ScriptingBox } from "./ScriptingBox"; @@ -220,7 +221,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo blacklistedAttrs={emptyPath} renderInWrapper={false} components={{ - FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView, + FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox, diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/client/views/nodes/EquationBox.scss diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx new file mode 100644 index 000000000..8ded4111c --- /dev/null +++ b/src/client/views/nodes/EquationBox.tsx @@ -0,0 +1,66 @@ +import EquationEditor from 'equation-editor-react'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { createSchema, makeInterface } from '../../../fields/Schema'; +import { StrCast, NumCast } from '../../../fields/Types'; +import { ViewBoxBaseComponent } from '../DocComponent'; +import { FieldView, FieldViewProps } from './FieldView'; +import './LabelBox.scss'; +import { Id } from '../../../fields/FieldSymbols'; +import { simulateMouseClick } from '../../../Utils'; +import { TraceMobx } from '../../../fields/util'; +import { reaction, action } from 'mobx'; +import { Docs } from '../../documents/Documents'; + +const EquationSchema = createSchema({}); + +type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>; +const EquationDocument = makeInterface(EquationSchema, documentSchema); + +@observer +export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); } + public static SelectOnLoad: string = ""; + _ref: React.RefObject<EquationEditor> = React.createRef(); + componentDidMount() { + if (EquationBox.SelectOnLoad === this.rootDoc[Id]) { + this.props.select(false); + + this._ref.current!.mathField.focus(); + this._ref.current!.mathField.select(); + } + reaction(() => this.props.isSelected(), + selected => { + if (this._ref.current) { + if (selected) this._ref.current.element.current.children[0].addEventListener("keydown", this.keyPressed, true); + else this._ref.current.element.current.children[0].removeEventListener("keydown", this.keyPressed); + } + }, { fireImmediately: true }); + } + @action + keyPressed = (e: KeyboardEvent) => { + if (e.key === "Enter") { + const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", "")); + const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace("px", "")); + this.layoutDoc._width = _width; + this.layoutDoc._height = _height; + const nextEq = Docs.Create.EquationDocument({ title: "# math", text: StrCast(this.dataDoc.text), x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10, _width, _height: 35 }); + EquationBox.SelectOnLoad = nextEq[Id]; + this.props.addDocument?.(nextEq); + e.stopPropagation(); + } + if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc); + } + onChange = (str: string) => this.dataDoc.text = str; + render() { + TraceMobx(); + return (<div onPointerDown={e => this.props.isSelected() && !e.ctrlKey && e.stopPropagation()}> + <EquationEditor ref={this._ref} + value={this.dataDoc.text || "y"} + onChange={this.onChange} + autoCommands="pi theta sqrt sum prod alpha beta gamma rho" + autoOperatorNames="sin cos tan" /></div> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 288cd14c9..5066578aa 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1541,7 +1541,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp /> </div> {this.props.noSidebar || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} - {this.sidebarHandle} + {this.Document._singleLine ? (null) : this.sidebarHandle} {!this.layoutDoc._showAudio ? (null) : this.audioHandle} </div> </div> diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index a1a404e10..eae4c0179 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -144,6 +144,10 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind("Alt-\\", setBlockType(schema.nodes.paragraph)); bind("Shift-Ctrl-\\", setBlockType(schema.nodes.code_block)); + bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { + dispatch(state.tr.replaceSelectionWith(schema.nodes.dashField.create({ fieldKey: "math" + Utils.GenerateGuid() }))); + }) + for (let i = 1; i <= 6; i++) { bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i })); } diff --git a/webpack.config.js b/webpack.config.js index c973be1ed..30967d618 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -75,6 +75,10 @@ module.exports = { }] }, { + test: /\.(woff|woff2|ttf|eot|otf|svg)$/, + use: 'file-loader?name=fonts/[name].[ext]!static' + }, + { test: /\.scss|css$/, use: [{ loader: "style-loader" @@ -84,7 +88,7 @@ module.exports = { }, { loader: "sass-loader" - } + }, ] }, { |