Browse Source

Merge branch 'scroll-to-view' into dev

pull/1130/head
Artur Arseniev 9 years ago
parent
commit
6f57773d37
  1. 2
      dist/css/grapes.min.css
  2. 33
      src/canvas/index.js
  3. 4
      src/canvas/view/CanvasView.js
  4. 24
      src/dom_components/index.js
  5. 29
      src/dom_components/view/ComponentView.js
  6. 13
      src/navigator/config/config.js
  7. 8
      src/navigator/index.js
  8. 16
      src/navigator/view/ItemView.js
  9. 4
      src/styles/scss/_gjs_layers.scss
  10. 16
      src/utils/mixins.js

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

33
src/canvas/index.js

@ -1,4 +1,4 @@
import { on, off, hasDnd } from 'utils/mixins'; import { on, off, hasDnd, getElement } from 'utils/mixins';
import Droppable from 'utils/Droppable'; import Droppable from 'utils/Droppable';
module.exports = () => { module.exports = () => {
@ -86,6 +86,14 @@ module.exports = () => {
return CanvasView.frame.el; return CanvasView.frame.el;
}, },
/**
* Returns the frame document
* @return {HTMLElement}
*/
getDocument() {
return this.getFrameEl().contentDocument;
},
/** /**
* Returns body element of the frame * Returns body element of the frame
* @return {HTMLElement} * @return {HTMLElement}
@ -342,6 +350,29 @@ module.exports = () => {
return this.getFrameEl().contentDocument.activeElement.tagName !== 'BODY'; return this.getFrameEl().contentDocument.activeElement.tagName !== 'BODY';
}, },
/**
* Scroll canvas to the element if it's not visible. The scrolling is
* executed via `scrollIntoView` API and options of this method are
* passed to it. For instance, you can scroll smoothly with
* `{ behavior: 'smooth' }`. You can also force the scroll
* @param {HTMLElement|Component} el
* @param {Object} [opts={}] Options, same as options for `scrollIntoView`
* @example
* const selected = editor.getSelected();
* // Scroll smoothly (this behavior can be polyfilled)
* cv.scrollTo(selected, { behavior: 'smooth' });
* // Force the scroll, even if the element is alredy visible
* cv.scrollTo(selected, { force: true });
*/
scrollTo(el, opts = {}) {
const elem = getElement(el);
const cv = this.getCanvasView();
if (!cv.isElInViewport(elem) || opts.force) {
elem.scrollIntoView(opts);
}
},
/** /**
* Start autoscroll * Start autoscroll
*/ */

4
src/canvas/view/CanvasView.js

@ -1,4 +1,4 @@
import { on, off } from 'utils/mixins'; import { on, off, getElement } from 'utils/mixins';
const FrameView = require('./FrameView'); const FrameView = require('./FrameView');
const $ = Backbone.$; const $ = Backbone.$;
@ -23,7 +23,7 @@ module.exports = Backbone.View.extend({
* @return {Boolean} * @return {Boolean}
*/ */
isElInViewport(el) { isElInViewport(el) {
const rect = el.getBoundingClientRect(); const rect = getElement(el).getBoundingClientRect();
const frameRect = this.getFrameOffset(1); const frameRect = this.getFrameOffset(1);
const rTop = rect.top; const rTop = rect.top;
const rLeft = rect.left; const rLeft = rect.left;

24
src/dom_components/index.js

@ -32,6 +32,8 @@
* } * }
* ... * ...
*/ */
import { isEmpty } from 'underscore';
module.exports = () => { module.exports = () => {
var c = {}; var c = {};
let em; let em;
@ -190,6 +192,7 @@ module.exports = () => {
c.am = em.get('AssetManager') || ''; c.am = em.get('AssetManager') || '';
em.get('Parser').compTypes = componentTypes; em.get('Parser').compTypes = componentTypes;
em.on('change:selectedComponent', this.componentChanged, this); em.on('change:selectedComponent', this.componentChanged, this);
em.on('change:componentHovered', this.componentHovered, this);
} }
// Build wrapper // Build wrapper
@ -525,6 +528,27 @@ module.exports = () => {
}); });
model && model.set('status', 'selected'); model && model.set('status', 'selected');
},
/**
* Triggered when the component is hovered
* @private
*/
componentHovered() {
const em = c.em;
const model = em.get('componentHovered');
const previous = em.previous('componentHovered');
const state = 'hovered';
// Deselect the previous component
previous &&
previous.get('status') == state &&
previous.set({
status: '',
state: ''
});
model && isEmpty(model.get('status')) && model.set('status', state);
} }
}; };
}; };

29
src/dom_components/view/ComponentView.js

