diff --git a/apps/ServiceWorker.mjs b/apps/ServiceWorker.mjs index 3ae1cbb90..43bf4fa38 100644 --- a/apps/ServiceWorker.mjs +++ b/apps/ServiceWorker.mjs @@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase { */ singleton: true, /** - * @member {String} version='6.7.5' + * @member {String} version='6.7.6' */ - version: '6.7.5' + version: '6.7.6' } /** diff --git a/apps/covid/view/country/Table.mjs b/apps/covid/view/country/Table.mjs index 493e66fa4..d24a1ca32 100644 --- a/apps/covid/view/country/Table.mjs +++ b/apps/covid/view/country/Table.mjs @@ -121,7 +121,7 @@ class Table extends Container { if (oldValue !== undefined) { let me = this, selectionModel = me.selectionModel, - view = me.getView(), + view = me.view, id; if (view) { @@ -158,7 +158,7 @@ class Table extends Container { if (me.store.getCount() > 0) { if (item) { - item = me.getView().getRecordByRowId(item)?.country; + item = me.view.getRecordByRowId(item)?.country; } // in case getRecordByRowId() has no match, the initial row creation will include the selection diff --git a/apps/sharedcovid/view/country/Table.mjs b/apps/sharedcovid/view/country/Table.mjs index 149d40405..1d2408093 100644 --- a/apps/sharedcovid/view/country/Table.mjs +++ b/apps/sharedcovid/view/country/Table.mjs @@ -124,7 +124,7 @@ class Table extends Container { id; if (value) { - id = `${me.getView().id}__tr__${value}`; // the store can not be loaded on the first selection + id = `${me.view.id}__tr__${value}`; // the store can not be loaded on the first selection if (!selectionModel.isSelected(id)) { selectionModel.select(id); @@ -155,7 +155,7 @@ class Table extends Container { if (me.store.getCount() > 0) { if (item) { - item = me.getView().getRecordByRowId(item)?.country; + item = me.view.getRecordByRowId(item)?.country; } // in case getRecordByRowId() has no match, the initial row creation will include the selection diff --git a/examples/ServiceWorker.mjs b/examples/ServiceWorker.mjs index 3ae1cbb90..43bf4fa38 100644 --- a/examples/ServiceWorker.mjs +++ b/examples/ServiceWorker.mjs @@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase { */ singleton: true, /** - * @member {String} version='6.7.5' + * @member {String} version='6.7.6' */ - version: '6.7.5' + version: '6.7.6' } /** diff --git a/examples/dialog/DemoDialog.mjs b/examples/dialog/DemoDialog.mjs index b4679844a..72fe6a369 100644 --- a/examples/dialog/DemoDialog.mjs +++ b/examples/dialog/DemoDialog.mjs @@ -8,10 +8,41 @@ import SelectField from '../../src/form/field/Select.mjs'; */ class DemoDialog extends Dialog { static config = { - className: 'Neo.examples.dialog.DemoWindow', - modal : true, - title : 'My Dialog', - + /** + * @member {String} className='Neo.examples.dialog.DemoDialog' + * @protected + */ + className: 'Neo.examples.dialog.DemoDialog', + /** + * Custom config to dynamically enable / disable the animateTargetId + * @member {Boolean} animated_=true + */ + animated_: true, + /** + * @member {Object} containerConfig + */ + containerConfig: { + style: { + padding: '1em' + } + }, + /** + * Custom config to show the current dialog number + * @member {Number} index=1 + */ + index: 1, + /** + * @member {Boolean} modal=true + */ + modal: true, + /** + * Custom config used by animated_ + * @member {String|null} optionalAnimateTargetId=null + */ + optionalAnimateTargetId: null, + /** + * @member {Object} wrapperStyle + */ wrapperStyle: { width : '40%' } @@ -26,8 +57,9 @@ class DemoDialog extends Dialog { const me = this; me.items = [{ - module : SelectField, - labelText: 'Select', + module : SelectField, + labelText : 'Select', + labelWidth: 80, store: { data: (() => { @@ -37,43 +69,90 @@ class DemoDialog extends Dialog { result.push({ id : i, name : `Option ${i + 1}` - }); + }) } - return result; + return result })() } }, { module : Button, handler : me.createDialog.bind(me), iconCls : 'fa fa-window-maximize', - reference: 'create-second-dialog-button', - text : 'Create new modal Dialog', + reference: 'create-dialog-button', + style : {marginTop: '3em'}, + text : 'Create Dialog ' + (me.index + 1), }] } + /** + * Triggered after the animated config got changed + * @param {Boolean} value + * @param {Boolean} oldValue + * @protected + */ + afterSetAnimated(value, oldValue) { + let me = this; + + me.animateTargetId = value ? me.optionalAnimateTargetId : null; + + if (me.dialog) { + me.dialog.animated = value + } + } + + /** + * Triggered after the modal config got changed + * @param {Boolean} value + * @param {Boolean} oldValue + * @protected + */ + afterSetModal(value, oldValue) { + super.afterSetModal(value, oldValue); + + if (this.dialog) { + this.dialog.modal = value + } + } + /** * @param {Object} data */ createDialog(data) { - let me = this; + let me = this, + button = data.component, + nextIndex = me.index + 1; - data.component.disabled = true; + button.disabled = true; me.dialog = Neo.create(DemoDialog, { - appName : me.appName, - boundaryContainerId: me.boundaryContainerId, - listeners : {close: me.onWindowClose, scope: me}, - modal : true, - title : 'Second Dialog' - }); + animated : me.animated, + appName : me.appName, + boundaryContainerId : me.boundaryContainerId, + index : nextIndex, + listeners : {close: me.onWindowClose, scope: me}, + modal : me.app.mainView.down({valueLabelText: 'Modal'}).checked, + optionalAnimateTargetId: button.id, + style : {left: me.getOffset(), top: me.getOffset()}, + title : 'Dialog ' + nextIndex + }) + } + + /** + * We want new dialogs to have a random left & top offset between -100px & 100px, + * to ensure they are not at the exact same position. + * @returns {String} + */ + getOffset() { + let offset = Math.floor(Math.random() * 200 - 100); + return `calc(50% + ${offset}px)` } /** * */ onWindowClose() { - this.getReference('create-second-dialog-button').disabled = false; + this.getReference('create-dialog-button').disabled = false } } diff --git a/examples/dialog/MainContainer.mjs b/examples/dialog/MainContainer.mjs index 1baf91e31..2aab843bc 100644 --- a/examples/dialog/MainContainer.mjs +++ b/examples/dialog/MainContainer.mjs @@ -38,16 +38,17 @@ class MainContainer extends Viewport { me.items = [{ module: Toolbar, items :[{ - module : Button, - handler: me.createDialog.bind(me), - iconCls: 'fa fa-window-maximize', - text : 'Create Dialog', + module : Button, + handler : me.createDialog.bind(me), + iconCls : 'fa fa-window-maximize', + reference: 'create-dialog-button', + text : 'Create Dialog', }, { module : CheckBox, checked : true, hideLabel : true, hideValueLabel: false, - listeners : {change: me.onDragLimitChange, scope: me}, + listeners : {change: me.onConfigChange.bind(me, 'boundaryContainerId')}, style : {marginLeft: '3em'}, valueLabelText: 'Limit Drag&Drop to the document.body' }, { @@ -55,7 +56,16 @@ class MainContainer extends Viewport { checked : true, hideLabel : true, hideValueLabel: false, + listeners : {change: me.onConfigChange.bind(me, 'animated')}, style : {marginLeft: '3em'}, + valueLabelText: 'Animated' + }, { + module : CheckBox, + checked : true, + hideLabel : true, + hideValueLabel: false, + listeners : {change: me.onConfigChange.bind(me, 'modal')}, + style : {marginLeft: '1em'}, valueLabelText: 'Modal' }, '->', { module : Button, @@ -75,30 +85,31 @@ class MainContainer extends Viewport { data.component.disabled = true; me.dialog = Neo.create(DemoDialog, { - animateTargetId : data.component.id, - appName : me.appName, - boundaryContainerId: me.boundaryContainerId, - listeners : {close: me.onWindowClose, scope: me}, - modal : me.down({ valueLabelText : 'Modal' }).checked - }); + animated : me.down({valueLabelText: 'Animated'}).checked, + appName : me.appName, + boundaryContainerId : me.boundaryContainerId, + listeners : {close: me.onWindowClose, scope: me}, + modal : me.down({valueLabelText: 'Modal'}).checked, + optionalAnimateTargetId: data.component.id, + title : 'Dialog 1' + }) } /** - * @param {Object} data + * @param {String} config + * @param {Object} opts */ - onDragLimitChange(data) { - this.dialog.boundaryContainerId = data.value ? 'document.body' : null + onConfigChange(config, opts) { + if (this.dialog) { + this.dialog[config] = opts.value ? 'document.body' : null + } } /** * */ onWindowClose() { - let button = this.down({ - text: 'Create Dialog' - }); - - button.disabled = false; + this.getReference('create-dialog-button').disabled = false } /** diff --git a/package.json b/package.json index 0dabdab33..c13473bf9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neo.mjs", - "version": "6.7.5", + "version": "6.7.6", "description": "The webworkers driven UI framework", "type": "module", "repository": { diff --git a/resources/scss/src/dialog/Base.scss b/resources/scss/src/dialog/Base.scss index 0329ab9c0..1b02edf8b 100644 --- a/resources/scss/src/dialog/Base.scss +++ b/resources/scss/src/dialog/Base.scss @@ -39,6 +39,12 @@ transition-timing-function: ease-out; } + &.neo-dragproxy { + > * { + height: fit-content; + } + } + &.neo-maximized { height : 98% !important; left : 1% !important; diff --git a/resources/scss/src/draggable/DragProxyComponent.scss b/resources/scss/src/draggable/DragProxyComponent.scss index 84c133c0d..48ba54935 100644 --- a/resources/scss/src/draggable/DragProxyComponent.scss +++ b/resources/scss/src/draggable/DragProxyComponent.scss @@ -14,12 +14,12 @@ } > * { - height : 100% !important; - left : 0 !important; - position : relative !important; - opacity : 1 !important; - top : 0 !important; - transform: none !important; - width : 100% !important; + height : 100%; + left : 0; + position : relative; + opacity : 1; + top : 0; + transform: none; + width : 100%; } -} \ No newline at end of file +} diff --git a/src/DefaultConfig.mjs b/src/DefaultConfig.mjs index a23298d48..48d0cafb9 100644 --- a/src/DefaultConfig.mjs +++ b/src/DefaultConfig.mjs @@ -236,12 +236,12 @@ const DefaultConfig = { useVdomWorker: true, /** * buildScripts/injectPackageVersion.mjs will update this value - * @default '6.7.5' + * @default '6.7.6' * @memberOf! module:Neo * @name config.version * @type String */ - version: '6.7.5' + version: '6.7.6' }; Object.assign(DefaultConfig, { diff --git a/src/dialog/Base.mjs b/src/dialog/Base.mjs index a8ce07ccb..ce4c0a081 100644 --- a/src/dialog/Base.mjs +++ b/src/dialog/Base.mjs @@ -136,11 +136,21 @@ class Base extends Panel { construct(config) { super.construct(config); - let me = this; + let me = this, + style = me.style; me.createHeader(); - me.autoShow && me.show() + if (!me.animateTargetId) { + Neo.assignDefaults(style, { + left : '50%', + top : '50%', + transform: 'translate(-50%, -50%)', + width : '50%' + }); + + me.style = style + } } /** @@ -224,7 +234,7 @@ class Base extends Panel { cls = me.vdom.cls; // todo: using wrapperCls NeoArray.toggle(cls, 'neo-maximized', value); - me.update(); + me.update() } /** @@ -234,16 +244,12 @@ class Base extends Panel { * @protected */ afterSetModal(value, oldValue) { - const - me = this, - { cls } = me.vdom; + let me = this; - NeoArray.toggle(cls, 'neo-modal', value); + NeoArray.toggle(me.vdom.cls, 'neo-modal', value); me.update(); - if (me.rendered) { - me.syncModalMask() - } + me.rendered && me.syncModalMask() } /** @@ -322,10 +328,12 @@ class Base extends Panel { me.closeOrHide(false); - await Neo.applyDeltas(appName, [ - {id, cls: {remove: ['animated-hiding-showing']}}, - {id, action: 'removeNode'} - ]) + if (me.closeAction === 'hide') { + await Neo.applyDeltas(appName, [ + {id, cls: {remove: ['animated-hiding-showing']}}, + {id, action: 'removeNode'} + ]) + } } /** @@ -360,7 +368,7 @@ class Base extends Panel { add: ['animated-hiding-showing'] }, style: { - height : style?.height || '', + height : style?.height || null, // height will point to the animation origin, so we need a reset left : style?.left || '50%', top : style?.top || '50%', transform: style?.transform || 'translate(-50%, -50%)', @@ -404,13 +412,20 @@ class Base extends Panel { * @param {Boolean} animate=!!this.animateTargetId */ async closeOrHide(animate=!!this.animateTargetId) { - const - me = this, - { id } = me; + let me = this; me[me.closeAction](animate); await me.timeout(30); - me.syncModalMask(id) + me.syncModalMask(me.id) + } + + /** + * Action when clicking the X button inside the header toolbar. + * @param {Object} data + * @protected + */ + closeOrHideAction(data) { + this.closeOrHide() } /** @@ -447,7 +462,7 @@ class Base extends Panel { let me = this, map = { - close : me.closeOrHide, + close : me.closeOrHideAction, maximize: me.maximize }; @@ -500,6 +515,23 @@ class Base extends Panel { me.syncModalMask() } + /** + * + */ + init() { + super.init(); + + let me = this; + + if (me.animateTargetId) { + me.autoShow && me.show() + } else { + me.timeout(100).then(() => { + me.syncModalMask() + }) + } + } + /** * @param {Object} [data] */ @@ -642,7 +674,7 @@ class Base extends Panel { */ syncModalMask(id=this.id) { // This should sync the visibility and position of the modal mask element. - Neo.main.DomAccess.syncModalMask({ id, modal: this.modal }); + Neo.main.DomAccess.syncModalMask({ id, modal: this.modal }) } } diff --git a/src/main/DomAccess.mjs b/src/main/DomAccess.mjs index 15d664e15..84cf96cf4 100644 --- a/src/main/DomAccess.mjs +++ b/src/main/DomAccess.mjs @@ -390,6 +390,10 @@ class DomAccess extends Base { return returnData } + /** + * @param {Object|String} data + * @returns {Neo.util.Rectangle} + */ getClippedRect(data) { let node = this.getElement(typeof data === 'object' ? data.id : data), { defaultView } = node.ownerDocument, @@ -404,21 +408,6 @@ class DomAccess extends Base { return rect } - onDocumentMutation(mutations) { - const me = this; - - // If the mutations are purely align subjects being added or removed, take no action. - if (!mutations.every(({ type, addedNodes, removedNodes }) => { - if (type === 'childList') { - const nodes = [...Array.from(addedNodes), ...Array.from(removedNodes)]; - - return nodes.every(a => me.isAlignSubject(a)) - } - })) { - me.syncAligns(); - } - } - /** * @param {String|HTMLElement} nodeId * @returns {HTMLElement} @@ -537,6 +526,24 @@ class DomAccess extends Base { return value; } + /** + * @param {Array} mutations + */ + onDocumentMutation(mutations) { + const me = this; + + // If the mutations are purely align subjects being added or removed, take no action. + if (!mutations.every(({ type, addedNodes, removedNodes }) => { + if (type === 'childList') { + const nodes = [...Array.from(addedNodes), ...Array.from(removedNodes)]; + + return nodes.every(a => me.isAlignSubject(a)) + } + })) { + me.syncAligns(); + } + } + /** * */ @@ -548,6 +555,7 @@ class DomAccess extends Base { /** * @param {Object} data + * @param {String} data.id * @param {String} data.nodeId */ onGetOffscreenCanvas(data) { @@ -627,7 +635,7 @@ class DomAccess extends Base { data, replyId: data.id, success: true - }); + }) } /** @@ -636,7 +644,7 @@ class DomAccess extends Base { */ read(data) { if (typeof data === 'function') { - data(); + data() } } @@ -836,6 +844,11 @@ class DomAccess extends Base { }) } + /** + * @param {Object} data + * @param {String} data.id + * @param {Boolean} data.modal + */ syncModalMask({ id, modal }) { const el = id && this.getElement(id); @@ -843,7 +856,7 @@ class DomAccess extends Base { if (el && modal && el.ownerDocument.contains(el) && el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('display') !== 'none') { document.body.insertBefore(this.modalMask, el); } - // Otherwise, the mask needs to be blow the next topmost modal dialog if possible, or hidden + // Otherwise, the mask needs to be below the next topmost modal dialog if possible, or hidden else { const modals = document.querySelectorAll('.neo-modal'), @@ -851,9 +864,8 @@ class DomAccess extends Base { // Move the mask under the next topmost modal now modal "id" is gone. if (topmostModal) { - this.syncModalMask({ id : topmostModal.id, modal : true }) - } - else { + this.syncModalMask({ id: topmostModal.id, modal: true }) + } else { this._modalMask?.remove() } } diff --git a/src/worker/App.mjs b/src/worker/App.mjs index 35c5f502b..34d991086 100644 --- a/src/worker/App.mjs +++ b/src/worker/App.mjs @@ -200,8 +200,8 @@ class App extends Base { } return import( - /* webpackInclude: /[\\\/]app.mjs$/ */ - /* webpackExclude: /[\\\/]node_modules/ */ + /* webpackInclude: /(?:\/|\\)app.mjs$/ */ + /* webpackExclude: /(?:\/|\\)node_modules/ */ /* webpackMode: "lazy" */ `../../${path}.mjs` )