diff options
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 4 | ||||
| -rw-r--r-- | src/client/util/Scripting.ts | 14 | ||||
| -rw-r--r-- | src/client/util/request-image-size.js | 73 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 42 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 8 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 1 |
8 files changed, 124 insertions, 22 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2ace6a4cc..4193e0b5f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -34,7 +34,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; -var requestImageSize = require('request-image-size'); +var requestImageSize = require('../util/request-image-size'); var path = require('path'); export enum DocTypes { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c92daa5..b707dbe57 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -90,6 +90,8 @@ export namespace DragManager { handlers: DragHandlers; hideSource: boolean | (() => boolean); + + withoutShiftDrag?: boolean; } export interface DragDropDisposer { @@ -312,7 +314,7 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } - if (e.shiftKey && CollectionDockingView.Instance) { + if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) { AbortDrag(); CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40e2ad6bb..30a05154a 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -12,7 +12,7 @@ import { Doc, Field } from '../../new_fields/Doc'; import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; import { List } from '../../new_fields/List'; import { RichTextField } from '../../new_fields/RichTextField'; -import { ScriptField, ComputedField } from '../../fields/ScriptField'; +import { ScriptField, ComputedField } from '../../new_fields/ScriptField'; export interface ScriptSucccess { success: true; @@ -39,7 +39,6 @@ export interface CompileError { } export type CompileResult = CompiledScript | CompileError; - function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); if ((options.typecheck !== false && errors) || !script) { @@ -64,10 +63,20 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an } } let thisParam = args.this || capturedVariables.this; + let batch: { end(): void } | undefined = undefined; try { + if (!options.editable) { + batch = Doc.MakeReadOnly(); + } const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); + if (batch) { + batch.end(); + } return { success: true, result }; } catch (error) { + if (batch) { + batch.end(); + } return { success: false, error }; } }; @@ -133,6 +142,7 @@ export interface ScriptOptions { params?: { [name: string]: string }; capturedVariables?: { [name: string]: Field }; typecheck?: boolean; + editable?: boolean; } export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js new file mode 100644 index 000000000..0f9328872 --- /dev/null +++ b/src/client/util/request-image-size.js @@ -0,0 +1,73 @@ +/** + * request-image-size: Detect image dimensions via request. + * Licensed under the MIT license. + * + * https://github.com/FdezRomero/request-image-size + * © 2017 Rodrigo Fernández Romero + * + * Based on the work of Johannes J. Schmidt + * https://github.com/jo/http-image-size + */ + +const request = require('request'); +const imageSize = require('image-size'); +const HttpError = require('standard-http-error'); + +module.exports = function requestImageSize(options) { + let opts = { + encoding: null + }; + + if (options && typeof options === 'object') { + opts = Object.assign(options, opts); + } else if (options && typeof options === 'string') { + opts = Object.assign({ uri: options }, opts); + } else { + return Promise.reject(new Error('You should provide an URI string or a "request" options object.')); + } + + opts.encoding = null; + + return new Promise((resolve, reject) => { + const req = request(opts); + + req.on('response', res => { + if (res.statusCode >= 400) { + return reject(new HttpError(res.statusCode, res.statusMessage)); + } + + let buffer = new Buffer([]); + let size; + let imageSizeError; + + res.on('data', chunk => { + buffer = Buffer.concat([buffer, chunk]); + + try { + size = imageSize(buffer); + } catch (err) { + imageSizeError = err; + return; + } + + if (size) { + resolve(size); + return req.abort(); + } + }); + + res.on('error', err => reject(err)); + + res.on('end', () => { + if (!size) { + return reject(imageSizeError); + } + + size.downloaded = buffer.length; + return resolve(size); + }); + }); + + req.on('error', err => reject(err)); + }); +}; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c43497cfd..f1473139c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -24,6 +24,10 @@ import { SubCollectionViewProps } from "./CollectionSubView"; import { ParentDocSelector } from './ParentDocumentSelector'; import React = require("react"); import { MainView } from '../MainView'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faFile } from '@fortawesome/free-solid-svg-icons'; +library.add(faFile); @observer export class CollectionDockingView extends React.Component<SubCollectionViewProps> { @@ -135,10 +139,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); - if (this._goldenLayout.root.contentItems[0].isRow) { + if (this._goldenLayout.root.contentItems.length === 0) { + this._goldenLayout.root.addChild(newContentItem); + } else if (this._goldenLayout.root.contentItems[0].isRow) { this._goldenLayout.root.contentItems[0].addChild(newContentItem); - } - else { + } else { var collayout = this._goldenLayout.root.contentItems[0]; var newRow = collayout.layoutManager.createContentItem({ type: "row" }, this._goldenLayout); collayout.parent.replaceChild(collayout, newRow); @@ -275,7 +280,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else - if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { + if ((className === "lm_title" || className === "lm_tab lm_active") && e.shiftKey) { e.stopPropagation(); e.preventDefault(); let x = e.clientX; @@ -293,7 +298,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp handlers: { dragComplete: emptyFunction, }, - hideSource: false + hideSource: false, + withoutShiftDrag: true }); } })); @@ -340,8 +346,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async doc => { if (doc instanceof Doc) { - let counter: any = this.htmlToElement(`<span class="messageCounter">0</div>`); - tab.element.append(counter); + let dragSpan = document.createElement("span"); + dragSpan.style.position = "relative"; + dragSpan.style.bottom = "6px"; + dragSpan.style.paddingLeft = "4px"; + dragSpan.style.paddingRight = "2px"; let upDiv = document.createElement("span"); const stack = tab.contentItem.parent; // shifts the focus to this tab when another tab is dragged over it @@ -353,15 +362,24 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } tab.setActive(true); }; - ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); - tab.reactComponents = [upDiv]; + ReactDOM.render(<span onPointerDown={ + e => { + e.preventDefault(); + e.stopPropagation(); + DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, { + handlers: { dragComplete: emptyFunction }, + hideSource: false + }); + }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan); + ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={doc => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); + tab.reactComponents = [dragSpan, upDiv]; + tab.element.append(dragSpan); tab.element.append(upDiv); - counter.DashDocId = tab.contentItem.config.props.documentId; - tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], + tab.reactionDisposer = reaction(() => [doc.title], () => { - counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); + //TODO why can't this just be doc instead of the id? tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; } }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f756fe625..4b4e7465a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -217,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } else { // if (modes[e.deltaMode] === 'pixels') coefficient = 50; // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height?? - let deltaScale = (1 - (e.deltaY / coefficient)); + let deltaScale = e.deltaY > 0 ? (1 / 1.1) : 1.1; if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) { deltaScale = 1 / this.zoomScaling(); } diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 3d626eef0..cd65c42bc 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); import { NumCast, Cast, FieldValue } from "../../../new_fields/Types"; import { Doc, Field } from "../../../new_fields/Doc"; -import { ComputedField } from "../../../fields/ScriptField"; +import { ComputedField } from "../../../new_fields/ScriptField"; @observer export class KeyValueBox extends React.Component<FieldViewProps> { @@ -38,10 +38,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> { } public static SetField(doc: Doc, key: string, value: string) { let eq = value.startsWith("="); + let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; let dubEq = value.startsWith(":="); value = dubEq ? value.substr(2) : value; - let options: ScriptOptions = { addReturn: true }; + let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; let script = CompileScript(value, options); if (!script.compiled) { @@ -49,12 +50,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> { } let field = new ComputedField(script); if (!dubEq) { - let res = script.run(); + let res = script.run({ this: target }); if (!res.success) return false; field = res.result; } if (Field.IsField(field, true)) { - let target = eq ? doc : Doc.GetProto(doc); target[key] = field; return true; } diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 420a1ad94..da0aa6ac4 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -11,7 +11,6 @@ import "./KeyValuePair.scss"; import React = require("react"); import { Doc, Opt, Field } from '../../../new_fields/Doc'; import { FieldValue } from '../../../new_fields/Types'; -import { ComputedField } from '../../../fields/ScriptField'; import { KeyValueBox } from './KeyValueBox'; // Represents one row in a key value plane |
