diff options
author | Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> | 2021-07-31 12:46:59 -0700 |
---|---|---|
committer | Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> | 2021-07-31 12:46:59 -0700 |
commit | 23f5f7362cb8deaaff805cd6b8dc0c8ae9e2f3b7 (patch) | |
tree | b91e6ffe50433184fbc55b0271729d721fcf4737 /src | |
parent | d470a08e9ce329f1a6ff9a4591c41e1e04fb62bb (diff) | |
parent | 41ccf50f2b551edd6827c9fd6296b9ff87a65915 (diff) |
Merge branch 'master' into trails-aubrey
Diffstat (limited to 'src')
50 files changed, 2701 insertions, 1996 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f8ccbb102..4e45f6b7c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -45,14 +45,14 @@ import { LabelBox } from "../views/nodes/LabelBox"; import { LinkBox } from "../views/nodes/LinkBox"; import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup"; import { PDFBox } from "../views/nodes/PDFBox"; -import { PresBox } from "../views/nodes/PresBox"; +import { PresBox } from "../views/nodes/trails/PresBox"; import { ScreenshotBox } from "../views/nodes/ScreenshotBox"; import { ScriptingBox } from "../views/nodes/ScriptingBox"; import { SliderBox } from "../views/nodes/SliderBox"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; import { VideoBox } from "../views/nodes/VideoBox"; import { WebBox } from "../views/nodes/WebBox"; -import { PresElementBox } from "../views/presentationview/PresElementBox"; +import { PresElementBox } from "../views/nodes/trails/PresElementBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 9cfea7f3f..238f1ac0a 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 = $('<div class="lm_popin" title="' + this.config.labels.popin + '">' + '<div class="lm_icon"></div>' + @@ -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) { @@ -1567,15 +1567,15 @@ showCloseIcon: true, responsiveMode: 'onload', // Can be onload, always, or none. tabOverlapAllowance: 0, // maximum pixel overlap per tab - reorderOnTabMenuClick: true, + reorderOnTabMenuClick: false, //do not reorder! - horizontal scroll tabControlOffset: 10 }, dimensions: { - borderWidth: 5, + borderWidth: 3, borderGrabWidth: 5, minItemHeight: 10, - minItemWidth: 10, - headerHeight: 20, + minItemWidth: 20, + headerHeight: 27, dragProxyWidth: 300, dragProxyHeight: 200 }, @@ -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); @@ -2390,24 +2390,24 @@ this._createControls(); }; + // '<ul class="lm_tabdropdown_list"></ul>', lm.controls.Header._template = [ '<div class="lm_header">', '<ul class="lm_tabs"></ul>', '<ul class="lm_controls"></ul>', - '<ul class="lm_tabdropdown_list"></ul>', '</div>' ].join(''); 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; @@ -2477,32 +2477,33 @@ } } - 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 (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--) { - this.tabs[j] = this.tabs[j - 1]; - } - this.tabs[0] = activeTab; - this.parent.config.activeItemIndex = 0; - } - } + // glr: removed for new tab manager + // 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 (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--) { + // this.tabs[j] = this.tabs[j - 1]; + // } + // this.tabs[0] = activeTab; + // this.parent.config.activeItemIndex = 0; + // } + // } this._updateTabSizes(); 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 +2515,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 +2532,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 +2549,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 +2574,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(); + // 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 +2609,9 @@ }); } - /** - * Close button - */ + /** + * Close button + */ if (this._isClosable()) { closeStack = lm.utils.fnBind(this.parent.remove, this.parent); label = this._getHeaderSetting('close'); @@ -2618,30 +2619,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,32 +2656,29 @@ }, - /** - * 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; } - //Show the menu based on function argument - this.tabDropdownButton.element.toggle(showTabMenu === true); - var size = function (val) { return val ? 'width' : 'height'; }; @@ -2835,14 +2833,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 +2886,37 @@ } }; - /** - * The tab's html template - * - * @type {String} - */ + /** + * The tab's html template + * + * @type {String} + */ lm.controls.Tab._template = '<li class="lm_tab"><i class="lm_left"></i>' + '<div class="lm_title_wrap"><input class="lm_title"/></div><div class="lm_close_tab"></div>' + '<i class="lm_right"></i></li>'; 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 +2930,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 +2948,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 +2971,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 +2993,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 +3038,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 +3094,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 +3144,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 +3178,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 +3242,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 +3269,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 +3291,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 +3320,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 +3359,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 +3371,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 +3383,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 +3415,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 +3438,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 +3460,9 @@ } }, - /**************************************** - * SELECTOR - ****************************************/ + /**************************************** + * SELECTOR + ****************************************/ getItemsByFilter: function (filter) { var result = [], next = function (contentItem) { @@ -3508,9 +3506,9 @@ return instances; }, - /**************************************** - * PACKAGE PRIVATE - ****************************************/ + /**************************************** + * PACKAGE PRIVATE + ****************************************/ _$getItemsByProperty: function (key, value) { return this.getItemsByFilter(function (item) { return item[key] === value; @@ -3555,11 +3553,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 +3565,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 +3593,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 +3617,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 +3650,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 +3668,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 +3696,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 +3718,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 +3796,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 +3839,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 +3915,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 +3984,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 +4003,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 +4017,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 +4042,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 +4058,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 +4072,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 +4093,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 +4121,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 +4162,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 +4198,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 +4217,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 +4230,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 +4240,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 +4264,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 +4286,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 +4305,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 +4340,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 +4359,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 +4375,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 +4398,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 +4417,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), @@ -4575,13 +4573,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 +4605,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 +4660,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 +4673,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 +4701,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 +4759,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 = { @@ -4875,7 +4873,7 @@ this.layoutManager.dropTargetIndicator.highlightArea({ x1: headerOffset.left, x2: headerOffset.left + 100, - y1: headerOffset.top + this.header.element.height() - 20, + y1: headerOffset.top + this.header.element.height() - 25, y2: headerOffset.top + this.header.element.height() }); @@ -4971,13 +4969,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', @@ -5037,154 +5035,154 @@ 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<String|Boolean>} dictionary - * - * @returns {String} The minified version - */ + /** + * Minifies value based on a dictionary + * + * @param {String|Boolean} value + * @param {Array<String|Boolean>} 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 +5193,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 +5215,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 +5272,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 +5293,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 +5324,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 +5343,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 +5391,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/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 27bef915d..fe181759c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -34,6 +34,9 @@ import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; +import { computedFn } from "mobx-utils"; +import { ColorScheme } from "./SettingsManager"; +import { Colors } from "../views/global/globalEnums"; export let resolvedPorts: { server: number, socket: number }; @@ -475,7 +478,7 @@ export class CurrentUserUtils { { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, - { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, + // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, @@ -538,8 +541,8 @@ export class CurrentUserUtils { { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, // { title: "Filter", target: Cast(doc.currentFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, - { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, - { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, + // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, + // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' }, ]; } @@ -565,7 +568,6 @@ export class CurrentUserUtils { dontUndo: true, title, target, - backgroundColor: "black", _dropAction: "alias", _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]), _width: 60, @@ -580,8 +582,10 @@ export class CurrentUserUtils { title: "menuItemPanel", childDropAction: "alias", _chromeHidden: true, + backgroundColor: Colors.DARK_GRAY, + boxShadow: "rgba(0,0,0,0)", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - backgroundColor: "black", ignoreClick: true, + ignoreClick: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true @@ -591,8 +595,6 @@ export class CurrentUserUtils { PromiseValue(Cast(doc.menuStack, Doc)).then(stack => { stack && PromiseValue(stack.data).then(btns => { DocListCastAsync(btns).then(bts => bts?.forEach(btn => { - btn.color = "white"; - btn._backgroundColor = ""; btn.dontUndo = true; btn.system = true; if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files @@ -763,7 +765,7 @@ export class CurrentUserUtils { await doc.myDashboards; if (doc.myDashboards === undefined) { doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Dashboards", _height: 400, childHideLinkButton: true, + title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true @@ -779,7 +781,7 @@ export class CurrentUserUtils { await doc.myPresentations; if (doc.myPresentations === undefined) { doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Presentations", _height: 100, + title: "My Trails", _showTitle: "title", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true @@ -798,7 +800,7 @@ export class CurrentUserUtils { doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], { - title: "My Documents", _height: 100, + title: "My Documents", _showTitle: "title", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, @@ -812,7 +814,7 @@ export class CurrentUserUtils { // setup Recently Closed library item if (doc.myRecentlyClosedDocs === undefined) { doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "Recently Closed", treeViewShowClearButton: true, childHideLinkButton: true, + title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true @@ -843,7 +845,7 @@ export class CurrentUserUtils { doc.treeViewOpen = true; doc.treeViewExpandedView = "fields"; doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true })) as any as Doc; @@ -863,6 +865,7 @@ export class CurrentUserUtils { static async setupSidebarButtons(doc: Doc) { CurrentUserUtils.setupSidebarContainer(doc); await CurrentUserUtils.setupToolsBtnPanel(doc); + CurrentUserUtils.setupImportSidebar(doc); CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupFilesystem(doc); @@ -895,6 +898,7 @@ export class CurrentUserUtils { (doc["dockedBtn-undo"] as Doc).dontUndo = true; (doc["dockedBtn-redo"] as Doc).dontUndo = true; } + // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { if (doc.myOverlayDocs === undefined) { @@ -928,7 +932,8 @@ export class CurrentUserUtils { if (!sharedDocs) { sharedDocs = Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, _chromeHidden: true, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _chromeHidden: true, boxShadow: "0 0", }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; } @@ -943,14 +948,14 @@ export class CurrentUserUtils { static setupImportSidebar(doc: Doc) { if (doc.myImportDocs === undefined) { doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { - title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, + title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true, })); } if (doc.myImportPanel === undefined) { const uploads = Cast(doc.myImportDocs, Doc, null); const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); - doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true })); + doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); } } @@ -1011,10 +1016,14 @@ export class CurrentUserUtils { const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); }, { 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; + doc._showLabel = false; + doc._showMenuLabel = true; doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); @@ -1205,7 +1214,7 @@ export class CurrentUserUtils { const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); Doc.AddDocToList(myPresentations, "data", presentation); userDoc.activePresentation = presentation; - const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); @@ -1261,6 +1270,4 @@ Scripting.addGlobal(function createNewPresentation() { return MainView.Instance. Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, - "imports files from device directly into the import sidebar"); -Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, - "toggle between regular rendeing and an informal sketch/comic style"); + "imports files from device directly into the import sidebar");
\ No newline at end of file diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 777394b05..3987497b8 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -18,6 +18,12 @@ const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; +export enum ColorScheme { + Dark = "Dark", + Light = "Light", + System = "Match System" +} + @observer export class SettingsManager extends React.Component<{}> { public static Instance: SettingsManager; @@ -32,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; } constructor(props: {}) { super(props); @@ -69,6 +75,28 @@ export class SettingsManager extends React.Component<{}> { else DocServer.Control.makeEditable(); }); + @undoBatch + @action + changeColorScheme = action((e: React.ChangeEvent) => { + const scheme: ColorScheme = (e.currentTarget as any).value; + switch (scheme) { + case ColorScheme.Light: + Doc.UserDoc().colorScheme = ColorScheme.Light; + addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" }); + break; + case ColorScheme.Dark: + Doc.UserDoc().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; + }); + break; + } + }); + + @computed get colorsContent() { const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', @@ -90,8 +118,7 @@ export class SettingsManager extends React.Component<{}> { </Flyout> </div>; - const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; - const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"]; + const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System]; return <div className="colors-content"> <div className="preferences-color"> @@ -102,14 +129,11 @@ export class SettingsManager extends React.Component<{}> { <div className="preferences-color-text">Border/Header Color</div> {userColorFlyout} </div> - <div className="preferences-font"> - <div className="preferences-font-text">Default Font</div> - <div className="preferences-font-controls"> - <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}> - {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)} - </select> - <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} > - {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)} + <div className="preferences-colorScheme"> + <div className="preferences-color-text">Color Scheme</div> + <div className="preferences-color-controls"> + <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.UserDoc().colorScheme)}> + {colorSchemes.map(scheme => <option key={scheme} value={scheme}> {scheme} </option>)} </select> </div> </div> @@ -132,6 +156,16 @@ export class SettingsManager extends React.Component<{}> { checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} /> <div className="preferences-check">Raise on drag</div> </div> + <div> + <input type="checkbox" onChange={e => Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel} + checked={BoolCast(Doc.UserDoc()._showLabel)} /> + <div className="preferences-check">Show tool button labels</div> + </div> + <div> + <input type="checkbox" onChange={e => Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel} + checked={BoolCast(Doc.UserDoc()._showMenuLabel)} /> + <div className="preferences-check">Show menu button labels</div> + </div> </div>; } @@ -149,6 +183,27 @@ export class SettingsManager extends React.Component<{}> { </div>; } + @computed get textContent() { + + const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text", "Roboto"]; + const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"]; + + return ( + <div className="tab-content appearances-content"> + <div className="preferences-font"> + <div className="preferences-font-text">Default Font</div> + <div className="preferences-font-controls"> + <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}> + {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)} + </select> + <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} > + {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)} + </select> + </div> + </div> + </div>); + } + @action changeVal = (e: React.ChangeEvent, pass: string) => { const value = (e.target as any).value; @@ -228,7 +283,7 @@ export class SettingsManager extends React.Component<{}> { // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }]; const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent }, - { title: "Appearance", ele: this.appearanceContent }]; + { title: "Appearance", ele: this.appearanceContent }, { title: "Text", ele: this.textContent }]; return <div className="settings-interface"> <div className="settings-panel"> diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss index 2bac03af4..8a0e5480e 100644 --- a/src/client/views/AntimodeMenu.scss +++ b/src/client/views/AntimodeMenu.scss @@ -6,9 +6,9 @@ z-index: 10001; height: $antimodemenu-height; background: $dark-gray; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); + border-bottom: $standard-border; + // box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); // border-radius: 0px 6px 6px 6px; - z-index: 1001; display: flex; &.with-rows { diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss index 2a0b494f5..157f3a4f2 100644 --- a/src/client/views/DocumentButtonBar.scss +++ b/src/client/views/DocumentButtonBar.scss @@ -44,18 +44,19 @@ $linkGap : 3px; } .documentButtonBar { - margin-top: $linkGap; - grid-column: 1/4; - width: max-content; - height: auto; display: flex; flex-direction: row; + gap: 3px; } .documentButtonBar-button { - pointer-events: auto; - padding-right: 5px; - width: 25px; + cursor: pointer; + display: flex; + width: 30px; + height: 30px; + align-content: center; + justify-content: center; + align-items: center; } .documentButtonBar-linker { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index a5d80cd22..1e5380971 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../fields/Doc"; +import { Doc, DocCastAsync } from "../../fields/Doc"; import { RichTextField } from '../../fields/RichTextField'; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from "../../Utils"; @@ -24,7 +24,7 @@ import { DocumentView } from './nodes/DocumentView'; import { GoogleRef } from "./nodes/formattedText/FormattedTextBox"; import { TemplateMenu } from "./TemplateMenu"; import React = require("react"); -import { PresBox } from './nodes/PresBox'; +import { PresBox } from './nodes/trails/PresBox'; import { undoBatch } from '../util/UndoManager'; import { CollectionViewType } from './collections/CollectionView'; const higflyout = require("@hig/flyout"); @@ -348,16 +348,17 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV if (!this.view0) return (null); const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField; + const doc = this.view0?.props.Document; const considerPull = isText && this.considerGoogleDocsPull; const considerPush = isText && this.considerGoogleDocsPush; return <div className="documentButtonBar"> <div className="documentButtonBar-button"> <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} /> </div> - {DocumentLinksButton.StartLink || !Doc.UserDoc()["documentLinksButton-fullMenu"] ? <div className="documentButtonBar-button"> + {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink != doc ? <div className="documentButtonBar-button"> <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} /> </div> : (null)} - {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button"> + {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button"> {this.templateButton} </div> /*<div className="documentButtonBar-button"> diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 1715f35e7..952d8d150 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -263,7 +263,6 @@ $linkGap : 3px; } .link-button-container { - padding: $linkGap; border-radius: 10px; width: max-content; height: auto; @@ -271,6 +270,9 @@ $linkGap : 3px; flex-direction: row; z-index: 998; position: absolute; + justify-content: center; + align-items: center; + gap: 5px; background: $medium-gray; } diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 07ca0257c..d913f2069 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -22,10 +22,6 @@ height: 100%; } -.mainContent-div-flyout { - left: calc(-1 * var(--flyoutHandleWidth)); -} - // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; @@ -111,6 +107,14 @@ user-select: none; } +.properties-container { + height: 100%; + position: relative; + left: 100%; + top: calc(-100% - 36px); + z-index: 3000; +} + .mainView-propertiesDragger { //background-color: rgb(140, 139, 139); background-color: $light-gray; @@ -118,7 +122,6 @@ width: 17px; position: absolute; top: 50%; - border: 1px black solid; border-radius: 0; border-top-left-radius: 10px; border-bottom-left-radius: 10px; @@ -141,18 +144,6 @@ } } -.mainiView-propertiesView { - display: flex; - flex-direction: column; - height: 100%; - position: absolute; - right: 0; - top: 0; - border-left: solid 1px; - z-index: 100000; - cursor: auto; -} - .mainView-innerContent, .mainView-innerContent-dark { display: contents; flex-direction: row; @@ -171,10 +162,10 @@ } .propertiesView { - right: 0; + left: 0; position: absolute; z-index: 2; - background-color: $medium-gray; + background-color: $light-gray; .editable-title { background-color: $light-gray; } @@ -220,6 +211,7 @@ .mainView-menuPanel { min-width: var(--menuPanelWidth); background-color: $dark-gray; + border-right: $standard-border; .collectionStackingView { scrollbar-width: none; @@ -419,31 +411,4 @@ display: block; width: 500px; height: 1000px; -} - -.lm_drag_tab { - padding: 0; - width: 15px !important; - height: 15px !important; - position: relative !important; - display: inline-flex !important; - align-items: center; - top: 0 !important; - right: unset !important; - left: 0 !important; -} -.lm_close_tab { - padding: 0; - width: 15px !important; - height: 15px !important; - position: relative !important; - display: inline-flex !important; - align-items: center; - top: 0 !important; - right: unset !important; - left: 0 !important; -} -.lm_tab, .lm_tab_active { - display: flex !important; - padding-right: 0 !important; }
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 836b8130b..edb2aca75 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -63,6 +63,8 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { SearchBox } from './search/SearchBox'; import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider'; +import { TopBar } from './topbar/TopBar'; +import { Colors } from './global/globalEnums'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -78,7 +80,7 @@ export class MainView extends React.Component { @observable private _sidebarContent: any = this.userDoc?.sidebar; @observable private _flyoutWidth: number = 0; - @computed private get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } + @computed private get topOffset() { return Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove @computed private get leftOffset() { return this.menuPanelWidth() - 2; } @computed private get userDoc() { return Doc.UserDoc(); } @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } @@ -178,12 +180,12 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { const targClass = targets[0].className.toString(); - if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { - const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); - !check && SearchBox.Instance.resetSearch(true); - } + // if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { + // const check = targets.some((thing) => + // (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || + // thing.className === "collectionSchema-header-menuOptions")); + // !check && SearchBox.Instance.resetSearch(true); + // } !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu(); } @@ -192,7 +194,7 @@ export class MainView extends React.Component { initEventListeners = () => { window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page window.addEventListener("dragover", e => e.preventDefault(), false); - document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); + // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); document.addEventListener("pointerdown", this.globalPointerDown); document.addEventListener("click", (e: MouseEvent) => { if (!e.cancelBubble) { @@ -242,8 +244,9 @@ export class MainView extends React.Component { } getPWidth = () => this._panelWidth - this.propertiesWidth(); - getPHeight = () => this._panelHeight; + getPHeight = () => this._panelHeight - (CollectionMenu.Instance?.Pinned ? 35 : 0); getContentsHeight = () => this._panelHeight; + getMenuPanelHeight = () => this._panelHeight + (CollectionMenu.Instance?.Pinned ? 35 : 0); @computed get mainDocView() { return <DocumentView key="main" @@ -275,10 +278,12 @@ export class MainView extends React.Component { @computed get dockingContent() { return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }} + // style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.propertiesWidth()}px)` }}> + // FIXME update with property panel width style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined, - width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)` + //TODO:glr width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)` }}> {!this.mainContainer ? (null) : this.mainDocView} </div>; @@ -358,7 +363,7 @@ export class MainView extends React.Component { removeDocument={returnFalse} ScreenToLocalTransform={this.sidebarScreenToLocal} PanelWidth={this.menuPanelWidth} - PanelHeight={this.getContentsHeight} + PanelHeight={this.getMenuPanelHeight} renderDepth={0} docViewPath={returnEmptyDoclist} focus={DocUtils.DefaultFocus} @@ -401,20 +406,27 @@ export class MainView extends React.Component { } @computed get mainInnerContent() { + const width = this.propertiesWidth() + this._flyoutWidth + this.menuPanelWidth(); + const transform = this._flyoutWidth ? 'translate(-28px, 0px)' : undefined; return <> {this.menuPanel} <div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}> {this.flyout} - <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined, }} onPointerDown={this.onFlyoutPointerDown} > + <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} > <FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" /> </div> + <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}> + <CollectionMenu /> - {this.dockingContent} + {this.dockingContent} - <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> - <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" /> + <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._flyoutWidth ? 0 : this.propertiesWidth() - 1 }}> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? Colors.WHITE : Colors.BLACK} size="sm" /> + </div> + <div className="properties-container"> + {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />} + </div> </div> - {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />} </div> </>; } @@ -525,35 +537,8 @@ export class MainView extends React.Component { @computed get search() { TraceMobx(); - return <div className="mainView-searchPanel"> - <SearchBox Document={CurrentUserUtils.MySearchPanelDoc} - DataDoc={CurrentUserUtils.MySearchPanelDoc} - fieldKey="data" - dropAction="move" - isSelected={returnTrue} - isContentActive={returnTrue} - select={returnTrue} - setHeight={returnFalse} - addDocument={undefined} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - rootSelected={returnTrue} - styleProvider={DefaultStyleProvider} - layerProvider={undefined} - removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} - PanelWidth={this.getPWidth} - PanelHeight={this.getPHeight} - renderDepth={0} - focus={DocUtils.DefaultFocus} - docViewPath={returnEmptyDoclist} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> + return <div className="mainView-topbar"> + <TopBar /> </div>; } @@ -605,7 +590,6 @@ export class MainView extends React.Component { <GoogleAuthenticationManager /> <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} /> {this.search} - <CollectionMenu /> {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)} {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 4df3e4f00..8136edf04 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -24,7 +24,7 @@ import { EditableView } from "./EditableView"; import { InkStrokeProperties } from "./InkStrokeProperties"; import { DocumentView, StyleProviderFunc } from "./nodes/DocumentView"; import { KeyValueBox } from "./nodes/KeyValueBox"; -import { PresBox } from "./nodes/PresBox"; +import { PresBox } from "./nodes/trails/PresBox"; import { PropertiesButtons } from "./PropertiesButtons"; import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector"; import "./PropertiesView.scss"; diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss index 56346b68b..fd0ac9d5c 100644 --- a/src/client/views/_nodeModuleOverrides.scss +++ b/src/client/views/_nodeModuleOverrides.scss @@ -1,8 +1,50 @@ +@import "./global/globalCssVariables"; // this file is for overriding all the css from installed node modules // goldenlayout stuff div .lm_header { background: $dark-gray; + overflow: hidden; + height: 27px !important; +} + +/* Width */ +.lm_header::-webkit-scrollbar { + -webkit-appearance: none; + display: none; +} + +/* Width */ +.lm_header:hover::-webkit-scrollbar { + -webkit-appearance: none; + display: block; + height: 0px; +} + +/* Track */ +.lm_header:hover::-webkit-scrollbar-track { + -webkit-appearance: none; + display: none; +} + +/* Handle */ +.lm_header:hover::-webkit-scrollbar-thumb { + -webkit-appearance: none; + background: $dark-gray; +} + +/* Handle on hover */ +.lm_header:hover::-webkit-scrollbar-thumb:hover { + -webkit-appearance: none; + background: $dark-gray; +} + +.lm_tabs { + display: flex; + position: absolute; + width: calc(100% - 60px); + overflow: scroll; + background: $dark-gray; } .lm_tab { @@ -15,7 +57,14 @@ div .lm_header { } .lm_header .lm_controls { - right: 1em !important; + align-items: center; + position: absolute; + background-color: $dark-gray; + border-radius: 5px; + display: flex; + justify-content: space-evenly; + height: 23px; + width: 65px; } // @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index a054f0ae1..77e7b86ea 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,40 +1,46 @@ -@import "../../views/global/globalCssVariables.scss"; +@import "../global/globalCssVariables.scss"; .lm_title { - margin-top: 3px; - border-radius: 5px; - border: solid 0px dimgray; - border-width: 2px 2px 0px; - height: 20px; - transform: translate(0px, -3px); + -webkit-appearance: none; + display: inline-block; + align-self: center; + align-items: center; + height: 100%; + overflow: hidden; + text-overflow: ellipsis; + background: transparent; + border: solid 0px transparent; cursor: grab; + color: $black; } .lm_title.focus-visible { + -webkit-appearance: none; cursor: text; } .lm_title_wrap { overflow: hidden; - height: 19px; - margin-top: -2px; - display: inline-block; + align-items: center; + align-self: center; + background: transparent; + width: max-content; + height: 100%; + display: flex; } .lm_active .lm_title { - border: solid 1px lightgray; -} - -.lm_header .lm_tab .lm_close_tab { - position: absolute; - text-align: center; + -webkit-appearance: none; + // font-weight: 700; } .lm_header .lm_tab { - padding-right: 20px; - margin-top: -1px; - border-bottom: 1px black; + padding: 0px; + opacity: 0.7; + box-shadow: none; + height: 24px; + // border-bottom: 1px black; .collectionDockingView-gear { display: none; @@ -42,9 +48,13 @@ } .lm_header .lm_tab.lm_active { - padding-right: 20px; - margin-top: 1px; - border-bottom: unset; + padding: 0; + opacity: 1; + margin: 0; + box-shadow: none; + height: 27px; + margin-right: 2px; + // border-bottom: unset; .collectionDockingView-gear { display: inline-block; @@ -55,6 +65,41 @@ display: inline; } +.lm_drag_tab { + padding: 0; + width: 15px !important; + height: 15px !important; + position: relative !important; + display: inline-flex !important; + align-items: center; + top: 0 !important; + right: unset !important; + left: 0 !important; +} + +.lm_close_tab { + padding: 0; + align-self: center; + margin-right: 5px; + background-color: black; + border-radius: 3px; + opacity: 1 !important; + width: 15px !important; + height: 15px !important; + position: relative !important; + display: inline-flex !important; + align-items: center; + top: 0 !important; + right: unset !important; + left: 0 !important; +} + +.lm_tab, +.lm_tab_active { + display: flex !important; + padding-right: 0 !important; +} + .collectiondockingview-container { width: 100%; height: 100%; @@ -82,16 +127,17 @@ } .lm_content { - background: white; + background: $white; } .lm_controls>li { - opacity: 0.6; - transform: scale(1.2); + opacity: 1; + transform: scale(1); } .lm_controls .lm_popout { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAAAAABHICnvAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfkCBsXMgbrEyzaAAAAT0lEQVQY02NgIAcIu8tgEW3/u4IDQ5B14/8LQlhFhckVFfCJjIyIOfP/QWpEZGSQJFS05s9fIPj3/z+YmseCTxS7CZS7DI+PsYcOjpAkDAA6H0KZxzDzlgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMC0wOC0yN1QyMzo1MDowNi0wNDowMDvgVpQAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjAtMDgtMjdUMjM6NTA6MDYtMDQ6MDBKve4oAAAAAElFTkSuQmCC) + transform: rotate(45deg); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAQUlEQVR4nHXOQQ4AMAgCQeT/f6aXpsGK3jSTuCVJAAr7iBdoAwCKd0nwfaAdHbYERw5b44+E8JoBjEYGMBq5gAYP3usUDu2IvoUAAAAASUVORK5CYII=); } .lm_maximised .lm_controls .lm_maximise { @@ -311,8 +357,6 @@ background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center; } - .flexlayout__popup_menu {} - .flexlayout__popup_menu_item { padding: 2px 10px 2px 10px; color: #ddd; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 388f9a909..a8471f8e2 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -445,4 +445,4 @@ Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(d "opens up document in a lightbox", "(doc: any)"); Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, "opens up document in tab on right side of the screen", "(doc: any)"); -Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); +Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss index ec8805907..86610ac20 100644 --- a/src/client/views/collections/CollectionLinearView.scss +++ b/src/client/views/collections/CollectionLinearView.scss @@ -20,19 +20,21 @@ } .bottomPopup-background { - padding-right: 14px; + background: $light-blue; + display: flex; height: 35; - transform: translate3d(6px, 5px, 0px); - padding-top: 6.5px; - padding-bottom: 7px; - padding-left: 5px; + transform: translate3d(6px, 0px, 0px); + align-content: center; + justify-content: center; + align-items: center; } .bottomPopup-text { + color: black; display: inline; white-space: nowrap; padding-left: 8px; - padding-right: 4px; + padding-right: 20px; vertical-align: middle; font-size: 12.5px; } @@ -43,8 +45,8 @@ padding-left: 8px; padding-right: 8px; vertical-align: middle; - background-color: lightgrey; - border-radius: 5.5px; + background-color: #efefef; + border-radius: 3px; color: black; margin-right: 5px; } @@ -52,11 +54,12 @@ .bottomPopup-exit { display: inline; white-space: nowrap; + margin-right: 10px; padding-left: 8px; padding-right: 8px; vertical-align: middle; - background-color: lightgrey; - border-radius: 5.5px; + background-color: #f3b6b6; + border-radius: 3px; color: black; } diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index e0b90304b..52c836556 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -167,24 +167,22 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { })} </div> {DocumentLinksButton.StartLink ? <span className="bottomPopup-background" style={{ - background: backgroundColor === color ? "black" : backgroundColor, pointerEvents: "all" }} onPointerDown={e => e.stopPropagation()} > <span className="bottomPopup-text" > - Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'} + Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b> </span> - <Tooltip title={<><div className="dash-tooltip">{LinkDescriptionPopup.showDescriptions ? "Turn off description pop-up" : - "Turn on description pop-up"} </div></>} placement="top"> + <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top"> <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}> Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} </span> </Tooltip> - <Tooltip title={<><div className="dash-tooltip">Exit link clicking mode </div></>} placement="top"> + <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top"> <span className="bottomPopup-exit" onClick={this.exitLongLinks}> - Clear + Stop </span> </Tooltip> diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index c0fc774d3..f04b19ef7 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -38,10 +38,10 @@ border: unset; .collectionMenu-divider { - height: 85%; + height: 100%; margin-left: 3px; margin-right: 3px; - width: 1.5px; + width: 2px; background-color: $medium-gray; } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 3f2cc27a6..f96615ded 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -29,7 +29,7 @@ import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; -import { PresBox } from "../nodes/PresBox"; +import { PresBox } from "../nodes/trails/PresBox"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss index 9acbc4f85..a963f1cb9 100644 --- a/src/client/views/collections/TabDocView.scss +++ b/src/client/views/collections/TabDocView.scss @@ -1,19 +1,62 @@ input.lm_title:focus, -input.lm_title -{ +input.lm_title { max-width: unset !important; + outline: none; transition-delay: unset; - width: 100%; + width: max-content; cursor: text; } + input.lm_title { transition-delay: 0.35s; - width: 100px; + width: max-content; cursor: pointer; } -.tabDocView-drag { - margin: auto; + +.lm_iconWrap { + display: flex; + color: black; + width: 15px; + height: 15px; + align-items: center; + align-self: center; + justify-content: center; + margin: 3px; + border-radius: 20%; + + .moreInfoDot { + background-color: white; + border-radius: 100%; + width: 3px; + height: 3px; + margin: 0.5px; + } +} + +.ffMenu { + display: grid; + grid-auto-rows: 35px; + grid-auto-columns: auto auto auto auto auto; + right: 10px; + bottom: 50px; + position: absolute; + min-height: 35px; + height: max-content; + border: solid 2px black; + border-radius: 5px; + background-color: #bddbe6; + width: max-content; + min-width: 35px; + + .ffMenuButton { + display: flex; + width: 35px; + height: 35px; + align-items: center; + justify-content: center; + } } + .miniMap-hidden, .miniMap { position: absolute; @@ -37,6 +80,7 @@ input.lm_title { } } } + .miniMap-hidden { position: absolute; bottom: 0; @@ -46,7 +90,8 @@ input.lm_title { transform: translate(20px, 20px) rotate(45deg); border-radius: 30px; padding: 2px; - > svg { + + >svg { margin-top: 3px; transform: translate(0px, 7px); } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 12f5dd155..f2ed2a5a2 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -1,3 +1,4 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import 'golden-layout/src/css/goldenlayout-base.css'; @@ -9,9 +10,9 @@ import * as ReactDOM from 'react-dom'; import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { FieldId } from "../../../fields/RefField"; -import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -24,15 +25,15 @@ import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { LightboxView } from '../LightboxView'; import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView"; -import { FieldViewProps } from '../nodes/FieldView'; -import { PinProps, PresBox, PresMovement } from '../nodes/PresBox'; +import { PinProps, PresBox, PresMovement } from '../nodes/trails'; import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; -import { CollectionViewType, CollectionView } from './CollectionView'; +import { CollectionView, CollectionViewType } from './CollectionView'; import "./TabDocView.scss"; import React = require("react"); +import Color = require('color'); const _global = (window /* browser */ || global /* node */) as any; interface TabDocViewProps { @@ -52,6 +53,14 @@ export class TabDocView extends React.Component<TabDocViewProps> { @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); } + @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); } + // @computed get renderBounds() { + // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; + // const xbounds = bounds[2] - bounds[0]; + // const ybounds = bounds[3] - bounds[1]; + // const dim = Math.max(xbounds, ybounds); + // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; + // } get stack() { return (this.props as any).glContainer.parent.parent; } get tab() { return (this.props as any).glContainer.tab; } @@ -65,15 +74,25 @@ export class TabDocView extends React.Component<TabDocViewProps> { tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true); tab.DashDoc = doc; CollectionDockingView.Instance.tabMap.add(tab); - + const iconType: IconProp = Doc.toIcon(doc); // setup the title element and set its size according to the # of chars in the title. Show the full title when clicked. const titleEle = tab.titleElement[0]; + const iconWrap = document.createElement("div"); + const closeWrap = document.createElement("div"); + + titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; titleEle.onchange = undoBatch(action((e: any) => { titleEle.size = e.currentTarget.value.length + 3; Doc.GetProto(doc).title = e.currentTarget.value; })); + + const dragBtnDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction); + }; + + if (tab.element[0].children[1].children.length === 1) { const toggle = document.createElement("div"); toggle.style.width = "10px"; @@ -83,18 +102,42 @@ export class TabDocView extends React.Component<TabDocViewProps> { toggle.style.borderTopRightRadius = "7px"; toggle.style.position = "relative"; toggle.style.display = "inline-block"; - toggle.style.background = "gray"; - toggle.style.borderLeft = "solid 1px black"; + toggle.style.background = "transparent"; toggle.onclick = (e: MouseEvent) => { if (tab.contentItem === tab.header.parent.getActiveContentItem()) { tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background; } }; - tab.element[0].style.borderTopRightRadius = "8px"; - tab.element[0].children[1].appendChild(toggle); - tab._disposers.layerDisposer = reaction(() => - ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), - ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true }); + iconWrap.className = "lm_iconWrap"; + iconWrap.id = "lm_iconWrap"; + closeWrap.className = "lm_iconWrap"; + closeWrap.id = "lm_closeWrap"; + closeWrap.onclick = (e: MouseEvent) => { + tab.header.parent.contentItem.remove(); + Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); + }; + const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />; + const closeIcon = <FontAwesomeIcon icon={"times"} />; + ReactDOM.render(docIcon, iconWrap); + ReactDOM.render(closeIcon, closeWrap); + // tab.element[0].append(closeWrap); + tab.element[0].prepend(iconWrap); + tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), + ({ layer, color }) => { + const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color + titleEle.style.color = textColor; + titleEle.style.backgroundColor = "transparent"; + iconWrap.style.color = textColor; + closeWrap.style.color = textColor; + moreInfoDrag.style.backgroundColor = textColor; + tab.element[0].style.background = !layer ? color : "dimgrey"; + }, { fireImmediately: true }); + // TODO:glr fix + // tab.element[0].style.borderTopRightRadius = "8px"; + // tab.element[0].children[1].appendChild(toggle); + // tab._disposers.layerDisposer = reaction(() => + // ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), + // ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true }); } // shifts the focus to this tab when another tab is dragged over it tab.element[0].onmouseenter = (e: MouseEvent) => { @@ -103,13 +146,11 @@ export class TabDocView extends React.Component<TabDocViewProps> { tab.setActive(true); } }; - const dragBtnDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction); - }; + // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected titleEle.onpointerdown = action((e: any) => { - if (e.target.className !== "lm_close_tab") { + if (e.target.className !== "lm_iconWrap") { if (this.view) SelectionManager.SelectView(this.view, false); else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); @@ -123,25 +164,30 @@ export class TabDocView extends React.Component<TabDocViewProps> { const toggle = tab.element[0].children[1].children[0] as HTMLInputElement; selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch"); - toggle.style.fontWeight = selected ? "bold" : ""; - toggle.style.textTransform = selected ? "uppercase" : ""; + // toggle.style.fontWeight = selected ? "bold" : ""; + // toggle.style.textTransform = selected ? "uppercase" : ""; })); //attach the selection doc buttons menu to the drag handle - const stack = tab.contentItem.parent; - const dragHdl = document.createElement("div"); - dragHdl.className = "lm_drag_tab"; + const stack: HTMLDivElement = tab.contentItem.parent; + const header: HTMLDivElement = tab; + console.log("Stack: " + stack.id, stack.className) + stack.onscroll = action((e: any) => { + console.log('scrolling...') + }) + const moreInfoDrag = document.createElement("div"); + moreInfoDrag.className = "lm_iconWrap"; tab._disposers.buttonDisposer = reaction(() => this.view, view => - view && [ReactDOM.render(<span className="tabDocView-drag" onPointerDown={dragBtnDown}><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, dragHdl), tab._disposers.buttonDisposer?.()], + view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()], { fireImmediately: true }); - tab.reactComponents = [dragHdl]; - tab.closeElement.before(dragHdl); + // tab.reactComponents = [moreInfoDrag]; + // tab.element[0].children[3].before(moreInfoDrag); // highlight the tab when the tab document is brushed in any part of the UI tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { titleEle.value = title; - titleEle.style.padding = degree ? 0 : 2; - titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; + // titleEle.style.padding = degree ? 0 : 2; + // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; }, { fireImmediately: true }); // clean up the tab when it is closed @@ -221,9 +267,9 @@ export class TabDocView extends React.Component<TabDocViewProps> { })).observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined); - this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), - ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), - { fireImmediately: true }); + // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), + // ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), + // { fireImmediately: true }); } componentWillUnmount() { @@ -243,10 +289,10 @@ export class TabDocView extends React.Component<TabDocViewProps> { } // adds a tab to the layout based on the locaiton parameter which can be: - // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, + // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, // add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right - // replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents, - // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, + // replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents, + // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack addDocTab = (doc: Doc, location: string) => { @@ -460,4 +506,4 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { </div> </div>; } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8ef0057bd..143d8e070 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -38,7 +38,7 @@ import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDo import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; -import { PresBox } from "../../nodes/PresBox"; +import { PresBox } from "../../nodes/trails/PresBox"; import { StyleLayers, StyleProp } from "../../StyleProvider"; import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1f4fcb2a5..846d28214 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,7 +19,8 @@ import { Transform } from "../../../util/Transform"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { ContextMenu } from "../../ContextMenu"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; -import { PresBox, PresMovement } from "../../nodes/PresBox"; +import { PresBox } from "../../nodes/trails/PresBox"; +import { PresMovement } from "../../nodes/trails/PresEnums"; import { PreviewCursor } from "../../PreviewCursor"; import { CollectionDockingView } from "../CollectionDockingView"; import { SubCollectionViewProps } from "../CollectionSubView"; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index 0c434eae5..fd99abce5 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -103,6 +103,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { 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/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index ead5e166e..a9f33c4da 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -11,6 +11,8 @@ $medium-blue: #4476F7; $pink: #E0217D; $yellow: #F5D747; +$logout-red: #ca4444; + $drop-shadow: "#32323215"; //padding @@ -21,8 +23,6 @@ $large-padding: 32px; //icon sizes $icon-size: 28px; -$antimodemenu-height: 36px; - // fonts $sans-serif: "Noto Sans", sans-serif; $large-header: 16px; @@ -33,11 +33,16 @@ $small-text: 9px; // misc values $border-radius: 0.3em; $search-thumnail-size: 130; +$topbar-height: 32px; +$antimodemenu-height: 36px; // dragged items $contextMenu-zindex: 100000; // context menu shows up over everything $radialMenu-zindex: 100000; // context menu shows up over everything +// borders +$standard-border: solid 1px #9F9F9F; + $searchpanel-height: 32px; $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc $docDecorations-zindex: 998; // then doc decorations appear over everything else diff --git a/src/client/views/global/globalEnums.tsx b/src/client/views/global/globalEnums.tsx index 1e0381c33..2aeb8e338 100644 --- a/src/client/views/global/globalEnums.tsx +++ b/src/client/views/global/globalEnums.tsx @@ -31,4 +31,8 @@ export enum Padding { export enum IconSizes { ICON_SIZE = "28px", +} + +export enum Borders { + STANDARD = "solid 1px #9F9F9F" }
\ No newline at end of file diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index 839ebf894..e45a91d57 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -22,7 +22,7 @@ .linkEditor-info { //border-bottom: 0.5px solid $light-gray; //padding-bottom: 1px; - padding-top: 5px; + padding: 12px; padding-left: 5px; //margin-bottom: 6px; display: flex; @@ -61,7 +61,7 @@ } .linkEditor-description { - padding-left: 6.5px; + padding-left: 26px; padding-right: 6.5px; padding-bottom: 3.5px; @@ -107,9 +107,9 @@ } .linkEditor-followingDropdown { - padding-left: 6.5px; + padding-left: 26px; padding-right: 6.5px; - padding-bottom: 6px; + padding-bottom: 15px; &:hover { cursor: pointer; diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index a2ea42999..19c6463d3 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -7,20 +7,19 @@ z-index: 2001; .linkMenu-list, - .linkMenu-listEditor - { + .linkMenu-listEditor { display: inline-block; position: relative; - border: 1px solid black; - box-shadow: 3px 3px 1.5px grey; + border: 1px solid #e4e4e4; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); background: white; - min-width: 170px; - max-height: 170px; + max-height: 230px; overflow-y: scroll; z-index: 10; - } - .linkMenu-list { + } + + .linkMenu-list { white-space: nowrap; overflow-x: hidden; width: 240px; @@ -46,13 +45,13 @@ } .linkMenu-group-name { + padding: 10px; &:hover { - p { - background-color: lightgray; - - } + // p { + // background-color: lightgray; + // } p.expand-one { width: calc(100% + 20px); @@ -65,10 +64,9 @@ p { width: 100%; - //padding: 4px 6px; line-height: 12px; border-radius: 5px; - font-weight: bold; + text-transform: capitalize; } .linkEditor-tableButton { diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index c7888c5ee..6fc860447 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -15,6 +15,9 @@ interface Props { changeFlyout: () => void; } +/** + * the outermost component for the link menu of a node that contains a list of its linked nodes + */ @observer export class LinkMenu extends React.Component<Props> { private _editorRef = React.createRef<HTMLDivElement>(); @@ -36,6 +39,11 @@ export class LinkMenu extends React.Component<Props> { } } + /** + * maps each link to a JSX element to be rendered + * @param groups LinkManager containing info of all of the links + * @returns list of link JSX elements if there at least one linked element + */ renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => { const linkItems = Array.from(groups.entries()).map(group => <LinkMenuGroup diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 74af78234..c7586a467 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -40,7 +40,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { return ( <div className="linkMenu-group" ref={this._menuRef}> <div className="linkMenu-group-name"> - <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p> + <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"}> {this.props.groupType}:</p> </div> <div className="linkMenu-group-wrapper"> {groupItems} diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index 4f9881565..90722daf9 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -4,7 +4,7 @@ // border-top: 0.5px solid $medium-gray; position: relative; display: flex; - border-bottom: 0.5px solid black; + border-top: 0.5px solid #cdcdcd; padding-left: 6.5px; padding-right: 2px; @@ -55,8 +55,8 @@ .linkMenu-destination-title { text-decoration: none; - color: rgb(85, 120, 196); - font-size: 14px; + color: #4476F7; + font-size: 16px; padding-bottom: 2px; padding-right: 4px; margin-right: 4px; @@ -76,7 +76,7 @@ text-decoration: none; font-style: italic; color: rgb(95, 97, 102); - font-size: 10px; + font-size: 9px; margin-left: 20px; max-width: 125px; height: auto; diff --git a/src/client/views/linking/LinkPopup.scss b/src/client/views/linking/LinkPopup.scss new file mode 100644 index 000000000..8ae65158d --- /dev/null +++ b/src/client/views/linking/LinkPopup.scss @@ -0,0 +1,45 @@ +.linkPopup-container { + background: white; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); + top: 35px; + height: 200px; + width: 200px; + position: absolute; + padding: 15px; + border-radius: 3px; + + input { + border: 1px solid #b9b9b9; + border-radius: 20px; + height: 25px; + width: 100%; + padding-left: 10px; + } + + .divider { + margin: 10px 0; + height: 20px; + width: 100%; + + .line { + height: 1px; + background-color: #b9b9b9; + width: 100%; + position: relative; + top: 12px; + } + + .divider-text { + width: 20px; + background-color: white; + text-align: center; + position: relative; + margin: auto; + } + } + + + .searchBox-container { + background: pink; + } +}
\ No newline at end of file diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx new file mode 100644 index 000000000..2c4b718f4 --- /dev/null +++ b/src/client/views/linking/LinkPopup.tsx @@ -0,0 +1,114 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; +import { action, observable, runInAction } from 'mobx'; +import { observer } from "mobx-react"; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { Cast, StrCast } from '../../../fields/Types'; +import { WebField } from '../../../fields/URLField'; +import { emptyFunction, setupMoveUpEvents, returnFalse, returnTrue, returnEmptyDoclist, returnEmptyFilter } from '../../../Utils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { DocumentManager } from '../../util/DocumentManager'; +import { DragManager } from '../../util/DragManager'; +import { Hypothesis } from '../../util/HypothesisUtils'; +import { LinkManager } from '../../util/LinkManager'; +import { undoBatch } from '../../util/UndoManager'; +import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; +import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView'; +import { LinkDocPreview } from '../nodes/LinkDocPreview'; +import './LinkPopup.scss'; +import React = require("react"); +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { DefaultStyleProvider } from '../StyleProvider'; +import { Transform } from '../../util/Transform'; +import { DocUtils } from '../../documents/Documents'; +import { SearchBox } from '../search/SearchBox'; +import { EditorView } from 'prosemirror-view'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; + +interface LinkPopupProps { + showPopup: boolean; + // groupType: string; + // linkDoc: Doc; + // docView: DocumentView; + // sourceDoc: Doc; +} + +/** + * Popup component for creating links from text to Dash documents + */ + +@observer +export class LinkPopup extends React.Component<LinkPopupProps> { + @observable private linkURL: string = ""; + @observable public view?: EditorView; + + + + // TODO: should check for valid URL + @undoBatch + makeLinkToURL = (target: string, lcoation: string) => { + ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); + } + + @action + onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => { + this.linkURL = e.target.value; + console.log(this.linkURL) + } + + + getPWidth = () => 500; + getPHeight = () => 500; + + render() { + const popupVisibility = this.props.showPopup ? "block" : "none"; + return ( + <div className="linkPopup-container" style={{ display: popupVisibility }}> + <div className="linkPopup-url-container"> + <input autoComplete="off" type="text" value={this.linkURL} placeholder="Enter URL..." onChange={this.onLinkChange} /> + <button onPointerDown={e => this.makeLinkToURL(this.linkURL, "add:right")} + style={{ display: "block", margin: "10px auto", }}>Apply hyperlink</button> + </div> + <div className="divider"> + <div className="line"></div> + <p className="divider-text">or</p> + </div> + <div className="linkPopup-document-search-container"> + {/* <i></i> + <input defaultValue={""} autoComplete="off" type="text" placeholder="Search for Document..." id="search-input" + className="linkPopup-searchBox searchBox-input" /> */} + + <SearchBox Document={CurrentUserUtils.MySearchPanelDoc} + DataDoc={CurrentUserUtils.MySearchPanelDoc} + fieldKey="data" + dropAction="move" + isSelected={returnTrue} + isContentActive={returnTrue} + select={returnTrue} + setHeight={returnFalse} + addDocument={undefined} + addDocTab={returnTrue} + pinToPres={emptyFunction} + rootSelected={returnTrue} + styleProvider={DefaultStyleProvider} + layerProvider={undefined} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} + PanelWidth={this.getPWidth} + PanelHeight={this.getPHeight} + renderDepth={0} + focus={DocUtils.DefaultFocus} + docViewPath={returnEmptyDoclist} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} /> + </div> + </div> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index cb07c949f..b6fc04b73 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,7 +11,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView"; import { CollectionView } from "../collections/CollectionView"; import { InkingStroke } from "../InkingStroke"; -import { PresElementBox } from "../presentationview/PresElementBox"; +import { PresElementBox } from "../nodes/trails/PresElementBox"; import { SearchBox } from "../search/SearchBox"; import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; @@ -32,7 +32,7 @@ import { LabelBox } from "./LabelBox"; import { LinkAnchorBox } from "./LinkAnchorBox"; import { LinkBox } from "./LinkBox"; import { PDFBox } from "./PDFBox"; -import { PresBox } from "./PresBox"; +import { PresBox } from "./trails/PresBox"; import { ScreenshotBox } from "./ScreenshotBox"; import { ScriptingBox } from "./ScriptingBox"; import { SliderBox } from "./SliderBox"; diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss index daffaf9e7..9bab72d55 100644 --- a/src/client/views/nodes/DocumentLinksButton.scss +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -1,16 +1,27 @@ @import "../global/globalCssVariables.scss"; +.documentLinksButton-menu { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-content: center; + justify-content: center; + align-items: center; +} + .documentLinksButton-cont { min-width: 20; min-height: 20; position: absolute; } + .documentLinksButton, .documentLinksButton-endLink, .documentLinksButton-startLink { - height: 20px; - width: 20px; + height: 25px; + width: 25px; position: absolute; border-radius: 50%; opacity: 0.9; @@ -37,23 +48,21 @@ font-weight: bold; &:hover { - background: $medium-gray; transform: scale(1.05); cursor: pointer; } } .documentLinksButton-endLink { - border: red solid 2px; - + border: $medium-blue 2px dashed; + color: $medium-blue; &:hover { - background: deepskyblue; + background: $light-gray; transform: scale(1.05); cursor: pointer; } } .documentLinksButton-startLink { - border: red solid 2px; - background-color: rgba(255, 192, 203, 0.5); + background-color: $medium-blue; }
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index a6d07374a..cec06d2d4 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -20,6 +20,7 @@ import './DocumentLinksButton.scss'; import { DocServer } from "../../DocServer"; import { LightboxView } from "../LightboxView"; import { cat } from "shelljs"; +import { Colors } from "../global/globalEnums"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -30,12 +31,12 @@ interface DocumentLinksButtonProps { Offset?: (number | undefined)[]; AlwaysOn?: boolean; InMenu?: boolean; - StartLink?: boolean; + StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed) } @observer export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> { private _linkButton = React.createRef<HTMLDivElement>(); - @observable public static StartLink: Doc | undefined; + @observable public static StartLink: Doc | undefined; //origin's Doc, if defined @observable public static StartLinkView: DocumentView | undefined; @observable public static AnnotationId: string | undefined; @observable public static AnnotationUri: string | undefined; @@ -45,6 +46,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp public static invisibleWebRef = React.createRef<HTMLDivElement>(); @action public static ClearLinkEditor() { DocumentLinksButton.LinkEditorDocView = undefined; } + @action @undoBatch onLinkButtonMoved = (e: PointerEvent) => { if (this.props.InMenu && this.props.StartLink) { @@ -120,7 +122,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp if (DocumentLinksButton.StartLink === this.props.View.props.Document) { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; - } else { + } else { //if this LinkButton's Document is undefined DocumentLinksButton.StartLink = this.props.View.props.Document; DocumentLinksButton.StartLinkView = this.props.View; } @@ -131,6 +133,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } } + completeLink = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => { if (doubleTap && !this.props.StartLink) { @@ -141,7 +144,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) { const sourceDoc = DocumentLinksButton.StartLink; const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document; - const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "long drag"); + const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "links"); //why is long drag here when this is used for completing links by clicking? LinkManager.currentLink = linkDoc; @@ -184,7 +187,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } else if (startLink !== endLink) { endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink; startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink; - const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true); + const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "link", undefined, undefined, true); LinkManager.currentLink = linkDoc; @@ -242,45 +245,57 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp return results; } + /** + * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node) + */ @computed get linkButtonInner() { const btnDim = this.props.InMenu ? "20px" : "30px"; const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />; - return <div className="documentLinksButton-cont" ref={this._linkButton} - style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }} - > - <div className={"documentLinksButton"} - onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick} - style={{ - backgroundColor: this.props.InMenu ? "" : "#add8e6", - color: this.props.InMenu ? "white" : "black", - width: btnDim, - height: btnDim, - }} > - {this.props.InMenu ? - this.props.StartLink ? - <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> - : link - : Array.from(this.filteredLinks).length} - </div> - {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? - <div className={"documentLinksButton-endLink"} + return (!this.props.InMenu ? + <div className="documentLinksButton-cont" ref={this._linkButton} + style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }} + > + <div className={"documentLinksButton"} + onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick} style={{ - width: btnDim, height: btnDim, - backgroundColor: DocumentLinksButton.StartLink ? "" : "grey", - opacity: DocumentLinksButton.StartLink ? "" : "50%", - border: DocumentLinksButton.StartLink ? "" : "none", - cursor: DocumentLinksButton.StartLink ? "pointer" : "default" - }} - onPointerDown={DocumentLinksButton.StartLink && this.completeLink} - onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)} /> - : (null) - } - {DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? - <div className={"documentLinksButton-startLink"} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }} /> - : (null) - } - </div >; + backgroundColor: Colors.LIGHT_BLUE, + color: Colors.BLACK, + width: btnDim, + height: btnDim, + }}> + {Array.from(this.filteredLinks).length} + </div> + </div> + : + <div className="documentLinksButton-menu"> + {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node + <div className={"documentLinksButton-endLink"} + style={{ + width: btnDim, height: btnDim, + backgroundColor: DocumentLinksButton.StartLink ? "" : Colors.LIGHT_GRAY, + opacity: DocumentLinksButton.StartLink ? "" : "50%", + border: DocumentLinksButton.StartLink ? "" : "none", + cursor: DocumentLinksButton.StartLink ? "pointer" : "default" + }} + onPointerDown={DocumentLinksButton.StartLink && this.completeLink} + onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}> + {this.props.StartLink ? + <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> + : link} + </div> + : (null) + } + { + this.props.InMenu ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again + <div className={'documentLinksButton' + (DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink) ? '-startLink' : ''} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }}> + {this.props.StartLink ? + <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> + : link} + </div> + : (null)} + </div> + ) } render() { @@ -290,6 +305,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp const buttonTitle = "Tap to view links; double tap to open link collection"; const title = this.props.InMenu ? menuTitle : buttonTitle; + //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) : this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ? <Tooltip title={<div className="dash-tooltip">{title}</div>}> diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 281f25fb3..7f164ca48 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -147,7 +147,7 @@ .documentView-titleWrapper, .documentView-titleWrapper-hover { overflow: hidden; - color: gray; + color: $black; transform-origin: top left; top: 0; width: 100%; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a7b4c4171..4232f98b5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -43,7 +43,7 @@ import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkDocPreview } from "./LinkDocPreview"; -import { PresBox } from './PresBox'; +import { PresBox } from './trails/PresBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); import { ScriptingBox } from "./ScriptingBox"; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 6ae4b9726..0d415e238 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -14,6 +14,7 @@ import { DocComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import './FontIconBox.scss'; +import { Colors } from '../global/globalEnums'; const FontIconSchema = createSchema({ icon: "string", }); @@ -47,7 +48,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( const icon = StrCast(this.dataDoc.icon, "user") as any; const presSize = shape === 'round' ? 25 : 30; const presTrailsIcon = <img src={`/assets/${"presTrails.png"}`} - style={{ width: presSize, height: presSize, filter: `invert(${color === "white" ? "100%" : "0%"})`, marginBottom: "5px" }} />; + style={{ width: presSize, height: presSize, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})`, marginBottom: "5px" }} />; const button = <button className={`menuButton-${shape}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: backgroundColor, }}> <div className="menuButton-wrap"> diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6738d481d..5c3010dbc 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -215,6 +215,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp AnchorMenu.Instance.Status = "marquee"; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); + console.log("highlight") return undefined; }); /** diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 59b2d3753..2523dda38 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -852,6 +852,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { @undoBatch makeLinkToURL = (target: string, lcoation: string) => { ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); + console.log((this.view as any)?.TextView); } @undoBatch @@ -963,7 +964,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.createHighlighterButton(), this.createLinkButton(), this.createBrushButton(), - <div className="richTextMenu-divider" key="divider 2" />, + <div className="collectionMenu-divider" key="divider 2" />, this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight), @@ -976,7 +977,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const row2 = <div className="antimodeMenu-row row-2" key="row2"> {this.collapsed ? this.getDragger() : (null)} <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}> - <div className="richTextMenu-divider" key="divider 3" /> + <div className="collectionMenu-divider" key="divider 3" /> {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { this.activeFontSize = val; SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); @@ -985,12 +986,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.activeFontFamily = val; SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); })), - <div className="richTextMenu-divider" key="divider 4" />, + <div className="collectionMenu-divider" key="divider 4" />, this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), - this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule), - <div className="richTextMenu-divider" key="divider 5" />,]} + this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule) + ]} </div> {/* <div key="collapser"> {<div key="collapser"> diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 5d1c5f4eb..06932d145 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -1,4 +1,4 @@ -@import "../global/globalCssVariables"; +@import "../../global/globalCssVariables"; .presBox-cont { cursor: auto; @@ -889,7 +889,7 @@ height: 13; font-size: 12; display: flex; - background-color: #white; + background-color: $white; } .subtitle { @@ -926,7 +926,7 @@ .presBox-buttons { position: relative; width: 100%; - background: gray; + background: $medium-gray; min-height: 35px; padding-top: 5px; padding-bottom: 5px; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 11a735645..033371e96 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -5,67 +5,33 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; -import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../fields/Doc"; -import { documentSchema } from "../../../fields/documentSchemas"; -import { InkTool } from "../../../fields/InkField"; -import { List } from "../../../fields/List"; -import { PrefetchProxy } from "../../../fields/Proxy"; -import { listSpec, makeInterface } from "../../../fields/Schema"; -import { ScriptField } from "../../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { returnFalse, returnOne, returnTrue, emptyFunction } from '../../../Utils'; -import { Docs } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DocumentManager } from "../../util/DocumentManager"; -import { Scripting } from "../../util/Scripting"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionView, CollectionViewType } from "../collections/CollectionView"; -import { TabDocView } from "../collections/TabDocView"; -import { ViewBoxBaseComponent } from "../DocComponent"; -import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; -import { FieldView, FieldViewProps } from './FieldView'; +import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc"; +import { documentSchema } from "../../../../fields/documentSchemas"; +import { InkTool } from "../../../../fields/InkField"; +import { List } from "../../../../fields/List"; +import { PrefetchProxy } from "../../../../fields/Proxy"; +import { listSpec, makeInterface } from "../../../../fields/Schema"; +import { ScriptField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils'; +import { Docs } from "../../../documents/Documents"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import { DocumentManager } from "../../../util/DocumentManager"; +import { Scripting } from "../../../util/Scripting"; +import { SelectionManager } from "../../../util/SelectionManager"; +import { undoBatch, UndoManager } from "../../../util/UndoManager"; +import { CollectionDockingView } from "../../collections/CollectionDockingView"; +import { CollectionView, CollectionViewType } from "../../collections/CollectionView"; +import { TabDocView } from "../../collections/TabDocView"; +import { ViewBoxBaseComponent } from "../../DocComponent"; +import { Colors } from "../../global/globalEnums"; +import { LightboxView } from "../../LightboxView"; +import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentView"; +import { FieldView, FieldViewProps } from '../FieldView'; import "./PresBox.scss"; import Color = require("color"); -import { LightboxView } from "../LightboxView"; - -export enum PresMovement { - Zoom = "zoom", - Pan = "pan", - Jump = "jump", - None = "none", -} - -export enum PresEffect { - Zoom = "Zoom", - Lightspeed = "Lightspeed", - Fade = "Fade in", - Flip = "Flip", - Rotate = "Rotate", - Bounce = "Bounce", - Roll = "Roll", - None = "None", - Left = "left", - Right = "right", - Center = "center", - Top = "top", - Bottom = "bottom" -} - -enum PresStatus { - Autoplay = "auto", - Manual = "manual", - Edit = "edit" -} - -export enum PresColor { - LightBlue = "#AEDDF8", - DarkBlue = "#5B9FDD", - LightBackground = "#ececec", - SlideBackground = "#d5dce2", -} +import { PresEffect, PresStatus, PresMovement } from "./PresEnums"; export class PinProps { audioRange?: boolean; @@ -1207,9 +1173,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"} </div> : - <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}> + <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> {this.setMovementName(activeItem.presMovement, activeItem)} - <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} /> + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}> <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div> <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div> @@ -1254,7 +1220,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-doubleButton"> {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>} {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>} - <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip> </div> {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <> <div className="ribbon-doubleButton" > @@ -1289,9 +1255,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> {isPresCollection ? (null) : <div className="ribbon-box"> Effects - <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}> + <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> {effect} - <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} /> + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}> <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.None || !targetDoc.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div> <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div> @@ -1308,11 +1274,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> </div> <div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}> - <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${PresColor.LightBlue}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip> </div> </div>} <div className="ribbon-final-box"> @@ -1365,7 +1331,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <> {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)} <div className="ribbon-doubleButton"> - <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }} + <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : "" }} onClick={() => { activeItem.presPinView = !activeItem.presPinView; targetDoc.presPinView = activeItem.presPinView; @@ -1505,7 +1471,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> Start time (s) </div> - <div id={"startTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}> + <div id={"startTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}> <input className="presBox-input" style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} type="number" value={NumCast(activeItem.presStartTime)} @@ -1517,7 +1483,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> Duration (s) </div> - <div className="slider-number" style={{ backgroundColor: PresColor.LightBlue }}> + <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}> {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10} </div> </div> @@ -1525,7 +1491,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> End time (s) </div> - <div id={"endTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}> + <div id={"endTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}> <input className="presBox-input" style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} type="number" value={NumCast(activeItem.presEndTime)} @@ -1543,16 +1509,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this._batch = UndoManager.StartBatch("presEndTime"); const endBlock = document.getElementById("endTime"); if (endBlock) { - endBlock.style.color = PresColor.LightBackground; - endBlock.style.backgroundColor = PresColor.DarkBlue; + endBlock.style.color = Colors.LIGHT_GRAY; + endBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } }} onPointerUp={() => { this._batch?.end(); const endBlock = document.getElementById("endTime"); if (endBlock) { - endBlock.style.color = "black"; - endBlock.style.backgroundColor = PresColor.LightBackground; + endBlock.style.color = Colors.BLACK; + endBlock.style.backgroundColor = Colors.LIGHT_GRAY; } }} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { @@ -1567,16 +1533,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this._batch = UndoManager.StartBatch("presStartTime"); const startBlock = document.getElementById("startTime"); if (startBlock) { - startBlock.style.color = PresColor.LightBackground; - startBlock.style.backgroundColor = PresColor.DarkBlue; + startBlock.style.color = Colors.LIGHT_GRAY; + startBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } }} onPointerUp={() => { this._batch?.end(); const startBlock = document.getElementById("startTime"); if (startBlock) { - startBlock.style.color = "black"; - startBlock.style.backgroundColor = PresColor.LightBackground; + startBlock.style.color = Colors.BLACK; + startBlock.style.backgroundColor = Colors.LIGHT_GRAY; } }} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { @@ -1660,15 +1626,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div> <div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> <div className="layout-container" style={{ height: 'max-content' }}> - <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} /> - <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} /> + <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}> <div className="title">Title</div> <div className="subtitle">Subtitle</div> </div> - <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}> <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> </div> - <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}> <div className="title" style={{ alignSelf: 'center' }}>Title</div> <div className="content">Text goes here</div> </div> @@ -1700,26 +1666,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-box"> Choose type: <div className="ribbon-doubleButton"> - <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : PresColor.LightBlue }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div> - <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? PresColor.LightBlue : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div> + <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : Colors.LIGHT_BLUE }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div> + <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div> </div> </div> <div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}> Preset layouts: <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}> - <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'blank')} /> - <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'title')}> + <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'blank')} /> + <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'title')}> <div className="title">Title</div> <div className="subtitle">Subtitle</div> </div> - <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'header')}> + <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'header')}> <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> </div> - <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'content')}> + <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'content')}> <div className="title" style={{ alignSelf: 'center' }}>Title</div> <div className="content">Text goes here</div> </div> - <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'twoColumns')}> + <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'twoColumns')}> <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div> <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div> <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div> @@ -1879,8 +1845,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-box"> {this.stringType} selected <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeChild}>Contents</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? PresColor.LightBlue : "" }} onClick={this.editProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeChild}>Contents</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editProgressivize}>Edit</div> </div> <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}> <div className="presBox-subheading">Active text color</div> @@ -1895,12 +1861,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> {this.viewedColorPicker} <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeZoom}>Zoom</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.editZoomProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeZoom}>Zoom</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editZoomProgressivize}>Edit</div> </div> <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeScroll}>Scroll</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.editScrollProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeScroll}>Scroll</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editScrollProgressivize}>Edit</div> </div> </div> <div className="ribbon-final-box"> @@ -1910,7 +1876,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}> <FontAwesomeIcon icon={"caret-left"} size={"lg"} /> </div> - <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? PresColor.DarkBlue : PresColor.LightBlue }} + <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }} onClick={action(() => targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing)} > {NumCast(targetDoc._currentFrame)} </div> @@ -1924,7 +1890,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {this.frameListHeader} {this.frameList} </div> - <div className="ribbon-toggle" style={{ height: 20, backgroundColor: PresColor.LightBlue }} onClick={() => console.log(" TODO: play frames")}>Play</div> + <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(" TODO: play frames")}>Play</div> </div> </div> </div> @@ -2140,7 +2106,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>); } tags.push( - <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? PresColor.LightBlue : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}> + <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}> <div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div> <div className="progressivizeButton-frame">{doc.appearFrame}</div> <div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div> @@ -2223,6 +2189,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); + const activeColor = Colors.LIGHT_BLUE; + const inactiveColor = Colors.WHITE; return (mode === CollectionViewType.Carousel3D) ? (null) : ( <div id="toolbarContainer" className={'presBox-toolbar'}> {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}> @@ -2230,7 +2198,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} /> </div></Tooltip> */} <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}> - <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}> + <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}> <FontAwesomeIcon icon={"exchange-alt"} /> </div> </Tooltip> @@ -2239,7 +2207,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="toolbar-divider" /> {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}> <div className={"toolbar-button"} - style={{ color: this._expandBoolean ? PresColors.DarkBlue : 'white' }} + style={{ color: this._expandBoolean ? Colors.MEDIUM_BLUE : 'white' }} onClick={this.toggleExpandMode}> <FontAwesomeIcon icon={"eye"} /> </div> @@ -2247,12 +2215,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="toolbar-divider" /> */} <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}> <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}> - <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? PresColor.DarkBlue : 'white' }} /> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? activeColor : inactiveColor }} /> </div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}> <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}> - <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? PresColor.DarkBlue : 'white' }} /> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} /> </div> </Tooltip> </> @@ -2389,7 +2357,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); // Case 1: There are still other frames and should go through all frames before going to next slide return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip> @@ -2428,8 +2396,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ? <div className="miniPres"> - <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + PresColor.DarkBlue : undefined }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip> diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss index 1ad4b820e..1ad4b820e 100644 --- a/src/client/views/presentationview/PresElementBox.scss +++ b/src/client/views/nodes/trails/PresElementBox.scss diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 19893af7e..238d025dc 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -2,27 +2,29 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, Opt } from "../../../fields/Doc"; -import { documentSchema } from '../../../fields/documentSchemas'; -import { Id } from "../../../fields/FieldSymbols"; -import { createSchema, makeInterface } from '../../../fields/Schema'; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../Utils"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DocumentManager } from "../../util/DocumentManager"; -import { DragManager } from "../../util/DragManager"; -import { Transform } from "../../util/Transform"; -import { undoBatch } from "../../util/UndoManager"; -import { ViewBoxBaseComponent } from '../DocComponent'; -import { EditableView } from "../EditableView"; -import { DocumentView, DocumentViewProps } from "../nodes/DocumentView"; -import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import { PresBox, PresColor, PresMovement } from "../nodes/PresBox"; -import { StyleProp } from "../StyleProvider"; +import { DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { Id } from "../../../../fields/FieldSymbols"; +import { createSchema, makeInterface } from '../../../../fields/Schema'; +import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import { DocumentManager } from "../../../util/DocumentManager"; +import { DragManager } from "../../../util/DragManager"; +import { Transform } from "../../../util/Transform"; +import { undoBatch } from "../../../util/UndoManager"; +import { ViewBoxBaseComponent } from '../../DocComponent'; +import { EditableView } from "../../EditableView"; +import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView"; +import { FieldView, FieldViewProps } from '../../nodes/FieldView'; +import { PresBox } from "./PresBox"; +import { Colors } from "../../global/globalEnums"; +import { StyleProp } from "../../StyleProvider"; import "./PresElementBox.scss"; import React = require("react"); -import { DocUtils } from "../../documents/Documents"; +import { DocUtils } from "../../../documents/Documents"; +import { PresMovement } from "./PresEnums"; export const presSchema = createSchema({ presentationTargetDoc: Doc, @@ -213,11 +215,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc const height = slide.clientHeight; const halfLine = height / 2; if (y <= halfLine) { - slide.style.borderTop = "solid 2px #5B9FDD"; + slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; slide.style.borderBottom = "0px"; } else if (y > halfLine) { slide.style.borderTop = "0px"; - slide.style.borderBottom = "solid 2px #5B9FDD"; + slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; } } document.removeEventListener("pointermove", this.onPointerMove); @@ -296,7 +298,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc const miniView: boolean = this.toolbarWidth <= 110; const presBox: Doc = this.presBox; //presBox const presBoxColor: string = StrCast(presBox._backgroundColor); - const presColorBool: boolean = presBoxColor ? (presBoxColor !== "white" && presBoxColor !== "transparent") : false; + const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false; const targetDoc: Doc = this.targetDoc; const activeItem: Doc = this.rootDoc; return ( @@ -304,7 +306,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc key={this.props.Document[Id] + this.indexInPres} ref={this._itemRef} style={{ - backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? "#AEDDF8" : "transparent", + backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent", opacity: this._dragging ? 0.3 : 1 }} onClick={e => { @@ -360,7 +362,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc style={{ zIndex: 1000 - this.indexInPres, fontWeight: 700, - backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : PresColor.DarkBlue : undefined, + backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined, height: activeItem.groupWithUp ? 53 : 18, transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined }}> diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts new file mode 100644 index 000000000..93ab323fb --- /dev/null +++ b/src/client/views/nodes/trails/PresEnums.ts @@ -0,0 +1,28 @@ +export enum PresMovement { + Zoom = "zoom", + Pan = "pan", + Jump = "jump", + None = "none", +} + +export enum PresEffect { + Zoom = "Zoom", + Lightspeed = "Lightspeed", + Fade = "Fade in", + Flip = "Flip", + Rotate = "Rotate", + Bounce = "Bounce", + Roll = "Roll", + None = "None", + Left = "left", + Right = "right", + Center = "center", + Top = "top", + Bottom = "bottom" +} + +export enum PresStatus { + Autoplay = "auto", + Manual = "manual", + Edit = "edit" +}
\ No newline at end of file diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts new file mode 100644 index 000000000..8f3f7b03a --- /dev/null +++ b/src/client/views/nodes/trails/index.ts @@ -0,0 +1,3 @@ +export * from "./PresBox"; +export * from "./PresElementBox"; +export * from "./PresEnums";
\ No newline at end of file diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index c24c4eaaf..70ca19842 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -10,6 +10,7 @@ import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu"; import "./AnchorMenu.scss"; import { SelectionManager } from "../../util/SelectionManager"; +import { LinkPopup } from "../linking/LinkPopup"; @observer export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -38,6 +39,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @observable private _valueValue: string = ""; @observable private _added: boolean = false; @observable private highlightColor: string = "rgba(245, 230, 95, 0.616)"; + @observable private _showLinkPopup: boolean = false; @observable public _colorBtn = false; @observable public Highlighting: boolean = false; @@ -80,6 +82,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { } } + @action + toggleLinkPopup = (e: React.MouseEvent) => { + //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button + console.log(window.getSelection().toString()) + //change popup visibility field to visible + this._showLinkPopup = !this._showLinkPopup; + } + @computed get highlighter() { const button = <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}> @@ -136,6 +146,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { <FontAwesomeIcon icon="comment-alt" size="lg" /> </button> </Tooltip>, + + //NOTE: link popup is currently incomplete + // <Tooltip key="link" title={<div className="dash-tooltip">{"Link selected text to document or URL"}</div>}> + // <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}> + // <FontAwesomeIcon icon="link" size="lg" /> + // </button> + // </Tooltip>, + // <LinkPopup showPopup={this._showLinkPopup} /> ] : [ <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}> <button className="antimodeMenu-button" onPointerDown={this.Delete}> diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss new file mode 100644 index 000000000..ebdf030e7 --- /dev/null +++ b/src/client/views/topbar/TopBar.scss @@ -0,0 +1,213 @@ +@import "../global/globalCssVariables"; + +.topbar-container { + display: flex; + flex-direction: column; + width: 100%; + position: relative; + font-size: 10px; + line-height: 1; + overflow-y: auto; + overflow-x: visible; + background: $dark-gray; + overflow: visible; + z-index: 1000; + + .topbar-bar { + height: $topbar-height; + display: grid; + grid-auto-columns: 33.3% 33.3% 33.3%; + align-items: center; + background-color: $dark-gray; + + .topBar-icon { + cursor: pointer; + font-size: 12px; + width: fit-content; + display: flex; + justify-content: center; + gap: 4px; + align-items: center; + justify-self: center; + align-self: center; + border-radius: 5px; + padding: 5px; + transition: linear 0.1s; + color: $black; + background-color: $light-gray; + } + + .topBar-icon:hover { + background-color: $light-blue; + } + + + .topbar-center { + grid-column: 2; + display: inline-flex; + justify-content: center; + align-items: center; + gap: 5px; + + .topbar-lozenge-dashboard { + display: flex; + + .topbar-dashboards { + display: inline-flex; + } + + .topbar-dashSelect { + border: none; + background-color: $dark-gray; + color: $white; + font-family: 'Roboto'; + font-size: 17; + font-weight: 500; + + &:hover { + cursor: pointer; + } + } + } + } + + + .topbar-right { + grid-column: 3; + position: relative; + display: flex; + justify-content: flex-end; + gap: 5px; + margin-right: 5px; + } + + .topbar-left { + grid-column: 1; + color: black; + font-family: 'Roboto'; + position: relative; + display: flex; + width: 450; + gap: 5px; + + .topBar-icon:hover { + background-color: $logout-red; + } + + .topbar-lozenge-user, + .topbar-lozenge { + height: 23; + font-size: 12; + color: white; + font-family: 'Roboto'; + font-weight: 400; + padding: 4px; + align-self: center; + margin-left: 7px; + display: flex; + align-items: center; + + .topbar-dashSelect { + border: none; + background-color: transparent; + color: black; + font-family: 'Roboto'; + font-size: 17; + font-weight: 500; + + &:hover { + cursor: pointer; + } + } + } + + .topbar-logoff { + border-radius: 3px; + background: olivedrab; + color: white; + display: none; + margin-left: 5px; + padding: 1px 2px 1px 2px; + cursor: pointer; + } + + .topbar-logoff { + background: red; + } + + .topbar-lozenge-user:hover { + .topbar-logoff { + display: inline-block; + } + } + } + + .topbar-barChild { + + &.topbar-collection { + flex: 0 1 auto; + margin-left: 2px; + margin-right: 2px + } + + &.topbar-input { + margin:5px; + border-radius:20px; + border:$dark-gray; + display: block; + width: 130px; + -webkit-transition: width 0.4s; + transition: width 0.4s; + /* align-self: stretch; */ + outline: none; + + &:focus { + width: 500px; + outline: none; + } + } + + &.topbar-filter { + align-self: stretch; + + button { + transform: none; + + &:hover { + transform: none; + } + } + } + + &.topbar-submit { + margin-left: 2px; + margin-right: 2px + } + + &.topbar-close { + color: $white; + max-height: $topbar-height; + } + } + } +} + +.topbar-results { + display: flex; + flex-direction: column; + top: 300px; + display: flex; + flex-direction: column; + height: 100%; + overflow: visible; + + .no-result { + width: 500px; + background: $light-gray; + padding: 10px; + height: 50px; + text-transform: uppercase; + text-align: left; + font-weight: bold; + } +}
\ No newline at end of file diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx new file mode 100644 index 000000000..bd9935333 --- /dev/null +++ b/src/client/views/topbar/TopBar.tsx @@ -0,0 +1,67 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { observer } from "mobx-react"; +import * as React from 'react'; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { StrCast } from '../../../fields/Types'; +import { Utils } from '../../../Utils'; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { SettingsManager } from "../../util/SettingsManager"; +import { undoBatch } from "../../util/UndoManager"; +import { Borders, Colors } from "../global/globalEnums"; +import "./TopBar.scss"; + +/** + * REACT TYPE: FUNCTIONAL + * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user + * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard. + */ +@observer +export class TopBar extends React.Component { + render() { + const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data); + return ( + //TODO:glr Add support for light / dark mode + <div style={{ pointerEvents: "all" }} className="topbar-container"> + <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}> + <div className="topbar-left"> + <div className="topbar-lozenge-user"> + {`${Doc.CurrentUserEmail}`} + </div> + <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}> + {"Sign out"} + </div> + </div> + <div className="topbar-center" > + <div className="topbar-lozenge-dashboard"> + <select className="topbar-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])} + value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)} + style={{ color: Colors.WHITE }}> + {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)} + </select> + </div> + <div className="topbar-dashboards"> + <div className="topbar-icon" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))} + > + {"New"}<FontAwesomeIcon icon="plus"></FontAwesomeIcon> + </div> + {Doc.UserDoc().noviceMode ? (null) : <div className="topbar-icon" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))} + > + {"Snapshot"}<FontAwesomeIcon icon="camera"></FontAwesomeIcon> + </div>} + </div> + </div> + <div className="topbar-right" > + <div className="topbar-icon"> + {"Help"}<FontAwesomeIcon icon="question-circle"></FontAwesomeIcon> + </div> + <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}> + {"Settings"}<FontAwesomeIcon icon="cog"></FontAwesomeIcon> + </div> + + </div> + </div> + </div > + ); + } +}
\ No newline at end of file diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6b6f0fed0..3b8109385 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,6 +23,7 @@ import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField"; import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); +import { IconProp } from "@fortawesome/fontawesome-svg-core"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1194,7 +1195,10 @@ export namespace Doc { case DocumentType.IMG: return "image"; case DocumentType.COMPARISON: return "columns"; case DocumentType.RTF: return "sticky-note"; - case DocumentType.COL: return !doc?.isFolder ? "folder" + (isOpen ? "-open" : "") : "chevron-" + (isOpen ? "down" : "right"); + case DocumentType.COL: + const folder: IconProp = isOpen ? "folder-open" : "folder"; + const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right" + return !doc?.isFolder ? folder : chevron; case DocumentType.WEB: return "globe-asia"; case DocumentType.SCREENSHOT: return "photo-video"; case DocumentType.WEBCAM: return "video"; |