@ -98,15 +98,18 @@ module.exports = Backbone.View.extend({
* @param {Event} e * @param {Event} e
* @private * @private
* */ * */
updateStatus(e) { updateStatus(opts = {}) {
var el = this.el; const em = this.em;
var status = this.model.get('status'); const el = this.el;
var pfx = this.pfx; const status = this.model.get('status');
var ppfx = this.ppfx; const pfx = this.pfx;
var selectedCls = pfx + 'selected'; const ppfx = this.ppfx;
var selectedParentCls = selectedCls + '-parent'; const selectedCls = `${pfx}selected`;
var freezedCls = `${ppfx}freezed`; const selectedParentCls = `${selectedCls}-parent`;
this.$el.removeClass(`${selectedCls} ${selectedParentCls} ${freezedCls}`); const freezedCls = `${ppfx}freezed`;
const hoveredCls = `${ppfx}hovered`;
const toRemove = [selectedCls, selectedParentCls, freezedCls, hoveredCls];
this.$el.removeClass(toRemove.join(' '));
var actualCls = el.getAttribute('class') || ''; var actualCls = el.getAttribute('class') || '';
var cls = ''; var cls = '';
@ -123,13 +126,13 @@ module.exports = Backbone.View.extend({
case 'freezed-selected': case 'freezed-selected':
cls = `${actualCls} ${freezedCls} ${selectedCls}`; cls = `${actualCls} ${freezedCls} ${selectedCls}`;
break; break;
case 'hovered':
cls = !opts.avoidHover ? `${actualCls} ${hoveredCls}` : '';
break;
} }
cls = cls.trim(); cls = cls.trim();
cls && el.setAttribute('class', cls);
if (cls) {
el.setAttribute('class', cls);
}
}, },
/** /**

13
src/navigator/config/config.js

@ -18,5 +18,16 @@ module.exports = {
showWrapper: 1, showWrapper: 1,
// Show hovered components in canvas // Show hovered components in canvas
showHover: 1 showHover: 1,
// Scroll to selected component in Canvas when it's selected in Layers
// true, false or `scrollIntoView`-like options
scrollCanvas: { behavior: 'smooth' },
// Scroll to selected component in Layers when it's selected in Canvas
// true, false or `scrollIntoView`-like options
scrollLayers: 1,
// Highlight when a layer component is hovered
highlightHover: 1
}; };

8
src/navigator/index.js

@ -63,10 +63,11 @@ module.exports = () => {
* Triggered when the selected component is changed * Triggered when the selected component is changed
* @private * @private
*/ */
componentChanged(e, md, opts = {}) { componentChanged(selected, opts = {}) {
if (opts.fromLayers) return; if (opts.fromLayers) return;
const opened = em.get('opened'); const opened = em.get('opened');
const model = em.getSelected(); const model = em.getSelected();
const scroll = config.scrollLayers;
let parent = model && model.collection ? model.collection.parent : null; let parent = model && model.collection ? model.collection.parent : null;
for (let cid in opened) opened[cid].set('open', 0); for (let cid in opened) opened[cid].set('open', 0);
@ -75,6 +76,11 @@ module.exports = () => {
opened[parent.cid] = parent; opened[parent.cid] = parent;
parent = parent.collection ? parent.collection.parent : null; parent = parent.collection ? parent.collection.parent : null;
} }
if (model && scroll) {
const el = model.viewLayer && model.viewLayer.el;
el && el.scrollIntoView(scroll);
}
}, },
render() { render() {

16
src/navigator/view/ItemView.js

@ -82,6 +82,7 @@ module.exports = require('backbone').View.extend({
this.clsNoChild = `${pfx}layer-no-chld`; this.clsNoChild = `${pfx}layer-no-chld`;
this.$el.data('model', model); this.$el.data('model', model);
this.$el.data('collection', components); this.$el.data('collection', components);
model.viewLayer = this;
}, },
getVisibilityEl() { getVisibilityEl() {
@ -196,7 +197,14 @@ module.exports = require('backbone').View.extend({
*/ */
handleSelect(e) { handleSelect(e) {
e.stopPropagation(); e.stopPropagation();
this.em && this.em.setSelected(this.model, { fromLayers: 1 }); const { em, config } = this;
if (em) {
const model = this.model;
em.setSelected(model, { fromLayers: 1 });
const scroll = config.scrollCanvas;
scroll && em.get('Canvas').scrollTo(model, scroll);
}
}, },
/** /**
@ -242,7 +250,11 @@ module.exports = require('backbone').View.extend({
* @param Event * @param Event
* */ * */
updateStatus(e) { updateStatus(e) {
ComponentView.prototype.updateStatus.apply(this, arguments); ComponentView.prototype.updateStatus.apply(this, [
{
avoidHover: !this.config.highlightHover
}
]);
}, },
/** /**

4
src/styles/scss/_gjs_layers.scss

@ -90,6 +90,10 @@
top: 0; top: 0;
} }
&.#{$nv-prefix}hovered .#{$nv-prefix}layer-title {
background-color: rgba(255, 255, 255, 0.015);
}
&.#{$nv-prefix}selected .#{$nv-prefix}layer-title { &.#{$nv-prefix}selected .#{$nv-prefix}layer-title {
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
} }

16
src/utils/mixins.js

@ -1,4 +1,4 @@
import { omit, keys, isUndefined } from 'underscore'; import { omit, keys, isUndefined, isElement } from 'underscore';
const elProt = window.Element.prototype; const elProt = window.Element.prototype;
const matches = const matches =
@ -97,6 +97,19 @@ const hasDnd = em => {
); );
}; };
/**
* Ensure to fetch the element from the input argument
* @param {HTMLElement|Component} el Component or HTML element
* @return {HTMLElement}
*/
const getElement = el => {
if (isElement(el)) {
return el;
} else if (el.getEl) {
return el.getEl();
}
};
export { export {
on, on,
off, off,
@ -104,6 +117,7 @@ export {
upFirst, upFirst,
matches, matches,
camelCase, camelCase,
getElement,
shallowDiff, shallowDiff,
normalizeFloat, normalizeFloat,
getUnitFromValue getUnitFromValue

Loading…
Cancel
Save