From 0dc2b16041bf3c774723b00b440369357931c15b Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Mon, 12 Jul 2021 15:51:27 -0400 Subject: some formatting changes --- src/client/views/collections/schemaView/CollectionSchemaView.scss | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/schemaView/CollectionSchemaView.scss b/src/client/views/collections/schemaView/CollectionSchemaView.scss index b57fee0e4..0662eabaf 100644 --- a/src/client/views/collections/schemaView/CollectionSchemaView.scss +++ b/src/client/views/collections/schemaView/CollectionSchemaView.scss @@ -134,6 +134,13 @@ min-height: 30px; border: 0 !important; } + .rt-tr:nth-of-type(even) { + direction: ltr; + flex: 0 1 auto; + min-height: 30px; + border: 0 !important; + background-color: lightgray; + } .rt-tr { width: 100%; min-height: 30px; -- cgit v1.2.3-70-g09d2 From b5494be46ea1e02b85d24d9405f0d9c60ba2b0f1 Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Sat, 17 Jul 2021 15:34:46 -0400 Subject: some styling changes --- .../views/collections/collectionSchema/CollectionSchemaView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 0662eabaf..e866ec079 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -134,12 +134,12 @@ min-height: 30px; border: 0 !important; } - .rt-tr:nth-of-type(even) { + .rt-tr-group:nth-of-type(even) { direction: ltr; flex: 0 1 auto; min-height: 30px; border: 0 !important; - background-color: lightgray; + background-color: red; } .rt-tr { width: 100%; -- cgit v1.2.3-70-g09d2 From 6330a8adc6e85bfa5e0b24016e5d76d85bc07e41 Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Wed, 21 Jul 2021 08:56:39 -0400 Subject: fixed date formatting --- .../collectionSchema/CollectionSchemaCells.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index f75179cea..a8d901f4d 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -351,13 +351,16 @@ export class CollectionSchemaDateCell extends CollectionSchemaCell { //} } + // If the cell is not clicked on, render the date normally. Otherwise, render a date picker. render() { - return !this.props.isFocused ? {this._date ? Field.toString(this._date as Field) : "--"} : - this.handleChange(date)} - onChange={date => this.handleChange(date)} - />; + return !this.props.isFocused ? {this._date ? Field.toString(this._date as Field) : "--"} : +
+ this.handleChange(date)} + onChange={date => this.handleChange(date)} + /> +
} } -- cgit v1.2.3-70-g09d2 From 02c5628bdaf43100a6e40baab781342bc27df464 Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Wed, 21 Jul 2021 12:46:13 -0400 Subject: fixed the fact that text, number, and boolean were ignoring input types when accepting input --- .../collectionSchema/CollectionSchemaCells.tsx | 114 +++++++++++++-------- 1 file changed, 69 insertions(+), 45 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index a8d901f4d..8b7fb9be8 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -214,6 +214,55 @@ export class CollectionSchemaCell extends React.Component { positions.pop(); } } + + // handles input procedures for string cells + let stringInput = (value: string, retVal: boolean): void => { + let valueSansQuotes = value; + if (this._isEditing) { + const vsqLength = valueSansQuotes.length; + // get rid of outer quotes + valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0, + valueSansQuotes.charAt(vsqLength - 1) == "\"" ? vsqLength - 1 : vsqLength); + } + let inputAsString = '"'; + // escape any quotes in the string + for (const i of valueSansQuotes) { + if (i == '"') { + inputAsString += '\\"'; + } else { + inputAsString += i; + } + } + // add a closing quote + inputAsString += '"'; + //two options here: we can strip off outer quotes or we can figure out what's going on with the script + const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + } + + // handles input procedure for number cells + let numberInput = (value: string, retVal: boolean): void => { + const inputscript = value.substring(value.startsWith("=") ? 1 : 0); + // if commas are not stripped, the parser only considers the numbers after the last comma + let inputSansCommas = ""; + for (let s of inputscript) { + if (!(s == ",")) { + inputSansCommas += s; + } + } + const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = value.length !== value.length || value.length - 2 !== value.length + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + } + + // handles input procedure for boolean cells + let boolInput = (value: string, retVal: boolean): void => { + const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = value.length !== value.length || value.length - 2 !== value.length + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + } + const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined"; return (
{ } // check if the input is a boolean let inputIsBool: boolean = value == "false" || value == "true"; - // what to do in the case - if (!inputIsNum && !inputIsBool && !value.startsWith("=")) { - // if it's not a number, it's a string, and should be processed as such - // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically - // after each edit - let valueSansQuotes = value; - if (this._isEditing) { - const vsqLength = valueSansQuotes.length; - // get rid of outer quotes - valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0, - valueSansQuotes.charAt(vsqLength - 1) == "\"" ? vsqLength - 1 : vsqLength); - } - let inputAsString = '"'; - // escape any quotes in the string - for (const i of valueSansQuotes) { - if (i == '"') { - inputAsString += '\\"'; - } else { - inputAsString += i; - } - } - // add a closing quote - inputAsString += '"'; - //two options here: we can strip off outer quotes or we can figure out what's going on with the script - const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - // handle numbers and expressions - } else if (inputIsNum || value.startsWith("=")) { - //TODO: make accept numbers - const inputscript = value.substring(value.startsWith("=") ? 1 : 0); - // if commas are not stripped, the parser only considers the numbers after the last comma - let inputSansCommas = ""; - for (let s of inputscript) { - if (!(s == ",")) { - inputSansCommas += s; - } + + if (type == undefined) { + // what to do in the case + if (!inputIsNum && !inputIsBool && !value.startsWith("=")) { + // if it's not a number, it's a string, and should be processed as such + // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically + // after each edit + stringInput(value, retVal); + // handle numbers and expressions + } else if (inputIsNum || value.startsWith("=")) { + numberInput(value, retVal); + // handle booleans + } else if (inputIsBool) { + boolInput(value, retVal); } - const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = value.length !== value.length || value.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - // handle booleans - } else if (inputIsBool) { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = value.length !== value.length || value.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + } else if (type == "string") { + stringInput(value, retVal); + } else if (type == "number" && inputIsNum) { + numberInput(value, retVal); + } else if (type == "boolean" && inputIsBool) { + boolInput(value, retVal); } } if (retVal) { -- cgit v1.2.3-70-g09d2 From ee0e2c6c53db65c5524b67ed09369cc0e0e6f173 Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Wed, 21 Jul 2021 16:10:04 -0400 Subject: Fixed minor bugs --- .../collectionSchema/CollectionSchemaCells.tsx | 27 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index 8b7fb9be8..f7dfaaeb4 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -300,8 +300,16 @@ export class CollectionSchemaCell extends React.Component { inputIsNum = false; } } + + let contentsAreNum = true; + for (let s of contents) { + if (isNaN(parseInt(s)) && !(s == ".") && !(s == ",")) { + contentsAreNum = false; + } + } // check if the input is a boolean let inputIsBool: boolean = value == "false" || value == "true"; + let contentsAreBool: boolean = contents == "false" || contents == "true"; if (type == undefined) { // what to do in the case @@ -317,12 +325,23 @@ export class CollectionSchemaCell extends React.Component { } else if (inputIsBool) { boolInput(value, retVal); } + // if the cell type is a string } else if (type == "string") { stringInput(value, retVal); - } else if (type == "number" && inputIsNum) { - numberInput(value, retVal); - } else if (type == "boolean" && inputIsBool) { - boolInput(value, retVal); + // if the cell type is a number + } else if (type == "number") { + if (inputIsNum) { + numberInput(value, retVal); + } else if (!contentsAreNum) { + stringInput("", retVal); + } + // if the cell type is a boolean + } else if (type == "boolean") { + if (inputIsBool) { + boolInput(value, retVal); + } else if (!contentsAreBool) { + stringInput("", retVal); + } } } if (retVal) { -- cgit v1.2.3-70-g09d2 From b3fde759b304f6401595f4e43bb00cc6fa65c915 Mon Sep 17 00:00:00 2001 From: dinhanhtruong <70963346+dinhanhtruong@users.noreply.github.com> Date: Thu, 29 Jul 2021 17:50:15 -0400 Subject: added message to empty tab screen --- src/client/goldenLayout.js | 3056 ++++++++++---------- src/client/views/MainView.scss | 30 +- .../views/collections/CollectionDockingView.scss | 23 + 3 files changed, 1574 insertions(+), 1535 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 9cfea7f3f..7fca8d9d5 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -82,16 +82,16 @@ return target; }; - /** - * This is based on Paul Irish's shim, but looks quite odd in comparison. Why? - * Because - * a) it shouldn't affect the global requestAnimationFrame function - * b) it shouldn't pass on the time that has passed - * - * @param {Function} fn - * - * @returns {void} - */ + /** + * This is based on Paul Irish's shim, but looks quite odd in comparison. Why? + * Because + * a) it shouldn't affect the global requestAnimationFrame function + * b) it shouldn't pass on the time that has passed + * + * @param {Function} fn + * + * @returns {void} + */ lm.utils.animFrame = function (fn) { return (window.requestAnimationFrame || window.webkitRequestAnimationFrame || @@ -178,16 +178,16 @@ .replace('.', ''); }; - /** - * A basic XSS filter. It is ultimately up to the - * implementing developer to make sure their particular - * applications and usecases are save from cross site scripting attacks - * - * @param {String} input - * @param {Boolean} keepTags - * - * @returns {String} filtered input - */ + /** + * A basic XSS filter. It is ultimately up to the + * implementing developer to make sure their particular + * applications and usecases are save from cross site scripting attacks + * + * @param {String} input + * @param {Boolean} keepTags + * + * @returns {String} filtered input + */ lm.utils.filterXss = function (input, keepTags) { var output = input @@ -206,41 +206,41 @@ } }; - /** - * Removes html tags from a string - * - * @param {String} input - * - * @returns {String} input without tags - */ + /** + * Removes html tags from a string + * + * @param {String} input + * + * @returns {String} input without tags + */ lm.utils.stripTags = function (input) { return $.trim(input.replace(/(<([^>]+)>)/ig, '')); }; - /** - * A generic and very fast EventEmitter - * implementation. On top of emitting the - * actual event it emits an - * - * lm.utils.EventEmitter.ALL_EVENT - * - * event for every event triggered. This allows - * to hook into it and proxy events forwards - * - * @constructor - */ + /** + * A generic and very fast EventEmitter + * implementation. On top of emitting the + * actual event it emits an + * + * lm.utils.EventEmitter.ALL_EVENT + * + * event for every event triggered. This allows + * to hook into it and proxy events forwards + * + * @constructor + */ lm.utils.EventEmitter = function () { this._mSubscriptions = {}; this._mSubscriptions[lm.utils.EventEmitter.ALL_EVENT] = []; - /** - * Listen for events - * - * @param {String} sEvent The name of the event to listen to - * @param {Function} fCallback The callback to execute when the event occurs - * @param {[Object]} oContext The value of the this pointer within the callback function - * - * @returns {void} - */ + /** + * Listen for events + * + * @param {String} sEvent The name of the event to listen to + * @param {Function} fCallback The callback to execute when the event occurs + * @param {[Object]} oContext The value of the this pointer within the callback function + * + * @returns {void} + */ this.on = function (sEvent, fCallback, oContext) { if (!lm.utils.isFunction(fCallback)) { throw new Error('Tried to listen to event ' + sEvent + ' with non-function callback ' + fCallback); @@ -253,14 +253,14 @@ this._mSubscriptions[sEvent].push({ fn: fCallback, ctx: oContext }); }; - /** - * Emit an event and notify listeners - * - * @param {String} sEvent The name of the event - * @param {Mixed} various additional arguments that will be passed to the listener - * - * @returns {void} - */ + /** + * Emit an event and notify listeners + * + * @param {String} sEvent The name of the event + * @param {Mixed} various additional arguments that will be passed to the listener + * + * @returns {void} + */ this.emit = function (sEvent) { var i, ctx, args; @@ -286,15 +286,15 @@ } }; - /** - * Removes a listener for an event, or all listeners if no callback and context is provided. - * - * @param {String} sEvent The name of the event - * @param {Function} fCallback The previously registered callback method (optional) - * @param {Object} oContext The previously registered context (optional) - * - * @returns {void} - */ + /** + * Removes a listener for an event, or all listeners if no callback and context is provided. + * + * @param {String} sEvent The name of the event + * @param {Function} fCallback The previously registered callback method (optional) + * @param {Object} oContext The previously registered context (optional) + * + * @returns {void} + */ this.unbind = function (sEvent, fCallback, oContext) { if (!this._mSubscriptions[sEvent]) { throw new Error('No subscribtions to unsubscribe for event ' + sEvent); @@ -318,28 +318,28 @@ } }; - /** - * Alias for unbind - */ + /** + * Alias for unbind + */ this.off = this.unbind; - /** - * Alias for emit - */ + /** + * Alias for emit + */ this.trigger = this.emit; }; - /** - * The name of the event that's triggered for every other event - * - * usage - * - * myEmitter.on( lm.utils.EventEmitter.ALL_EVENT, function( eventName, argsArray ){ - * //do stuff - * }); - * - * @type {String} - */ + /** + * The name of the event that's triggered for every other event + * + * usage + * + * myEmitter.on( lm.utils.EventEmitter.ALL_EVENT, function( eventName, argsArray ){ + * //do stuff + * }); + * + * @type {String} + */ lm.utils.EventEmitter.ALL_EVENT = '__all'; lm.utils.DragListener = function (eElement, nButtonCode) { lm.utils.EventEmitter.call(this); @@ -349,14 +349,14 @@ this._eBody = $(document.body); this._nButtonCode = nButtonCode || 0; - /** - * The delay after which to start the drag in milliseconds - */ + /** + * The delay after which to start the drag in milliseconds + */ this._nDelay = 200; - /** - * The distance the mouse needs to be moved to qualify as a drag - */ + /** + * The distance the mouse needs to be moved to qualify as a drag + */ this._nDistance = 10;//TODO - works better with delay only this._nX = 0; @@ -459,16 +459,16 @@ }; } }); - /** - * The main class that will be exposed as GoldenLayout. - * - * @public - * @constructor - * @param {GoldenLayout config} config - * @param {[DOM element container]} container Can be a jQuery selector string or a Dom element. Defaults to body - * - * @returns {VOID} - */ + /** + * The main class that will be exposed as GoldenLayout. + * + * @public + * @constructor + * @param {GoldenLayout config} config + * @param {[DOM element container]} container Can be a jQuery selector string or a Dom element. Defaults to body + * + * @returns {VOID} + */ lm.LayoutManager = function (config, container) { if (!$ || typeof $.noConflict !== 'function') { @@ -521,59 +521,59 @@ }; }; - /** - * Hook that allows to access private classes - */ + /** + * Hook that allows to access private classes + */ lm.LayoutManager.__lm = lm; - /** - * Takes a GoldenLayout configuration object and - * replaces its keys and values recursively with - * one letter codes - * - * @static - * @public - * @param {Object} config A GoldenLayout config object - * - * @returns {Object} minified config - */ + /** + * Takes a GoldenLayout configuration object and + * replaces its keys and values recursively with + * one letter codes + * + * @static + * @public + * @param {Object} config A GoldenLayout config object + * + * @returns {Object} minified config + */ lm.LayoutManager.minifyConfig = function (config) { return (new lm.utils.ConfigMinifier()).minifyConfig(config); }; - /** - * Takes a configuration Object that was previously minified - * using minifyConfig and returns its original version - * - * @static - * @public - * @param {Object} minifiedConfig - * - * @returns {Object} the original configuration - */ + /** + * Takes a configuration Object that was previously minified + * using minifyConfig and returns its original version + * + * @static + * @public + * @param {Object} minifiedConfig + * + * @returns {Object} the original configuration + */ lm.LayoutManager.unminifyConfig = function (config) { return (new lm.utils.ConfigMinifier()).unminifyConfig(config); }; lm.utils.copy(lm.LayoutManager.prototype, { - /** - * Register a component with the layout manager. If a configuration node - * of type component is reached it will look up componentName and create the - * associated component - * - * { - * type: "component", - * componentName: "EquityNewsFeed", - * componentState: { "feedTopic": "us-bluechips" } - * } - * - * @public - * @param {String} name - * @param {Function} constructor - * - * @returns {void} - */ + /** + * Register a component with the layout manager. If a configuration node + * of type component is reached it will look up componentName and create the + * associated component + * + * { + * type: "component", + * componentName: "EquityNewsFeed", + * componentState: { "feedTopic": "us-bluechips" } + * } + * + * @public + * @param {String} name + * @param {Function} constructor + * + * @returns {void} + */ registerComponent: function (name, constructor) { if (typeof constructor !== 'function') { throw new Error('Please register a constructor function'); @@ -586,12 +586,12 @@ this._components[name] = constructor; }, - /** - * Creates a layout configuration object based on the the current state - * - * @public - * @returns {Object} GoldenLayout configuration - */ + /** + * Creates a layout configuration object based on the the current state + * + * @public + * @returns {Object} GoldenLayout configuration + */ toConfig: function (root) { var config, next, i; @@ -603,18 +603,18 @@ throw new Error('Root must be a ContentItem'); } - /* - * settings & labels - */ + /* + * settings & labels + */ config = { settings: lm.utils.copy({}, this.config.settings), dimensions: lm.utils.copy({}, this.config.dimensions), labels: lm.utils.copy({}, this.config.labels) }; - /* - * Content - */ + /* + * Content + */ config.content = []; next = function (configNode, item) { var key, i; @@ -641,30 +641,30 @@ next(config, this.root); } - /* - * Retrieve config for subwindows - */ + /* + * Retrieve config for subwindows + */ this._$reconcilePopoutWindows(); config.openPopouts = []; for (i = 0; i < this.openPopouts.length; i++) { config.openPopouts.push(this.openPopouts[i].toConfig()); } - /* - * Add maximised item - */ + /* + * Add maximised item + */ config.maximisedItemId = this._maximisedItem ? '__glMaximised' : null; return config; }, - /** - * Returns a previously registered component - * - * @public - * @param {String} name The name used - * - * @returns {Function} - */ + /** + * Returns a previously registered component + * + * @public + * @param {String} name The name used + * + * @returns {Function} + */ getComponent: function (name) { if (this._components[name] === undefined) { throw new lm.errors.ConfigurationError('Unknown component "' + name + '"'); @@ -673,44 +673,44 @@ return this._components[name]; }, - /** - * Creates the actual layout. Must be called after all initial components - * are registered. Recurses through the configuration and sets up - * the item tree. - * - * If called before the document is ready it adds itself as a listener - * to the document.ready event - * - * @public - * - * @returns {void} - */ + /** + * Creates the actual layout. Must be called after all initial components + * are registered. Recurses through the configuration and sets up + * the item tree. + * + * If called before the document is ready it adds itself as a listener + * to the document.ready event + * + * @public + * + * @returns {void} + */ init: function () { - /** - * Create the popout windows straight away. If popouts are blocked - * an error is thrown on the same 'thread' rather than a timeout and can - * be caught. This also prevents any further initilisation from taking place. - */ + /** + * Create the popout windows straight away. If popouts are blocked + * an error is thrown on the same 'thread' rather than a timeout and can + * be caught. This also prevents any further initilisation from taking place. + */ if (this._subWindowsCreated === false) { this._createSubWindows(); this._subWindowsCreated = true; } - /** - * If the document isn't ready yet, wait for it. - */ + /** + * If the document isn't ready yet, wait for it. + */ if (document.readyState === 'loading' || document.body === null) { $(document).ready(lm.utils.fnBind(this.init, this)); return; } - /** - * If this is a subwindow, wait a few milliseconds for the original - * page's js calls to be executed, then replace the bodies content - * with GoldenLayout - */ + /** + * If this is a subwindow, wait a few milliseconds for the original + * page's js calls to be executed, then replace the bodies content + * with GoldenLayout + */ if (this.isSubWindow === true && this._creationTimeoutPassed === false) { setTimeout(lm.utils.fnBind(this.init, this), 7); this._creationTimeoutPassed = true; @@ -732,15 +732,15 @@ this.emit('initialised'); }, - /** - * Updates the layout managers size - * - * @public - * @param {[int]} width height in pixels - * @param {[int]} height width in pixels - * - * @returns {void} - */ + /** + * Updates the layout managers size + * + * @public + * @param {[int]} width height in pixels + * @param {[int]} height width in pixels + * + * @returns {void} + */ updateSize: function (width, height) { if (arguments.length === 2) { this.width = width; @@ -763,13 +763,13 @@ } }, - /** - * Destroys the LayoutManager instance itself as well as every ContentItem - * within it. After this is called nothing should be left of the LayoutManager. - * - * @public - * @returns {void} - */ + /** + * Destroys the LayoutManager instance itself as well as every ContentItem + * within it. After this is called nothing should be left of the LayoutManager. + * + * @public + * @returns {void} + */ destroy: function () { if (this.isInitialised === false) { return; @@ -793,16 +793,16 @@ this._dragSources = []; }, - /** - * Recursively creates new item tree structures based on a provided - * ItemConfiguration object - * - * @public - * @param {Object} config ItemConfig - * @param {[ContentItem]} parent The item the newly created item should be a child of - * - * @returns {lm.items.ContentItem} - */ + /** + * Recursively creates new item tree structures based on a provided + * ItemConfiguration object + * + * @public + * @param {Object} config ItemConfig + * @param {[ContentItem]} parent The item the newly created item should be a child of + * + * @returns {lm.items.ContentItem} + */ createContentItem: function (config, parent) { var typeErrorMsg, contentItem; @@ -823,9 +823,9 @@ } - /** - * We add an additional stack around every component that's not within a stack anyways. - */ + /** + * We add an additional stack around every component that's not within a stack anyways. + */ if ( // If this is a component config.type === 'component' && @@ -851,17 +851,17 @@ return contentItem; }, - /** - * Creates a popout window with the specified content and dimensions - * - * @param {Object|lm.itemsAbstractContentItem} configOrContentItem - * @param {[Object]} dimensions A map with width, height, left and top - * @param {[String]} parentId the id of the element this item will be appended to - * when popIn is called - * @param {[Number]} indexInParent The position of this item within its parent element + /** + * Creates a popout window with the specified content and dimensions + * + * @param {Object|lm.itemsAbstractContentItem} configOrContentItem + * @param {[Object]} dimensions A map with width, height, left and top + * @param {[String]} parentId the id of the element this item will be appended to + * when popIn is called + * @param {[Number]} indexInParent The position of this item within its parent element - * @returns {lm.controls.BrowserPopout} - */ + * @returns {lm.controls.BrowserPopout} + */ createPopout: function (configOrContentItem, dimensions, parentId, indexInParent) { var config = configOrContentItem, isItem = configOrContentItem instanceof lm.items.AbstractContentItem, @@ -879,14 +879,14 @@ config = this.toConfig(configOrContentItem).content; parentId = lm.utils.getUniqueId(); - /** - * If the item is the only component within a stack or for some - * other reason the only child of its parent the parent will be destroyed - * when the child is removed. - * - * In order to support this we move up the tree until we find something - * that will remain after the item is being popped out - */ + /** + * If the item is the only component within a stack or for some + * other reason the only child of its parent the parent will be destroyed + * when the child is removed. + * + * In order to support this we move up the tree until we find something + * that will remain after the item is being popped out + */ parent = configOrContentItem.parent; child = configOrContentItem; while (parent.contentItems.length === 1 && !parent.isRoot) { @@ -946,16 +946,16 @@ return browserPopout; }, - /** - * Attaches DragListener to any given DOM element - * and turns it into a way of creating new ContentItems - * by 'dragging' the DOM element into the layout - * - * @param {jQuery DOM element} element - * @param {Object|Function} itemConfig for the new item to be created, or a function which will provide it - * - * @returns {void} - */ + /** + * Attaches DragListener to any given DOM element + * and turns it into a way of creating new ContentItems + * by 'dragging' the DOM element into the layout + * + * @param {jQuery DOM element} element + * @param {Object|Function} itemConfig for the new item to be created, or a function which will provide it + * + * @returns {void} + */ createDragSource: function (element, itemConfig) { this.config.settings.constrainDragToContainer = false; var dragSource = new lm.controls.DragSource($(element), itemConfig, this); @@ -964,17 +964,17 @@ return dragSource; }, - /** - * Programmatically selects an item. This deselects - * the currently selected item, selects the specified item - * and emits a selectionChanged event - * - * @param {lm.item.AbstractContentItem} item# - * @param {[Boolean]} _$silent Wheather to notify the item of its selection - * @event selectionChanged - * - * @returns {VOID} - */ + /** + * Programmatically selects an item. This deselects + * the currently selected item, selects the specified item + * and emits a selectionChanged event + * + * @param {lm.item.AbstractContentItem} item# + * @param {[Boolean]} _$silent Wheather to notify the item of its selection + * @event selectionChanged + * + * @returns {VOID} + */ selectItem: function (item, _$silent) { if (this.config.settings.selectionEnabled !== true) { @@ -998,9 +998,9 @@ this.emit('selectionChanged', item); }, - /************************* - * PACKAGE PRIVATE - *************************/ + /************************* + * PACKAGE PRIVATE + *************************/ _$maximiseItem: function (contentItem) { if (this._maximisedItem !== null) { this._$minimiseItem(this._maximisedItem); @@ -1028,20 +1028,20 @@ this.emit('stateChanged'); }, - /** - * This method is used to get around sandboxed iframe restrictions. - * If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute - * (as is the case with codepens) the parent window is forbidden from calling certain - * methods on the child, such as window.close() or setting document.location.href. - * - * This prevented GoldenLayout popouts from popping in in codepens. The fix is to call - * _$closeWindow on the child window's gl instance which (after a timeout to disconnect - * the invoking method from the close call) closes itself. - * - * @packagePrivate - * - * @returns {void} - */ + /** + * This method is used to get around sandboxed iframe restrictions. + * If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute + * (as is the case with codepens) the parent window is forbidden from calling certain + * methods on the child, such as window.close() or setting document.location.href. + * + * This prevented GoldenLayout popouts from popping in in codepens. The fix is to call + * _$closeWindow on the child window's gl instance which (after a timeout to disconnect + * the invoking method from the close call) closes itself. + * + * @packagePrivate + * + * @returns {void} + */ _$closeWindow: function () { window.setTimeout(function () { window.close(); @@ -1088,13 +1088,13 @@ var i, area, allContentItems = this._getAllContentItems(); this._itemAreas = []; - /** - * If the last item is dragged out, highlight the entire container size to - * allow to re-drop it. allContentItems[ 0 ] === this.root at this point - * - * Don't include root into the possible drop areas though otherwise since it - * will used for every gap in the layout, e.g. splitters - */ + /** + * If the last item is dragged out, highlight the entire container size to + * allow to re-drop it. allContentItems[ 0 ] === this.root at this point + * + * Don't include root into the possible drop areas though otherwise since it + * will used for every gap in the layout, e.g. splitters + */ if (allContentItems.length === 1) { this._itemAreas.push(this.root._$getArea()); return; @@ -1124,18 +1124,18 @@ } }, - /** - * Takes a contentItem or a configuration and optionally a parent - * item and returns an initialised instance of the contentItem. - * If the contentItem is a function, it is first called - * - * @packagePrivate - * - * @param {lm.items.AbtractContentItem|Object|Function} contentItemOrConfig - * @param {lm.items.AbtractContentItem} parent Only necessary when passing in config - * - * @returns {lm.items.AbtractContentItem} - */ + /** + * Takes a contentItem or a configuration and optionally a parent + * item and returns an initialised instance of the contentItem. + * If the contentItem is a function, it is first called + * + * @packagePrivate + * + * @param {lm.items.AbtractContentItem|Object|Function} contentItemOrConfig + * @param {lm.items.AbtractContentItem} parent Only necessary when passing in config + * + * @returns {lm.items.AbtractContentItem} + */ _$normalizeContentItem: function (contentItemOrConfig, parent) { if (!contentItemOrConfig) { throw new Error('No content item defined'); @@ -1158,15 +1158,15 @@ } }, - /** - * Iterates through the array of open popout windows and removes the ones - * that are effectively closed. This is necessary due to the lack of reliably - * listening for window.close / unload events in a cross browser compatible fashion. - * - * @packagePrivate - * - * @returns {void} - */ + /** + * Iterates through the array of open popout windows and removes the ones + * that are effectively closed. This is necessary due to the lack of reliably + * listening for window.close / unload events in a cross browser compatible fashion. + * + * @packagePrivate + * + * @returns {void} + */ _$reconcilePopoutWindows: function () { var openPopouts = [], i; @@ -1185,17 +1185,17 @@ }, - /*************************** - * PRIVATE - ***************************/ - /** - * Returns a flattened array of all content items, - * regardles of level or type - * - * @private - * - * @returns {void} - */ + /*************************** + * PRIVATE + ***************************/ + /** + * Returns a flattened array of all content items, + * regardles of level or type + * + * @private + * + * @returns {void} + */ _getAllContentItems: function () { var allContentItems = []; @@ -1214,13 +1214,13 @@ return allContentItems; }, - /** - * Binds to DOM/BOM events on init - * - * @private - * - * @returns {void} - */ + /** + * Binds to DOM/BOM events on init + * + * @private + * + * @returns {void} + */ _bindEvents: function () { if (this._isFullPage) { $(window).resize(this._resizeFunction); @@ -1228,27 +1228,27 @@ $(window).on('unload beforeunload', this._unloadFunction); }, - /** - * Debounces resize events - * - * @private - * - * @returns {void} - */ + /** + * Debounces resize events + * + * @private + * + * @returns {void} + */ _onResize: function () { clearTimeout(this._resizeTimeoutId); this._resizeTimeoutId = setTimeout(lm.utils.fnBind(this.updateSize, this), 100); }, - /** - * Extends the default config with the user specific settings and applies - * derivations. Please note that there's a seperate method (AbstractContentItem._extendItemNode) - * that deals with the extension of item configs - * - * @param {Object} config - * @static - * @returns {Object} config - */ + /** + * Extends the default config with the user specific settings and applies + * derivations. Please note that there's a seperate method (AbstractContentItem._extendItemNode) + * that deals with the extension of item configs + * + * @param {Object} config + * @static + * @returns {Object} config + */ _createConfig: function (config) { var windowConfigKey = lm.utils.getQueryStringParam('gl-window'); @@ -1283,14 +1283,14 @@ return config; }, - /** - * This is executed when GoldenLayout detects that it is run - * within a previously opened popout window. - * - * @private - * - * @returns {void} - */ + /** + * This is executed when GoldenLayout detects that it is run + * within a previously opened popout window. + * + * @private + * + * @returns {void} + */ _adjustToWindowMode: function () { var popInButton = $('
' + '
' + @@ -1310,26 +1310,26 @@ .css('visibility', 'visible') .append(popInButton); - /* - * This seems a bit pointless, but actually causes a reflow/re-evaluation getting around - * slickgrid's "Cannot find stylesheet." bug in chrome - */ + /* + * This seems a bit pointless, but actually causes a reflow/re-evaluation getting around + * slickgrid's "Cannot find stylesheet." bug in chrome + */ var x = document.body.offsetHeight; // jshint ignore:line - /* - * Expose this instance on the window object - * to allow the opening window to interact with - * it - */ + /* + * Expose this instance on the window object + * to allow the opening window to interact with + * it + */ window.__glInstance = this; }, - /** - * Creates Subwindows (if there are any). Throws an error - * if popouts are blocked. - * - * @returns {void} - */ + /** + * Creates Subwindows (if there are any). Throws an error + * if popouts are blocked. + * + * @returns {void} + */ _createSubWindows: function () { var i, popout; @@ -1345,13 +1345,13 @@ } }, - /** - * Determines what element the layout will be created in - * - * @private - * - * @returns {void} - */ + /** + * Determines what element the layout will be created in + * + * @private + * + * @returns {void} + */ _setContainer: function () { var container = $(this.container || 'body'); @@ -1377,13 +1377,13 @@ this.container = container; }, - /** - * Kicks of the initial, recursive creation chain - * - * @param {Object} config GoldenLayout Config - * - * @returns {void} - */ + /** + * Kicks of the initial, recursive creation chain + * + * @param {Object} config GoldenLayout Config + * + * @returns {void} + */ _create: function (config) { var errorMsg; @@ -1410,12 +1410,12 @@ } }, - /** - * Called when the window is closed or the user navigates away - * from the page - * - * @returns {void} - */ + /** + * Called when the window is closed or the user navigates away + * from the page + * + * @returns {void} + */ _onUnload: function () { if (this.config.settings.closePopoutsOnUnload === true) { for (var i = 0; i < this.openPopouts.length; i++) { @@ -1424,11 +1424,11 @@ } }, - /** - * Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth. - * - * @returns {void} - */ + /** + * Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth. + * + * @returns {void} + */ _adjustColumnsResponsive: function () { // If there is no min width set, or not content items, do nothing. @@ -1470,21 +1470,21 @@ this._updatingColumnsResponsive = false; }, - /** - * Determines if responsive layout should be used. - * - * @returns {bool} - True if responsive layout should be used; otherwise false. - */ + /** + * Determines if responsive layout should be used. + * + * @returns {bool} - True if responsive layout should be used; otherwise false. + */ _useResponsiveLayout: function () { return this.config.settings && (this.config.settings.responsiveMode == 'always' || (this.config.settings.responsiveMode == 'onload' && this._firstLoad)); }, - /** - * Adds all children of a node to another container recursively. - * @param {object} container - Container to add child content items to. - * @param {object} node - Node to search for content items. - * @returns {void} - */ + /** + * Adds all children of a node to another container recursively. + * @param {object} container - Container to add child content items to. + * @param {object} node - Node to search for content items. + * @returns {void} + */ _addChildContentItemsToContainer: function (container, node) { if (node.type === 'stack') { node.contentItems.forEach(function (item) { @@ -1499,10 +1499,10 @@ } }, - /** - * Finds all the stack containers. - * @returns {array} - The found stack containers. - */ + /** + * Finds all the stack containers. + * @returns {array} - The found stack containers. + */ _findAllStackContainers: function () { var stackContainers = []; this._findAllStackContainersRecursive(stackContainers, this.root); @@ -1510,14 +1510,14 @@ return stackContainers; }, - /** - * Finds all the stack containers. - * - * @param {array} - Set of containers to populate. - * @param {object} - Current node to process. - * - * @returns {void} - */ + /** + * Finds all the stack containers. + * + * @param {array} - Set of containers to populate. + * @param {object} - Current node to process. + * + * @returns {void} + */ _findAllStackContainersRecursive: function (stackContainers, node) { node.contentItems.forEach(lm.utils.fnBind(function (item) { if (item.type == 'stack') { @@ -1530,9 +1530,9 @@ } }); - /** - * Expose the Layoutmanager as the single entrypoint using UMD - */ + /** + * Expose the Layoutmanager as the single entrypoint using UMD + */ (function () { /* global define */ if (typeof define === 'function' && define.amd) { @@ -1583,7 +1583,7 @@ close: 'close', maximise: 'maximise', minimise: 'minimise', - popout: 'open in new window', + popout: 'new tab', popin: 'pop in', tabDropdown: 'additional tabs' } @@ -1611,36 +1611,36 @@ lm.utils.copy(lm.container.ItemContainer.prototype, { - /** - * Get the inner DOM element the container's content - * is intended to live in - * - * @returns {DOM element} - */ + /** + * Get the inner DOM element the container's content + * is intended to live in + * + * @returns {DOM element} + */ getElement: function () { return this._contentElement; }, - /** - * Hide the container. Notifies the containers content first - * and then hides the DOM node. If the container is already hidden - * this should have no effect - * - * @returns {void} - */ + /** + * Hide the container. Notifies the containers content first + * and then hides the DOM node. If the container is already hidden + * this should have no effect + * + * @returns {void} + */ hide: function () { this.emit('hide'); this.isHidden = true; this._element.hide(); }, - /** - * Shows a previously hidden container. Notifies the - * containers content first and then shows the DOM element. - * If the container is already visible this has no effect. - * - * @returns {void} - */ + /** + * Shows a previously hidden container. Notifies the + * containers content first and then shows the DOM element. + * If the container is already visible this has no effect. + * + * @returns {void} + */ show: function () { this.emit('show'); this.isHidden = false; @@ -1651,19 +1651,19 @@ } }, - /** - * Set the size from within the container. Traverses up - * the item tree until it finds a row or column element - * and resizes its items accordingly. - * - * If this container isn't a descendant of a row or column - * it returns false - * @todo Rework!!! - * @param {Number} width The new width in pixel - * @param {Number} height The new height in pixel - * - * @returns {Boolean} resizeSuccesful - */ + /** + * Set the size from within the container. Traverses up + * the item tree until it finds a row or column element + * and resizes its items accordingly. + * + * If this container isn't a descendant of a row or column + * it returns false + * @todo Rework!!! + * @param {Number} width The new width in pixel + * @param {Number} height The new height in pixel + * + * @returns {Boolean} resizeSuccesful + */ setSize: function (width, height) { var rowOrColumn = this.parent, rowOrColumnChild = this, @@ -1679,9 +1679,9 @@ rowOrColumn = rowOrColumn.parent; - /** - * No row or column has been found - */ + /** + * No row or column has been found + */ if (rowOrColumn.isRoot) { return false; } @@ -1707,13 +1707,13 @@ return true; }, - /** - * Closes the container if it is closable. Can be called by - * both the component within at as well as the contentItem containing - * it. Emits a close event before the container itself is closed. - * - * @returns {void} - */ + /** + * Closes the container if it is closable. Can be called by + * both the component within at as well as the contentItem containing + * it. Emits a close event before the container itself is closed. + * + * @returns {void} + */ close: function () { if (this._config.isClosable) { this.emit('close'); @@ -1721,55 +1721,55 @@ } }, - /** - * Returns the current state object - * - * @returns {Object} state - */ + /** + * Returns the current state object + * + * @returns {Object} state + */ getState: function () { return this._config.componentState; }, - /** - * Merges the provided state into the current one - * - * @param {Object} state - * - * @returns {void} - */ + /** + * Merges the provided state into the current one + * + * @param {Object} state + * + * @returns {void} + */ extendState: function (state) { this.setState($.extend(true, this.getState(), state)); }, - /** - * Notifies the layout manager of a stateupdate - * - * @param {serialisable} state - */ + /** + * Notifies the layout manager of a stateupdate + * + * @param {serialisable} state + */ setState: function (state) { this._config.componentState = state; this.parent.emitBubblingEvent('stateChanged'); }, - /** - * Set's the components title - * - * @param {String} title - */ + /** + * Set's the components title + * + * @param {String} title + */ setTitle: function (title) { this.parent.setTitle(title); }, - /** - * Set's the containers size. Called by the container's component. - * To set the size programmatically from within the container please - * use the public setSize method - * - * @param {[Int]} width in px - * @param {[Int]} height in px - * - * @returns {void} - */ + /** + * Set's the containers size. Called by the container's component. + * To set the size programmatically from within the container please + * use the public setSize method + * + * @param {[Int]} width in px + * @param {[Int]} height in px + * + * @returns {void} + */ _$setSize: function (width, height) { if (width !== this.width || height !== this.height) { this.width = width; @@ -1784,22 +1784,22 @@ } }); - /** - * Pops a content item out into a new browser window. - * This is achieved by - * - * - Creating a new configuration with the content item as root element - * - Serializing and minifying the configuration - * - Opening the current window's URL with the configuration as a GET parameter - * - GoldenLayout when opened in the new window will look for the GET parameter - * and use it instead of the provided configuration - * - * @param {Object} config GoldenLayout item config - * @param {Object} dimensions A map with width, height, top and left - * @param {String} parentId The id of the element the item will be appended to on popIn - * @param {Number} indexInParent The position of this element within its parent - * @param {lm.LayoutManager} layoutManager - */ + /** + * Pops a content item out into a new browser window. + * This is achieved by + * + * - Creating a new configuration with the content item as root element + * - Serializing and minifying the configuration + * - Opening the current window's URL with the configuration as a GET parameter + * - GoldenLayout when opened in the new window will look for the GET parameter + * and use it instead of the provided configuration + * + * @param {Object} config GoldenLayout item config + * @param {Object} dimensions A map with width, height, top and left + * @param {String} parentId The id of the element the item will be appended to on popIn + * @param {Number} indexInParent The position of this element within its parent + * @param {lm.LayoutManager} layoutManager + */ lm.controls.BrowserPopout = function (config, dimensions, parentId, indexInParent, layoutManager) { lm.utils.EventEmitter.call(this); this.isInitialised = false; @@ -1853,10 +1853,10 @@ } }, - /** - * Returns the popped out item to its original position. If the original - * parent isn't available anymore it falls back to the layout's topmost element - */ + /** + * Returns the popped out item to its original position. If the original + * parent isn't available anymore it falls back to the layout's topmost element + */ popIn: function () { var childConfig, parentItem, @@ -1864,22 +1864,22 @@ if (this._parentId) { - /* - * The $.extend call seems a bit pointless, but it's crucial to - * copy the config returned by this.getGlInstance().toConfig() - * onto a new object. Internet Explorer keeps the references - * to objects on the child window, resulting in the following error - * once the child window is closed: - * - * The callee (server [not server application]) is not available and disappeared - */ + /* + * The $.extend call seems a bit pointless, but it's crucial to + * copy the config returned by this.getGlInstance().toConfig() + * onto a new object. Internet Explorer keeps the references + * to objects on the child window, resulting in the following error + * once the child window is closed: + * + * The callee (server [not server application]) is not available and disappeared + */ childConfig = $.extend(true, {}, this.getGlInstance().toConfig()).content[0]; parentItem = this._layoutManager.root.getItemsById(this._parentId)[0]; - /* - * Fallback if parentItem is not available. Either add it to the topmost - * item or make it the topmost item if the layout is empty - */ + /* + * Fallback if parentItem is not available. Either add it to the topmost + * item or make it the topmost item if the layout is empty + */ if (!parentItem) { if (this._layoutManager.root.contentItems.length > 0) { parentItem = this._layoutManager.root.contentItems[0]; @@ -1894,28 +1894,28 @@ this.close(); }, - /** - * Creates the URL and window parameter - * and opens a new window - * - * @private - * - * @returns {void} - */ + /** + * Creates the URL and window parameter + * and opens a new window + * + * @private + * + * @returns {void} + */ _createWindow: function () { var checkReadyInterval, url = this._createUrl(), - /** - * Bogus title to prevent re-usage of existing window with the - * same title. The actual title will be set by the new window's - * GoldenLayout instance if it detects that it is in subWindowMode - */ + /** + * Bogus title to prevent re-usage of existing window with the + * same title. The actual title will be set by the new window's + * GoldenLayout instance if it detects that it is in subWindowMode + */ title = Math.floor(Math.random() * 1000000).toString(36), - /** - * The options as used in the window.open string - */ + /** + * The options as used in the window.open string + */ options = this._serializeWindowOptions({ width: this._dimensions.width, height: this._dimensions.height, @@ -1946,12 +1946,12 @@ .on('load', lm.utils.fnBind(this._positionWindow, this)) .on('unload beforeunload', lm.utils.fnBind(this._onClose, this)); - /** - * Polling the childwindow to find out if GoldenLayout has been initialised - * doesn't seem optimal, but the alternatives - adding a callback to the parent - * window or raising an event on the window object - both would introduce knowledge - * about the parent to the child window which we'd rather avoid - */ + /** + * Polling the childwindow to find out if GoldenLayout has been initialised + * doesn't seem optimal, but the alternatives - adding a callback to the parent + * window or raising an event on the window object - both would introduce knowledge + * about the parent to the child window which we'd rather avoid + */ checkReadyInterval = setInterval(lm.utils.fnBind(function () { if (this._popoutWindow.__glInstance && this._popoutWindow.__glInstance.isInitialised) { this._onInitialised(); @@ -1960,13 +1960,13 @@ }, this), 10); }, - /** - * Serialises a map of key:values to a window options string - * - * @param {Object} windowOptions - * - * @returns {String} serialised window options - */ + /** + * Serialises a map of key:values to a window options string + * + * @param {Object} windowOptions + * + * @returns {String} serialised window options + */ _serializeWindowOptions: function (windowOptions) { var windowOptionsString = [], key; @@ -1977,12 +1977,12 @@ return windowOptionsString.join(','); }, - /** - * Creates the URL for the new window, including the - * config GET parameter - * - * @returns {String} URL - */ + /** + * Creates the URL for the new window, including the + * config GET parameter + * + * @returns {String} URL + */ _createUrl: function () { var config = { content: this._config }, storageKey = 'gl-window-config-' + lm.utils.getUniqueId(), @@ -2008,57 +2008,57 @@ } }, - /** - * Move the newly created window roughly to - * where the component used to be. - * - * @private - * - * @returns {void} - */ + /** + * Move the newly created window roughly to + * where the component used to be. + * + * @private + * + * @returns {void} + */ _positionWindow: function () { this._popoutWindow.moveTo(this._dimensions.left, this._dimensions.top); this._popoutWindow.focus(); }, - /** - * Callback when the new window is opened and the GoldenLayout instance - * within it is initialised - * - * @returns {void} - */ + /** + * Callback when the new window is opened and the GoldenLayout instance + * within it is initialised + * + * @returns {void} + */ _onInitialised: function () { this.isInitialised = true; this.getGlInstance().on('popIn', this.popIn, this); this.emit('initialised'); }, - /** - * Invoked 50ms after the window unload event - * - * @private - * - * @returns {void} - */ + /** + * Invoked 50ms after the window unload event + * + * @private + * + * @returns {void} + */ _onClose: function () { setTimeout(lm.utils.fnBind(this.emit, this, ['closed']), 50); } }); - /** - * This class creates a temporary container - * for the component whilst it is being dragged - * and handles drag events - * - * @constructor - * @private - * - * @param {Number} x The initial x position - * @param {Number} y The initial y position - * @param {lm.utils.DragListener} dragListener - * @param {lm.LayoutManager} layoutManager - * @param {lm.item.AbstractContentItem} contentItem - * @param {lm.item.AbstractContentItem} originalParent - */ + /** + * This class creates a temporary container + * for the component whilst it is being dragged + * and handles drag events + * + * @constructor + * @private + * + * @param {Number} x The initial x position + * @param {Number} y The initial y position + * @param {lm.utils.DragListener} dragListener + * @param {lm.LayoutManager} layoutManager + * @param {lm.item.AbstractContentItem} contentItem + * @param {lm.item.AbstractContentItem} originalParent + */ lm.controls.DragProxy = function (x, y, dragListener, layoutManager, contentItem, originalParent) { lm.utils.EventEmitter.call(this); @@ -2120,19 +2120,19 @@ lm.utils.copy(lm.controls.DragProxy.prototype, { - /** - * Callback on every mouseMove event during a drag. Determines if the drag is - * still within the valid drag area and calls the layoutManager to highlight the - * current drop area - * - * @param {Number} offsetX The difference from the original x position in px - * @param {Number} offsetY The difference from the original y position in px - * @param {jQuery DOM event} event - * - * @private - * - * @returns {void} - */ + /** + * Callback on every mouseMove event during a drag. Determines if the drag is + * still within the valid drag area and calls the layoutManager to highlight the + * current drop area + * + * @param {Number} offsetX The difference from the original x position in px + * @param {Number} offsetY The difference from the original y position in px + * @param {jQuery DOM event} event + * + * @private + * + * @returns {void} + */ _onDrag: function (offsetX, offsetY, event) { event = event.originalEvent && event.originalEvent.touches ? event.originalEvent.touches[0] : event; @@ -2148,16 +2148,16 @@ this._setDropPosition(x, y); }, - /** - * Sets the target position, highlighting the appropriate area - * - * @param {Number} x The x position in px - * @param {Number} y The y position in px - * - * @private - * - * @returns {void} - */ + /** + * Sets the target position, highlighting the appropriate area + * + * @param {Number} x The x position in px + * @param {Number} y The y position in px + * + * @private + * + * @returns {void} + */ _setDropPosition: function (x, y) { this.element.css({ left: x, top: y }); this._area = this._layoutManager._$getArea(x, y); @@ -2168,43 +2168,43 @@ } }, - /** - * Callback when the drag has finished. Determines the drop area - * and adds the child to it - * - * @private - * - * @returns {void} - */ + /** + * Callback when the drag has finished. Determines the drop area + * and adds the child to it + * + * @private + * + * @returns {void} + */ _onDrop: function () { this._layoutManager.dropTargetIndicator.hide(); - /* - * Valid drop area found - */ + /* + * Valid drop area found + */ if (this._area !== null) { this._area.contentItem._$onDrop(this._contentItem, this._area); - /** - * No valid drop area available at present, but one has been found before. - * Use it - */ + /** + * No valid drop area available at present, but one has been found before. + * Use it + */ } else if (this._lastValidArea !== null) { this._lastValidArea.contentItem._$onDrop(this._contentItem, this._lastValidArea); - /** - * No valid drop area found during the duration of the drag. Return - * content item to its original position if a original parent is provided. - * (Which is not the case if the drag had been initiated by createDragSource) - */ + /** + * No valid drop area found during the duration of the drag. Return + * content item to its original position if a original parent is provided. + * (Which is not the case if the drag had been initiated by createDragSource) + */ } else if (this._originalParent) { this._originalParent.addChild(this._contentItem); - /** - * The drag didn't ultimately end up with adding the content item to - * any container. In order to ensure clean up happens, destroy the - * content item. - */ + /** + * The drag didn't ultimately end up with adding the content item to + * any container. In order to ensure clean up happens, destroy the + * content item. + */ } else { this._contentItem._$destroy(); } @@ -2215,18 +2215,18 @@ this._layoutManager.emit('itemDropped', this._contentItem); }, - /** - * Removes the item from its original position within the tree - * - * @private - * - * @returns {void} - */ + /** + * Removes the item from its original position within the tree + * + * @private + * + * @returns {void} + */ _updateTree: function () { - /** - * parent is null if the drag had been initiated by a external drag source - */ + /** + * parent is null if the drag had been initiated by a external drag source + */ if (this._contentItem.parent) { this._contentItem.parent.removeChild(this._contentItem, true); } @@ -2234,13 +2234,13 @@ this._contentItem._$setParent(this); }, - /** - * Updates the Drag Proxie's dimensions - * - * @private - * - * @returns {void} - */ + /** + * Updates the Drag Proxie's dimensions + * + * @private + * + * @returns {void} + */ _setDimensions: function () { var dimensions = this._layoutManager.config.dimensions, width = dimensions.dragProxyWidth, @@ -2259,16 +2259,16 @@ } }); - /** - * Allows for any DOM item to create a component on drag - * start tobe dragged into the Layout - * - * @param {jQuery element} element - * @param {Object} itemConfig the configuration for the contentItem that will be created - * @param {LayoutManager} layoutManager - * - * @constructor - */ + /** + * Allows for any DOM item to create a component on drag + * start tobe dragged into the Layout + * + * @param {jQuery element} element + * @param {Object} itemConfig the configuration for the contentItem that will be created + * @param {LayoutManager} layoutManager + * + * @constructor + */ lm.controls.DragSource = function (element, itemConfig, layoutManager) { this._element = element; this._itemConfig = itemConfig; @@ -2280,11 +2280,11 @@ lm.utils.copy(lm.controls.DragSource.prototype, { - /** - * Called initially and after every drag - * - * @returns {void} - */ + /** + * Called initially and after every drag + * + * @returns {void} + */ _createDragListener: function () { if (this._dragListener !== null) { this._dragListener.destroy(); @@ -2306,14 +2306,14 @@ } }, - /** - * Callback for the DragListener's dragStart event - * - * @param {int} x the x position of the mouse on dragStart - * @param {int} y the x position of the mouse on dragStart - * - * @returns {void} - */ + /** + * Callback for the DragListener's dragStart event + * + * @param {int} x the x position of the mouse on dragStart + * @param {int} y the x position of the mouse on dragStart + * + * @returns {void} + */ _onDragStart: function (x, y) { var itemConfig = this._itemConfig; if (lm.utils.isFunction(itemConfig)) { @@ -2355,12 +2355,12 @@ this.element.hide(); } }); - /** - * This class represents a header above a Stack ContentItem. - * - * @param {lm.LayoutManager} layoutManager - * @param {lm.item.AbstractContentItem} parent - */ + /** + * This class represents a header above a Stack ContentItem. + * + * @param {lm.LayoutManager} layoutManager + * @param {lm.item.AbstractContentItem} parent + */ lm.controls.Header = function (layoutManager, parent) { lm.utils.EventEmitter.call(this); @@ -2400,14 +2400,14 @@ lm.utils.copy(lm.controls.Header.prototype, { - /** - * Creates a new tab and associates it with a contentItem - * - * @param {lm.item.AbstractContentItem} contentItem - * @param {Integer} index The position of the tab - * - * @returns {void} - */ + /** + * Creates a new tab and associates it with a contentItem + * + * @param {lm.item.AbstractContentItem} contentItem + * @param {Integer} index The position of the tab + * + * @returns {void} + */ createTab: function (contentItem, index) { var tab, i; @@ -2441,13 +2441,13 @@ this._updateTabSizes(); }, - /** - * Finds a tab based on the contentItem its associated with and removes it. - * - * @param {lm.item.AbstractContentItem} contentItem - * - * @returns {void} - */ + /** + * Finds a tab based on the contentItem its associated with and removes it. + * + * @param {lm.item.AbstractContentItem} contentItem + * + * @returns {void} + */ removeTab: function (contentItem) { for (var i = 0; i < this.tabs.length; i++) { if (this.tabs[i].contentItem === contentItem) { @@ -2460,11 +2460,11 @@ throw new Error('contentItem is not controlled by this header'); }, - /** - * The programmatical equivalent of clicking a Tab. - * - * @param {lm.item.AbstractContentItem} contentItem - */ + /** + * The programmatical equivalent of clicking a Tab. + * + * @param {lm.item.AbstractContentItem} contentItem + */ setActiveContentItem: function (contentItem) { var i, j, isActive, activeTab; @@ -2478,10 +2478,10 @@ } if (this.layoutManager.config.settings.reorderOnTabMenuClick) { - /** - * If the tab selected was in the dropdown, move everything down one to make way for this one to be the first. - * This will make sure the most used tabs stay visible. - */ + /** + * If the tab selected was in the dropdown, move everything down one to make way for this one to be the first. + * This will make sure the most used tabs stay visible. + */ if (this._lastVisibleTabIndex !== -1 && this.parent.config.activeItemIndex > this._lastVisibleTabIndex) { activeTab = this.tabs[this.parent.config.activeItemIndex]; for (j = this.parent.config.activeItemIndex; j > 0; j--) { @@ -2496,13 +2496,13 @@ this.parent.emitBubblingEvent('stateChanged'); }, - /** - * Programmatically operate with header position. - * - * @param {string} position one of ('top','left','right','bottom') to set or empty to get it. - * - * @returns {string} previous header position - */ + /** + * Programmatically operate with header position. + * + * @param {string} position one of ('top','left','right','bottom') to set or empty to get it. + * + * @returns {string} previous header position + */ position: function (position) { var previous = this.parent._header.show; if (previous && !this.parent._side) @@ -2514,14 +2514,14 @@ return previous; }, - /** - * Programmatically set closability. - * - * @package private - * @param {Boolean} isClosable Whether to enable/disable closability. - * - * @returns {Boolean} Whether the action was successful - */ + /** + * Programmatically set closability. + * + * @package private + * @param {Boolean} isClosable Whether to enable/disable closability. + * + * @returns {Boolean} Whether the action was successful + */ _$setClosable: function (isClosable) { if (this.closeButton && this._isClosable()) { this.closeButton.element[isClosable ? "show" : "hide"](); @@ -2531,13 +2531,13 @@ return false; }, - /** - * Destroys the entire header - * - * @package private - * - * @returns {void} - */ + /** + * Destroys the entire header + * + * @package private + * + * @returns {void} + */ _$destroy: function () { this.emit('destroy', this); @@ -2548,20 +2548,20 @@ this.element.remove(); }, - /** - * get settings from header - * - * @returns {string} when exists - */ + /** + * get settings from header + * + * @returns {string} when exists + */ _getHeaderSetting: function (name) { if (name in this.parent._header) return this.parent._header[name]; }, - /** - * Creates the popout, maximise and close buttons in the header's top right corner - * - * @returns {void} - */ + /** + * Creates the popout, maximise and close buttons in the header's top right corner + * + * @returns {void} + */ _createControls: function () { var closeStack, popout, @@ -2573,26 +2573,26 @@ tabDropdownLabel, showTabDropdown; - /** - * Dropdown to show additional tabs. - */ + /** + * Dropdown to show additional tabs. + */ showTabDropdown = lm.utils.fnBind(this._showAdditionalTabsDropdown, this); tabDropdownLabel = this.layoutManager.config.labels.tabDropdown; this.tabDropdownButton = new lm.controls.HeaderButton(this, tabDropdownLabel, 'lm_tabdropdown', showTabDropdown); this.tabDropdownButton.element.hide(); - /** - * Popout control to launch component in new window. - */ + /** + * Popout control to launch component in new window. + */ if (this._getHeaderSetting('popout')) { popout = lm.utils.fnBind(this._onPopoutClick, this); label = this._getHeaderSetting('popout'); new lm.controls.HeaderButton(this, label, 'lm_popout', popout); } - /** - * Maximise control - set the component to the full size of the layout - */ + /** + * Maximise control - set the component to the full size of the layout + */ if (this._getHeaderSetting('maximise')) { maximise = lm.utils.fnBind(this.parent.toggleMaximise, this.parent); maximiseLabel = this._getHeaderSetting('maximise'); @@ -2608,9 +2608,9 @@ }); } - /** - * Close button - */ + /** + * Close button + */ if (this._isClosable()) { closeStack = lm.utils.fnBind(this.parent.remove, this.parent); label = this._getHeaderSetting('close'); @@ -2618,30 +2618,30 @@ } }, - /** - * Shows drop down for additional tabs when there are too many to display. - * - * @returns {void} - */ + /** + * Shows drop down for additional tabs when there are too many to display. + * + * @returns {void} + */ _showAdditionalTabsDropdown: function () { this.tabDropdownContainer.show(); }, - /** - * Hides drop down for additional tabs when there are too many to display. - * - * @returns {void} - */ + /** + * Hides drop down for additional tabs when there are too many to display. + * + * @returns {void} + */ _hideAdditionalTabsDropdown: function (e) { this.tabDropdownContainer.hide(); }, - /** - * Checks whether the header is closable based on the parent config and - * the global config. - * - * @returns {Boolean} Whether the header is closable. - */ + /** + * Checks whether the header is closable based on the parent config and + * the global config. + * + * @returns {Boolean} Whether the header is closable. + */ _isClosable: function () { return this.parent.config.isClosable && this.layoutManager.config.settings.showCloseIcon; }, @@ -2655,24 +2655,24 @@ }, - /** - * Invoked when the header's background is clicked (not it's tabs or controls) - * - * @param {jQuery DOM event} event - * - * @returns {void} - */ + /** + * Invoked when the header's background is clicked (not it's tabs or controls) + * + * @param {jQuery DOM event} event + * + * @returns {void} + */ _onHeaderClick: function (event) { if (event.target === this.element[0]) { this.parent.select(); } }, - /** - * Pushes the tabs to the tab dropdown if the available space is not sufficient - * - * @returns {void} - */ + /** + * Pushes the tabs to the tab dropdown if the available space is not sufficient + * + * @returns {void} + */ _updateTabSizes: function (showTabMenu) { if (this.tabs.length === 0) { return; @@ -2835,14 +2835,14 @@ } }); - /** - * Represents an individual tab within a Stack's header - * - * @param {lm.controls.Header} header - * @param {lm.items.AbstractContentItem} contentItem - * - * @constructor - */ + /** + * Represents an individual tab within a Stack's header + * + * @param {lm.controls.Header} header + * @param {lm.items.AbstractContentItem} contentItem + * + * @constructor + */ lm.controls.Tab = function (header, contentItem) { this.header = header; this.contentItem = contentItem; @@ -2888,37 +2888,37 @@ } }; - /** - * The tab's html template - * - * @type {String} - */ + /** + * The tab's html template + * + * @type {String} + */ lm.controls.Tab._template = '
  • ' + '
    ' + '
  • '; lm.utils.copy(lm.controls.Tab.prototype, { - /** - * Sets the tab's title to the provided string and sets - * its title attribute to a pure text representation (without - * html tags) of the same string. - * - * @public - * @param {String} title can contain html - */ + /** + * Sets the tab's title to the provided string and sets + * its title attribute to a pure text representation (without + * html tags) of the same string. + * + * @public + * @param {String} title can contain html + */ setTitle: function (title) { this.element.attr('title', lm.utils.stripTags(title)); this.titleElement.html(title); }, - /** - * Sets this tab's active state. To programmatically - * switch tabs, use header.setActiveContentItem( item ) instead. - * - * @public - * @param {Boolean} isActive - */ + /** + * Sets this tab's active state. To programmatically + * switch tabs, use header.setActiveContentItem( item ) instead. + * + * @public + * @param {Boolean} isActive + */ setActive: function (isActive) { if (isActive === this.isActive) { return; @@ -2932,12 +2932,12 @@ } }, - /** - * Destroys the tab - * - * @private - * @returns {void} - */ + /** + * Destroys the tab + * + * @private + * @returns {void} + */ _$destroy: function () { this._layoutManager.emit('tabDestroyed', this); this.element.off('mousedown touchstart', this._onTabClickFn); @@ -2950,15 +2950,15 @@ this.element.remove(); }, - /** - * Callback for the DragListener - * - * @param {Number} x The tabs absolute x position - * @param {Number} y The tabs absolute y position - * - * @private - * @returns {void} - */ + /** + * Callback for the DragListener + * + * @param {Number} x The tabs absolute x position + * @param {Number} y The tabs absolute y position + * + * @private + * @returns {void} + */ _onDragStart: function (x, y) { if (this.contentItem.parent.isMaximised === true) { this.contentItem.parent.toggleMaximise(); @@ -2973,14 +2973,14 @@ ); }, - /** - * Callback when the tab is clicked - * - * @param {jQuery DOM event} event - * - * @private - * @returns {void} - */ + /** + * Callback when the tab is clicked + * + * @param {jQuery DOM event} event + * + * @private + * @returns {void} + */ _onTabClick: function (event) { // left mouse button or tap if (event.button === 0 || event.type === 'touchstart') { @@ -2995,30 +2995,30 @@ } }, - /** - * Callback when the tab's close button is - * clicked - * - * @param {jQuery DOM event} event - * - * @private - * @returns {void} - */ + /** + * Callback when the tab's close button is + * clicked + * + * @param {jQuery DOM event} event + * + * @private + * @returns {void} + */ _onCloseClick: function (event) { event.stopPropagation(); this.header.parent.removeChild(this.contentItem); }, - /** - * Callback to capture tab close button mousedown - * to prevent tab from activating. - * - * @param (jQuery DOM event) event - * - * @private - * @returns {void} - */ + /** + * Callback to capture tab close button mousedown + * to prevent tab from activating. + * + * @param (jQuery DOM event) event + * + * @private + * @returns {void} + */ _onCloseMousedown: function (event) { event.stopPropagation(); } @@ -3040,9 +3040,9 @@ }, transitionElements: function (fromElement, toElement) { - /** - * TODO - This is not quite as cool as expected. Review. - */ + /** + * TODO - This is not quite as cool as expected. Review. + */ return; this._toElement = toElement; this._animationStartTime = lm.utils.now(); @@ -3096,27 +3096,27 @@ lm.errors.ConfigurationError.prototype = new Error(); - /** - * This is the baseclass that all content items inherit from. - * Most methods provide a subset of what the sub-classes do. - * - * It also provides a number of functions for tree traversal - * - * @param {lm.LayoutManager} layoutManager - * @param {item node configuration} config - * @param {lm.item} parent - * - * @event stateChanged - * @event beforeItemDestroyed - * @event itemDestroyed - * @event itemCreated - * @event componentCreated - * @event rowCreated - * @event columnCreated - * @event stackCreated - * - * @constructor - */ + /** + * This is the baseclass that all content items inherit from. + * Most methods provide a subset of what the sub-classes do. + * + * It also provides a number of functions for tree traversal + * + * @param {lm.LayoutManager} layoutManager + * @param {item node configuration} config + * @param {lm.item} parent + * + * @event stateChanged + * @event beforeItemDestroyed + * @event itemDestroyed + * @event itemCreated + * @event componentCreated + * @event rowCreated + * @event columnCreated + * @event stackCreated + * + * @constructor + */ lm.items.AbstractContentItem = function (layoutManager, config, parent) { lm.utils.EventEmitter.call(this); @@ -3146,26 +3146,26 @@ lm.utils.copy(lm.items.AbstractContentItem.prototype, { - /** - * Set the size of the component and its children, called recursively - * - * @abstract - * @returns void - */ + /** + * Set the size of the component and its children, called recursively + * + * @abstract + * @returns void + */ setSize: function () { throw new Error('Abstract Method'); }, - /** - * Calls a method recursively downwards on the tree - * - * @param {String} functionName the name of the function to be called - * @param {[Array]}functionArguments optional arguments that are passed to every function - * @param {[bool]} bottomUp Call methods from bottom to top, defaults to false - * @param {[bool]} skipSelf Don't invoke the method on the class that calls it, defaults to false - * - * @returns {void} - */ + /** + * Calls a method recursively downwards on the tree + * + * @param {String} functionName the name of the function to be called + * @param {[Array]}functionArguments optional arguments that are passed to every function + * @param {[bool]} bottomUp Call methods from bottom to top, defaults to false + * @param {[bool]} skipSelf Don't invoke the method on the class that calls it, defaults to false + * + * @returns {void} + */ callDownwards: function (functionName, functionArguments, bottomUp, skipSelf) { var i; @@ -3180,53 +3180,53 @@ } }, - /** - * Removes a child node (and its children) from the tree - * - * @param {lm.items.ContentItem} contentItem - * - * @returns {void} - */ + /** + * Removes a child node (and its children) from the tree + * + * @param {lm.items.ContentItem} contentItem + * + * @returns {void} + */ removeChild: function (contentItem, keepChild) { - /* - * Get the position of the item that's to be removed within all content items this node contains - */ + /* + * Get the position of the item that's to be removed within all content items this node contains + */ var index = lm.utils.indexOf(contentItem, this.contentItems); - /* - * Make sure the content item to be removed is actually a child of this item - */ + /* + * Make sure the content item to be removed is actually a child of this item + */ if (index === -1) { throw new Error('Can\'t remove child item. Unknown content item'); } - /** - * Call ._$destroy on the content item. This also calls ._$destroy on all its children - */ + /** + * Call ._$destroy on the content item. This also calls ._$destroy on all its children + */ if (keepChild !== true) { this.contentItems[index]._$destroy(); } - /** - * Remove the content item from this nodes array of children - */ + /** + * Remove the content item from this nodes array of children + */ this.contentItems.splice(index, 1); - /** - * Remove the item from the configuration - */ + /** + * Remove the item from the configuration + */ this.config.content.splice(index, 1); - /** - * If this node still contains other content items, adjust their size - */ + /** + * If this node still contains other content items, adjust their size + */ if (this.contentItems.length > 0) { this.callDownwards('setSize'); - /** - * If this was the last content item, remove this node as well - */ + /** + * If this was the last content item, remove this node as well + */ } else if (!(this instanceof lm.items.Root) && this.config.isClosable === true) { const stack = this; const rowOrCol = stack.parent; @@ -3244,14 +3244,14 @@ } }, - /** - * Sets up the tree structure for the newly added child - * The responsibility for the actual DOM manipulations lies - * with the concrete item - * - * @param {lm.items.AbstractContentItem} contentItem - * @param {[Int]} index If omitted item will be appended - */ + /** + * Sets up the tree structure for the newly added child + * The responsibility for the actual DOM manipulations lies + * with the concrete item + * + * @param {lm.items.AbstractContentItem} contentItem + * @param {[Int]} index If omitted item will be appended + */ addChild: function (contentItem, index) { if (index === undefined) { index = this.contentItems.length; @@ -3271,15 +3271,15 @@ } }, - /** - * Replaces oldChild with newChild. This used to use jQuery.replaceWith... which for - * some reason removes all event listeners, so isn't really an option. - * - * @param {lm.item.AbstractContentItem} oldChild - * @param {lm.item.AbstractContentItem} newChild - * - * @returns {void} - */ + /** + * Replaces oldChild with newChild. This used to use jQuery.replaceWith... which for + * some reason removes all event listeners, so isn't really an option. + * + * @param {lm.item.AbstractContentItem} oldChild + * @param {lm.item.AbstractContentItem} newChild + * + * @returns {void} + */ replaceChild: function (oldChild, newChild, _$destroyOldChild) { newChild = this.layoutManager._$normalizeContentItem(newChild); @@ -3293,23 +3293,23 @@ parentNode.replaceChild(newChild.element[0], oldChild.element[0]); - /* - * Optionally destroy the old content item - */ + /* + * Optionally destroy the old content item + */ if (_$destroyOldChild === true) { oldChild.parent = null; oldChild._$destroy(); } - /* - * Wire the new contentItem into the tree - */ + /* + * Wire the new contentItem into the tree + */ this.contentItems[index] = newChild; newChild.parent = this; - /* - * Update tab reference - */ + /* + * Update tab reference + */ if (this.isStack) { this.header.tabs[index].contentItem = newChild; } @@ -3322,33 +3322,33 @@ this.callDownwards('setSize'); }, - /** - * Convenience method. - * Shorthand for this.parent.removeChild( this ) - * - * @returns {void} - */ + /** + * Convenience method. + * Shorthand for this.parent.removeChild( this ) + * + * @returns {void} + */ remove: function () { this.parent.removeChild(this); }, - /** - * Removes the component from the layout and creates a new - * browser window with the component and its children inside - * - * @returns {lm.controls.BrowserPopout} - */ + /** + * Removes the component from the layout and creates a new + * browser window with the component and its children inside + * + * @returns {lm.controls.BrowserPopout} + */ popout: function () { var browserPopout = this.layoutManager.createPopout(this); this.emitBubblingEvent('stateChanged'); return browserPopout; }, - /** - * Maximises the Item or minimises it if it is already maximised - * - * @returns {void} - */ + /** + * Maximises the Item or minimises it if it is already maximised + * + * @returns {void} + */ toggleMaximise: function (e) { e && e.preventDefault(); if (this.isMaximised === true) { @@ -3361,11 +3361,11 @@ this.emitBubblingEvent('stateChanged'); }, - /** - * Selects the item if it is not already selected - * - * @returns {void} - */ + /** + * Selects the item if it is not already selected + * + * @returns {void} + */ select: function () { if (this.layoutManager.selectedItem !== this) { this.layoutManager.selectItem(this, true); @@ -3373,11 +3373,11 @@ } }, - /** - * De-selects the item if it is selected - * - * @returns {void} - */ + /** + * De-selects the item if it is selected + * + * @returns {void} + */ deselect: function () { if (this.layoutManager.selectedItem === this) { this.layoutManager.selectedItem = null; @@ -3385,28 +3385,28 @@ } }, - /** - * Set this component's title - * - * @public - * @param {String} title - * - * @returns {void} - */ + /** + * Set this component's title + * + * @public + * @param {String} title + * + * @returns {void} + */ setTitle: function (title) { this.config.title = title; this.emit('titleChanged', title); this.emit('stateChanged'); }, - /** - * Checks whether a provided id is present - * - * @public - * @param {String} id - * - * @returns {Boolean} isPresent - */ + /** + * Checks whether a provided id is present + * + * @public + * @param {String} id + * + * @returns {Boolean} isPresent + */ hasId: function (id) { if (!this.config.id) { return false; @@ -3417,15 +3417,15 @@ } }, - /** - * Adds an id. Adds it as a string if the component doesn't - * have an id yet or creates/uses an array - * - * @public - * @param {String} id - * - * @returns {void} - */ + /** + * Adds an id. Adds it as a string if the component doesn't + * have an id yet or creates/uses an array + * + * @public + * @param {String} id + * + * @returns {void} + */ addId: function (id) { if (this.hasId(id)) { return; @@ -3440,15 +3440,15 @@ } }, - /** - * Removes an existing id. Throws an error - * if the id is not present - * - * @public - * @param {String} id - * - * @returns {void} - */ + /** + * Removes an existing id. Throws an error + * if the id is not present + * + * @public + * @param {String} id + * + * @returns {void} + */ removeId: function (id) { if (!this.hasId(id)) { throw new Error('Id not found'); @@ -3462,9 +3462,9 @@ } }, - /**************************************** - * SELECTOR - ****************************************/ + /**************************************** + * SELECTOR + ****************************************/ getItemsByFilter: function (filter) { var result = [], next = function (contentItem) { @@ -3508,9 +3508,9 @@ return instances; }, - /**************************************** - * PACKAGE PRIVATE - ****************************************/ + /**************************************** + * PACKAGE PRIVATE + ****************************************/ _$getItemsByProperty: function (key, value) { return this.getItemsByFilter(function (item) { return item[key] === value; @@ -3555,11 +3555,11 @@ } }, - /** - * Destroys this item ands its children - * - * @returns {void} - */ + /** + * Destroys this item ands its children + * + * @returns {void} + */ _$destroy: function () { this.emitBubblingEvent('beforeItemDestroyed'); this.callDownwards('_$destroy', [], true, true); @@ -3567,17 +3567,17 @@ this.emitBubblingEvent('itemDestroyed'); }, - /** - * Returns the area the component currently occupies in the format - * - * { - * x1: int - * xy: int - * y1: int - * y2: int - * contentItem: contentItem - * } - */ + /** + * Returns the area the component currently occupies in the format + * + * { + * x1: int + * xy: int + * y1: int + * y2: int + * contentItem: contentItem + * } + */ _$getArea: function (element) { element = element || this.element; @@ -3595,17 +3595,17 @@ }; }, - /** - * The tree of content items is created in two steps: First all content items are instantiated, - * then init is called recursively from top to bottem. This is the basic init function, - * it can be used, extended or overwritten by the content items - * - * Its behaviour depends on the content item - * - * @package private - * - * @returns {void} - */ + /** + * The tree of content items is created in two steps: First all content items are instantiated, + * then init is called recursively from top to bottem. This is the basic init function, + * it can be used, extended or overwritten by the content items + * + * Its behaviour depends on the content item + * + * @package private + * + * @returns {void} + */ _$init: function () { var i; this.setSize(); @@ -3619,26 +3619,26 @@ this.emitBubblingEvent(this.type + 'Created'); }, - /** - * Emit an event that bubbles up the item tree. - * - * @param {String} name The name of the event - * - * @returns {void} - */ + /** + * Emit an event that bubbles up the item tree. + * + * @param {String} name The name of the event + * + * @returns {void} + */ emitBubblingEvent: function (name) { var event = new lm.utils.BubblingEvent(name, this); this.emit(name, event); }, - /** - * Private method, creates all content items for this node at initialisation time - * PLEASE NOTE, please see addChild for adding contentItems add runtime - * @private - * @param {configuration item node} config - * - * @returns {void} - */ + /** + * Private method, creates all content items for this node at initialisation time + * PLEASE NOTE, please see addChild for adding contentItems add runtime + * @private + * @param {configuration item node} config + * + * @returns {void} + */ _createContentItems: function (config) { var oContentItem, i; @@ -3652,13 +3652,13 @@ } }, - /** - * Extends an item configuration node with default settings - * @private - * @param {configuration item node} config - * - * @returns {configuration item node} extended config - */ + /** + * Extends an item configuration node with default settings + * @private + * @param {configuration item node} config + * + * @returns {configuration item node} extended config + */ _extendItemNode: function (config) { for (var key in lm.config.itemDefaultConfig) { @@ -3670,26 +3670,26 @@ return config; }, - /** - * Called for every event on the item tree. Decides whether the event is a bubbling - * event and propagates it to its parent - * - * @param {String} name the name of the event - * @param {lm.utils.BubblingEvent} event - * - * @returns {void} - */ + /** + * Called for every event on the item tree. Decides whether the event is a bubbling + * event and propagates it to its parent + * + * @param {String} name the name of the event + * @param {lm.utils.BubblingEvent} event + * + * @returns {void} + */ _propagateEvent: function (name, event) { if (event instanceof lm.utils.BubblingEvent && event.isPropagationStopped === false && this.isInitialised === true) { - /** - * In some cases (e.g. if an element is created from a DragSource) it - * doesn't have a parent and is not below root. If that's the case - * propagate the bubbling event from the top level of the substree directly - * to the layoutManager - */ + /** + * In some cases (e.g. if an element is created from a DragSource) it + * doesn't have a parent and is not below root. If that's the case + * propagate the bubbling event from the top level of the substree directly + * to the layoutManager + */ if (this.isRoot === false && this.parent) { this.parent.emit.apply(this.parent, Array.prototype.slice.call(arguments, 0)); } else { @@ -3698,16 +3698,16 @@ } }, - /** - * All raw events bubble up to the root element. Some events that - * are propagated to - and emitted by - the layoutManager however are - * only string-based, batched and sanitized to make them more usable - * - * @param {String} name the name of the event - * - * @private - * @returns {void} - */ + /** + * All raw events bubble up to the root element. Some events that + * are propagated to - and emitted by - the layoutManager however are + * only string-based, batched and sanitized to make them more usable + * + * @param {String} name the name of the event + * + * @private + * @returns {void} + */ _scheduleEventPropagationToLayoutManager: function (name, event) { if (lm.utils.indexOf(name, this._throttledEvents) === -1) { this.layoutManager.emit(name, event.origin); @@ -3720,25 +3720,25 @@ }, - /** - * Callback for events scheduled by _scheduleEventPropagationToLayoutManager - * - * @param {String} name the name of the event - * - * @private - * @returns {void} - */ + /** + * Callback for events scheduled by _scheduleEventPropagationToLayoutManager + * + * @param {String} name the name of the event + * + * @private + * @returns {void} + */ _propagateEventToLayoutManager: function (name, event) { this._pendingEventPropagations[name] = false; this.layoutManager.emit(name, event); } }); - /** - * @param {[type]} layoutManager [description] - * @param {[type]} config [description] - * @param {[type]} parent [description] - */ + /** + * @param {[type]} layoutManager [description] + * @param {[type]} config [description] + * @param {[type]} parent [description] + */ lm.items.Component = function (layoutManager, config, parent) { lm.items.AbstractContentItem.call(this, layoutManager, config, parent); @@ -3798,11 +3798,11 @@ lm.items.AbstractContentItem.prototype._$destroy.call(this); }, - /** - * Dragging onto a component directly is not an option - * - * @returns null - */ + /** + * Dragging onto a component directly is not an option + * + * @returns null + */ _$getArea: function () { return null; } @@ -3841,9 +3841,9 @@ this.element.width(width); this.element.height(height); - /* - * Root can be empty - */ + /* + * Root can be empty + */ if (this.contentItems[0]) { this.contentItems[0].element.width(width); this.contentItems[0].element.height(height); @@ -3917,18 +3917,18 @@ lm.utils.copy(lm.items.RowOrColumn.prototype, { - /** - * Add a new contentItem to the Row or Column - * - * @param {lm.item.AbstractContentItem} contentItem - * @param {[int]} index The position of the new item within the Row or Column. - * If no index is provided the item will be added to the end - * @param {[bool]} _$suspendResize If true the items won't be resized. This will leave the item in - * an inconsistent state and is only intended to be used if multiple - * children need to be added in one go and resize is called afterwards - * - * @returns {void} - */ + /** + * Add a new contentItem to the Row or Column + * + * @param {lm.item.AbstractContentItem} contentItem + * @param {[int]} index The position of the new item within the Row or Column. + * If no index is provided the item will be added to the end + * @param {[bool]} _$suspendResize If true the items won't be resized. This will leave the item in + * an inconsistent state and is only intended to be used if multiple + * children need to be added in one go and resize is called afterwards + * + * @returns {void} + */ addChild: function (contentItem, index, _$suspendResize) { var newItemSize, itemSize, i, splitterElement; @@ -3986,14 +3986,14 @@ }, - /** - * Removes a child of this element - * - * @param {lm.items.AbstractContentItem} contentItem - * @param {boolean} keepChild If true the child will be removed, but not destroyed - * - * @returns {void} - */ + /** + * Removes a child of this element + * + * @param {lm.items.AbstractContentItem} contentItem + * @param {boolean} keepChild If true the child will be removed, but not destroyed + * + * @returns {void} + */ removeChild: function (contentItem, keepChild) { var removedItemSize = contentItem.config[this._dimension], index = lm.utils.indexOf(contentItem, this.contentItems), @@ -4005,10 +4005,10 @@ throw new Error('Can\'t remove child. ContentItem is not child of this Row or Column'); } - /** - * Remove the splitter before the item or after if the item happens - * to be the first in the row/column - */ + /** + * Remove the splitter before the item or after if the item happens + * to be the first in the row/column + */ if (this._splitter[splitterIndex]) { this._splitter[splitterIndex]._$destroy(); this._splitter.splice(splitterIndex, 1); @@ -4019,9 +4019,9 @@ if (this.contentItems[i].config.fixed) fixedItemSize += this.contentItems[i].config[this._dimension]; } - /** - * Allocate the space that the removed item occupied to the remaining items - */ + /** + * Allocate the space that the removed item occupied to the remaining items + */ for (i = 0; i < this.contentItems.length; i++) { if (this.contentItems[i].config.fixed) ; @@ -4044,14 +4044,14 @@ } }, - /** - * Replaces a child of this Row or Column with another contentItem - * - * @param {lm.items.AbstractContentItem} oldChild - * @param {lm.items.AbstractContentItem} newChild - * - * @returns {void} - */ + /** + * Replaces a child of this Row or Column with another contentItem + * + * @param {lm.items.AbstractContentItem} oldChild + * @param {lm.items.AbstractContentItem} newChild + * + * @returns {void} + */ replaceChild: function (oldChild, newChild) { var size = oldChild.config[this._dimension]; lm.items.AbstractContentItem.prototype.replaceChild.call(this, oldChild, newChild); @@ -4060,11 +4060,11 @@ this.emitBubblingEvent('stateChanged'); }, - /** - * Called whenever the dimensions of this item or one of its parents change - * - * @returns {void} - */ + /** + * Called whenever the dimensions of this item or one of its parents change + * + * @returns {void} + */ setSize: function () { if (this.contentItems.length > 0) { this._calculateRelativeSizes(); @@ -4074,15 +4074,15 @@ this.emit('resize'); }, - /** - * Invoked recursively by the layout manager. AbstractContentItem.init appends - * the contentItem's DOM elements to the container, RowOrColumn init adds splitters - * in between them - * - * @package private - * @override AbstractContentItem._$init - * @returns {void} - */ + /** + * Invoked recursively by the layout manager. AbstractContentItem.init appends + * the contentItem's DOM elements to the container, RowOrColumn init adds splitters + * in between them + * + * @package private + * @override AbstractContentItem._$init + * @returns {void} + */ _$init: function () { if (this.isInitialised === true) return; @@ -4095,15 +4095,15 @@ } }, - /** - * Turns the relative sizes calculated by _calculateRelativeSizes into - * absolute pixel values and applies them to the children's DOM elements - * - * Assigns additional pixels to counteract Math.floor - * - * @private - * @returns {void} - */ + /** + * Turns the relative sizes calculated by _calculateRelativeSizes into + * absolute pixel values and applies them to the children's DOM elements + * + * Assigns additional pixels to counteract Math.floor + * + * @private + * @returns {void} + */ _setAbsoluteSizes: function () { var i, sizeData = this._calculateAbsoluteSizes(); @@ -4123,10 +4123,10 @@ } }, - /** - * Calculates the absolute sizes of all of the children of this Item. - * @returns {object} - Set with absolute sizes and additional pixels. - */ + /** + * Calculates the absolute sizes of all of the children of this Item. + * @returns {object} - Set with absolute sizes and additional pixels. + */ _calculateAbsoluteSizes: function () { var i, totalSplitterSize = (this.contentItems.length - 1) * this._splitterSize, @@ -4164,27 +4164,27 @@ }; }, - /** - * Calculates the relative sizes of all children of this Item. The logic - * is as follows: - * - * - Add up the total size of all items that have a configured size - * - * - If the total == 100 (check for floating point errors) - * Excellent, job done - * - * - If the total is > 100, - * set the size of items without set dimensions to 1/3 and add this to the total - * set the size off all items so that the total is hundred relative to their original size - * - * - If the total is < 100 - * If there are items without set dimensions, distribute the remainder to 100 evenly between them - * If there are no items without set dimensions, increase all items sizes relative to - * their original size so that they add up to 100 - * - * @private - * @returns {void} - */ + /** + * Calculates the relative sizes of all children of this Item. The logic + * is as follows: + * + * - Add up the total size of all items that have a configured size + * + * - If the total == 100 (check for floating point errors) + * Excellent, job done + * + * - If the total is > 100, + * set the size of items without set dimensions to 1/3 and add this to the total + * set the size off all items so that the total is hundred relative to their original size + * + * - If the total is < 100 + * If there are items without set dimensions, distribute the remainder to 100 evenly between them + * If there are no items without set dimensions, increase all items sizes relative to + * their original size so that they add up to 100 + * + * @private + * @returns {void} + */ _calculateRelativeSizes: function () { var i, @@ -4200,17 +4200,17 @@ } } - /** - * Everything adds up to hundred, all good :-) - */ + /** + * Everything adds up to hundred, all good :-) + */ if (Math.round(total) === 100) { this._respectMinItemWidth(); return; } - /** - * Allocate the remaining size to the items without a set dimension - */ + /** + * Allocate the remaining size to the items without a set dimension + */ if (Math.round(total) < 100 && itemsWithoutSetDimension.length > 0) { for (i = 0; i < itemsWithoutSetDimension.length; i++) { itemsWithoutSetDimension[i].config[dimension] = (100 - total) / itemsWithoutSetDimension.length; @@ -4219,12 +4219,12 @@ return; } - /** - * If the total is > 100, but there are also items without a set dimension left, assing 50 - * as their dimension and add it to the total - * - * This will be reset in the next step - */ + /** + * If the total is > 100, but there are also items without a set dimension left, assing 50 + * as their dimension and add it to the total + * + * This will be reset in the next step + */ if (Math.round(total) > 100) { for (i = 0; i < itemsWithoutSetDimension.length; i++) { itemsWithoutSetDimension[i].config[dimension] = 50; @@ -4232,9 +4232,9 @@ } } - /** - * Set every items size relative to 100 relative to its size to total - */ + /** + * Set every items size relative to 100 relative to its size to total + */ for (i = 0; i < this.contentItems.length; i++) { this.contentItems[i].config[dimension] = (this.contentItems[i].config[dimension] / total) * 100; } @@ -4242,10 +4242,10 @@ this._respectMinItemWidth(); }, - /** - * Adjusts the column widths to respect the dimensions minItemWidth if set. - * @returns {} - */ + /** + * Adjusts the column widths to respect the dimensions minItemWidth if set. + * @returns {} + */ _respectMinItemWidth: function () { var minItemWidth = this.layoutManager.config.dimensions ? (this.layoutManager.config.dimensions.minItemWidth || 0) : 0, sizeData = null, @@ -4266,9 +4266,9 @@ sizeData = this._calculateAbsoluteSizes(); - /** - * Figure out how much we are under the min item size total and how much room we have to use. - */ + /** + * Figure out how much we are under the min item size total and how much room we have to use. + */ for (var i = 0; i < this.contentItems.length; i++) { contentItem = this.contentItems[i]; @@ -4288,16 +4288,16 @@ allEntries.push(entry); } - /** - * If there is nothing under min, or there is not enough over to make up the difference, do nothing. - */ + /** + * If there is nothing under min, or there is not enough over to make up the difference, do nothing. + */ if (totalUnderMin === 0 || totalUnderMin > totalOverMin) { return; } - /** - * Evenly reduce all columns that are over the min item width to make up the difference. - */ + /** + * Evenly reduce all columns that are over the min item width to make up the difference. + */ reducePercent = totalUnderMin / totalOverMin; remainingWidth = totalUnderMin; for (i = 0; i < entriesOverMin.length; i++) { @@ -4307,31 +4307,31 @@ entry.width -= reducedWidth; } - /** - * Take anything remaining from the last item. - */ + /** + * Take anything remaining from the last item. + */ if (remainingWidth !== 0) { allEntries[allEntries.length - 1].width -= remainingWidth; } - /** - * Set every items size relative to 100 relative to its size to total - */ + /** + * Set every items size relative to 100 relative to its size to total + */ for (i = 0; i < this.contentItems.length; i++) { this.contentItems[i].config.width = (allEntries[i].width / sizeData.totalWidth) * 100; } }, - /** - * Instantiates a new lm.controls.Splitter, binds events to it and adds - * it to the array of splitters at the position specified as the index argument - * - * What it doesn't do though is append the splitter to the DOM - * - * @param {Int} index The position of the splitter - * - * @returns {lm.controls.Splitter} - */ + /** + * Instantiates a new lm.controls.Splitter, binds events to it and adds + * it to the array of splitters at the position specified as the index argument + * + * What it doesn't do though is append the splitter to the DOM + * + * @param {Int} index The position of the splitter + * + * @returns {lm.controls.Splitter} + */ _createSplitter: function (index) { var splitter; splitter = new lm.controls.Splitter(this._isColumn, this._splitterSize, this._splitterGrabSize); @@ -4342,16 +4342,16 @@ return splitter; }, - /** - * Locates the instance of lm.controls.Splitter in the array of - * registered splitters and returns a map containing the contentItem - * before and after the splitters, both of which are affected if the - * splitter is moved - * - * @param {lm.controls.Splitter} splitter - * - * @returns {Object} A map of contentItems that the splitter affects - */ + /** + * Locates the instance of lm.controls.Splitter in the array of + * registered splitters and returns a map containing the contentItem + * before and after the splitters, both of which are affected if the + * splitter is moved + * + * @param {lm.controls.Splitter} splitter + * + * @returns {Object} A map of contentItems that the splitter affects + */ _getItemsForSplitter: function (splitter) { var index = lm.utils.indexOf(splitter, this._splitter); @@ -4361,11 +4361,11 @@ }; }, - /** - * Gets the minimum dimensions for the given item configuration array - * @param item - * @private - */ + /** + * Gets the minimum dimensions for the given item configuration array + * @param item + * @private + */ _getMinimumDimensions: function (arr) { var minWidth = 0, minHeight = 0; @@ -4377,14 +4377,14 @@ return { horizontal: minWidth, vertical: minHeight }; }, - /** - * Invoked when a splitter's dragListener fires dragStart. Calculates the splitters - * movement area once (so that it doesn't need calculating on every mousemove event) - * - * @param {lm.controls.Splitter} splitter - * - * @returns {void} - */ + /** + * Invoked when a splitter's dragListener fires dragStart. Calculates the splitters + * movement area once (so that it doesn't need calculating on every mousemove event) + * + * @param {lm.controls.Splitter} splitter + * + * @returns {void} + */ _onSplitterDragStart: function (splitter) { var items = this._getItemsForSplitter(splitter), minSize = this.layoutManager.config.dimensions[this._isColumn ? 'minItemHeight' : 'minItemWidth']; @@ -4400,16 +4400,16 @@ this._splitterMaxPosition = items.after.element[this._dimension]() - (afterMinSize || minSize); }, - /** - * Invoked when a splitter's DragListener fires drag. Updates the splitters DOM position, - * but not the sizes of the elements the splitter controls in order to minimize resize events - * - * @param {lm.controls.Splitter} splitter - * @param {Int} offsetX Relative pixel values to the splitters original position. Can be negative - * @param {Int} offsetY Relative pixel values to the splitters original position. Can be negative - * - * @returns {void} - */ + /** + * Invoked when a splitter's DragListener fires drag. Updates the splitters DOM position, + * but not the sizes of the elements the splitter controls in order to minimize resize events + * + * @param {lm.controls.Splitter} splitter + * @param {Int} offsetX Relative pixel values to the splitters original position. Can be negative + * @param {Int} offsetY Relative pixel values to the splitters original position. Can be negative + * + * @returns {void} + */ _onSplitterDrag: function (splitter, offsetX, offsetY) { var offset = this._isColumn ? offsetY : offsetX; @@ -4419,15 +4419,15 @@ } }, - /** - * Invoked when a splitter's DragListener fires dragStop. Resets the splitters DOM position, - * and applies the new sizes to the elements before and after the splitter and their children - * on the next animation frame - * - * @param {lm.controls.Splitter} splitter - * - * @returns {void} - */ + /** + * Invoked when a splitter's DragListener fires dragStop. Resets the splitters DOM position, + * and applies the new sizes to the elements before and after the splitter and their children + * on the next animation frame + * + * @param {lm.controls.Splitter} splitter + * + * @returns {void} + */ _onSplitterDragStop: function (splitter) { var items = this._getItemsForSplitter(splitter), @@ -4451,7 +4451,7 @@ lm.items.Stack = function (layoutManager, config, parent) { lm.items.AbstractContentItem.call(this, layoutManager, config, parent); - this.element = $('
    '); + this.element = $('

    Click to create a new tab

    '); this._activeContentItem = null; var cfg = layoutManager.config; this._header = { // defaults' reconstruction from old configuration style @@ -4575,13 +4575,13 @@ this.emitBubblingEvent('stateChanged'); }, - /** - * Validates that the stack is still closable or not. If a stack is able - * to close, but has a non closable component added to it, the stack is no - * longer closable until all components are closable. - * - * @returns {void} - */ + /** + * Validates that the stack is still closable or not. If a stack is able + * to close, but has a non closable component added to it, the stack is no + * longer closable until all components are closable. + * + * @returns {void} + */ _$validateClosability: function () { var contentItem, isClosable, @@ -4607,51 +4607,51 @@ }, - /** - * Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack. - * - * It was dropped on either the stacks header or the top, right, bottom or left bit of the content area - * (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case - * is relatively clear: We add the item to the existing stack... job done (might be good to have - * tab reordering at some point, but lets not sweat it right now) - * - * If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the - * top or bottom region we need to create a new column and place the items accordingly. - * Unless, of course if the stack is already within a column... in which case we want - * to add the newly created item to the existing column... - * either prepend or append it, depending on wether its top or bottom. - * - * Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen - * (left, top, right, bottom) * is child of the right parent (row, column) + header drop - * - * @param {lm.item} contentItem - * - * @returns {void} - */ + /** + * Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack. + * + * It was dropped on either the stacks header or the top, right, bottom or left bit of the content area + * (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case + * is relatively clear: We add the item to the existing stack... job done (might be good to have + * tab reordering at some point, but lets not sweat it right now) + * + * If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the + * top or bottom region we need to create a new column and place the items accordingly. + * Unless, of course if the stack is already within a column... in which case we want + * to add the newly created item to the existing column... + * either prepend or append it, depending on wether its top or bottom. + * + * Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen + * (left, top, right, bottom) * is child of the right parent (row, column) + header drop + * + * @param {lm.item} contentItem + * + * @returns {void} + */ _$onDrop: function (contentItem) { - /* - * The item was dropped on the header area. Just add it as a child of this stack and - * get the hell out of this logic - */ + /* + * The item was dropped on the header area. Just add it as a child of this stack and + * get the hell out of this logic + */ if (this._dropSegment === 'header') { this._resetHeaderDropZone(); this.addChild(contentItem, this._dropIndex); return; } - /* - * The stack is empty. Let's just add the element. - */ + /* + * The stack is empty. Let's just add the element. + */ if (this._dropSegment === 'body') { this.addChild(contentItem); return; } - /* - * The item was dropped on the top-, left-, bottom- or right- part of the content. Let's - * aggregate some conditions to make the if statements later on more readable - */ + /* + * The item was dropped on the top-, left-, bottom- or right- part of the content. Let's + * aggregate some conditions to make the if statements later on more readable + */ var isVertical = this._dropSegment === 'top' || this._dropSegment === 'bottom', isHorizontal = this._dropSegment === 'left' || this._dropSegment === 'right', insertBefore = this._dropSegment === 'top' || this._dropSegment === 'left', @@ -4662,9 +4662,9 @@ stack, rowOrColumn; - /* - * The content item can be either a component or a stack. If it is a component, wrap it into a stack - */ + /* + * The content item can be either a component or a stack. If it is a component, wrap it into a stack + */ if (contentItem.isComponent) { stack = this.layoutManager.createContentItem({ type: 'stack', @@ -4675,20 +4675,20 @@ contentItem = stack; } - /* - * If the item is dropped on top or bottom of a column or left and right of a row, it's already - * layd out in the correct way. Just add it as a child - */ + /* + * If the item is dropped on top or bottom of a column or left and right of a row, it's already + * layd out in the correct way. Just add it as a child + */ if (hasCorrectParent) { index = lm.utils.indexOf(this, this.parent.contentItems); this.parent.addChild(contentItem, insertBefore ? index : index + 1, true); this.config[dimension] *= 0.5; contentItem.config[dimension] = this.config[dimension]; this.parent.callDownwards('setSize'); - /* - * This handles items that are dropped on top or bottom of a row or left / right of a column. We need - * to create the appropriate contentItem for them to live in - */ + /* + * This handles items that are dropped on top or bottom of a row or left / right of a column. We need + * to create the appropriate contentItem for them to live in + */ } else { type = isVertical ? 'column' : 'row'; rowOrColumn = this.layoutManager.createContentItem({ type: type }, this); @@ -4703,15 +4703,15 @@ } }, - /** - * If the user hovers above the header part of the stack, indicate drop positions for tabs. - * otherwise indicate which segment of the body the dragged item would be dropped on - * - * @param {Int} x Absolute Screen X - * @param {Int} y Absolute Screen Y - * - * @returns {void} - */ + /** + * If the user hovers above the header part of the stack, indicate drop positions for tabs. + * otherwise indicate which segment of the body the dragged item would be dropped on + * + * @param {Int} x Absolute Screen X + * @param {Int} y Absolute Screen Y + * + * @returns {void} + */ _$highlightDropZone: function (x, y) { var segment, area; @@ -4761,17 +4761,17 @@ } }; - /** - * If this Stack is a parent to rows, columns or other stacks only its - * header is a valid dropzone. - */ + /** + * If this Stack is a parent to rows, columns or other stacks only its + * header is a valid dropzone. + */ if (this._activeContentItem && this._activeContentItem.isComponent === false) { return headerArea; } - /** - * Highlight the entire body if the stack is empty - */ + /** + * Highlight the entire body if the stack is empty + */ if (this.contentItems.length === 0) { this._contentAreaDimensions.body = { @@ -4971,13 +4971,13 @@ lm.utils.BubblingEvent.prototype.stopPropagation = function () { this.isPropagationStopped = true; }; - /** - * Minifies and unminifies configs by replacing frequent keys - * and values with one letter substitutes. Config options must - * retain array position/index, add new options at the end. - * - * @constructor - */ + /** + * Minifies and unminifies configs by replacing frequent keys + * and values with one letter substitutes. Config options must + * retain array position/index, add new options at the end. + * + * @constructor + */ lm.utils.ConfigMinifier = function () { this._keys = [ 'settings', @@ -5031,160 +5031,160 @@ 'close', 'maximise', 'minimise', - 'open in new window' + 'new tab' ]; }; lm.utils.copy(lm.utils.ConfigMinifier.prototype, { - /** - * Takes a GoldenLayout configuration object and - * replaces its keys and values recursively with - * one letter counterparts - * - * @param {Object} config A GoldenLayout config object - * - * @returns {Object} minified config - */ + /** + * Takes a GoldenLayout configuration object and + * replaces its keys and values recursively with + * one letter counterparts + * + * @param {Object} config A GoldenLayout config object + * + * @returns {Object} minified config + */ minifyConfig: function (config) { var min = {}; this._nextLevel(config, min, '_min'); return min; }, - /** - * Takes a configuration Object that was previously minified - * using minifyConfig and returns its original version - * - * @param {Object} minifiedConfig - * - * @returns {Object} the original configuration - */ + /** + * Takes a configuration Object that was previously minified + * using minifyConfig and returns its original version + * + * @param {Object} minifiedConfig + * + * @returns {Object} the original configuration + */ unminifyConfig: function (minifiedConfig) { var orig = {}; this._nextLevel(minifiedConfig, orig, '_max'); return orig; }, - /** - * Recursive function, called for every level of the config structure - * - * @param {Array|Object} orig - * @param {Array|Object} min - * @param {String} translationFn - * - * @returns {void} - */ + /** + * Recursive function, called for every level of the config structure + * + * @param {Array|Object} orig + * @param {Array|Object} min + * @param {String} translationFn + * + * @returns {void} + */ _nextLevel: function (from, to, translationFn) { var key, minKey; for (key in from) { - /** - * For in returns array indices as keys, so let's cast them to numbers - */ + /** + * For in returns array indices as keys, so let's cast them to numbers + */ if (from instanceof Array) key = parseInt(key, 10); - /** - * In case something has extended Object prototypes - */ + /** + * In case something has extended Object prototypes + */ if (!from.hasOwnProperty(key)) continue; - /** - * Translate the key to a one letter substitute - */ + /** + * Translate the key to a one letter substitute + */ minKey = this[translationFn](key, this._keys); - /** - * For Arrays and Objects, create a new Array/Object - * on the minified object and recurse into it - */ + /** + * For Arrays and Objects, create a new Array/Object + * on the minified object and recurse into it + */ if (typeof from[key] === 'object') { to[minKey] = from[key] instanceof Array ? [] : {}; this._nextLevel(from[key], to[minKey], translationFn); - /** - * For primitive values (Strings, Numbers, Boolean etc.) - * minify the value - */ + /** + * For primitive values (Strings, Numbers, Boolean etc.) + * minify the value + */ } else { to[minKey] = this[translationFn](from[key], this._values); } } }, - /** - * Minifies value based on a dictionary - * - * @param {String|Boolean} value - * @param {Array} dictionary - * - * @returns {String} The minified version - */ + /** + * Minifies value based on a dictionary + * + * @param {String|Boolean} value + * @param {Array} dictionary + * + * @returns {String} The minified version + */ _min: function (value, dictionary) { - /** - * If a value actually is a single character, prefix it - * with ___ to avoid mistaking it for a minification code - */ + /** + * If a value actually is a single character, prefix it + * with ___ to avoid mistaking it for a minification code + */ if (typeof value === 'string' && value.length === 1) { return '___' + value; } var index = lm.utils.indexOf(value, dictionary); - /** - * value not found in the dictionary, return it unmodified - */ + /** + * value not found in the dictionary, return it unmodified + */ if (index === -1) { return value; - /** - * value found in dictionary, return its base36 counterpart - */ + /** + * value found in dictionary, return its base36 counterpart + */ } else { return index.toString(36); } }, _max: function (value, dictionary) { - /** - * value is a single character. Assume that it's a translation - * and return the original value from the dictionary - */ + /** + * value is a single character. Assume that it's a translation + * and return the original value from the dictionary + */ if (typeof value === 'string' && value.length === 1) { return dictionary[parseInt(value, 36)]; } - /** - * value originally was a single character and was prefixed with ___ - * to avoid mistaking it for a translation. Remove the prefix - * and return the original character - */ + /** + * value originally was a single character and was prefixed with ___ + * to avoid mistaking it for a translation. Remove the prefix + * and return the original character + */ if (typeof value === 'string' && value.substr(0, 3) === '___') { return value[3]; } - /** - * value was not minified - */ + /** + * value was not minified + */ return value; } }); - /** - * An EventEmitter singleton that propagates events - * across multiple windows. This is a little bit trickier since - * windows are allowed to open childWindows in their own right - * - * This means that we deal with a tree of windows. Hence the rules for event propagation are: - * - * - Propagate events from this layout to both parents and children - * - Propagate events from parent to this and children - * - Propagate events from children to the other children (but not the emitting one) and the parent - * - * @constructor - * - * @param {lm.LayoutManager} layoutManager - */ + /** + * An EventEmitter singleton that propagates events + * across multiple windows. This is a little bit trickier since + * windows are allowed to open childWindows in their own right + * + * This means that we deal with a tree of windows. Hence the rules for event propagation are: + * + * - Propagate events from this layout to both parents and children + * - Propagate events from parent to this and children + * - Propagate events from children to the other children (but not the emitting one) and the parent + * + * @constructor + * + * @param {lm.LayoutManager} layoutManager + */ lm.utils.EventHub = function (layoutManager) { lm.utils.EventEmitter.call(this); this._layoutManager = layoutManager; @@ -5195,15 +5195,15 @@ $(window).on('gl_child_event', this._boundOnEventFromChild); }; - /** - * Called on every event emitted on this eventHub, regardles of origin. - * - * @private - * - * @param {Mixed} - * - * @returns {void} - */ + /** + * Called on every event emitted on this eventHub, regardles of origin. + * + * @private + * + * @param {Mixed} + * + * @returns {void} + */ lm.utils.EventHub.prototype._onEventFromThis = function () { var args = Array.prototype.slice.call(arguments); @@ -5217,40 +5217,40 @@ this._childEventSource = null; }; - /** - * Called by the parent layout. - * - * @param {Array} args Event name + arguments - * - * @returns {void} - */ + /** + * Called by the parent layout. + * + * @param {Array} args Event name + arguments + * + * @returns {void} + */ lm.utils.EventHub.prototype._$onEventFromParent = function (args) { this._dontPropagateToParent = args[0]; this.emit.apply(this, args); }; - /** - * Callback for child events raised on the window - * - * @param {DOMEvent} event - * @private - * - * @returns {void} - */ + /** + * Callback for child events raised on the window + * + * @param {DOMEvent} event + * @private + * + * @returns {void} + */ lm.utils.EventHub.prototype._onEventFromChild = function (event) { this._childEventSource = event.originalEvent.__gl; this.emit.apply(this, event.originalEvent.__glArgs); }; - /** - * Propagates the event to the parent by emitting - * it on the parent's DOM window - * - * @param {Array} args Event name + arguments - * @private - * - * @returns {void} - */ + /** + * Propagates the event to the parent by emitting + * it on the parent's DOM window + * + * @param {Array} args Event name + arguments + * @private + * + * @returns {void} + */ lm.utils.EventHub.prototype._propagateToParent = function (args) { var event, eventName = 'gl_child_event'; @@ -5274,14 +5274,14 @@ } }; - /** - * Propagate events to children - * - * @param {Array} args Event name + arguments - * @private - * - * @returns {void} - */ + /** + * Propagate events to children + * + * @param {Array} args Event name + arguments + * @private + * + * @returns {void} + */ lm.utils.EventHub.prototype._propagateToChildren = function (args) { var childGl, i; @@ -5295,25 +5295,25 @@ }; - /** - * Destroys the EventHub - * - * @public - * @returns {void} - */ + /** + * Destroys the EventHub + * + * @public + * @returns {void} + */ lm.utils.EventHub.prototype.destroy = function () { $(window).off('gl_child_event', this._boundOnEventFromChild); }; - /** - * A specialised GoldenLayout component that binds GoldenLayout container - * lifecycle events to react components - * - * @constructor - * - * @param {lm.container.ItemContainer} container - * @param {Object} state state is not required for react components - */ + /** + * A specialised GoldenLayout component that binds GoldenLayout container + * lifecycle events to react components + * + * @constructor + * + * @param {lm.container.ItemContainer} container + * @param {Object} state state is not required for react components + */ lm.utils.ReactComponentHandler = function (container, state) { this._reactComponent = null; this._originalComponentWillUpdate = null; @@ -5326,15 +5326,15 @@ lm.utils.copy(lm.utils.ReactComponentHandler.prototype, { - /** - * Creates the react class and component and hydrates it with - * the initial state - if one is present - * - * By default, react's getInitialState will be used - * - * @private - * @returns {void} - */ + /** + * Creates the react class and component and hydrates it with + * the initial state - if one is present + * + * By default, react's getInitialState will be used + * + * @private + * @returns {void} + */ _render: function () { this._reactComponent = ReactDOM.render(this._getReactComponent(), this._container.getElement()[0]); this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate || function () { @@ -5345,36 +5345,36 @@ } }, - /** - * Removes the component from the DOM and thus invokes React's unmount lifecycle - * - * @private - * @returns {void} - */ + /** + * Removes the component from the DOM and thus invokes React's unmount lifecycle + * + * @private + * @returns {void} + */ _destroy: function () { ReactDOM.unmountComponentAtNode(this._container.getElement()[0]); this._container.off('open', this._render, this); this._container.off('destroy', this._destroy, this); }, - /** - * Hooks into React's state management and applies the componentstate - * to GoldenLayout - * - * @private - * @returns {void} - */ + /** + * Hooks into React's state management and applies the componentstate + * to GoldenLayout + * + * @private + * @returns {void} + */ _onUpdate: function (nextProps, nextState) { this._container.setState(nextState); this._originalComponentWillUpdate.call(this._reactComponent, nextProps, nextState); }, - /** - * Retrieves the react class from GoldenLayout's registry - * - * @private - * @returns {React.Class} - */ + /** + * Retrieves the react class from GoldenLayout's registry + * + * @private + * @returns {React.Class} + */ _getReactClass: function () { var componentName = this._container._config.component; var reactClass; @@ -5393,12 +5393,12 @@ return reactClass; }, - /** - * Copies and extends the properties array and returns the React element - * - * @private - * @returns {React.Element} - */ + /** + * Copies and extends the properties array and returns the React element + * + * @private + * @returns {React.Element} + */ _getReactComponent: function () { var defaultProps = { glEventHub: this._container.layoutManager.eventHub, diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 07ca0257c..08bcd55ae 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -63,6 +63,10 @@ .mainView-container { color: $dark-gray; + .lm_goldenlayout { + background: $medium-gray; + } + .lm_title { background: $light-gray; color: $dark-gray; @@ -153,7 +157,8 @@ cursor: auto; } -.mainView-innerContent, .mainView-innerContent-dark { +.mainView-innerContent, +.mainView-innerContent-dark { display: contents; flex-direction: row; position: relative; @@ -175,44 +180,52 @@ position: absolute; z-index: 2; background-color: $medium-gray; + .editable-title { background-color: $light-gray; } } } + .mainView-libraryHandle { background-color: $light-gray; } -.mainView-innerContent-dark -{ + +.mainView-innerContent-dark { .propertiesView { background-color: #252525; + input { background-color: $medium-gray; } - .propertiesView-sharingTable - { + + .propertiesView-sharingTable { background-color: $medium-gray; } + .editable-title { background-color: $medium-gray; } + .propertiesView-field { background-color: $medium-gray; } } + .mainView-propertiesDragger, .mainView-libraryHandle { background: #353535; } } + .mainView-container-dark { .contextMenu-cont { background: $medium-gray; color: $white; + input::placeholder { - color:$white; + color: $white; } } } @@ -432,6 +445,7 @@ right: unset !important; left: 0 !important; } + .lm_close_tab { padding: 0; width: 15px !important; @@ -443,7 +457,9 @@ right: unset !important; left: 0 !important; } -.lm_tab, .lm_tab_active { + +.lm_tab, +.lm_tab_active { display: flex !important; padding-right: 0 !important; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index a054f0ae1..d1b8b2df0 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -55,6 +55,29 @@ display: inline; } +.empty-tabs-message { + position: absolute; + width: 100%; + z-index: 1; + top: 50%; + z-index: 1; + text-align: center; + font-size: 18; + color: $dark-gray; + + img { + position: relative; + top: -1px; + margin: 0 5px; + } +} + +.lm_header, +.lm_items { + z-index: 2; + position: relative; +} + .collectiondockingview-container { width: 100%; height: 100%; -- cgit v1.2.3-70-g09d2 From 02336134cff5f5789380bf5950864f4b95583cf6 Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Mon, 16 Aug 2021 16:09:13 -0400 Subject: commented column code --- .../collectionSchema/CollectionSchemaMovableColumn.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx index 456c38c68..2df95ffd8 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx @@ -21,27 +21,38 @@ export interface MovableColumnProps { ScreenToLocalTransform: () => Transform; } export class MovableColumn extends React.Component { + // The header of the column private _header?: React.RefObject = React.createRef(); + // The container of the function that is responsible for moving the column over to a new plac private _colDropDisposer?: DragManager.DragDropDisposer; + // initial column position private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; + // sensitivity to being dragged, in pixels private _sensitivity: number = 16; + // Column reference ID private _dragRef: React.RefObject = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { + // if the column is left-clicked and it is being dragged if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = "collectionSchema-col-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } } + onPointerLeave = (e: React.PointerEvent): void => { this._header!.current!.className = "collectionSchema-col-wrapper"; document.removeEventListener("pointermove", this.onDragMove, true); !e.buttons && document.removeEventListener("pointermove", this.onPointerMove); } + onDragMove = (e: PointerEvent): void => { + // only take into account the horizonal direction when a column is dragged const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); const rect = this._header!.current!.getBoundingClientRect(); + // Now store the point at the top center of the column when it was in its original position const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top); + // to be compared with its new horizontal position const before = x[0] < bounds[0]; this._header!.current!.className = "collectionSchema-col-wrapper"; if (before) this._header!.current!.className += " col-before"; @@ -58,11 +69,15 @@ export class MovableColumn extends React.Component { colDrop = (e: Event, de: DragManager.DropEvent) => { document.removeEventListener("pointermove", this.onDragMove, true); + // we only care about whether the column is shifted to the side const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); + // get the dimensions of the smallest rectangle that bounds the header const rect = this._header!.current!.getBoundingClientRect(); const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top); + // get whether the column was dragged before or after where it is now const before = x[0] < bounds[0]; const colDragData = de.complete.columnDragData; + // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables if (colDragData) { e.stopPropagation(); this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns); @@ -85,8 +100,10 @@ export class MovableColumn extends React.Component { document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); }; + // if the left mouse button is the one being held if (e.buttons === 1) { const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); + // If the movemnt of the drag exceeds the sensitivity value if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { document.removeEventListener("pointermove", this.onPointerMove); e.stopPropagation(); @@ -105,6 +122,7 @@ export class MovableColumn extends React.Component { onPointerDown = (e: React.PointerEvent, ref: React.RefObject) => { this._dragRef = ref; const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); + // If the cell thing dragged is not being edited if (!(e.target as any)?.tagName.includes("INPUT")) { this._startDragPosition = { x: dx, y: dy }; document.addEventListener("pointermove", this.onPointerMove); -- cgit v1.2.3-70-g09d2 From c5e96c72fcf149b9bcfe5f7f7a9c714de1d5fd9a Mon Sep 17 00:00:00 2001 From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com> Date: Wed, 25 Aug 2021 16:58:57 -0400 Subject: added comment, fixed path issue --- .../collectionSchema/CollectionSchemaCells.tsx | 239 +++++++++++---------- .../collectionSchema/CollectionSchemaHeaders.tsx | 2 +- 2 files changed, 126 insertions(+), 115 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index f7dfaaeb4..c8638dd12 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -26,7 +26,7 @@ import { SnappingManager } from "../../../util/SnappingManager"; import { undoBatch } from "../../../util/UndoManager"; import '../../../views/DocumentDecorations.scss'; import { EditableView } from "../../EditableView"; -import { MAX_ROW_HEIGHT } from '../../globalCssVariables.scss'; +import { MAX_ROW_HEIGHT } from '../../../../client/views/globalCssVariables.scss'; import { DocumentIconContainer } from "../../nodes/DocumentIcon"; import { OverlayView } from "../../OverlayView"; import "./CollectionSchemaView.scss"; @@ -38,27 +38,35 @@ export interface CellProps { row: number; col: number; rowProps: CellInfo; + // currently unused CollectionView: Opt; + // currently unused ContainingCollection: Opt; Document: Doc; + // column name fieldKey: string; + // currently unused renderDepth: number; + // called when a button is pressed on the node itself addDocTab: (document: Doc, where: string) => boolean; pinToPres: (document: Doc) => void; moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; isFocused: boolean; changeFocusedCellByIndex: (row: number, col: number) => void; + // set whether the cell is in the isEditing mode setIsEditing: (isEditing: boolean) => void; isEditable: boolean; setPreviewDoc: (doc: Doc) => void; setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean; getField: (row: number, col?: number) => void; + // currnetly unused showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void; } @observer export class CollectionSchemaCell extends React.Component { + // return a field key that is corrected for whether it COMMENT public static resolvedFieldKey(column: string, rowDoc: Doc) { const fieldKey = column; if (fieldKey.startsWith("*")) { @@ -72,7 +80,9 @@ export class CollectionSchemaCell extends React.Component { @observable protected _isEditing: boolean = false; protected _focusRef = React.createRef(); protected _rowDoc = this.props.rowProps.original; + // Gets the serialized data in proto form of the base proto that this document's proto inherits from protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original); + // methods for dragging and dropping protected _dropDisposer?: DragManager.DragDropDisposer; @observable contents: string = ""; @@ -81,6 +91,7 @@ export class CollectionSchemaCell extends React.Component { @action onKeyDown = (e: KeyboardEvent): void => { + // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) { document.removeEventListener("keydown", this.onKeyDown); this._isEditing = true; @@ -90,7 +101,11 @@ export class CollectionSchemaCell extends React.Component { @action isEditingCallback = (isEditing: boolean): void => { + // a general method that takes a boolean that determines whether the cell should be in + // is-editing mode + // remove the event listener if it's there document.removeEventListener("keydown", this.onKeyDown); + // it's not already in is-editing mode, re-add the event listener isEditing && document.addEventListener("keydown", this.onKeyDown); this._isEditing = isEditing; this.props.setIsEditing(isEditing); @@ -99,12 +114,16 @@ export class CollectionSchemaCell extends React.Component { @action onPointerDown = async (e: React.PointerEvent): Promise => { + // pan to the cell this.onItemDown(e); + // focus on it this.props.changeFocusedCellByIndex(this.props.row, this.props.col); this.props.setPreviewDoc(this.props.rowProps.original); + // console.log("click cell"); let url: string; if (url = StrCast(this.props.rowProps.row.href)) { + // opens up the the doc in a new window, blurring the old one try { new URL(url); const temp = window.open(url)!; @@ -119,18 +138,25 @@ export class CollectionSchemaCell extends React.Component { @undoBatch applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => { + // apply a specified change to the cell const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) }); if (!res.success) return false; + // change what is rendered to this new changed cell content doc[this.renderFieldKey] = res.result; return true; + // return whether the change was successful } private drop = (e: Event, de: DragManager.DropEvent) => { + // if the drag has data at its completion if (de.complete.docDragData) { + // if only one doc was dragged if (de.complete.docDragData.draggedDocuments.length === 1) { + // update the renderFieldKey this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0]; } else { + // create schema document reflecting the new column arrangement const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {}); this._rowDataDoc[this.renderFieldKey] = coll; } @@ -139,7 +165,9 @@ export class CollectionSchemaCell extends React.Component { } protected dropRef = (ele: HTMLElement | null) => { + // if the drop disposer is not undefined, run its function this._dropDisposer?.(); + // if ele is not null, give ele a non-undefined drop disposer ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); } @@ -163,33 +191,46 @@ export class CollectionSchemaCell extends React.Component { return {contents ? contents?.valueOf() : "undefined"}; } - @computed get renderFieldKey() { return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); } + @computed get renderFieldKey() { + // gets the resolved field key of this cell + return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); + } + onItemDown = async (e: React.PointerEvent) => { + // if the document is a document used to change UI for search results in schema view if (this.props.Document._searchDoc) { const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); + // Jump to the this document DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext, undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc)); } } + renderCellWithType(type: string | undefined) { const dragRef: React.RefObject = React.createRef(); + // the column const fieldKey = this.renderFieldKey; + // the exact cell const field = this._rowDoc[fieldKey]; const onPointerEnter = (e: React.PointerEvent): void => { + // e.buttons === 1 means the left moue pointer is down if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) { dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; } }; const onPointerLeave = (e: React.PointerEvent): void => { + // change the class name to indicate that the cell is no longer being dragged dragRef.current!.className = "collectionSchemaView-cellContainer"; }; let contents = Field.toString(field as Field); + // display 2 hyphens instead of a blank box for empty cells contents = contents === "" ? "--" : contents; + // classname reflects the tatus of the cell let className = "collectionSchemaView-cellWrapper"; if (this._isEditing) className += " editing"; if (this.props.isFocused && this.props.isEditable) className += " focused"; @@ -197,72 +238,27 @@ export class CollectionSchemaCell extends React.Component { const positions = []; if (StrCast(this.props.Document._searchString).toLowerCase() !== "") { + // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents let term = (field instanceof Promise) ? "...promise pending..." : contents.toLowerCase(); const search = StrCast(this.props.Document._searchString).toLowerCase(); let start = term.indexOf(search); let tally = 0; + // if search is found in term if (start !== -1) { positions.push(start); } + // if search is found in term, continue finding all instances of search in term while (start < contents?.length && start !== -1) { term = term.slice(start + search.length + 1); tally += start + search.length + 1; start = term.indexOf(search); positions.push(tally + start); } + // remove the last position if (positions.length > 1) { positions.pop(); } } - - // handles input procedures for string cells - let stringInput = (value: string, retVal: boolean): void => { - let valueSansQuotes = value; - if (this._isEditing) { - const vsqLength = valueSansQuotes.length; - // get rid of outer quotes - valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0, - valueSansQuotes.charAt(vsqLength - 1) == "\"" ? vsqLength - 1 : vsqLength); - } - let inputAsString = '"'; - // escape any quotes in the string - for (const i of valueSansQuotes) { - if (i == '"') { - inputAsString += '\\"'; - } else { - inputAsString += i; - } - } - // add a closing quote - inputAsString += '"'; - //two options here: we can strip off outer quotes or we can figure out what's going on with the script - const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - } - - // handles input procedure for number cells - let numberInput = (value: string, retVal: boolean): void => { - const inputscript = value.substring(value.startsWith("=") ? 1 : 0); - // if commas are not stripped, the parser only considers the numbers after the last comma - let inputSansCommas = ""; - for (let s of inputscript) { - if (!(s == ",")) { - inputSansCommas += s; - } - } - const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = value.length !== value.length || value.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - } - - // handles input procedure for boolean cells - let boolInput = (value: string, retVal: boolean): void => { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - const changeMade = value.length !== value.length || value.length - 2 !== value.length - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - } - const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined"; return (
    { } else { // check if the input is a number let inputIsNum = true; - for (let s of value) { - if (isNaN(parseInt(s)) && !(s == ".") && !(s == ",")) { + for (const s of value) { + if (isNaN(parseInt(s)) && !(s === ".") && !(s === ",")) { inputIsNum = false; } } - - let contentsAreNum = true; - for (let s of contents) { - if (isNaN(parseInt(s)) && !(s == ".") && !(s == ",")) { - contentsAreNum = false; - } - } // check if the input is a boolean - let inputIsBool: boolean = value == "false" || value == "true"; - let contentsAreBool: boolean = contents == "false" || contents == "true"; - - if (type == undefined) { - // what to do in the case - if (!inputIsNum && !inputIsBool && !value.startsWith("=")) { - // if it's not a number, it's a string, and should be processed as such - // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically - // after each edit - stringInput(value, retVal); - // handle numbers and expressions - } else if (inputIsNum || value.startsWith("=")) { - numberInput(value, retVal); - // handle booleans - } else if (inputIsBool) { - boolInput(value, retVal); + const inputIsBool: boolean = value === "false" || value === "true"; + // what to do in the case + if (!inputIsNum && !inputIsBool && !value.startsWith("=")) { + // if it's not a number, it's a string, and should be processed as such + // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically + // after each edit + let valueSansQuotes = value; + if (this._isEditing) { + const vsqLength = valueSansQuotes.length; + // get rid of outer quotes + valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0, + valueSansQuotes.charAt(vsqLength - 1) === "\"" ? vsqLength - 1 : vsqLength); } - // if the cell type is a string - } else if (type == "string") { - stringInput(value, retVal); - // if the cell type is a number - } else if (type == "number") { - if (inputIsNum) { - numberInput(value, retVal); - } else if (!contentsAreNum) { - stringInput("", retVal); + let inputAsString = '"'; + // escape any quotes in the string + for (const i of valueSansQuotes) { + if (i === '"') { + inputAsString += '\\"'; + } else { + inputAsString += i; + } } - // if the cell type is a boolean - } else if (type == "boolean") { - if (inputIsBool) { - boolInput(value, retVal); - } else if (!contentsAreBool) { - stringInput("", retVal); + // add a closing quote + inputAsString += '"'; + //two options here: we can strip off outer quotes or we can figure out what's going on with the script + const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length; + // change it if a change is made, otherwise, just compile using the old cell conetnts + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + // handle numbers and expressions + } else if (inputIsNum || value.startsWith("=")) { + //TODO: make accept numbers + const inputscript = value.substring(value.startsWith("=") ? 1 : 0); + // if commas are not stripped, the parser only considers the numbers after the last comma + let inputSansCommas = ""; + for (const s of inputscript) { + if (!(s === ",")) { + inputSansCommas += s; + } } + const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = value.length !== value.length || value.length - 2 !== value.length; + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); + // handle booleans + } else if (inputIsBool) { + const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + const changeMade = value.length !== value.length || value.length - 2 !== value.length; + script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); } } if (retVal) { @@ -351,6 +354,7 @@ export class CollectionSchemaCell extends React.Component { return retVal; })} OnFillDown={async (value: string) => { + // computes all of the value preceded by := const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); script.compiled && DocListCast(this.props.Document[this.props.fieldKey]). forEach((doc, i) => value.startsWith(":=") ? @@ -381,7 +385,10 @@ export class CollectionSchemaStringCell extends CollectionSchemaCell { render() @observer export class CollectionSchemaDateCell extends CollectionSchemaCell { - @computed get _date(): Opt { return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined; } + @computed get _date(): Opt { + // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. + return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined; + } @action handleChange = (date: any) => { @@ -394,16 +401,13 @@ export class CollectionSchemaDateCell extends CollectionSchemaCell { //} } - // If the cell is not clicked on, render the date normally. Otherwise, render a date picker. render() { - return !this.props.isFocused ? {this._date ? Field.toString(this._date as Field) : "--"} : -
    - this.handleChange(date)} - onChange={date => this.handleChange(date)} - /> -
    + return !this.props.isFocused ? {this._date ? Field.toString(this._date as Field) : "--"} : + this.handleChange(date)} + onChange={date => this.handleChange(date)} + />; } } @@ -423,8 +427,9 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { typecheck: true, transformer: DocumentIconContainer.getTransformer() }); - + // compile the script const results = script.compiled && script.run(); + // if the script was compiled and run if (results && results.success) { this._rowDoc[this.renderFieldKey] = results.result; return true; @@ -442,6 +447,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { @action isEditingCallback = (isEditing: boolean): void => { + // the isEditingCallback from a general CollectionSchemaCell document.removeEventListener("keydown", this.onKeyDown); isEditing && document.addEventListener("keydown", this.onKeyDown); this._isEditing = isEditing; @@ -450,6 +456,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { } render() { + // if there's a doc, render it return !this._doc ? this.renderCellWithType("document") :
    Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; + // If there is a path, follow it; otherwise, follow a link to a default image icon const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; - const aspect = Doc.NativeAspect(this._rowDoc); - let width = Math.min(75, this.props.rowProps.width); - const height = Math.min(75, width / aspect); - width = height * aspect; + const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio + let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px + const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px + width = height * aspect; // increase the width of the image if necessary to maintain proportionality const reference = React.createRef(); return
    @@ -523,13 +531,13 @@ export class CollectionSchemaListCell extends CollectionSchemaCell { @computed get _field() { return this._rowDoc[this.renderFieldKey]; } @computed get _optionsList() { return this._field as List; } - @observable private _opened = false; + @observable private _opened = false; // whether the list is opened @observable private _text = "select an item"; - @observable private _selectedNum = 0; + @observable private _selectedNum = 0; // the index of the list item selected @action onSetValue = (value: string) => { - // change if its a document + // change if it's a document this._optionsList[this._selectedNum] = this._text = value; (this._field as List).splice(this._selectedNum, 1, value); @@ -537,6 +545,7 @@ export class CollectionSchemaListCell extends CollectionSchemaCell { @action onSelected = (element: string, index: number) => { + // if an item is selected, the private variables should update to reflect this this._text = element; this._selectedNum = index; } @@ -550,6 +559,7 @@ export class CollectionSchemaListCell extends CollectionSchemaCell { const link = false; const reference = React.createRef(); + // if the list is not opened, don't display it; otherwise, do. if (this._optionsList?.length) { const options = !this._opened ? (null) :
    @@ -617,6 +627,7 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { @observer export class CollectionSchemaButtons extends CollectionSchemaCell { + // the navigation buttons for schema view when it is used for search. render() { return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? <> :
    @@ -628,4 +639,4 @@ export class CollectionSchemaButtons extends CollectionSchemaCell {
    ; } -} \ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index b2115b22e..2da9409f2 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -25,6 +25,7 @@ export interface AddColumnHeaderProps { @observer export class CollectionSchemaAddColumnHeader extends React.Component { + // the button that allows the user to add a column render() { return ( @@ -32,7 +33,6 @@ export class CollectionSchemaAddColumnHeader extends React.Component Date: Sat, 28 Aug 2021 16:58:02 -0400 Subject: added variable link weights based on relationship importance Link lines are thicker for links belonging to more important relationships. Thickness varies linearly from 3px to 12px. Removed dashed linked lines. --- src/client/documents/Documents.ts | 1 + src/client/util/LinkManager.ts | 2 ++ .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 15 ++++++++++++--- src/client/views/linking/LinkEditor.tsx | 8 +++++++- src/fields/Doc.ts | 1 + 5 files changed, 23 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index db1e67203..08b2b8964 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -270,6 +270,7 @@ export class DocumentOptions { border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered linkRelationshipList?: List; // for storing different link relationships (when set by user in the link editor) + linkRelationshipSizes?: List; //stores number of links contained in each relationship linkColorList?: List; // colors of links corresponding to specific link relationships } export namespace Docs { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 84ff3a3ff..12ba57f45 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -105,8 +105,10 @@ export class LinkManager { if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { const linkRelationshipList = new List(); const linkColorList = new List(); + const linkRelationshipSizes = new List(); Doc.UserDoc().linkRelationshipList = linkRelationshipList; Doc.UserDoc().linkColorList = linkColorList; + Doc.UserDoc().linkRelationshipSizes = linkRelationshipSizes; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 59891b7a1..92696138e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -41,7 +41,7 @@ export class CollectionFreeFormLinkView extends React.Component this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() + setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; @@ -175,15 +175,24 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; + const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List; + const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); + //access stroke color using index of the relationship in the color list (default black) - const strokeColor = linkRelationshipList.indexOf(linkRelationship) == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + const strokeColor = currRelationshipIndex == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + + //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has) + //thickness varies linearly from 3px to 12px for increasing link count + const strokeWidth: string = currRelationshipIndex == -1 ? "3px" : Math.floor(2 + 10 * (linkRelationshipSizes[currRelationshipIndex] / Math.max(...linkRelationshipSizes))) + "px"; + return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> - {textX === undefined ? (null) : {StrCast(this.props.LinkDocs[0].description)} diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index fba95cad2..f326e1440 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, StrListCast } from "../../../fields/Doc"; +import { Doc, NumListCast, StrListCast } from "../../../fields/Doc"; import { DateCast, StrCast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; @@ -43,12 +43,18 @@ export class LinkEditor extends React.Component { if (LinkManager.currentLink) { LinkManager.currentLink.linkRelationship = value; const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); + // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color if (linkRelationshipList && !linkRelationshipList.includes(value)) { linkRelationshipList.push(value); + linkRelationshipSizes.push(1); const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; linkColorList.push(randColor) + } else { + //increment relationship size if rel already exists + linkRelationshipSizes[linkRelationshipList.indexOf(value)] = linkRelationshipSizes[linkRelationshipList.indexOf(value)] + 1; } this.relationshipButtonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.relationshipButtonColor = ""), 750); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 17f41fac8..c57f31068 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -80,6 +80,7 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { export async function DocCastAsync(field: FieldResult): Promise> { return Cast(field, Doc); } +export function NumListCast(field: FieldResult) { return Cast(field, listSpec("number"), []); } export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []); } export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; } -- cgit v1.2.3-70-g09d2 From 64119b5d8766725025b8b2bfda72f2401bba0f00 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 16 Sep 2021 21:04:57 -0400 Subject: added search() component method. changed search menu to call search on documents that match search string. added seach bar for web pages. --- src/client/views/GlobalKeyHandler.ts | 16 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 18 ++++- src/client/views/nodes/WebBox.scss | 86 ++++++++++++++++++++++ src/client/views/nodes/WebBox.tsx | 65 +++++++++++----- src/client/views/pdf/PDFViewer.tsx | 13 +--- src/client/views/search/SearchBox.tsx | 15 ++-- 8 files changed, 172 insertions(+), 46 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index f66c9c788..de6f4ae8b 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -10,10 +10,11 @@ import { Cast, PromiseValue } from "../../fields/Types"; import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager"; import { DocServer } from "../DocServer"; import { DocumentType } from "../documents/DocumentTypes"; -import { DictationManager } from "../util/DictationManager"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; import { GroupManager } from "../util/GroupManager"; import { SelectionManager } from "../util/SelectionManager"; +import { SettingsManager } from "../util/SettingsManager"; import { SharingManager } from "../util/SharingManager"; import { SnappingManager } from "../util/SnappingManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; @@ -27,8 +28,6 @@ import { LightboxView } from "./LightboxView"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { AnchorMenu } from "./pdf/AnchorMenu"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { SettingsManager } from "../util/SettingsManager"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -222,10 +221,13 @@ export class KeyManager { PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); break; case "f": - const searchBtn = Doc.UserDoc().searchBtn as Doc; - - if (searchBtn) { - MainView.Instance.selectMenu(searchBtn); + if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) { + SelectionManager.Views()[0].ComponentView?.search?.("", false, false); + } else { + const searchBtn = Doc.UserDoc().searchBtn as Doc; + if (searchBtn) { + MainView.Instance.selectMenu(searchBtn); + } } break; case "o": diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index be0b078ec..94cf1c5a6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -902,7 +902,9 @@ export class CollectionFreeFormView extends CollectionSubView string; getScrollHeight?: () => number; + search?: (str:string, bwd?:boolean, clear?:boolean) => boolean; } export interface DocumentViewSharedProps { renderDepth: number; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index ce851b830..274d166f1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -96,7 +96,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.search(string, fwd); + public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { + if (!this._searching && !clear) { + this._searching = true; + setTimeout(() => { + this._searchRef.current?.focus(); + this._searchRef.current?.select(); + this._searchRef.current?.setRangeText(searchString); + }); + } + return this._pdfViewer?.search(searchString, bwd, clear) || false; + }); public prevAnnotation = () => this._pdfViewer?.prevAnnotation(); public nextAnnotation = () => this._pdfViewer?.nextAnnotation(); public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; }; @@ -184,8 +194,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
    +
    + +
    ; + } + searchStringChanged = (e: React.ChangeEvent) => this._searchString = e.currentTarget.value; showInfo = action((anno: Opt) => this._overlayAnnoInfo = anno); setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); @@ -623,25 +666,7 @@ export class WebBox extends ViewBoxAnnotatableComponent
    - {/*
    - { - if (e.key === "Enter") { - (this._iframe?.contentWindow as any)?.find(e.target.value); - } - }} onChange={e => { - this._iframe?.contentWindow?.getSelection()?.empty(); - (this._iframe?.contentWindow as any)?.find(e.target.value) - }}> -
    */} + {!this.props.isContentActive() ? (null) : this.searchUI}
    ); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d953c6b6c..7aa18e46f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -125,12 +125,6 @@ export class PDFViewer extends React.Component { } }); - this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.props.rootDoc), - m => { - if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0); - else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200); - }, { fireImmediately: true }); - this._disposers.selected = reaction(() => this.props.isSelected(), selected => { // if (!selected) { @@ -337,10 +331,10 @@ export class PDFViewer extends React.Component { } @action - search = (searchString: string, fwd: boolean, clear: boolean = false) => { + search = (searchString: string, bwd?: boolean, clear: boolean = false) => { const findOpts = { caseSensitive: false, - findPrevious: !fwd, + findPrevious: bwd, highlightAll: true, phraseSearch: true, query: searchString @@ -348,7 +342,7 @@ export class PDFViewer extends React.Component { if (clear) { this._pdfViewer?.findController.executeCommand('reset', { query: "" }); } else if (!searchString) { - fwd ? this.nextAnnotation() : this.prevAnnotation(); + bwd ? this.prevAnnotation() : this.nextAnnotation(); } else if (this._pdfViewer?.pageViewsReady) { this._pdfViewer.findController.executeCommand('findagain', findOpts); } @@ -357,6 +351,7 @@ export class PDFViewer extends React.Component { this._mainCont.current.addEventListener("pagesloaded", executeFind); this._mainCont.current.addEventListener("pagerendered", executeFind); } + return true; } @action diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 9c353e9d0..3612bd7c4 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -104,9 +104,9 @@ export class SearchBox extends ViewBoxBaseComponent { - this.selectElement(doc); + onResultClick = action(async (doc: Doc) => { this._selectedResult = doc; + this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false)); }); makeLink = action((linkTo: Doc) => { @@ -269,8 +269,8 @@ export class SearchBox extends ViewBoxBaseComponent { - await DocumentManager.Instance.jumpToDocument(doc, true); + selectElement = async (doc: Doc, finishFunc: () => void) => { + await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc); } /** @@ -307,7 +307,12 @@ export class SearchBox extends ViewBoxBaseComponent
    {title}
    }> -
    this.makeLink(result[0]) : () => this.onResultClick(result[0])} className={className}> +
    this.makeLink(result[0]) : + e => { + this.onResultClick(result[0]); + e.stopPropagation(); + }} className={className}>
    {title}
    -- cgit v1.2.3-70-g09d2 From 582c709a2b08e2f6c12793121d0c8b264ddc2ca2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Sep 2021 03:28:19 -0400 Subject: close context menu when starting a right-click drag on a webbox. .fixed dismissing marquee menu from outer view when clicking on nested webbox. AltKey drags marquee, not CtrlKey. --- .../views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index cedeb1112..ecff01e67 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -16,7 +16,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu { public showMarquee: () => void = unimplementedFunction; public hideMarquee: () => void = unimplementedFunction; public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; - + public isShown = () => { return this._opacity > 0; } constructor(props: Readonly<{}>) { super(props); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index f230550e4..ca1b0d8ac 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -35,6 +35,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from "./LinkDocPreview"; import "./WebBox.scss"; import React = require("react"); +import { MarqueeOptionsMenu } from "../collections/collectionFreeForm"; const _global = (window /* browser */ || global /* node */) as any; const htmlToText = require("html-to-text"); @@ -247,9 +248,10 @@ export class WebBox extends ViewBoxAnnotatableComponent; return ( -
    +
    Date: Fri, 17 Sep 2021 14:07:09 -0400 Subject: added 'unset' docFilters. changed link doc views to use comparison box with title/caption. fixed linkEditor to write to data doc. generalized comparisonBox rendering to use parameterized fields. fixed pdf/web to honor pointerEvents none prop and fixed textAnnotations to get rendered once as an Annotation. moved filterIcon stuff into DocumentView --- src/Utils.ts | 4 +++- src/client/DocServer.ts | 2 ++ src/client/documents/Documents.ts | 17 +++++++++++------ src/client/views/collections/CollectionView.tsx | 12 ------------ .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 6 +++--- src/client/views/linking/LinkEditor.tsx | 11 ++++++----- src/client/views/nodes/ComparisonBox.tsx | 8 +++++--- src/client/views/nodes/DocumentView.tsx | 19 +++++++++++++++++-- src/client/views/nodes/LinkBox.tsx | 19 ++++++------------- src/client/views/nodes/LinkDescriptionPopup.tsx | 7 ++++--- src/client/views/nodes/PDFBox.tsx | 9 ++++----- src/client/views/nodes/WebBox.tsx | 14 ++++++++------ src/client/views/pdf/Annotation.scss | 3 +++ src/client/views/pdf/PDFViewer.tsx | 13 +++++++------ src/fields/Doc.ts | 2 +- 15 files changed, 80 insertions(+), 66 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index 6eacd8296..e11f1154e 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -125,7 +125,9 @@ export namespace Utils { // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`;// bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field } - + export function PropUnsetFilter(prop: string) { + return `${prop}:any,${noRecursionHack}:unset`; + } export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) { return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")"; diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index e498a7cca..3b376a0e7 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -241,6 +241,7 @@ export namespace DocServer { // the field has been returned from the server const getSerializedField = Utils.EmitCallback(_socket, MessageStore.GetRefField, id); + console.log(id) // when the serialized RefField has been received, go head and begin deserializing it into an object. // Here, once deserialized, we also invoke .proto to 'load' the document's prototype, which ensures that all // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. @@ -264,6 +265,7 @@ export namespace DocServer { } else { delete _cache[id]; } + console.log(id, field); return field; // either way, overwrite or delete any promises cached at this id (that we inserted as flags // to indicate that the field was in the process of being fetched). Now everything diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f50f306a3..e8185400e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -962,13 +962,17 @@ export namespace DocUtils { // metadata facets that exist const exists = Object.keys(facet).filter(value => facet[value] === "exists"); + // metadata facets that exist + const unsets = Object.keys(facet).filter(value => facet[value] === "unset"); + // facets that have an x next to them const xs = Object.keys(facet).filter(value => facet[value] === "x"); - if (!exists.length && !xs.length && !checks.length && !matches.length) return true; + if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined); + const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined); const satisfiesMatchFacets = !matches.length ? true : matches.some(value => { if (facetKey.startsWith("*")) { // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc const allKeys = Array.from(Object.keys(d)); @@ -980,11 +984,11 @@ export namespace DocUtils { }); // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") { - if (satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; } // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria else { - if (!satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; } } @@ -1088,10 +1092,11 @@ export namespace DocUtils { "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null), - linkDisplay: true, hidden: true, + linkDisplay: true, + hidden: true, linkRelationship, - _layoutKey: "layout_linkView", + _showCaption: "description", + _showTitle: "linkRelationship", description }, id), showPopup); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 5c9c8063b..38e027fb3 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -246,12 +246,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); } - /** - * Shows the filter icon if it's a user-created collection which isn't a dashboard and has some docFilters applied on it or on the current dashboard. - */ - @computed get showFilterIcon() { - return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered(); - } @observable _subView: any = undefined; @@ -280,12 +274,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent {this.showIsTagged()} {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} - {this.showFilterIcon ? - { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })} - /> - : (null)}
    ); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 3b3e069d8..2cb487588 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,6 +1,6 @@ import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../../fields/Doc"; +import { Doc, Field } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { List } from "../../../../fields/List"; import { NumCast, StrCast } from "../../../../fields/Types"; @@ -180,7 +180,7 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; //access stroke color using index of the relationship in the color list (default black) @@ -189,7 +189,7 @@ export class CollectionFreeFormLinkView extends React.Component {textX === undefined ? (null) : - {StrCast(this.props.LinkDocs[0].description)} + {Field.toString(this.props.LinkDocs[0].description as any as Field)} } ); } diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 240a71c3e..219f7d3a2 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -2,13 +2,14 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, StrListCast } from "../../../fields/Doc"; -import { DateCast, StrCast } from "../../../fields/Types"; +import { Doc, StrListCast, Field } from "../../../fields/Doc"; +import { DateCast, StrCast, Cast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; import './LinkEditor.scss'; import { LinkRelationshipSearch } from "./LinkRelationshipSearch"; import React = require("react"); +import { ToString } from "../../../fields/FieldSymbols"; interface LinkEditorProps { @@ -20,7 +21,7 @@ interface LinkEditorProps { @observer export class LinkEditor extends React.Component { - @observable description = StrCast(LinkManager.currentLink?.description); + @observable description = Field.toString(LinkManager.currentLink?.description as any as Field); @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); @observable openDropdown: boolean = false; @observable showInfo: boolean = false; @@ -41,7 +42,7 @@ export class LinkEditor extends React.Component { @undoBatch setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { - LinkManager.currentLink.linkRelationship = value; + Doc.GetProto(LinkManager.currentLink).linkRelationship = value; const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color @@ -79,7 +80,7 @@ export class LinkEditor extends React.Component { @undoBatch setDescripValue = action((value: string) => { if (LinkManager.currentLink) { - LinkManager.currentLink.description = value; + Doc.GetProto(LinkManager.currentLink).description = value; this.buttonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.buttonColor = ""), 750); return true; diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 6708a08ee..72c7e4f45 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -15,6 +15,7 @@ import "./ComparisonBox.scss"; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import React = require("react"); +import { DocumentType } from '../../documents/DocumentTypes'; export const comparisonSchema = createSchema({}); @@ -87,7 +88,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent; }; const displayDoc = (which: string) => { - const whichDoc = Cast(this.dataDoc[`compareBox-${which}`], Doc, null); + var whichDoc = Cast(this.dataDoc[which], Doc, null); + if (whichDoc.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); return whichDoc ? <> - {displayBox("after", 1, this.props.PanelWidth() - 3)} + {displayBox(this.fieldKey === "data" ? "compareBox-after" : `${this.fieldKey}2`, 1, this.props.PanelWidth() - 3)}
    - {displayBox("before", 0, 0)} + {displayBox(this.fieldKey === "data" ? "compareBox-before" : `${this.fieldKey}1`, 0, 0)}
    (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? "w-resize" : undefined }} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 46a8b6629..35249c7f4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty import { AudioField } from "../../../fields/URLField"; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick } from "../../../Utils"; +import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; @@ -802,6 +802,12 @@ export class DocumentViewInternal extends DocComponent StrListCast(this.props.Document._docFilters); + collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters); + @computed get showFilterIcon() { + return this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" : + this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined + } rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; panelHeight = () => this.props.PanelHeight() - this.headerMargin; screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); @@ -832,7 +838,7 @@ export class DocumentViewInternal extends DocComponent; return
    this.props.PanelHeight() || 1; anchorStyleProvider = (doc: Opt, props: Opt, property: string): any => { switch (property) { + case StyleProp.ShowTitle: return ""; case StyleProp.PointerEvents: return "none"; case StyleProp.LinkSource: return this.props.Document;// pass the LinkSource to the LinkAnchorBox default: return this.props.styleProvider?.(doc, props, property); @@ -901,6 +908,8 @@ export class DocumentViewInternal extends DocComponent } + {this.showFilterIcon ? + { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })} + /> + : (null)}
    ; } } diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 55ea45bb8..b82d16677 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -2,10 +2,10 @@ import React = require("react"); import { observer } from "mobx-react"; import { documentSchema } from "../../../fields/documentSchemas"; import { makeInterface } from "../../../fields/Schema"; -import { returnFalse } from "../../../Utils"; -import { CollectionTreeView } from "../collections/CollectionTreeView"; +import { emptyFunction, returnFalse } from "../../../Utils"; import { ViewBoxBaseComponent } from "../DocComponent"; import { StyleProp } from "../StyleProvider"; +import { ComparisonBox } from "./ComparisonBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./LinkBox.scss"; @@ -20,22 +20,15 @@ export class LinkBox extends ViewBoxBaseComponent( if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => this.dataDoc.treeViewOpen = true); return
    - - + moveDocument={returnFalse} />
    ; } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index b62a4dd56..a9d33f161 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -1,8 +1,9 @@ import React = require("react"); +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import "./LinkDescriptionPopup.scss"; -import { observable, action } from "mobx"; +import { Doc } from "../../../fields/Doc"; import { LinkManager } from "../../util/LinkManager"; +import "./LinkDescriptionPopup.scss"; import { TaskCompletionBox } from "./TaskCompletedBox"; @@ -25,7 +26,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { onDismiss = (add: boolean) => { LinkDescriptionPopup.descriptionPopup = false; if (add) { - LinkManager.currentLink && (LinkManager.currentLink.description = this.description); + LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index f0a502e31..30b4dc92a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -3,9 +3,9 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt, WidthSym, StrListCast } from "../../../fields/Doc"; import { documentSchema } from '../../../fields/documentSchemas'; -import { makeInterface } from "../../../fields/Schema"; +import { makeInterface, listSpec } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; @@ -25,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -249,9 +250,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - return 1; - } + contentScaling = () => 1; @computed get renderPdfView() { TraceMobx(); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6a20ca14a..60a83d612 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -532,7 +532,8 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.urlContent}
    ; @@ -575,10 +576,11 @@ export class WebBox extends ViewBoxAnnotatableComponent this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; - transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")]; + transparentFilter = () => [Utils.IsTransparentFilter(), ...this.basicFilter()]; + opaqueFilter = () => [Utils.IsOpaqueFilter(), ...this.basicFilter()]; render() { - const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined; + const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined; const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const scale = previewScale * (this.props.scaling?.() || 1); const renderAnnotations = (docFilters?: () => string[]) => @@ -593,7 +595,7 @@ export class WebBox extends ViewBoxAnnotatableComponent; return (
    + style={{ pointerEvents: this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none" }} >
    { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); - const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight); + const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight); if (scrollTo !== undefined) { focusSpeed = 500; @@ -502,8 +502,9 @@ export class PDFViewer extends React.Component { overlayTransform = () => this.scrollXf().scale(1 / this._zoomed); panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); - transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")]; + transparentFilter = () => [Utils.IsTransparentFilter(), ...this.basicFilter()]; + opaqueFilter = () => [Utils.IsOpaqueFilter(), ...this.basicFilter()]; @computed get overlayLayer() { const renderAnnotations = (docFilters?: () => string[]) => { select={emptyFunction} ContentScaling={this.contentZoom} bringToFront={emptyFunction} - docFilters={docFilters || this.props.docFilters} + docFilters={docFilters || this.basicFilter} dontRenderDocuments={docFilters ? false : true} CollectionView={undefined} ScreenToLocalTransform={this.overlayTransform} @@ -543,7 +544,7 @@ export class PDFViewer extends React.Component {
    ; } @computed get pdfViewerDiv() { - return
    ; + return
    ; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } @computed get standinViews() { @@ -556,7 +557,7 @@ export class PDFViewer extends React.Component { render() { TraceMobx(); return
    -
    , key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists", toggle?: boolean, fieldSuffix?: string, append: boolean = true) { + export function setDocFilter(container: Opt, key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists" | "unset", toggle?: boolean, fieldSuffix?: string, append: boolean = true) { if (!container) return; const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters"; const docFilters = Cast(container[filterField], listSpec("string"), []); -- cgit v1.2.3-70-g09d2 From 50886bf908a5a155849daa5920b42a86752f9d1b Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Sep 2021 15:09:55 -0400 Subject: fixed warnings --- src/client/DocServer.ts | 2 -- src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 3b376a0e7..e498a7cca 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -241,7 +241,6 @@ export namespace DocServer { // the field has been returned from the server const getSerializedField = Utils.EmitCallback(_socket, MessageStore.GetRefField, id); - console.log(id) // when the serialized RefField has been received, go head and begin deserializing it into an object. // Here, once deserialized, we also invoke .proto to 'load' the document's prototype, which ensures that all // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. @@ -265,7 +264,6 @@ export namespace DocServer { } else { delete _cache[id]; } - console.log(id, field); return field; // either way, overwrite or delete any promises cached at this id (that we inserted as flags // to indicate that the field was in the process of being fetched). Now everything diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index ecff01e67..71642216c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -16,7 +16,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu { public showMarquee: () => void = unimplementedFunction; public hideMarquee: () => void = unimplementedFunction; public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; - public isShown = () => { return this._opacity > 0; } + public isShown = () => this._opacity > 0; constructor(props: Readonly<{}>) { super(props); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 35249c7f4..d2b4b0348 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -806,7 +806,7 @@ export class DocumentViewInternal extends DocComponent StrListCast(this.props.Document._docRangeFilters); @computed get showFilterIcon() { return this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" : - this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined + this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined; } rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; panelHeight = () => this.props.PanelHeight() - this.headerMargin; -- cgit v1.2.3-70-g09d2 From fcab3f96513c759ca67281e871b970d5691348af Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Sep 2021 20:12:00 -0400 Subject: fixed making link to selected region in video --- src/client/views/collections/CollectionStackedTimeline.tsx | 5 +++-- src/client/views/nodes/VideoBox.tsx | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 65022fdfd..89da6692a 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -372,10 +372,11 @@ export class CollectionStackedTimeline extends CollectionSubView< startTag: string, endTag: string, anchorStartTime?: number, - anchorEndTime?: number + anchorEndTime?: number, + docAnchor?: Doc ) { if (anchorStartTime === undefined) return rootDoc; - const anchor = Docs.Create.LabelDocument({ + const anchor = docAnchor ?? Docs.Create.LabelDocument({ title: ComputedField.MakeFunction( `"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])` ) as any, diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 90de3227f..e3ba638b3 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -30,6 +30,7 @@ import { DragManager } from "../../util/DragManager"; import { DocumentManager } from "../../util/DocumentManager"; import { DocumentType } from "../../documents/DocumentTypes"; import { Tooltip } from "@material-ui/core"; +import { AnchorMenu } from "../pdf/AnchorMenu"; const path = require('path'); type VideoDocument = makeInterface<[typeof documentSchema]>; @@ -74,7 +75,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { const timecode = Cast(this.layoutDoc._currentTimecode, "number", null); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined) || this.rootDoc; + const marquee = AnchorMenu.Instance.GetAnchor?.(); + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc; } videoLoad = () => { -- cgit v1.2.3-70-g09d2 From e295f6694bed9a3a35a0858c8f17745ef1506f51 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Sep 2021 12:52:38 -0400 Subject: replaced convert to ink in marquee menu with create Group --- .../collectionFreeForm/MarqueeOptionsMenu.tsx | 38 ++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index 71642216c..1f59f9732 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -3,13 +3,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { observer } from "mobx-react"; import { unimplementedFunction } from "../../../../Utils"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { SelectionManager } from "../../../util/SelectionManager"; import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu"; @observer export class MarqueeOptionsMenu extends AntimodeMenu { static Instance: MarqueeOptionsMenu; - public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; + public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction; public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; @@ -26,42 +28,52 @@ export class MarqueeOptionsMenu extends AntimodeMenu { render() { const presPinWithViewIcon = ; const buttons = [ -
    Create a Collection
    } placement="bottom"> + Create a Collection
    } placement="bottom"> , -
    Summarize Documents
    } placement="bottom"> + Create a Grouping
    } placement="bottom"> , -
    Delete Documents
    } placement="bottom"> + Summarize Documents
    } placement="bottom"> , -
    Change to Text
    } placement="bottom"> + Delete Documents
    } placement="bottom"> , -
    Pin with selected region
    } placement="bottom"> + Pin with selected region
    } placement="bottom"> , ]; + if (false && !SelectionManager.Views().some(v => v.props.Document.type !== DocumentType.INK)) { + buttons.push( + Change to Text
    } placement="bottom"> + + ); + } return this.getElement(buttons); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From eb50e46332c2f0b48c7cb165d55245aab317ef08 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 21 Sep 2021 19:43:32 -0400 Subject: fixed up some darkScheme css, enabled comic mode for developers, enabled opening up fields on dashboards in myDocuments --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 30 ++++++++++------ src/client/util/SelectionManager.ts | 14 ++------ src/client/util/SettingsManager.tsx | 19 +++++----- src/client/views/ContextMenu.scss | 1 - src/client/views/DocumentDecorations.scss | 28 +++++++++++---- src/client/views/DocumentDecorations.tsx | 42 +++++++++++++--------- src/client/views/MainView.scss | 12 +++---- src/client/views/MainView.tsx | 14 ++++---- src/client/views/StyleProvider.tsx | 5 +-- .../views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 9 +++-- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++- .../collectionSchema/CollectionSchemaView.tsx | 7 ++-- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/button/FontIconBox.tsx | 2 -- .../views/nodes/formattedText/DashDocView.tsx | 3 +- src/mobile/MobileInterface.tsx | 4 +-- 19 files changed, 118 insertions(+), 89 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fafbc4a7d..206f65bfd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -862,7 +862,7 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true }); const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true }); - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d5dc9e2be..568a9ddbd 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -20,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { TreeView } from "../views/collections/TreeView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; @@ -38,7 +39,6 @@ import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; -import { TreeView } from "../views/collections/TreeView"; interface Button { title?: string; @@ -816,16 +816,19 @@ export class CurrentUserUtils { _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true, explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." })); - // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); - // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); + const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); - (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); - (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); - (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "user-friends", "times"]); - // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); - // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()'); + // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); + // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); + // (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "user-friends", "times"]); + (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); + (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "chalkboard", "tv", "camera", "users", "times"]); + (doc.myDashboards as any as Doc).childContextMenuFilters = new List([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]); } return doc.myDashboards as any as Doc; } @@ -1302,7 +1305,6 @@ export class CurrentUserUtils { }, { fireImmediately: true }); // Document properties on load doc.system = true; - doc.darkScheme = ColorScheme.Dark; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc._raiseWhenDragged = true; @@ -1321,7 +1323,6 @@ export class CurrentUserUtils { doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); - doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // @@ -1358,6 +1359,10 @@ export class CurrentUserUtils { // }); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); + if (doc.activeDashboard instanceof Doc) { + // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) + doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; + } return doc; } @@ -1637,4 +1642,7 @@ Scripting.addGlobal(function makeTopLevelFolder() { const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder); +}); +Scripting.addGlobal(function toggleComicMode() { + Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); \ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index bac13373c..e507ec3bf 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -2,7 +2,6 @@ import { action, observable, ObservableMap } from "mobx"; import { computedFn } from "mobx-utils"; import { Doc, Opt } from "../../fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; -import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView"; import { CollectionViewType } from "../views/collections/CollectionView"; import { DocumentView } from "../views/nodes/DocumentView"; @@ -13,12 +12,10 @@ export namespace SelectionManager { @observable IsDragging: boolean = false; SelectedViews: ObservableMap = new ObservableMap(); @observable SelectedSchemaDocument: Doc | undefined; - @observable SelectedSchemaCollection: CollectionSchemaView | undefined; @action - SelectSchemaView(collectionView: Opt, doc: Opt) { + SelectSchemaViewDoc(doc: Opt) { manager.SelectedSchemaDocument = doc; - manager.SelectedSchemaCollection = collectionView; } @action SelectView(docView: DocumentView, ctrlPressed: boolean): void { @@ -33,7 +30,6 @@ export namespace SelectionManager { } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) { Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false)); manager.SelectedSchemaDocument = undefined; - manager.SelectedSchemaCollection = undefined; manager.SelectedViews.clear(); manager.SelectedViews.set(docView, docView.rootDoc); } @@ -47,7 +43,6 @@ export namespace SelectionManager { } @action DeselectAll(): void { - manager.SelectedSchemaCollection = undefined; manager.SelectedSchemaDocument = undefined; Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false)); manager.SelectedViews.clear(); @@ -62,8 +57,8 @@ export namespace SelectionManager { export function SelectView(docView: DocumentView, ctrlPressed: boolean): void { manager.SelectView(docView, ctrlPressed); } - export function SelectSchemaView(colSchema: Opt, document: Opt): void { - manager.SelectSchemaView(colSchema, document); + export function SelectSchemaViewDoc(document: Opt): void { + manager.SelectSchemaViewDoc(document); } const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed @@ -96,9 +91,6 @@ export namespace SelectionManager { export function SelectedSchemaDoc(): Doc | undefined { return manager.SelectedSchemaDocument; } - export function SelectedSchemaCollection(): CollectionSchemaView | undefined { - return manager.SelectedSchemaCollection; - } export function Docs(): Doc[] { return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index bd91db779..6a26dfdc7 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -19,9 +19,9 @@ export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export enum ColorScheme { - Dark = "Dark", - Light = "Light", - System = "Match System" + Dark = "-Dark", + Light = "-Light", + System = "-MatchSystem" } @observer @@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> { @observable activeTab = "Accounts"; @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } - @computed get colorScheme() { return Doc.UserDoc().colorScheme; } + @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; } constructor(props: {}) { super(props); @@ -81,16 +81,16 @@ export class SettingsManager extends React.Component<{}> { const scheme: ColorScheme = (e.currentTarget as any).value; switch (scheme) { case ColorScheme.Light: - Doc.UserDoc().colorScheme = ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" }); break; case ColorScheme.Dark: - Doc.UserDoc().colorScheme = ColorScheme.Dark; + CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark; addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" }); break; case ColorScheme.System: default: window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { - Doc.UserDoc().colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) }); break; } @@ -119,6 +119,7 @@ export class SettingsManager extends React.Component<{}> {
    ; const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System]; + const schemeMap = ["Light", "Dark", "Match system"]; return
    @@ -132,8 +133,8 @@ export class SettingsManager extends React.Component<{}> {
    Color Scheme
    - + {colorSchemes.map((scheme, i) => )}
    diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 47ae0424b..ea24dbf6d 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -8,7 +8,6 @@ flex-direction: column; background: whitesmoke; border-radius: 3px; - border: solid $light-gray 1px; } // .contextMenu-item:first-child { diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index a9f50f81b..d8ad47ecb 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -2,10 +2,14 @@ $linkGap: 3px; +.documentDecorations-Dark, .documentDecorations { position: absolute; z-index: 2000; } +.documentDecorations-Dark { + background: dimgray; +} .documentDecorations-container { z-index: $docDecorations-zindex; position: absolute; @@ -50,12 +54,17 @@ $linkGap: 3px; pointer-events: auto; background: $medium-gray; opacity: 0.1; - &:hover { opacity: 1; } } + .documentDecorations-resizer-Dark + { + background: $light-gray; + opacity: 0.2; + } + .documentDecorations-topLeftResizer, .documentDecorations-leftResizer, .documentDecorations-bottomLeftResizer { @@ -221,6 +230,7 @@ $linkGap: 3px; cursor: ns-resize; } + .documentDecorations-title-Dark, .documentDecorations-title { opacity: 1; grid-column-start: 2; @@ -233,14 +243,22 @@ $linkGap: 3px; height: 22px; position: absolute; - .documentDecorations-titleSpan { + .documentDecorations-titleSpan, + .documentDecorations-titleSpan-Dark { width: 100%; border-radius: 8px; - background: #ffffffcf; + background: #ffffffa0; position: absolute; display: inline-block; cursor: move; } + .documentDecorations-titleSpan-Dark { + background: hsla(0, 0%, 0%, 0.412); + } + } + .documentDecorations-title-Dark { + color: white; + background: black; } .documentDecorations-contextMenu { @@ -439,10 +457,6 @@ $linkGap: 3px; } } -.documentDecorations-darkScheme { - background: dimgray; -} - #template-list { position: absolute; top: 25px; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d785d5419..e9a54d6a5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -8,7 +8,7 @@ import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSy import { Document } from '../../fields/documentSchemas'; import { InkField } from "../../fields/InkField"; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, NumCast } from "../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../fields/Types"; import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { Docs } from "../documents/Documents"; @@ -27,6 +27,8 @@ import { LightboxView } from './LightboxView'; import { DocumentView } from "./nodes/DocumentView"; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import React = require("react"); +import { dark } from '@material-ui/core/styles/createPalette'; +import { color } from 'd3-color'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> { @@ -241,7 +243,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P InkStrokeProperties.Instance?._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK) .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc)); - if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles + const resizeHdl = this._resizeHdlId.split(" ")[0]; + if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles const project = (p: number[], a: number[], b: number[]) => { const atob = [b[0] - a[0], b[1] - a[1]]; const atop = [p[0] - a[0], p[1] - a[1]]; @@ -264,7 +267,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._snapY = thisPt.y; let dragBottom = false, dragRight = false, dragBotRight = false; let dX = 0, dY = 0, dW = 0, dH = 0; - switch (this._resizeHdlId) { + switch (this._resizeHdlId.split(" ")[0]) { case "": break; case "documentDecorations-topLeftResizer": dX = -1; @@ -437,11 +440,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
    ); + const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); const titleArea = this._edtingTitle ? - this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : + this.titleBlur()} + onChange={action(e => this._accumulatedTitle = e.target.value)} + onKeyPress={this.titleEntered} /> :
    - {`${this.selectionTitle}`} + {`${this.selectionTitle}`}
    ; let inMainMenuPanel = false; @@ -457,8 +466,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight)); const useRotation = seldoc.rootDoc.type === DocumentType.INK; + const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : ""; - return (
    + return (
    {SelectionManager.Views().length !== 1 || hideTitle ? (null) : topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)} -
    e.preventDefault()} /> -
    e.preventDefault()} /> -
    e.preventDefault()} /> -
    e.preventDefault()} /> -
    -
    e.preventDefault()} /> -
    e.preventDefault()} /> -
    e.preventDefault()} /> -
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    +
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    e.preventDefault()} /> +
    e.preventDefault()} /> {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")} diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 4f871f5ec..7fa841002 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -41,7 +41,7 @@ } .mainView-container, -.mainView-container-dark { +.mainView-container-Dark { width: 100%; height: 100%; position: absolute; @@ -65,7 +65,7 @@ } } -.mainView-container-dark { +.mainView-container-Dark { color: $light-gray; .lm_goldenlayout { @@ -91,7 +91,7 @@ .contextMenu-cont, .contextMenu-item { - background: $medium-gray; + background: $dark-gray; } .contextMenu-item:hover { @@ -144,7 +144,7 @@ } } -.mainView-innerContent, .mainView-innerContent-dark { +.mainView-innerContent, .mainView-innerContent-Dark { display: contents; flex-direction: row; position: relative; @@ -175,7 +175,7 @@ .mainView-libraryHandle { background-color: $light-gray; } -.mainView-innerContent-dark +.mainView-innerContent-Dark { .propertiesView { background-color: #252525; @@ -198,7 +198,7 @@ background: #353535; } } -.mainView-container-dark { +.mainView-container-Dark { .contextMenu-cont { background: $medium-gray; color: $white; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d854f118f..c99ba447c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -26,7 +26,7 @@ import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; import { Scripting } from '../util/Scripting'; import { SelectionManager } from '../util/SelectionManager'; -import { SettingsManager } from '../util/SettingsManager'; +import { SettingsManager, ColorScheme } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; @@ -88,7 +88,7 @@ export class MainView extends React.Component { @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } + @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @@ -430,10 +430,10 @@ export class MainView extends React.Component { const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined; return <> {this.leftMenuPanel} -
    +
    {this.flyout}
    - +
    @@ -441,7 +441,7 @@ export class MainView extends React.Component { {this.dockingContent}
    - +
    {this.propertiesWidth() < 10 ? (null) : } @@ -459,7 +459,7 @@ export class MainView extends React.Component { this._dashUIHeight = r.getBoundingClientRect().height; })).observe(r); }} style={{ - color: this.darkScheme ? "rgb(205,205,205)" : "black", + color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black", height: `calc(100% - ${this.topOfDashUI}px)`, width: "100%", }} > @@ -601,7 +601,7 @@ export class MainView extends React.Component { } render() { - return (
    ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} ref={r => { r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3c88a4830..1eb7a222e 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -22,6 +22,7 @@ import "./StyleProvider.scss"; import React = require("react"); import Color = require('color'); import { lightOrDark } from '../../Utils'; +import { ColorScheme } from '../util/SettingsManager'; export enum StyleLayers { Background = "background" @@ -49,7 +50,7 @@ export enum StyleProp { FontSize = "fontSize", // size of text font } -function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } +function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; } function toggleBackground(doc: Doc) { UndoManager.RunInBatch(() => runInAction(() => { @@ -143,7 +144,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground))); + Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)))); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 6a22acae8..3ea190a98 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -353,7 +353,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action setFocused = (doc: Doc) => this._focusedTable = doc; @action setPreviewDoc = (doc: Opt) => { - SelectionManager.SelectSchemaView(this, doc); + SelectionManager.SelectSchemaViewDoc(doc); this._previewDoc = doc; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 97de097e0..a3da0e0e4 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -481,13 +481,16 @@ export class TreeView extends React.Component { } @computed get validExpandViewTypes() { - if (this.doc.viewType === CollectionViewType.Docking) return [this.fieldKey]; + if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) { + return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"]; + } const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; const links = () => DocListCast(this.doc.links).length ? "links" : ""; - const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : ""; + const data = () => this.childDocs ? this.fieldKey : ""; const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - return [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); + const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; + return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); } @action expandNextviewType = () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 37444a9dc..b3c57d33a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -7,6 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../../fields/Types"; import { aggregateBounds } from "../../../../Utils"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import React = require("react"); +import { ColorScheme } from "../../../util/SettingsManager"; export interface ViewDefBounds { type: string; @@ -361,7 +362,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 94cf1c5a6..0b12f6c21 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -51,6 +51,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import Color = require("color"); +import { ColorScheme } from "../../../util/SettingsManager"; export const panZoomSchema = createSchema({ _panX: "number", @@ -1419,6 +1420,7 @@ export class CollectionFreeFormView extends CollectionSubView { const ctx = el?.getContext('2d'); @@ -1429,7 +1431,7 @@ export class CollectionFreeFormView extends CollectionSubView 50 ? [3, 3] : [1, 5]); ctx.clearRect(0, 0, w, h); if (ctx) { - ctx.strokeStyle = "rgba(0, 0, 0, 0.5)"; + ctx.strokeStyle = strokeStyle; ctx.beginPath(); for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) { ctx.moveTo(x, Cy - h); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index dfe99ffc8..12493ecc1 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -337,8 +337,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {this.renderTypes(this._col)} {this.renderColors(this._col)}
    - +
    ; } @@ -353,7 +354,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action setFocused = (doc: Doc) => this._focusedTable = doc; @action setPreviewDoc = (doc: Opt) => { - SelectionManager.SelectSchemaView(this, doc); + SelectionManager.SelectSchemaViewDoc(doc); this._previewDoc = doc; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3e15ed661..5d0b91b91 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -25,7 +25,6 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { LinkManager } from '../../util/LinkManager'; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from "../../util/SelectionManager"; -import { ColorScheme } from "../../util/SettingsManager"; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from "../../util/Transform"; @@ -50,6 +49,7 @@ import { ScriptingBox } from "./ScriptingBox"; import { PresBox } from './trails/PresBox'; import React = require("react"); import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { ColorScheme } from "../../util/SettingsManager"; const { Howl } = require('howler'); interface Window { @@ -1061,9 +1061,7 @@ export class DocumentViewInternal extends DocComponent(Fon render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
    diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index e519de1c5..149836e93 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -12,6 +12,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); import * as ReactDOM from 'react-dom'; import { observer } from "mobx-react"; +import { ColorScheme } from "../../../util/SettingsManager"; export class DashDocView { _fieldWrapper: HTMLSpanElement; // container for label and value @@ -20,7 +21,7 @@ export class DashDocView { this._fieldWrapper = document.createElement("span"); this._fieldWrapper.style.position = "relative"; this._fieldWrapper.style.textIndent = "0"; - this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); + this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "lightGray")); this._fieldWrapper.style.width = node.attrs.width; this._fieldWrapper.style.height = node.attrs.height; this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block"; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 404e828ea..652804126 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -17,7 +17,7 @@ import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents'; import { DocumentType } from "../client/documents/DocumentTypes"; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; import { Scripting } from '../client/util/Scripting'; -import { SettingsManager } from '../client/util/SettingsManager'; +import { SettingsManager, ColorScheme } from '../client/util/SettingsManager'; import { Transform } from '../client/util/Transform'; import { UndoManager } from "../client/util/UndoManager"; import { TabDocView } from '../client/views/collections/TabDocView'; @@ -403,7 +403,7 @@ export class MobileInterface extends React.Component { const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); - const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`); dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, cloneDashboard!]); -- cgit v1.2.3-70-g09d2 From 12ae56962397a786397158af9e442a955883d16f Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Sep 2021 03:30:58 -0400 Subject: fixed runtime bug inside toggleBold(). removed print statements. --- src/client/documents/Documents.ts | 3 +- src/client/util/CurrentUserUtils.ts | 3 +- src/client/views/MainView.tsx | 1 - .../collectionLinear/CollectionLinearView.tsx | 1 - src/client/views/nodes/button/FontIconBox.tsx | 126 +++++--------- .../views/nodes/formattedText/FormattedTextBox.tsx | 11 +- .../views/nodes/formattedText/RichTextMenu.tsx | 186 +++++++++++---------- src/client/views/nodes/formattedText/marks_rts.ts | 6 +- 8 files changed, 153 insertions(+), 184 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 206f65bfd..8ac647b99 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -141,8 +141,8 @@ export class DocumentOptions { _columnWidth?: number; _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden _fontSize?: string; - _fontWeight?: number; _fontFamily?: string; + _fontWeight?: string; _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views _curPage?: number; // current page of a PDF or other? paginated document _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds @@ -1188,7 +1188,6 @@ export namespace DocUtils { description: ":" + StrCast(note.title), event: undoBatch((args: { x: number, y: number }) => { const textDoc = Docs.Create.TextDocument("", { - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), _width: 200, x, y, _autoHeight: note._autoHeight !== false, title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1) }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5683a8c21..297d4b241 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1535,8 +1535,7 @@ export class CurrentUserUtils { public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { const tbox = Docs.Create.TextDocument("", { _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor, - _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), - _fontFamily: StrCast(Doc.UserDoc().fontFamily), title + _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, title }); const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c99ba447c..fbd3fece2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -471,7 +471,6 @@ export class MainView extends React.Component { this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250); this._sidebarContent.proto = button.target as any; this.LastButton = button; - console.log(button.title); }); closeFlyout = action(() => { diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 7fe95fef0..18a715edf 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -109,7 +109,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } myContextMenu = (e: React.MouseEvent) => { - console.log("STOPPING"); e.stopPropagation(); e.preventDefault(); } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 520034c3c..6a782b105 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -113,7 +113,7 @@ export class FontIconBox extends DocComponent(Fon // Script for checking the outcome of the toggle const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)"; - const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result; + const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0; if (numBtnType === NumButtonType.Slider) { @@ -158,7 +158,7 @@ export class FontIconBox extends DocComponent(Fon
    -
    setValue(checkResult - 1))}> +
    setValue(Number(checkResult) - 1))}>
    (Fon onChange={action((e) => setValue(Number(e.target.value)))} />
    -
    setValue(checkResult + 1))}> +
    setValue(Number(checkResult) + 1))}>
    @@ -261,8 +261,8 @@ export class FontIconBox extends DocComponent(Fon } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; } else if (script === 'setFont') { - const selected = SelectionManager.Docs().lastElement(); - text = StrCast((selected?.type === DocumentType.RTF ? selected : Doc.UserDoc())._fontFamily); + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; } @@ -337,7 +337,7 @@ export class FontIconBox extends DocComponent(Fon const colorBox = (func: (color: ColorState) => void) => ; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
    @@ -360,7 +360,7 @@ export class FontIconBox extends DocComponent(Fon onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} onPointerDown={e => e.stopPropagation()}> -
    +
    {label} {/* {dropdownCaret} */} {this.rootDoc.dropDownOpen ? @@ -387,7 +387,7 @@ export class FontIconBox extends DocComponent(Fon // Button label const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
    +
    {this.label}
    ; @@ -406,7 +406,7 @@ export class FontIconBox extends DocComponent(Fon } else { return (
    + style={{ opacity: 1, backgroundColor, color }}> {label}
    @@ -571,11 +571,7 @@ Scripting.addGlobal(function setView(view: string) { Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); if (checkResult) { - if (selected) { - return selected._backgroundColor; - } else { - return "#FFFFFF"; - } + return selected?._backgroundColor ?? "transparent"; } if (selected) selected._backgroundColor = color; Doc.UserDoc()._fontColor = color; @@ -596,7 +592,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult && selected) { if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE; - else return "transparent"; + return "transparent"; } selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); }); @@ -617,16 +613,18 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) { SelectionManager.Docs().map(doc => doc._fontFamily = font); const editorView = RichTextMenu.Instance.TextView?.EditorView; - editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView); - Doc.UserDoc()._fontFamily = font; - return Doc.UserDoc()._fontFamily; + if (checkResult) { + return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + } + if (editorView) RichTextMenu.Instance.setFontFamily(font); + else Doc.UserDoc().fontFamily = font; }); Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") { const editorView = RichTextMenu.Instance.TextView?.EditorView; const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); switch (info) { - case "family": return style?.activeColors[0]; + case "family": return style?.activeFamilies[0]; case "size": return style?.activeSizes[0]; case "color": return style?.activeColors[0]; case "highlight": return style?.activeHighlights[0]; @@ -643,7 +641,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch active = StrCast(Doc.UserDoc().textAlign); } if (active === align) return Colors.MEDIUM_BLUE; - else return "transparent"; + return "transparent"; } SelectionManager.Docs().map(doc => doc.textAlign = align); switch (align) { @@ -667,38 +665,25 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check if (checkResult) { const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); if (active === mapStyle) return Colors.MEDIUM_BLUE; - else return "transparent"; + return "transparent"; } if (editorView) { const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); - if (active === mapStyle) { - editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: "" })); - } else { - editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: mapStyle })); - } + editorView?.state && RichTextMenu.Instance.changeListType( + editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle })); } }); // toggle: Set overlay status of selected document Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { - if (selected) { - return selected._fontColor; - } else { - return Doc.UserDoc()._fontColor; - } + return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor; } - if (selected) { - selected._fontColor = color; - if (color) { - editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); - } - } - Doc.UserDoc()._fontColor = color; + if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); + else Doc.UserDoc()._fontColor = color; }); // toggle: Set overlay status of selected document @@ -707,11 +692,7 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { - if (selected) { - return selected._fontHighlight; - } else { - return Doc.UserDoc()._fontHighlight; - } + return (selected ?? Doc.UserDoc())._fontHighlight; } if (selected) { selected._fontColor = color; @@ -722,62 +703,43 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool Doc.UserDoc()._fontHighlight = color; }); - - // toggle: Set overlay status of selected document -Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) { +Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) { + if (typeof size === "number") size = size.toString(); + if (size && Number(size).toString() === size) size += "px"; + const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { - const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10); - return size; + return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", ""); } - const editorView = RichTextMenu.Instance.TextView?.EditorView; - editorView?.state && RichTextMenu.Instance.setFontSize(Number(size), editorView); - Doc.UserDoc()._fontSize = size + "px"; + if (editorView) RichTextMenu.Instance.setFontSize(size); + else Doc.UserDoc()._fontSize = size; }); Scripting.addGlobal(function toggleBold(checkResult?: boolean) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (editorView) { - const bold: boolean = editorView?.state && RichTextMenu.Instance.getBoldStatus(); - if (bold) return Colors.MEDIUM_BLUE; - else return "transparent"; - } - else return "transparent"; + return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent"; } - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (editorView) { - editorView?.state && RichTextMenu.Instance.toggleBold(editorView, true); - } - SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.bold = !doc.bold); - Doc.UserDoc().bold = !Doc.UserDoc().bold; - return Doc.UserDoc().bold; + if (editorView) RichTextMenu.Instance?.toggleBold(); + else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold"; }); Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return "transparent"; - } - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (editorView) { - editorView?.state && RichTextMenu.Instance.toggleUnderline(editorView, true); + return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent"; } - SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.underline = !doc.underline); - Doc.UserDoc().underline = !Doc.UserDoc().underline; - return Doc.UserDoc().underline; + if (editorView) RichTextMenu.Instance?.toggleUnderline(); + else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline"; }); Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return "transparent"; - } - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (editorView) { - editorView?.state && RichTextMenu.Instance.toggleItalic(editorView, true); + return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent"; } - SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.italic = !doc.italic); - Doc.UserDoc().italic = !Doc.UserDoc().italic; - return Doc.UserDoc().italic; + if (editorView) RichTextMenu.Instance?.toggleItalics(); + else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics"; }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 617fa0bee..16aa4de6c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1168,7 +1168,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp selectOnLoad && this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) { - this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; + this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), + schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }), + ...(Doc.UserDoc().fontColor !== "transparent" && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []), + ...(Doc.UserDoc().fontStyle === "italics" ? [schema.mark(schema.marks.em)] : []), + ...(Doc.UserDoc().textDecoration === "underline" ? [schema.mark(schema.marks.underline)] : []), + ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: StrCast(Doc.UserDoc().fontFamily) })] : []), + ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: StrCast(Doc.UserDoc().fontSize, "") })] : []), + ...(Doc.UserDoc().fontWeight === "bold" ? [schema.mark(schema.marks.strong)] : [])]; } } @@ -1596,7 +1603,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), - fontWeight: Cast(this.layoutDoc._fontWeight, "number", null), + fontWeight: Cast(this.layoutDoc._fontWeight, "string", null) as any, fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"), pointerEvents: interactive ? undefined : "none", }} diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 9904a7939..23a3d80a4 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,8 +1,7 @@ import React = require("react"); -import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; -import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, IReactionDisposer, observable, reaction, runInAction, computed } from "mobx"; import { observer } from "mobx-react"; import { lift, wrapIn } from "prosemirror-commands"; import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model"; @@ -10,10 +9,7 @@ import { wrapInList } from "prosemirror-schema-list"; import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc } from "../../../../fields/Doc"; -import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField'; import { Cast, StrCast } from "../../../../fields/Types"; -import { TraceMobx } from "../../../../fields/util"; -import { unimplementedFunction, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { LinkManager } from "../../../util/LinkManager"; import { SelectionManager } from "../../../util/SelectionManager"; @@ -29,7 +25,7 @@ const { toggleMark } = require("prosemirror-commands"); @observer export class RichTextMenu extends AntimodeMenu { - static Instance: RichTextMenu; + @observable static Instance: RichTextMenu; public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable private _linkToRef = React.createRef(); @@ -39,22 +35,22 @@ export class RichTextMenu extends AntimodeMenu { public _brushMap: Map> = new Map(); @observable private collapsed: boolean = false; - @observable private boldActive: boolean = false; - @observable private italicsActive: boolean = false; - @observable private underlineActive: boolean = false; - @observable private strikethroughActive: boolean = false; - @observable private subscriptActive: boolean = false; - @observable private superscriptActive: boolean = false; - - @observable private activeFontSize: string = ""; - @observable private activeFontFamily: string = ""; + @observable private _boldActive: boolean = false; + @observable private _italicsActive: boolean = false; + @observable private _underlineActive: boolean = false; + @observable private _strikethroughActive: boolean = false; + @observable private _subscriptActive: boolean = false; + @observable private _superscriptActive: boolean = false; + + @observable private _activeFontSize: string = "13px"; + @observable private _activeFontFamily: string = ""; @observable private activeListType: string = ""; @observable private activeAlignment: string = "left"; @observable private brushMarks: Set = new Set(); @observable private showBrushDropdown: boolean = false; - @observable private activeFontColor: string = "black"; + @observable private _activeFontColor: string = "black"; @observable private showColorDropdown: boolean = false; @observable private activeHighlightColor: string = "transparent"; @@ -67,10 +63,12 @@ export class RichTextMenu extends AntimodeMenu { _delayHide = false; constructor(props: Readonly<{}>) { super(props); - RichTextMenu.Instance = this; - this._canFade = false; - //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); - runInAction(() => this.Pinned = true); + runInAction(() => { + RichTextMenu.Instance = this; + this._canFade = false; + //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + this.Pinned = true; + }); } componentDidMount() { @@ -81,6 +79,14 @@ export class RichTextMenu extends AntimodeMenu { this._reaction?.(); } + @computed get bold() { return this._boldActive; } + @computed get underline() { return this._underlineActive; } + @computed get italics() { return this._italicsActive; } + @computed get strikeThrough() { return this._strikethroughActive; } + @computed get fontColor() { return this._activeFontColor; } + @computed get fontFamily() { return this._activeFontFamily; } + @computed get fontSize() { return this._activeFontSize; } + public delayHide = () => this._delayHide = true; @action @@ -110,10 +116,10 @@ export class RichTextMenu extends AntimodeMenu { this.activeListType = this.getActiveListStyle(); this.activeAlignment = this.getActiveAlignment(); - this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various"; - this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "..."; - this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "..."; - this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "..."; + this._activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various"; + this._activeFontSize = !activeSizes.length ? "13px" : activeSizes[0]; + this._activeFontColor = !activeColors.length ? "black" : activeColors.length > 0 ? String(activeColors[0]) : "..."; + this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length > 0 ? String(activeHighlights[0]) : "..."; // update link in current selection this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle)); @@ -125,7 +131,7 @@ export class RichTextMenu extends AntimodeMenu { if (node?.type === schema.nodes.ordered_list) { let attrs = node.attrs; if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family }; - if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: `${mark.attrs.fontSize}px` }; + if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: mark.attrs.fontSize }; if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color }; const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema); dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from)))); @@ -142,17 +148,6 @@ export class RichTextMenu extends AntimodeMenu { } } - getBoldStatus() { - if (this.view && this.TextView.props.isSelected(true)) { - const path = (this.view.state.selection.$from as any).path; - for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { - if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) { - return path[i].attrs.strong; - } - } - } - } - // finds font sizes and families in selection getActiveAlignment() { if (this.view && this.TextView.props.isSelected(true)) { @@ -193,25 +188,23 @@ export class RichTextMenu extends AntimodeMenu { if (this.TextView.props.isSelected(true)) { const state = this.view.state; const pos = this.view.state.selection.$from; - const ref_node = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - const marks = Array.from(ref_node.marks); - marks.push(...(this.view.state.storedMarks as any)); - marks.forEach(m => { - m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); - m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); - m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px"); - m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); + const marks: Mark[] = []; + if (state.selection.empty) { + const ref_node = this.reference_node(pos); + marks.push(...[...(ref_node !== this.view.state.doc && ref_node?.isText ? + [...(state.storedMarks ?? []), ...Array.from(ref_node.marks)] : [])]); + } else { + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + node.marks?.filter(mark => !mark.isInSet(marks)).map(mark => marks.push(mark)); }); } - !activeFamilies.length && (activeFamilies.push(StrCast(this.TextView.layoutDoc._fontFamily, StrCast(Doc.UserDoc().fontFamily)))); - !activeSizes.length && (activeSizes.push(StrCast(this.TextView.layoutDoc._fontSize, StrCast(Doc.UserDoc().fontSize)))); - !activeColors.length && (activeColors.push(StrCast(this.TextView.layoutDoc.color, StrCast(Doc.UserDoc().fontColor)))); + marks.forEach(m => { + m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); + m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); + m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize); + m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); + }); } - !activeFamilies.length && (activeFamilies.push(StrCast(Doc.UserDoc().fontFamily))); - !activeSizes.length && (activeSizes.push(StrCast(Doc.UserDoc().fontSize))); - !activeColors.length && (activeColors.push(StrCast(Doc.UserDoc().fontColor, "black"))); - !activeHighlights.length && (activeHighlights.push(StrCast(Doc.UserDoc().fontHighlight, ""))); return { activeFamilies, activeSizes, activeColors, activeHighlights }; } @@ -251,11 +244,12 @@ export class RichTextMenu extends AntimodeMenu { return []; } activeMarks = markGroup.filter(mark_type => { - if (mark_type === state.schema.marks.pFontSize) { - return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); - } + // if (mark_type === state.schema.marks.pFontSize) { + // return mark.isINSet + // ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); + // } const mark = state.schema.mark(mark_type); - return ref_node.marks.includes(mark); + return mark.isInSet(ref_node.marks); }); } } @@ -270,56 +264,66 @@ export class RichTextMenu extends AntimodeMenu { setActiveMarkButtons(activeMarks: MarkType[] | undefined) { if (!activeMarks) return; - this.boldActive = false; - this.italicsActive = false; - this.underlineActive = false; - this.strikethroughActive = false; - this.subscriptActive = false; - this.superscriptActive = false; + this._boldActive = false; + this._italicsActive = false; + this._underlineActive = false; + this._strikethroughActive = false; + this._subscriptActive = false; + this._superscriptActive = false; activeMarks.forEach(mark => { switch (mark.name) { - case "strong": this.boldActive = true; break; - case "em": this.italicsActive = true; break; - case "underline": this.underlineActive = true; break; - case "strikethrough": this.strikethroughActive = true; break; - case "subscript": this.subscriptActive = true; break; - case "superscript": this.superscriptActive = true; break; + case "strong": this._boldActive = true; break; + case "em": this._italicsActive = true; break; + case "underline": this._underlineActive = true; break; + case "strikethrough": this._strikethroughActive = true; break; + case "subscript": this._subscriptActive = true; break; + case "superscript": this._superscriptActive = true; break; } }); } - toggleBold = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.strong, { strong: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + toggleBold = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); + } } - toggleUnderline = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.underline, { underline: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + toggleUnderline = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.underline); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); + } } - toggleItalic = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.em, { em: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + toggleItalics = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.em); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); + } } - setFontSize = (size: number, view: EditorView) => { - const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); - view.focus(); - this.updateMenu(view, undefined, this.props); + setFontSize = (fontSize: string) => { + if (this.view) { + const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize }); + this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); + this.view.focus(); + this.updateMenu(this.view, undefined, this.props); + } } - setFontFamily = (family: string, view: EditorView) => { - const fmark = view.state.schema.marks.pFontFamily.create({ family: family }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); - view.focus(); - this.updateMenu(view, undefined, this.props); + setFontFamily = (family: string) => { + if (this.view) { + const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family }); + this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); + this.view.focus(); + this.updateMenu(this.view, undefined, this.props); + } } setHighlight(color: String, view: EditorView, dispatch: any) { @@ -330,7 +334,7 @@ export class RichTextMenu extends AntimodeMenu { } setColor(color: String, view: EditorView, dispatch: any) { - const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color }); + const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color }); if (view.state.selection.empty) { dispatch(view.state.tr.addStoredMark(colorMark)); return false; diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 655ee7e44..6103a28d6 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -61,13 +61,13 @@ export const marks: { [index: string]: MarkSpec } = { /** FONT SIZES */ pFontSize: { - attrs: { fontSize: { default: 10 } }, + attrs: { fontSize: { default: "10px" } }, parseDOM: [{ tag: "span", getAttrs(dom: any) { - return { fontSize: dom.style.fontSize ? Number(dom.style.fontSize.replace("px", "")) : "" }; + return { fontSize: dom.style.fontSize ? dom.style.fontSize.toString() : "" }; } }], - toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize}px;` }] : ['span', 0] + toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize};` }] : ['span', 0] }, /* FONTS */ -- cgit v1.2.3-70-g09d2 From 0079b81fdd56e8aac4ded4c82ecb013fe06057a2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Sep 2021 12:47:27 -0400 Subject: fixed default color of link lines in dark mode. --- .../collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 2cb487588..f6c2707da 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -3,9 +3,11 @@ import { observer } from "mobx-react"; import { Doc, Field } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { List } from "../../../../fields/List"; -import { NumCast, StrCast } from "../../../../fields/Types"; +import { NumCast } from "../../../../fields/Types"; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { LinkManager } from "../../../util/LinkManager"; +import { ColorScheme } from "../../../util/SettingsManager"; import { SnappingManager } from "../../../util/SnappingManager"; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinkView.scss"; @@ -184,7 +186,7 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; //access stroke color using index of the relationship in the color list (default black) - const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "white" : "black") : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> -- cgit v1.2.3-70-g09d2 From d5f9533d153e11e24d2ab7c03b4561170f0768fe Mon Sep 17 00:00:00 2001 From: Geireann <60007097+geireann@users.noreply.github.com> Date: Thu, 23 Sep 2021 11:18:30 -0400 Subject: small schema UI tweak --- src/client/util/CurrentUserUtils.ts | 2 -- .../collections/collectionSchema/CollectionSchemaView.scss | 3 ++- src/client/views/nodes/button/FontIconBox.tsx | 13 +++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 297d4b241..4ec83f2d7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1019,8 +1019,6 @@ export class CurrentUserUtils { title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, - switchToggle: true, - width: 100, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview()', diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 3074ce66e..9ebe14d6c 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -444,11 +444,12 @@ button.add-column { border: none; background-color: $white; width: 100%; - height: 100%; + height: fit-content; min-height: 26px; } } &.focused { + overflow: hidden; &.inactive { border: none; } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 511df8786..af4a581f1 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -861,4 +861,17 @@ Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) { selected.schemaPreviewWidth = 0; } } +}); + +/** STACK + * groupBy + */ +Scripting.addGlobal(function setGroupBy(key: string, checkResult?: boolean) { + SelectionManager.Docs().map(doc => doc._fontFamily = key); + const editorView = RichTextMenu.Instance.TextView?.EditorView; + if (checkResult) { + return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + } + if (editorView) RichTextMenu.Instance.setFontFamily(key); + else Doc.UserDoc().fontFamily = key; }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 780346cb03a2dcc10c1edcf4ecc4f57a091d36bc Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Sep 2021 15:26:30 -0400 Subject: fixed one crash when color string has capital letters. --- src/client/util/CurrentUserUtils.ts | 10 +++++----- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 2 +- src/client/views/collections/collectionSchema/SchemaTable.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 7 +++---- src/client/views/nodes/LabelBox.scss | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/formattedText/DashDocView.tsx | 2 +- src/client/views/nodes/formattedText/DashFieldView.scss | 2 +- 9 files changed, 15 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 4ec83f2d7..3c32c2359 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -226,9 +226,9 @@ export class CurrentUserUtils { const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); descriptionWrapper._columnHeaders = new List([ - new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false), - new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true), - new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true), + new SchemaHeaderField("[A Short Description]", "dimgray", undefined, undefined, undefined, false), + new SchemaHeaderField("[Long Description]", "dimgray", undefined, undefined, undefined, true), + new SchemaHeaderField("[Details]", "dimgray", undefined, undefined, undefined, true), ]); const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true }); detailView.isTemplateDoc = makeTemplate(detailView); @@ -349,7 +349,7 @@ export class CurrentUserUtils { static setupDefaultIconTemplates(doc: Doc) { if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.LabelDocument({ - title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", + title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimgray", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true }); // Docs.Create.TextDocument("", { @@ -1238,7 +1238,7 @@ export class CurrentUserUtils { static setupSearchSidebar(doc: Doc) { if (doc.mySearchPanel === undefined) { doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({ - backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, + backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true, childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true })) as any as Doc; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index b3c57d33a..9fed82dae 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -362,7 +362,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx index 3833f968b..d157832d9 100644 --- a/src/client/views/collections/collectionSchema/SchemaTable.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx @@ -567,7 +567,7 @@ export class SchemaTable extends React.Component {
    { this._timeout = undefined; clickFunc(); }, 350); } else clickFunc(); - } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right"); } else if (this.allLinks && this.Document.type !== DocumentType.LINK && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { this.allLinks.length && LinkManager.FollowLink(undefined, this.props.Document, this.props, e.altKey); } else { @@ -582,8 +580,9 @@ export class DocumentViewInternal extends DocComponent Doc.UnBrushDoc(this.rootDoc)}> { })} /> {!this._forceOpen ? (null) :
    this._isOpen = this._editing = this._forceOpen = false)}> - +
    }
    ); diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 149836e93..364be461f 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -21,7 +21,7 @@ export class DashDocView { this._fieldWrapper = document.createElement("span"); this._fieldWrapper.style.position = "relative"; this._fieldWrapper.style.textIndent = "0"; - this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "lightGray")); + this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "lightGray")); this._fieldWrapper.style.width = node.attrs.width; this._fieldWrapper.style.height = node.attrs.height; this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block"; diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss index e7dd286a5..c36e6804b 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.scss +++ b/src/client/views/nodes/formattedText/DashFieldView.scss @@ -8,7 +8,7 @@ height: 10px; position: relative; display: inline-block; - background: dimGray; + background: dimgray; } .dashFieldView-fieldCheck { min-width: 12px; -- cgit v1.2.3-70-g09d2 From 4e6a1d7a37c8c28014a9f7cd0d92f17c8f29454d Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 23 Sep 2021 19:30:52 +0000 Subject: Revert "Merge pull request #34 from brown-dash/linking-anh" This reverts commit 70d80e30de9963c353636d9780ffb83f3285aac7, reversing changes made to d5f9533d153e11e24d2ab7c03b4561170f0768fe. --- src/client/documents/Documents.ts | 1 - src/client/util/LinkManager.ts | 2 -- .../CollectionFreeFormLinkView.tsx | 15 +++------------ src/client/views/linking/LinkEditor.tsx | 20 ++------------------ src/client/views/linking/LinkMenuItem.tsx | 4 ++-- src/fields/Doc.ts | 1 - 6 files changed, 7 insertions(+), 36 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6fca0b67e..8ac647b99 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -301,7 +301,6 @@ export class DocumentOptions { border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered linkRelationshipList?: List; // for storing different link relationships (when set by user in the link editor) - linkRelationshipSizes?: List; //stores number of links contained in each relationship linkColorList?: List; // colors of links corresponding to specific link relationships } export namespace Docs { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 4c24fbafb..64da68f59 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -103,10 +103,8 @@ export class LinkManager { if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { const linkRelationshipList = new List(); const linkColorList = new List(); - const linkRelationshipSizes = new List(); Doc.UserDoc().linkRelationshipList = linkRelationshipList; Doc.UserDoc().linkColorList = linkColorList; - Doc.UserDoc().linkRelationshipSizes = linkRelationshipSizes; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 88cd0feb3..f6c2707da 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -42,7 +42,7 @@ export class CollectionFreeFormLinkView extends React.Component this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() + setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); @@ -180,24 +180,15 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; - const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List; - const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); - //access stroke color using index of the relationship in the color list (default black) - const strokeColor = currRelationshipIndex == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; - - //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has) - //thickness varies linearly from 3px to 12px for increasing link count - const strokeWidth: string = currRelationshipIndex == -1 ? "3px" : Math.floor(2 + 10 * (linkRelationshipSizes[currRelationshipIndex] / Math.max(...linkRelationshipSizes))) + "px"; - + const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "white" : "black") : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> - {textX === undefined ? (null) : {Field.toString(this.props.LinkDocs[0].description as any as Field)} diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 58c57a23b..219f7d3a2 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, NumListCast, StrListCast, Field } from "../../../fields/Doc"; +import { Doc, StrListCast, Field } from "../../../fields/Doc"; import { DateCast, StrCast, Cast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; @@ -42,28 +42,14 @@ export class LinkEditor extends React.Component { @undoBatch setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { - const prevRelationship = LinkManager.currentLink.linkRelationship as string; - LinkManager.currentLink.linkRelationship = value; Doc.GetProto(LinkManager.currentLink).linkRelationship = value; const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); - const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); - // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color if (linkRelationshipList && !linkRelationshipList.includes(value)) { linkRelationshipList.push(value); - linkRelationshipSizes.push(1); const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; - linkColorList.push(randColor) - // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes - } else if (linkRelationshipList && value != prevRelationship) { - //increment size of new relationship size - linkRelationshipSizes[linkRelationshipList.indexOf(value)] = linkRelationshipSizes[linkRelationshipList.indexOf(value)] + 1; - //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation) - if (linkRelationshipList.includes(prevRelationship)) { - linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] = linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] - 1; - } - + linkColorList.push(randColor); } this.relationshipButtonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.relationshipButtonColor = ""), 750); @@ -155,7 +141,6 @@ export class LinkEditor extends React.Component { style={{ width: "100%" }} id="input" value={this.relationship} - autoComplete={"off"} placeholder={"Enter link relationship"} onKeyDown={this.onRelationshipKey} onChange={this.handleRelationshipChange} @@ -184,7 +169,6 @@ export class LinkEditor extends React.Component {
    {
    {this.props.linkDoc.hidden ? "Show Anchor" : "Hide Anchor"}
    }>
    e.stopPropagation()}> -
    +
    {!this.props.linkDoc.linkDisplay ? "Show link" : "Hide link"}
    }>
    e.stopPropagation()}> -
    +
    {!this.props.linkDoc.linkAutoMove ? "Auto move dot" : "Freeze dot position"}
    }> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5133da603..57bd0f46f 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -80,7 +80,6 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { export async function DocCastAsync(field: FieldResult): Promise> { return Cast(field, Doc); } -export function NumListCast(field: FieldResult) { return Cast(field, listSpec("number"), []); } export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []); } export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; } -- cgit v1.2.3-70-g09d2 From 643df77e6366b7164307ffe195ed9de83b68e5ae Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Sep 2021 01:21:38 -0400 Subject: wrapped Color() calls in DashColor() to catch exceptions for color strings no found --- src/Utils.ts | 11 ++++++++++- src/client/views/StyleProvider.tsx | 5 ++--- src/client/views/collections/TabDocView.tsx | 3 +-- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 - src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 5 ++--- src/client/views/nodes/trails/PresBox.tsx | 3 +-- src/fields/Doc.ts | 9 ++++----- src/fields/RichTextUtils.ts | 4 ++-- 8 files changed, 22 insertions(+), 19 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index db33875ea..7ec4f69f3 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -573,10 +573,19 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe } } +export function DashColor(color: string) { + try { + return Color(color.toLowerCase()); + } catch (e) { + console.log("COLOR error:", e); + return Color("red"); + } +} + export function lightOrDark(color: any) { const nonAlphaColor = color.startsWith("#") ? (color as string).substring(0, 7) : color.startsWith("rgba") ? color.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : color; - const col = Color(nonAlphaColor.toLowerCase()).rgb(); + const col = DashColor(nonAlphaColor).rgb(); const colsum = (col.red() + col.green() + col.blue()); if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY; else return Colors.WHITE; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 507c3d2b2..cd6e11bda 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -7,7 +7,7 @@ import { Doc, Opt, StrListCast } from "../../fields/Doc"; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; -import { lightOrDark } from '../../Utils'; +import { lightOrDark, DashColor } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { ColorScheme } from '../util/SettingsManager'; @@ -21,7 +21,6 @@ import { FieldViewProps } from './nodes/FieldView'; import { SliderBox } from './nodes/SliderBox'; import "./StyleProvider.scss"; import React = require("react"); -import Color = require('color'); export enum StyleLayers { Background = "background" @@ -148,7 +147,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; @@ -165,7 +164,7 @@ export class CollectionFreeFormDocumentView extends DocComponent color !== "" && (Color(color).alpha() !== 1); + const isTransparent = (color: string) => color !== "" && (DashColor(color).alpha() !== 1); return isTransparent(StrCast(doc[key])); } if (typeof value === "string") { diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index 0b5f14d74..a19be5df9 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -2,7 +2,7 @@ import { AssertionError } from "assert"; import { docs_v1 } from "googleapis"; import { Fragment, Mark, Node } from "prosemirror-model"; import { sinkListItem } from "prosemirror-schema-list"; -import { Utils } from "../Utils"; +import { Utils, DashColor } from "../Utils"; import { Docs, DocUtils } from "../client/documents/Documents"; import { schema } from "../client/views/nodes/formattedText/schema_rts"; import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils"; @@ -482,7 +482,7 @@ export namespace RichTextUtils { } const fromHex = (color: string): docs_v1.Schema$OptionalColor => { - const c = Color(color); + const c = DashColor(color); return fromRgb.convert(c.red(), c.green(), c.blue()); }; -- cgit v1.2.3-70-g09d2 From e7e67ff7e3170b6eb5bf05ffe284ca08f4802459 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 12:30:35 -0400 Subject: added a hacky fix to what seems to be a Chrome bug when auto expanding the left flyout panel --- src/client/views/MainView.tsx | 6 ++++++ src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 35c5801e5..7edcd6217 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -467,7 +467,13 @@ export class MainView extends React.Component { } expandFlyout = action((button: Doc) => { + // bcz: What's going on here!? + // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div + // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be. + // interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);) this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250); + setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0); + this._sidebarContent.proto = button.target as any; this.LastButton = button; }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 81f6307d1..24a7d77e0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -646,8 +646,7 @@ export class MarqueeView extends React.Component e.preventDefault()} -- cgit v1.2.3-70-g09d2 From eed7f32cda030d9ef2d8aaae168c11832460a653 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 16:09:21 -0400 Subject: fix for some youtube urls that didn't upload. --- src/client/views/collections/CollectionSubView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index cb8b55cb2..e662f3ddf 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -356,7 +356,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const batch = UndoManager.StartBatch("youtube upload"); const generatedDocuments: Doc[] = []; - this.slowLoadDocuments((uriList || text).split("v=")[1], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); + this.slowLoadDocuments((uriList || text).split("v=")[1].split("&")[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); return; } -- cgit v1.2.3-70-g09d2 From f70aa7ba42153b2c283b7b9af04a5e75ea19a7ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Sep 2021 09:49:26 -0400 Subject: made docs with no author Admin for everyone. fixed run-time warnings: not memoized accesses when panning, NaNs drawing some labelBoxes, filter box filter boolean options, --- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +++++++++++++------- src/client/views/nodes/DocumentView.tsx | 6 +++--- src/client/views/nodes/FilterBox.tsx | 6 +++--- src/fields/util.ts | 3 ++- 4 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5b9f6139f..b2db1168d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { DateField } from "../../../../fields/DateField"; @@ -811,14 +811,14 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout instanceof Doc).map(pair => pair.layout); - const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc, "") && this.childSizeProvider(doc, "")). - map(doc => ({ ...this.childDataProvider(doc, ""), ...this.childSizeProvider(doc, "") })); + const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc); + const measuredDocs = docs.map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ""), size: this.childSizeProviderUnmemoized(doc, "") })) + .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! })); if (measuredDocs.length) { - const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) => // computes range of content + const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content ({ - xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) }, - yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) } + xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, + yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } }) , { xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, @@ -1105,10 +1105,16 @@ export class CollectionFreeFormView extends CollectionSubView { + return this._layoutPoolData.get(doc[Id] + (replica || "")); + } childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) { return this._layoutPoolData.get(doc[Id] + (replica || "")); }.bind(this)); + childSizeProviderUnmemoized = (doc: Doc, replica: string) => { + return this._layoutSizeData.get(doc[Id] + (replica || "")); + } childSizeProvider = computedFn(function childSizeProvider(this: any, doc: Doc, replica: string) { return this._layoutSizeData.get(doc[Id] + (replica || "")); }.bind(this)); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6a7cd108a..1187521b3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1147,10 +1147,10 @@ export class DocumentView extends React.Component { @computed get nativeScaling() { if (this.shouldNotScale) return 1; const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0; - if (this.fitWidth || this.props.PanelHeight() / this.effectiveNativeHeight > this.props.PanelWidth() / this.effectiveNativeWidth) { - return Math.max(minTextScale, this.props.PanelWidth() / this.effectiveNativeWidth); // width-limited or fitWidth + if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) { + return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth } - return Math.max(minTextScale, this.props.PanelHeight() / this.effectiveNativeHeight); // height-limited or unscaled + return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled } @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index e9f19bf9e..2041c7399 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -377,9 +377,9 @@ export class FilterBox extends ViewBoxBaseComponent
    - + +
    filters together
    diff --git a/src/fields/util.ts b/src/fields/util.ts index 439c4d333..3590c2dea 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -191,7 +191,8 @@ let HierarchyMapping: Map | undefined; function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[AclSym]; const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group - if (userChecked === (target.__fields?.author || target.author)) return AclAdmin; // target may be a Doc of Proxy, so check __fields.author and .author + const targetAuthor = (target.__fields?.author || target.author); // target may be a Doc of Proxy, so check __fields.author and .author + if (userChecked === targetAuthor || !targetAuthor) return AclAdmin; if (SnappingManager.GetCachedGroupByName("Admin")) return AclAdmin; if (targetAcls && Object.keys(targetAcls).length) { -- cgit v1.2.3-70-g09d2 From fc679d849ae8afa3ef66e4e0b2b2b816e1fb41d4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 4 Oct 2021 21:21:55 -0400 Subject: fixed how filters work in Schema View. made filtering the default for clicking in title area. --- .../views/collections/CollectionSchemaView.tsx | 575 --------------------- .../collectionSchema/CollectionSchemaHeaders.tsx | 72 ++- .../collectionSchema/CollectionSchemaView.tsx | 2 +- .../collections/collectionSchema/SchemaTable.tsx | 14 +- src/fields/Doc.ts | 2 +- 5 files changed, 40 insertions(+), 625 deletions(-) delete mode 100644 src/client/views/collections/CollectionSchemaView.tsx (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx deleted file mode 100644 index 3ea190a98..000000000 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ /dev/null @@ -1,575 +0,0 @@ -import React = require("react"); -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked } from "mobx"; -import { observer } from "mobx-react"; -import Measure from "react-measure"; -import { Resize } from "react-table"; -import "react-table/react-table.css"; -import { Doc, Opt } from "../../../fields/Doc"; -import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; -import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; -import { Cast, NumCast } from "../../../fields/Types"; -import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils"; -import { DocUtils } from "../../documents/Documents"; -import { SelectionManager } from "../../util/SelectionManager"; -import { SnappingManager } from "../../util/SnappingManager"; -import { Transform } from "../../util/Transform"; -import { undoBatch } from "../../util/UndoManager"; -import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/global/globalCssVariables.scss'; -import { SchemaTable } from "../collections/collectionSchema/SchemaTable"; -import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; -import '../DocumentDecorations.scss'; -import { DocumentView } from "../nodes/DocumentView"; -import { DefaultStyleProvider } from "../StyleProvider"; -import "./CollectionSchemaView.scss"; -import { CollectionSubView } from "./CollectionSubView"; -// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 - -export enum ColumnType { - Any, - Number, - String, - Boolean, - Doc, - Image, - List, - Date -} -// this map should be used for keys that should have a const type of value -const columnTypes: Map = new Map([ - ["title", ColumnType.String], - ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number], - ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], - ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] -]); - -@observer -export class CollectionSchemaView extends CollectionSubView(doc => doc) { - private _previewCont?: HTMLDivElement; - - @observable _previewDoc: Doc | undefined = undefined; - @observable _focusedTable: Doc = this.props.Document; - @observable _col: any = ""; - @observable _menuWidth = 0; - @observable _headerOpen = false; - @observable _headerIsEditing = false; - @observable _menuHeight = 0; - @observable _pointerX = 0; - @observable _pointerY = 0; - @observable _openTypes: boolean = false; - - @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } - @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } - @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); } - @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } - @computed get scale() { return this.props.ScreenToLocalTransform().Scale; } - @computed get columns() { return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); } - set columns(columns: SchemaHeaderField[]) { this.props.Document._schemaHeaders = new List(columns); } - - @computed get menuCoordinates() { - let searchx = 0; - let searchy = 0; - if (this.props.Document._searchDoc) { - const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0]; - if (el !== undefined) { - const rect = el.getBoundingClientRect(); - searchx = rect.x; - searchy = rect.y; - } - } - const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx; - const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy; - return this.props.ScreenToLocalTransform().transformPoint(x, y); - } - - get documentKeys() { - const docs = this.childDocs; - const keys: { [key: string]: boolean } = {}; - // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // is displayed (unlikely) it won't show up until something else changes. - //TODO Types - untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); - - this.columns.forEach(key => keys[key.heading] = true); - return Array.from(Object.keys(keys)); - } - - @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing; - - @undoBatch - setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => { - this._openTypes = false; - if (columnTypes.get(columnField.heading)) return; - - const columns = this.columns; - const index = columns.indexOf(columnField); - if (index > -1) { - columnField.setType(NumCast(type)); - columns[index] = columnField; - this.columns = columns; - } - }); - - @undoBatch - setColumnColor = (columnField: SchemaHeaderField, color: string): void => { - const columns = this.columns; - const index = columns.indexOf(columnField); - if (index > -1) { - columnField.setColor(color); - columns[index] = columnField; - this.columns = columns; // need to set the columns to trigger rerender - } - } - - @undoBatch - @action - setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => { - const columns = this.columns; - columns.forEach(col => col.setDesc(undefined)); - - const index = columns.findIndex(c => c.heading === columnField.heading); - const column = columns[index]; - column.setDesc(descending); - columns[index] = column; - this.columns = columns; - } - - renderTypes = (col: any) => { - if (columnTypes.get(col.heading)) return (null); - - const type = col.type; - - const anyType =
    this.setColumnType(col, ColumnType.Any)}> - - Any -
    ; - - const numType =
    this.setColumnType(col, ColumnType.Number)}> - - Number -
    ; - - const textType =
    this.setColumnType(col, ColumnType.String)}> - - Text -
    ; - - const boolType =
    this.setColumnType(col, ColumnType.Boolean)}> - - Checkbox -
    ; - - const listType =
    this.setColumnType(col, ColumnType.List)}> - - List -
    ; - - const docType =
    this.setColumnType(col, ColumnType.Doc)}> - - Document -
    ; - - const imageType =
    this.setColumnType(col, ColumnType.Image)}> - - Image -
    ; - - const dateType =
    this.setColumnType(col, ColumnType.Date)}> - - Date -
    ; - - - const allColumnTypes =
    - {anyType} - {numType} - {textType} - {boolType} - {listType} - {docType} - {imageType} - {dateType} -
    ; - - const justColType = type === ColumnType.Any ? anyType : type === ColumnType.Number ? numType : - type === ColumnType.String ? textType : type === ColumnType.Boolean ? boolType : - type === ColumnType.List ? listType : type === ColumnType.Doc ? docType : - type === ColumnType.Date ? dateType : imageType; - - return ( -
    this._openTypes = !this._openTypes)}> -
    - - -
    - {this._openTypes ? allColumnTypes : justColType} -
    - ); - } - - renderSorting = (col: any) => { - const sort = col.desc; - return ( -
    - -
    -
    this.setColumnSort(col, true)}> - - Sort descending -
    -
    this.setColumnSort(col, false)}> - - Sort ascending -
    -
    this.setColumnSort(col, undefined)}> - - Clear sorting -
    -
    -
    - ); - } - - renderColors = (col: any) => { - const selected = col.color; - - const pink = PastelSchemaPalette.get("pink2"); - const purple = PastelSchemaPalette.get("purple2"); - const blue = PastelSchemaPalette.get("bluegreen1"); - const yellow = PastelSchemaPalette.get("yellow4"); - const red = PastelSchemaPalette.get("red2"); - const gray = "#f1efeb"; - - return ( -
    - -
    -
    this.setColumnColor(col, pink!)}>
    -
    this.setColumnColor(col, purple!)}>
    -
    this.setColumnColor(col, blue!)}>
    -
    this.setColumnColor(col, yellow!)}>
    -
    this.setColumnColor(col, red!)}>
    -
    this.setColumnColor(col, gray)}>
    -
    -
    - ); - } - - @undoBatch - @action - changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => { - const columns = this.columns; - if (columns === undefined) { - this.columns = new List([new SchemaHeaderField(newKey, "f1efeb")]); - } else { - if (addNew) { - columns.push(new SchemaHeaderField(newKey, "f1efeb")); - this.columns = columns; - } else { - const index = columns.map(c => c.heading).indexOf(oldKey); - if (index > -1) { - const column = columns[index]; - column.setHeading(newKey); - columns[index] = column; - this.columns = columns; - if (filter) { - Doc.setDocFilter(this.props.Document, newKey, filter, "match"); - } - else { - this.props.Document._docFilters = undefined; - } - } - } - } - } - - @action - openHeader = (col: any, screenx: number, screeny: number) => { - this._col = col; - this._headerOpen = true; - this._pointerX = screenx; - this._pointerY = screeny; - } - - @action - closeHeader = () => { this._headerOpen = false; } - - @undoBatch - @action - deleteColumn = (key: string) => { - const columns = this.columns; - if (columns === undefined) { - this.columns = new List([]); - } else { - const index = columns.map(c => c.heading).indexOf(key); - if (index > -1) { - columns.splice(index, 1); - this.columns = columns; - } - } - this.closeHeader(); - } - - getPreviewTransform = (): Transform => { - return this.props.ScreenToLocalTransform().translate(- this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, - this.borderWidth); - } - - @action - onHeaderClick = (e: React.PointerEvent) => { - e.stopPropagation(); - } - - @action - onWheel(e: React.WheelEvent) { - const scale = this.props.ScreenToLocalTransform().Scale; - this.props.isContentActive(true) && e.stopPropagation(); - } - - @computed get renderMenuContent() { - TraceMobx(); - return
    - {this.renderTypes(this._col)} - {this.renderColors(this._col)} -
    - -
    -
    ; - } - - private createTarget = (ele: HTMLDivElement) => { - this._previewCont = ele; - super.CreateDropTarget(ele); - } - - isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable; - - @action setFocused = (doc: Doc) => this._focusedTable = doc; - - @action setPreviewDoc = (doc: Opt) => { - SelectionManager.SelectSchemaViewDoc(doc); - this._previewDoc = doc; - } - - //toggles preview side-panel of schema - @action - toggleExpander = () => { - this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0; - } - - onDividerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander); - } - @action - onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { - const nativeWidth = this._previewCont!.getBoundingClientRect(); - const minWidth = 40; - const maxWidth = 1000; - const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; - const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; - this.props.Document.schemaPreviewWidth = width; - return false; - } - - onPointerDown = (e: React.PointerEvent): void => { - if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { - if (this.props.isSelected(true)) e.stopPropagation(); - else this.props.select(false); - } - } - - @computed - get previewDocument(): Doc | undefined { return this._previewDoc; } - - @computed - get dividerDragger() { - return this.previewWidth() === 0 ? (null) : -
    -
    -
    ; - } - - @computed - get previewPanel() { - return
    - {!this.previewDocument ? (null) : - } -
    ; - } - - @computed - get schemaTable() { - return ; - } - - @computed - public get schemaToolbar() { - return
    -
    -
    - - Show Preview -
    -
    -
    ; - } - - onSpecificMenu = (e: React.MouseEvent) => { - if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) { - const cm = ContextMenu.Instance; - const options = cm.findByDescription("Options..."); - const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; - optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" }); - !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); - cm.displayMenu(e.clientX, e.clientY); - (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this. - e.stopPropagation(); - } - } - - @action - onTableClick = (e: React.MouseEvent): void => { - if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) { - this.setPreviewDoc(undefined); - } else { - e.stopPropagation(); - } - this.setFocused(this.props.Document); - this.closeHeader(); - } - - onResizedChange = (newResized: Resize[], event: any) => { - const columns = this.columns; - newResized.forEach(resized => { - const index = columns.findIndex(c => c.heading === resized.id); - const column = columns[index]; - column.setWidth(resized.value); - columns[index] = column; - }); - this.columns = columns; - } - - @action - setColumns = (columns: SchemaHeaderField[]) => this.columns = columns - - @undoBatch - reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { - const columns = [...columnsValues]; - const oldIndex = columns.indexOf(toMove); - const relIndex = columns.indexOf(relativeTo); - const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex; - - if (oldIndex === newIndex) return; - - columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); - this.columns = columns; - } - - onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); - - render() { - TraceMobx(); - if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); - const menuContent = this.renderMenuContent; - const menu =
    this.onZoomMenu(e)} - onPointerDown={e => this.onHeaderClick(e)} - style={{ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)` }}> - { - const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); - this._menuWidth = dim[0]; this._menuHeight = dim[1]; - })}> - {({ measureRef }) =>
    {menuContent}
    } -
    -
    ; - return
    -
    this.props.isContentActive(true) && e.stopPropagation()} - onDrop={e => this.onExternalDrop(e, {})} - ref={this.createTarget}> - {this.schemaTable} -
    - {this.dividerDragger} - {!this.previewWidth() ? (null) : this.previewPanel} - {this._headerOpen && this.props.isContentActive() ? menu : null} -
    ; - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index a25f962df..c659f749e 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -1,18 +1,17 @@ import React = require("react"); -import { IconProp, library } from "@fortawesome/fontawesome-svg-core"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; +import { action, computed, observable, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, Opt } from "../../../../fields/Doc"; +import { Doc, DocListCast, Opt, StrListCast } from "../../../../fields/Doc"; import { listSpec } from "../../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; import { ScriptField } from "../../../../fields/ScriptField"; import { Cast, StrCast } from "../../../../fields/Types"; import { undoBatch } from "../../../util/UndoManager"; -import { SearchBox } from "../../search/SearchBox"; +import { CollectionView } from "../CollectionView"; import { ColumnType } from "./CollectionSchemaView"; import "./CollectionSchemaView.scss"; -import { CollectionView } from "../CollectionView"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -26,9 +25,9 @@ export interface AddColumnHeaderProps { @observer export class CollectionSchemaAddColumnHeader extends React.Component { render() { - return ( - - ); + return ; } } @@ -234,7 +233,7 @@ export interface KeysDropdownProps { @observer export class KeysDropdown extends React.Component { @observable private _key: string = this.props.keyValue; - @observable private _searchTerm: string = this.props.keyValue; + @observable private _searchTerm: string = this.props.keyValue + ":"; @observable private _isOpen: boolean = false; @observable private _node: HTMLDivElement | null = null; @observable private _inputRef: React.RefObject = React.createRef(); @@ -326,11 +325,11 @@ export class KeysDropdown extends React.Component { || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null); return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm)); } - @action - renderOptions = (): JSX.Element[] | JSX.Element => { + + @computed get renderOptions() { if (!this._isOpen) { this.defaultMenuHeight = 0; - return <>; + return (null); } const options = this.showKeys.map(key => { return
    { return options; } - docSafe: Doc[] = []; + @computed get docSafe() { return DocListCast(this.props.dataDoc?.[this.props.fieldKey]); } - @action - renderFilterOptions = (): JSX.Element[] | JSX.Element => { + @computed get renderFilterOptions() { if (!this._isOpen || !this.props.dataDoc) { this.defaultMenuHeight = 0; - return <>; + return (null); } const keyOptions: string[] = []; const colpos = this._searchTerm.indexOf(":"); const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); - if (this.docSafe.length === 0) { - this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]); - } - const docs = this.docSafe; - docs.forEach((doc) => { + this.docSafe.forEach(doc => { const key = StrCast(doc[this._key]); if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") { keyOptions.push(key); } }); - const filters = Cast(this.props.Document._docFilters, listSpec("string")); - if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) { + const filters = StrListCast(this.props.Document._docFilters); + if (filters.some(filter => filter.split(":")[0] === this._key) === false) { this.props.col.setColor("rgb(241, 239, 235)"); this.closeResultsVisibility = "none"; } @@ -408,9 +402,9 @@ export class KeysDropdown extends React.Component { const options = keyOptions.map(key => { let bool = false; if (filters !== undefined) { - const ind = filters.findIndex(filter => filter.split(":")[0] === key); + const ind = filters.findIndex(filter => filter.split(":")[1] === key); const fields = ind === -1 ? undefined : filters[ind].split(":"); - bool = fields ? fields[1] === "check" : false; + bool = fields ? fields[2] === "check" : false; } return
    { e.stopPropagation()} onClick={e => e.stopPropagation()} - onChange={(e) => { - e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove"); - e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log(""); - e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter(); - e.target.checked === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove"); - }} + onChange={action(e => { + if (e.target.checked) { + Doc.setDocFilter(this.props.Document, this._key, key, "check"); + this.closeResultsVisibility = "contents"; + this.props.col.setColor("green"); + } else { + Doc.setDocFilter(this.props.Document, this._key, key, "remove"); + this.updateFilter(); + } + })} checked={bool} /> @@ -472,11 +470,7 @@ export class KeysDropdown extends React.Component { removeFilters = (e: React.PointerEvent): void => { const keyOptions: string[] = []; - if (this.docSafe.length === 0 && this.props.dataDoc) { - this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]); - } - const docs = this.docSafe; - docs.forEach((doc) => { + this.docSafe.forEach(doc => { const key = StrCast(doc[this._key]); if (keyOptions.includes(key) === false) { keyOptions.push(key); @@ -494,10 +488,6 @@ export class KeysDropdown extends React.Component {
    - {/* { - runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen }) - }} /> */} -
    { {!this._isOpen ? (null) :
    - {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()} + {this._searchTerm.includes(":") ? this.renderFilterOptions : this.renderOptions}
    }
    diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 12493ecc1..b89246489 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked } from "mobx"; +import { action, computed, observable, untracked, trace } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import { Resize } from "react-table"; diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx index d157832d9..bc5a9559f 100644 --- a/src/client/views/collections/collectionSchema/SchemaTable.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table"; import "react-table/react-table.css"; @@ -386,13 +386,13 @@ export class SchemaTable extends React.Component { @undoBatch @action createColumn = () => { - let index = 0; - let found = this.props.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1; - while (found) { - index++; - found = this.props.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; + const newFieldName = (index: number) => `New field${index ? ` (${index})` : ""}`; + for (let index = 0; index < 100; index++) { + if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) { + this.props.columns.push(new SchemaHeaderField(newFieldName(index), "#f1efeb")); + break; + } } - this.props.columns.push(new SchemaHeaderField(`New field ${index ? "(" + index + ")" : ""}`, "#f1efeb")); } @action diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d8690831f..9490edc2c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1142,7 +1142,7 @@ export namespace Doc { runInAction(() => { for (let i = 0; i < docFilters.length; i++) { const fields = docFilters[i].split(":"); // split key:value:modifier - if (fields[0] === key && (fields[1] === value || modifiers === "match" || modifiers === "remove")) { + if (fields[0] === key && (fields[1] === value || modifiers === "match")) { if (fields[2] === modifiers && modifiers && fields[1] === value) { if (toggle) modifiers = "remove"; else return; -- cgit v1.2.3-70-g09d2 From a46d659229120cdb139f716b1a48ec6b887807bb Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Oct 2021 19:40:40 -0400 Subject: fixed lightbox view to shrinkwrap collections when opened, but not force fit contents to allow users to navigate. fixed comparison box to show context of maker'/annotation documents. fixed ink pointer events to honor pointerEvents prop (eg when embedded in a comparisonBox that turns them off). --- src/client/views/InkingStroke.tsx | 2 +- src/client/views/LightboxView.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionSchema/CollectionSchemaCells.tsx | 1 - src/client/views/nodes/ComparisonBox.tsx | 12 +++++++++--- 5 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index d05a4a6e4..752db1413 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -209,7 +209,7 @@ export class InkingStroke extends ViewBoxBaseComponent { LightboxView.SetLightboxDoc(undefined); } }} > - +
    { DataDoc={undefined} LayoutTemplate={LightboxView.LightboxDocTemplate} addDocument={undefined} - fitContentsToDoc={this.fitToBox} + // fitContentsToDoc={this.fitToBox} // bcz: why do we want this? when we initially open a colletion, we shrinkwrap it which allows for user navigation. if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents... isDocumentActive={returnFalse} isContentActive={returnTrue} addDocTab={this.addDocTab} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b2db1168d..7dcd63b80 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1036,7 +1036,7 @@ export class CollectionFreeFormView extends CollectionSubView; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index ed196349e..0274cc49c 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -103,7 +103,6 @@ export class CollectionSchemaCell extends React.Component { this.props.changeFocusedCellByIndex(this.props.row, this.props.col); this.props.setPreviewDoc(this.props.rowProps.original); - console.log("click cell"); let url: string; if (url = StrCast(this.props.rowProps.row.href)) { try { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index f23c68409..b1aada158 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -89,14 +89,20 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { var whichDoc = Cast(this.dataDoc[which], Doc, null); - if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); + //if (whichDoc?.type === DocumentType.MARKER) + const targetDoc = Cast(whichDoc.annotationOn, Doc, null) ?? whichDoc; return whichDoc ? <> - { + whichDoc !== targetDoc && r?.focus(targetDoc); + }} + {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} isContentActive={returnFalse} isDocumentActive={returnFalse} styleProvider={this.docStyleProvider} - Document={whichDoc} + Document={targetDoc} DataDoc={undefined} + hideLinkButton={true} pointerEvents={"none"} /> {clearButton(which)} : // placeholder image if doc is missing -- cgit v1.2.3-70-g09d2 From 43496b4cd19a06d2682f2416a4273e25cdcb55a0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 7 Oct 2021 08:47:22 -0400 Subject: Revert "Revert "Merge pull request #34 from brown-dash/linking-anh"" This reverts commit 4e6a1d7a37c8c28014a9f7cd0d92f17c8f29454d. --- src/client/documents/Documents.ts | 1 + src/client/util/LinkManager.ts | 2 ++ .../CollectionFreeFormLinkView.tsx | 15 ++++++++++++--- src/client/views/linking/LinkEditor.tsx | 20 ++++++++++++++++++-- src/client/views/linking/LinkMenuItem.tsx | 4 ++-- src/fields/Doc.ts | 1 + 6 files changed, 36 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e3623c069..6d64b3286 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -302,6 +302,7 @@ export class DocumentOptions { border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered linkRelationshipList?: List; // for storing different link relationships (when set by user in the link editor) + linkRelationshipSizes?: List; //stores number of links contained in each relationship linkColorList?: List; // colors of links corresponding to specific link relationships } export namespace Docs { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 53bd13fb3..3f7f16113 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -107,8 +107,10 @@ export class LinkManager { if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { const linkRelationshipList = new List(); const linkColorList = new List(); + const linkRelationshipSizes = new List(); Doc.UserDoc().linkRelationshipList = linkRelationshipList; Doc.UserDoc().linkColorList = linkColorList; + Doc.UserDoc().linkRelationshipSizes = linkRelationshipSizes; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f6c2707da..88cd0feb3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -42,7 +42,7 @@ export class CollectionFreeFormLinkView extends React.Component this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() + setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); @@ -180,15 +180,24 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; + const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List; + const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); + //access stroke color using index of the relationship in the color list (default black) - const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "white" : "black") : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + const strokeColor = currRelationshipIndex == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + + //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has) + //thickness varies linearly from 3px to 12px for increasing link count + const strokeWidth: string = currRelationshipIndex == -1 ? "3px" : Math.floor(2 + 10 * (linkRelationshipSizes[currRelationshipIndex] / Math.max(...linkRelationshipSizes))) + "px"; + return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> - {textX === undefined ? (null) : {Field.toString(this.props.LinkDocs[0].description as any as Field)} diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 219f7d3a2..58c57a23b 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, StrListCast, Field } from "../../../fields/Doc"; +import { Doc, NumListCast, StrListCast, Field } from "../../../fields/Doc"; import { DateCast, StrCast, Cast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; @@ -42,14 +42,28 @@ export class LinkEditor extends React.Component { @undoBatch setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { + const prevRelationship = LinkManager.currentLink.linkRelationship as string; + LinkManager.currentLink.linkRelationship = value; Doc.GetProto(LinkManager.currentLink).linkRelationship = value; const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); + // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color if (linkRelationshipList && !linkRelationshipList.includes(value)) { linkRelationshipList.push(value); + linkRelationshipSizes.push(1); const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; - linkColorList.push(randColor); + linkColorList.push(randColor) + // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes + } else if (linkRelationshipList && value != prevRelationship) { + //increment size of new relationship size + linkRelationshipSizes[linkRelationshipList.indexOf(value)] = linkRelationshipSizes[linkRelationshipList.indexOf(value)] + 1; + //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation) + if (linkRelationshipList.includes(prevRelationship)) { + linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] = linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] - 1; + } + } this.relationshipButtonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.relationshipButtonColor = ""), 750); @@ -141,6 +155,7 @@ export class LinkEditor extends React.Component { style={{ width: "100%" }} id="input" value={this.relationship} + autoComplete={"off"} placeholder={"Enter link relationship"} onKeyDown={this.onRelationshipKey} onChange={this.handleRelationshipChange} @@ -169,6 +184,7 @@ export class LinkEditor extends React.Component {
    {
    {this.props.linkDoc.hidden ? "Show Anchor" : "Hide Anchor"}
    }>
    e.stopPropagation()}> -
    +
    {!this.props.linkDoc.linkDisplay ? "Show link" : "Hide link"}
    }>
    e.stopPropagation()}> -
    +
    {!this.props.linkDoc.linkAutoMove ? "Auto move dot" : "Freeze dot position"}
    }> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9490edc2c..a89bdf57a 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -79,6 +79,7 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { export async function DocCastAsync(field: FieldResult): Promise> { return Cast(field, Doc); } +export function NumListCast(field: FieldResult) { return Cast(field, listSpec("number"), []); } export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []); } export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; } -- cgit v1.2.3-70-g09d2 From 8f1a6266cf0a728ab2b7df11d45a023de266fae4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 7 Oct 2021 09:43:19 -0400 Subject: link menu ui tweaking. edge case checks for linkrelationship stuff. default links to have linkAutoMove --- src/Utils.ts | 2 +- src/client/documents/Documents.ts | 2 ++ src/client/util/LinkManager.ts | 11 +++-------- .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 8 +++++--- src/client/views/linking/LinkEditor.tsx | 14 +++++++++++--- src/client/views/linking/LinkMenuGroup.tsx | 1 - src/client/views/linking/LinkMenuItem.tsx | 18 +++++++++--------- 7 files changed, 31 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index 7ec4f69f3..53182cc9c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -575,7 +575,7 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe export function DashColor(color: string) { try { - return Color(color.toLowerCase()); + return color ? Color(color.toLowerCase()) : Color("transparent"); } catch (e) { console.log("COLOR error:", e); return Color("red"); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6d64b3286..da5c8efa9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -191,6 +191,7 @@ export class DocumentOptions { opacity?: number; defaultBackgroundColor?: string; _isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked + _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint isFolder?: boolean; lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide) activeFrame?: number; // the active frame of a document in a frame base collection @@ -1106,6 +1107,7 @@ export namespace DocUtils { "_acl-Public": SharingPermissions.Augment, linkDisplay: true, _hidden: true, + _linkAutoMove: true, linkRelationship, _showCaption: "description", _showTitle: "linkRelationship", diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 3f7f16113..90a8f2737 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -104,14 +104,9 @@ export class LinkManager { public createLinkrelationshipLists = () => { //create new lists for link relations and their associated colors if the lists don't already exist - if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { - const linkRelationshipList = new List(); - const linkColorList = new List(); - const linkRelationshipSizes = new List(); - Doc.UserDoc().linkRelationshipList = linkRelationshipList; - Doc.UserDoc().linkColorList = linkColorList; - Doc.UserDoc().linkRelationshipSizes = linkRelationshipSizes; - } + !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List()); + !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List()); + !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List()); } public addLink(linkDoc: Doc, checkExists = false) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 88cd0feb3..bb4cae8c6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -189,15 +189,17 @@ export class CollectionFreeFormLinkView extends React.Component; const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); + const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex]; + //access stroke color using index of the relationship in the color list (default black) - const strokeColor = currRelationshipIndex == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; + const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex]; //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has) //thickness varies linearly from 3px to 12px for increasing link count - const strokeWidth: string = currRelationshipIndex == -1 ? "3px" : Math.floor(2 + 10 * (linkRelationshipSizes[currRelationshipIndex] / Math.max(...linkRelationshipSizes))) + "px"; + const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px"; return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> - {textX === undefined ? (null) : {Field.toString(this.props.LinkDocs[0].description as any as Field)} diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 58c57a23b..5b5c3cd01 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -50,18 +50,26 @@ export class LinkEditor extends React.Component { const linkColorList = StrListCast(Doc.UserDoc().linkColorList); // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color - if (linkRelationshipList && !linkRelationshipList.includes(value)) { + if (!linkRelationshipList?.includes(value)) { linkRelationshipList.push(value); linkRelationshipSizes.push(1); const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; linkColorList.push(randColor) // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes } else if (linkRelationshipList && value != prevRelationship) { + const index = linkRelationshipList.indexOf(value); //increment size of new relationship size - linkRelationshipSizes[linkRelationshipList.indexOf(value)] = linkRelationshipSizes[linkRelationshipList.indexOf(value)] + 1; + if (index !== -1 && index < linkRelationshipSizes.length) { + const pvalue = linkRelationshipSizes[index]; + linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1); + } //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation) if (linkRelationshipList.includes(prevRelationship)) { - linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] = linkRelationshipSizes[linkRelationshipList.indexOf(prevRelationship)] - 1; + const pindex = linkRelationshipList.indexOf(prevRelationship); + if (pindex !== -1 && pindex < linkRelationshipSizes.length) { + const pvalue = linkRelationshipSizes[pindex]; + linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1)); + } } } diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index cb6571f92..03377ad4e 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -31,7 +31,6 @@ export class LinkMenuGroup extends React.Component { if (RGBcolor) { //set opacity to 0.25 by modifiying the rgb string color = RGBcolor.slice(0, RGBcolor.length - 1) + ", 0.25)"; - console.log(color); } } return color; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index e0b20ef6f..f19064e49 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -165,19 +165,19 @@ export class LinkMenuItem extends React.Component {
    -
    {this.props.linkDoc.hidden ? "Show Anchor" : "Hide Anchor"}
    }> -
    e.stopPropagation()}> -
    +
    {this.props.linkDoc.hidden ? "Show Link Anchor" : "Hide Link Anchor"}
    }> +
    e.stopPropagation()}> +
    -
    {!this.props.linkDoc.linkDisplay ? "Show link" : "Hide link"}
    }> -
    e.stopPropagation()}> -
    +
    {this.props.linkDoc.linkDisplay ? "Hide Link Line" : "Show Link Line"}
    }> +
    e.stopPropagation()}> +
    -
    {!this.props.linkDoc.linkAutoMove ? "Auto move dot" : "Freeze dot position"}
    }> -
    e.stopPropagation()}> -
    +
    {this.props.linkDoc.linkAutoMove ? "Click to freeze link anchor position" : "Click to auto move link anchor"}
    }> +
    e.stopPropagation()}> +
    Edit Link
    }> -- cgit v1.2.3-70-g09d2 From bdf0befa2b5eff79c2729254c2d053afe18b1646 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 16 Oct 2021 01:58:15 -0400 Subject: fixed warnings/errors & redirection to /home --- src/client/views/collections/TreeView.tsx | 12 ++++++------ .../collections/collectionSchema/CollectionSchemaHeaders.tsx | 4 ++-- src/client/views/linking/LinkEditor.tsx | 4 ++-- src/client/views/nodes/ComparisonBox.tsx | 2 +- src/server/server_Initialization.ts | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index a3da0e0e4..7f2128230 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -820,7 +820,7 @@ export class TreeView extends React.Component { childDocs: Doc[], treeView: CollectionTreeView, parentTreeView: CollectionTreeView | TreeView | undefined, - conainerCollection: Doc, + containerCollection: Doc, dataDoc: Doc | undefined, parentCollectionDoc: Doc | undefined, containerPrevSibling: Doc | undefined, @@ -846,16 +846,16 @@ export class TreeView extends React.Component { unobserveHeight: (ref: any) => void, contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[]) ) { - const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField); + const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField); if (viewSpecScript) { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = TreeView.sortDocs(childDocs, StrCast(conainerCollection.treeViewSortCriterion)); + const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion)); const rowWidth = () => panelWidth() - treeBulletWidth(); const treeViewRefs = new Map(); return docs.filter(child => child instanceof Doc).map((child, i) => { - const pair = Doc.GetLayoutDataDocPair(conainerCollection, dataDoc, child); + const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return (null); } @@ -883,7 +883,7 @@ export class TreeView extends React.Component { return treeViewRefs.set(child, r ? r : undefined)} document={pair.layout} dataDoc={pair.data} - containerCollection={conainerCollection} + containerCollection={containerCollection} prevSibling={docs[i]} treeView={treeView} indentDocument={indent} @@ -891,7 +891,7 @@ export class TreeView extends React.Component { onCheckedClick={onCheckedClick} onChildClick={onChildClick} renderDepth={renderDepth} - removeDoc={StrCast(conainerCollection.freezeChildren).includes("remove") ? undefined : remove} + removeDoc={StrCast(containerCollection.freezeChildren).includes("remove") ? undefined : remove} addDocument={addDocument} styleProvider={styleProvider} panelWidth={rowWidth} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index 074074bc5..1306b79cb 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -395,8 +395,8 @@ export class KeysDropdown extends React.Component { this.closeResultsVisibility = "none"; } for (let i = 0; i < (filters?.length ?? 0) - 1; i++) { - if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i].split(":")[1]) === false) { - keyOptions.push(filters![i + 1]); + if (filters[i] === this.props.col.heading && keyOptions.includes(filters[i].split(":")[1]) === false) { + keyOptions.push(filters[i + 1]); } } const options = keyOptions.map(key => { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 5b5c3cd01..db331bb75 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -54,9 +54,9 @@ export class LinkEditor extends React.Component { linkRelationshipList.push(value); linkRelationshipSizes.push(1); const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; - linkColorList.push(randColor) + linkColorList.push(randColor); // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes - } else if (linkRelationshipList && value != prevRelationship) { + } else if (linkRelationshipList && value !== prevRelationship) { const index = linkRelationshipList.indexOf(value); //increment size of new relationship size if (index !== -1 && index < linkRelationshipSizes.length) { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 0f962f95c..750213e67 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -88,7 +88,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent; }; const displayDoc = (which: string) => { - var whichDoc = Cast(this.dataDoc[which], Doc, null); + const whichDoc = Cast(this.dataDoc[which], Doc, null); //if (whichDoc?.type === DocumentType.MARKER) const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc; return whichDoc ? <> diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 0f4a067fc..00a801e03 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -37,6 +37,8 @@ export let resolvedServerUrl: string; export default async function InitializeServer(routeSetter: RouteSetter) { const app = buildWithMiddleware(express()); + // Root route of express app + app.get("/", (req, res) => res.redirect("/home")); app.use(express.static(publicDirectory, { setHeaders: res => res.setHeader("Access-Control-Allow-Origin", "*") -- cgit v1.2.3-70-g09d2 From 3e0a9ff2c708891a15a681e5af549caf0b18ff60 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 26 Oct 2021 12:06:36 -0400 Subject: when document is in lightbox view and a link is followed to its sidebar, we no longer reopen the document in its context. fixed selecting annotations on web/pdf. don't change document height when shown in a linkPreview. webBox fixes for pages with scripts. fixed range bounds when clicking on web text several fixes to web pages on server and client. client webbox allows clicks on divs with onclick instead of doing selection also hacky fix so that google search url doesn't keep expanding by removing 'q=' regions also added prevent/allow script menu item server grabs all html and hides id="google.." which are ads. also rewrites hrefs starting with http to route through corsProxy also removes target=_blank tags to prevent pages from opening outside of dash. also cleaned up routes and comments also when not logged in, references to anything in dash domain route to /home --- package-lock.json | 5 + package.json | 1 + src/Utils.ts | 2 +- src/client/util/LinkManager.ts | 6 +- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/InkControlPtHandles.tsx | 16 +-- src/client/views/InkStrokeProperties.ts | 72 +++++++------ src/client/views/InkTangentHandles.tsx | 6 +- src/client/views/InkingStroke.tsx | 9 +- src/client/views/collections/CollectionSubView.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/WebBox.tsx | 77 +++++++++++--- src/server/server_Initialization.ts | 115 ++++++++++++--------- 14 files changed, 206 insertions(+), 122 deletions(-) (limited to 'src/client/views/collections') diff --git a/package-lock.json b/package-lock.json index c1dd8506f..3aff3a549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9264,6 +9264,11 @@ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "optional": true }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", diff --git a/package.json b/package.json index 5d10c0d54..99c818062 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "libxmljs": "^0.19.7", "lodash": "^4.17.15", "material-ui": "^0.20.2", + "memorystream": "^0.3.1", "mobile-detect": "^1.4.4", "mobx": "^5.15.7", "mobx-react": "^5.4.4", diff --git a/src/Utils.ts b/src/Utils.ts index 53182cc9c..bfb29fe8d 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -599,7 +599,7 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi range.selectNodeContents(elem); var currentPos = 0; const endPos = range.endOffset; - while (currentPos + 1 < endPos) { + while (currentPos + 1 <= endPos) { range.setStart(elem, currentPos); range.setEnd(elem, currentPos + 1); const rangeRect = range.getBoundingClientRect(); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 90a8f2737..62b13e2c6 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -234,8 +234,10 @@ export class LinkManager { setTimeout(LightboxView.Next); finished?.(); } else { - const containerDoc = Cast(target.annotationOn, Doc, null) || target; - const targetContext = Cast(containerDoc?.context, Doc, null); + const containerAnnoDoc = Cast(target.annotationOn, Doc, null); + const containerDoc = containerAnnoDoc || target; + const containerDocContext = Cast(containerDoc?.context, Doc, null); + const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, finished); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 17a81149c..5b44a0552 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -195,11 +195,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P @action onRotateDown = (e: React.PointerEvent): void => { this._rotateUndo = UndoManager.StartBatch("rotatedown"); + const pt = { x: (this.Bounds.x + this.Bounds.r) / 2, y: (this.Bounds.y + this.Bounds.b) / 2 }; setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { const movement = { X: delta[0], Y: e.clientY - down[1] }; const angle = Math.max(1, Math.abs(movement.Y / 10)); - InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180)); + const selectedInk = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); + InkStrokeProperties.Instance?.rotateInk(selectedInk, 2 * movement.X / angle * (Math.PI / 180), pt); return false; }, () => { diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 0644488b3..73de4a3e0 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -3,18 +3,20 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../fields/Doc"; import { ControlPoint, InkData, PointData } from "../../fields/InkField"; +import { List } from "../../fields/List"; import { listSpec } from "../../fields/Schema"; import { Cast } from "../../fields/Types"; import { setupMoveUpEvents } from "../../Utils"; import { Transform } from "../util/Transform"; import { UndoManager } from "../util/UndoManager"; import { Colors } from "./global/globalEnums"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { List } from "../../fields/List"; import { InkingStroke } from "./InkingStroke"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { DocumentView } from "./nodes/DocumentView"; export interface InkControlProps { inkDoc: Doc; + inkView: DocumentView; inkCtrlPoints: InkData; screenCtrlPoints: InkData; screenSpaceLineWidth: number; @@ -51,12 +53,12 @@ export class InkControlPtHandles extends React.Component { setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); - InkStrokeProperties.Instance?.moveControlPtHandle(delta[0] * screenScale, delta[1] * screenScale, controlIndex); + InkStrokeProperties.Instance?.moveControlPtHandle(this.props.inkView, delta[0] * screenScale, delta[1] * screenScale, controlIndex); return false; }), action(() => { if (this.controlUndo) { - InkStrokeProperties.Instance?.snapControl(this.props.inkDoc, controlIndex); + InkStrokeProperties.Instance?.snapControl(this.props.inkView, controlIndex); } this.controlUndo?.end(); this.controlUndo = undefined; @@ -71,11 +73,11 @@ export class InkControlPtHandles extends React.Component { } else { if (brokenIndices?.includes(equivIndex)) { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); + InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, equivIndex, handleIndexA, handleIndexB); } if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, controlIndex, handleIndexA, handleIndexB); } } this.controlUndo?.end(); @@ -98,7 +100,7 @@ export class InkControlPtHandles extends React.Component { @action onDelete = (e: KeyboardEvent) => { if (["-", "Backspace", "Delete"].includes(e.key)) { - InkStrokeProperties.Instance?.deletePoints(); + InkStrokeProperties.Instance?.deletePoints(this.props.inkView); e.stopPropagation(); } } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index ee30caa3d..33e25bbbb 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -1,16 +1,15 @@ import { Bezier } from "bezier-js"; -import { action, computed, observable, reaction } from "mobx"; -import { Doc } from "../../fields/Doc"; -import { Document } from "../../fields/documentSchemas"; +import { action, observable, reaction } from "mobx"; +import { Doc, Opt } from "../../fields/Doc"; import { InkData, InkField, InkTool, PointData } from "../../fields/InkField"; import { List } from "../../fields/List"; import { listSpec } from "../../fields/Schema"; import { Cast, NumCast } from "../../fields/Types"; import { DocumentType } from "../documents/DocumentTypes"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import { InkingStroke } from "./InkingStroke"; +import { DocumentView } from "./nodes/DocumentView"; export class InkStrokeProperties { static Instance: InkStrokeProperties | undefined; @@ -25,21 +24,16 @@ export class InkStrokeProperties { reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false)); } - @computed get selectedInk() { - const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); - return inks.length ? inks : undefined; - } - /** * Helper function that enables other functions to be applied to a particular ink instance. * @param func The inputted function. * @param requireCurrPoint Indicates whether the current selected point is needed. */ - applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { + applyFunction = (strokes: Opt, func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { var appliedFunc = false; - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currentPoint !== -1)) { - const doc = Document(inkView.rootDoc); + (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(action(inkView => { + if (!requireCurrPoint || this._currentPoint !== -1) { + const doc = inkView.rootDoc; if (doc.type === DocumentType.INK && doc.width && doc.height) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { @@ -47,7 +41,7 @@ export class InkStrokeProperties { const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); const ptsXscale = ((NumCast(doc._width) - NumCast(doc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1; const ptsYscale = ((NumCast(doc._height) - NumCast(doc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1; - const newPoints = func(doc, ink, ptsXscale, ptsYscale); + const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth)); if (newPoints) { const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X)); const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y)); @@ -73,8 +67,9 @@ export class InkStrokeProperties { */ @undoBatch @action - addPoints = (t: number, i: number, controls: { X: number, Y: number }[]) => { - this.applyFunction((doc: Doc, ink: InkData) => { + addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number, Y: number }[]) => { + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t); const splicepts = [...newsegs.left.points, ...newsegs.right.points]; @@ -143,9 +138,10 @@ export class InkStrokeProperties { */ @undoBatch @action - deletePoints = () => this.applyFunction((doc: Doc, ink: InkData) => { + deletePoints = (inkView: DocumentView) => this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const newPoints: { X: number, Y: number }[] = []; - const toRemove = Math.floor(((this._currentPoint + 2) / 4)); + const toRemove = Math.floor((this._currentPoint + 2) / 4); const last = this._currentPoint === ink.length - 1; for (let i = 0; i < ink.length; i++) { if (Math.floor((i + 2) / 4) !== toRemove && (toRemove !== 0 || i > 3)) { @@ -164,17 +160,22 @@ export class InkStrokeProperties { */ @undoBatch @action - rotateInk = (angle: number) => { - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { - const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); - const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); - const centerPoint = { X: (oldXrange.min + oldXrange.max) / 2, Y: (oldYrange.min + oldYrange.max) / 2 }; + rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: { x: number, y: number }) => { + this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => { + const oldXrangeMin = Math.min(...ink.map(p => p.X)); + const oldYrangeMin = Math.min(...ink.map(p => p.Y)); + const docViewCenterPt = view.screenToLocalTransform().transformPoint(scrpt.x, scrpt.y); + const inkCenterPt = { + X: (docViewCenterPt[0] - inkStrokeWidth / 2) / xScale + oldXrangeMin, + Y: (docViewCenterPt[1] - inkStrokeWidth / 2) / yScale + oldYrangeMin + }; const newPoints = ink.map(i => { - const pt = { X: i.X - centerPoint.X, Y: i.Y - centerPoint.Y }; + const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y }; const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * yScale / xScale; const newY = Math.sin(angle) * pt.X * xScale / yScale + Math.cos(angle) * pt.Y; - return { X: newX + centerPoint.X, Y: newY + centerPoint.Y }; + return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y }; }); + const doc = view.rootDoc; doc.rotation = NumCast(doc.rotation) + angle; return newPoints; }); @@ -185,8 +186,8 @@ export class InkStrokeProperties { */ @undoBatch @action - moveControlPtHandle = (deltaX: number, deltaY: number, controlIndex: number) => - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { + moveControlPtHandle = (inkView: DocumentView, deltaX: number, deltaY: number, controlIndex: number) => + this.applyFunction(inkView, (view: DocumentView, ink: InkData, xScale: number, yScale: number) => { const order = controlIndex % 4; const closed = InkingStroke.IsClosed(ink); @@ -235,7 +236,8 @@ export class InkStrokeProperties { /** * Handles the movement/scaling of a control point. */ - snapControl = (inkDoc: Doc, controlIndex: number) => { + snapControl = (inkView: DocumentView, controlIndex: number) => { + const inkDoc = inkView.rootDoc; const ink = Cast(inkDoc.data, InkField)?.inkData; if (ink) { const closed = InkingStroke.IsClosed(ink); @@ -257,8 +259,8 @@ export class InkStrokeProperties { const near = Math.sqrt((nearestPt.X - refPt.X) * (nearestPt.X - refPt.X) * ptsXscale * ptsXscale + (nearestPt.Y - refPt.Y) * (nearestPt.Y - refPt.Y) * ptsYscale * ptsYscale); - if (near / (this.selectedInk?.lastElement().props.ScreenToLocalTransform().Scale || 1) < 10) { - return this.moveControlPtHandle((nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); + if (near / (inkView.props.ScreenToLocalTransform().Scale || 1) < 10) { + return this.moveControlPtHandle(inkView, (nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); } } return false; @@ -269,8 +271,9 @@ export class InkStrokeProperties { * @param handleIndexA The handle point that retains its current position. * @param handleIndexB The handle point that is rotated to be 180 degrees from its opposite. */ - snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { - this.applyFunction((doc: Doc, ink: InkData) => { + snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => { + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"), []); const ind = brokenIndices.findIndex(value => value === controlIndex); if (ind !== -1) { @@ -330,8 +333,9 @@ export class InkStrokeProperties { */ @undoBatch @action - moveTangentHandle = (deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { + moveTangentHandle = (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => + this.applyFunction(inkView, (view: DocumentView, ink: InkData, xScale: number, yScale: number) => { + const doc = view.rootDoc; const closed = InkingStroke.IsClosed(ink); const oldHandlePoint = ink[handleIndex]; const oppositeHandlePoint = ink[oppositeHandleIndex]; diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index df5bebf31..f88a20448 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -10,11 +10,13 @@ import { emptyFunction, setupMoveUpEvents } from "../../Utils"; import { Transform } from "../util/Transform"; import { UndoManager } from "../util/UndoManager"; import { Colors } from "./global/globalEnums"; -import { InkStrokeProperties } from "./InkStrokeProperties"; import { InkingStroke } from "./InkingStroke"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { DocumentView } from "./nodes/DocumentView"; export interface InkHandlesProps { inkDoc: Doc; + inkView: DocumentView; screenCtrlPoints: InkData; screenSpaceLineWidth: number; ScreenToLocalTransform: () => Transform; @@ -37,7 +39,7 @@ export class InkTangentHandles extends React.Component { setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { if (!controlUndo) controlUndo = UndoManager.StartBatch("DocDecs move tangent"); if (e.altKey) this.onBreakTangent(controlIndex); - InkStrokeProperties.Instance?.moveTangentHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex); + InkStrokeProperties.Instance?.moveTangentHandle(this.props.inkView, -delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex); return false; }, () => { controlUndo?.end(); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 752db1413..d312331d0 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,17 +1,17 @@ import React = require("react"); -import { Bezier } from "bezier-js"; import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../fields/Doc"; import { documentSchema } from "../../fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../fields/InkField"; import { makeInterface } from "../../fields/Schema"; -import { Cast, NumCast, StrCast, BoolCast } from "../../fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InteractionUtils } from "../util/InteractionUtils"; +import { SnappingManager } from "../util/SnappingManager"; import { ContextMenu } from "./ContextMenu"; import { ViewBoxBaseComponent } from "./DocComponent"; import { Colors } from "./global/globalEnums"; @@ -20,7 +20,6 @@ import "./InkStroke.scss"; import { InkStrokeProperties } from "./InkStrokeProperties"; import { InkTangentHandles } from "./InkTangentHandles"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; -import { SnappingManager } from "../util/SnappingManager"; import Color = require("color"); type InkDocument = makeInterface<[typeof documentSchema]>; @@ -83,7 +82,7 @@ export class InkingStroke extends ViewBoxBaseComponent (schemaCtor: (doc: Doc) => T, moreProps?: // } } if (uriList) { - console.log("Web URI = ", uriList); // const existingWebDoc = await Hypothesis.findWebDoc(uriList); // if (existingWebDoc) { // const alias = Doc.MakeAlias(existingWebDoc); @@ -390,7 +389,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: // addDocument(alias); // } else { - console.log("Adding ..."); const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig) ...options, title: uriList.split("#annotations:")[0], @@ -399,8 +397,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: _nativeWidth: 850, useCors: true }); - console.log(" ... " + newDoc.title); - console.log(" ... " + addDocument(newDoc) + " " + newDoc.title); + addDocument(newDoc); } return; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7dcd63b80..febccbfcc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -75,7 +75,7 @@ export type collectionFreeformViewProps = { scaleField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; - dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are trnasparent or not. + dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not. // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents. }; @@ -1472,7 +1472,7 @@ export class CollectionFreeFormView extends CollectionSubView -
    +
    {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)} this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); contentScaling = () => this.ContentScale; onClickFunc = () => this.onClickHandler; - setHeight = (height: number) => this.layoutDoc._height = height; + setHeight = (height: number) => { + if (this.props.renderDepth !== -1) { + this.layoutDoc._height = height; + } + } setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); isContentActive = (outsideReaction?: boolean) => { return CurrentUserUtils.SelectedTool !== InkTool.None || diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 37d716993..9956cc36b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -58,8 +58,10 @@ export class WebBox extends ViewBoxAnnotatableComponent(); private _searchRef = React.createRef(); private _searchString = ""; + @observable private _webUrl = ""; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render. + @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled @observable private _searching: boolean = false; - @observable _showSidebar = false; + @observable private _showSidebar = false; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt; @observable private _marqueeing: number[] | undefined; @@ -79,6 +81,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._webUrl = this._url); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it. } @action @@ -239,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. } else { this._iframeClick = this._iframe ?? undefined; @@ -269,9 +272,24 @@ export class WebBox extends ViewBoxAnnotatableComponent { const iframe = this._iframe; - this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp); + let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString()); + if (requrlraw !== this._url.toString()) { + if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) { + const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g); + const newsearch = matches?.lastElement()!; + if (matches) { + requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch)); + for (let i = 1; i < Array.from(matches)?.length; i++) { + requrlraw = requrlraw.replace(matches[i], ""); + } + } + requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", ""); + } + this.submitURL(requrlraw, undefined, true); + } if (iframe?.contentDocument) { - iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown); + iframe.contentDocument.addEventListener("pointerup", this.iframeUp); + iframe.contentDocument.addEventListener("pointerdown", this.iframeDown); this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight); setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); const initialScroll = this._initialScroll; @@ -281,13 +299,15 @@ export class WebBox extends ViewBoxAnnotatableComponent { + iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; - for (let ele = e.target; ele; ele = ele.parentElement) { + for (let ele = e.target as any; ele; ele = ele.parentElement) { href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href; } - if (href && this.webField?.origin) { - this.submitURL(href.replace(Utils.prepend(""), this.webField?.origin)); + const origin = this.webField?.origin; + if (href && origin) { + e.stopPropagation(); + setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin))); if (this._outerRef.current) { this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop); this._outerRef.current.scrollLeft = 0; @@ -338,8 +358,15 @@ export class WebBox extends ViewBoxAnnotatableComponent([...history, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -350,10 +377,16 @@ export class WebBox extends ViewBoxAnnotatableComponent([this._url]); else this.dataDoc[this.fieldKey + "-future"] = new List([...future, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); - console.log(this._urlHash); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -362,9 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0)); } - @action - submitURL = (newUrl?: string, preview?: boolean) => { + submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => { if (!newUrl) return; if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl; try { @@ -376,7 +408,10 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); + funcs.push({ + description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => { + this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts; + if (this._iframe) { + runInAction(() => this._hackHide = true); + setTimeout(action(() => this._hackHide = false)); + } + }, icon: "snowflake" + }); funcs.push({ description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => { const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); @@ -468,18 +512,19 @@ export class WebBox extends ViewBoxAnnotatableComponent; } else if (field instanceof WebField) { - const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._url) : this._url; + const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl; view =