Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Next Region #3664

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import _ from 'underscore';
import extend from './utils/extend';
import buildRegion from './common/build-region';
import normalizeRegion from './common/normalize-region';
import CommonMixin from './mixins/common';
import DestroyMixin from './mixins/destroy';
import RadioMixin from './mixins/radio';
Expand Down Expand Up @@ -50,11 +50,25 @@ _.extend(Application.prototype, CommonMixin, DestroyMixin, RadioMixin, {

if (!region) { return; }

const defaults = {
regionClass: this.regionClass
};
this._region = this._buildRegion(region);
},

_buildRegion(definition) {
if (definition instanceof Region) {
return definition;
}

if (_.isFunction(definition)) {
return this._buildRegion(definition.call(this));
}

const options = normalizeRegion(definition, this.regionClass);

const RegionClass = options.regionClass;

delete options.regionClass;

this._region = buildRegion(region, defaults);
return new RegionClass(options);
},

getRegion() {
Expand Down
2 changes: 1 addition & 1 deletion src/collection-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const CollectionView = Backbone.View.extend({
const $emptyEl = this.$container || this.$el;

if (this._emptyRegion && !this._emptyRegion.isDestroyed()) {
this._emptyRegion._setElement($emptyEl[0]);
this._emptyRegion.setElement($emptyEl[0]);
return this._emptyRegion;
}

Expand Down
37 changes: 0 additions & 37 deletions src/common/build-region.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/common/normalize-region.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import _ from 'underscore';
import MarionetteError from '../utils/error';

// return the region instance from the definition
export default function(definition, regionClass) {
if (_.isString(definition)) {
definition = { el: definition };
}

if (!_.isString(definition.el)) {
console.log(definition);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to remove this

throw new MarionetteError({
message: 'Improper region configuration type.',
url: 'marionette.region.html#defining-regions'
});
}

return _.extend({ regionClass }, definition);
}
135 changes: 73 additions & 62 deletions src/mixins/regions.js
Original file line number Diff line number Diff line change
@@ -1,107 +1,119 @@
import _ from 'underscore';
import _invoke from '../utils/invoke';
import buildRegion from '../common/build-region';
import normalizeRegion from '../common/normalize-region';
import Region from '../region';

// Performant startsWith
function startsWithUi(el) {
return el[0] === '@' &&
el[1] === 'u' &&
el[2] === 'i' &&
el[3] === '.';
}

// MixinOptions
// - regions
// - regionClass

export default {
regionClass: Region,

// Internal method to initialize the regions that have been defined in a
// `regions` attribute on this View.
// Internal method to initialize the regions attributes
_initRegions() {

// init regions hash
this.regions = this.regions || {};
// init regions instance hash
this._regions = {};

this.addRegions(_.result(this, 'regions'));
// init regions definition hash
this.regions = _.extend({}, _.result(this, 'regions'));
},

// Internal method to re-initialize all of the regions by updating
// the `el` that they point to
_reInitRegions() {
_invoke(this._regions, 'reset');
// Adds or rebinds all regions associated with the view
_bindRegions() {
const regions = this.addRegions(_.extend({}, this.regions));

this.triggerMethod('bind:regions', this, regions);
},

// Add a single region, by name, to the View
addRegion(name, definition) {
const regions = {};
regions[name] = definition;
return this.addRegions(regions)[name];
_buildRegion(name, RegionClass, options) {
const region = new RegionClass(options);

region._parentView = this;
region._name = name;

this._regions[name] = region;

return region;
},

// Add multiple regions as a {name: definition, name2: def2} object literal
addRegions(regions) {
// If there's nothing to add, stop here.
if (_.isEmpty(regions)) {
return;
_addRegion(options, name) {
const currentRegion = this.getRegion(name);

if (currentRegion) {
return currentRegion.setElement(options.el);
}

// Normalize region selectors hash to allow
// a user to use the @ui. syntax.
regions = this.normalizeUIValues(regions, 'el');
const RegionClass = options.regionClass;
delete options.regionClass;

// Add the regions definitions to the regions property
this.regions = _.extend({}, this.regions, regions);
return this._buildRegion(name, RegionClass, options);
},

// Finds an existing ui with `@ui.` or finds the `el` on the DOM
_findEl(el) {
if (_.isString(el) && startsWithUi(el)) {
return this.ui[el.slice(4)];
}

return this._addRegions(regions);
return this.Dom.findEl(this.el, el);
},

// internal method to build and add regions
_addRegions(regionDefinitions) {
const defaults = {
regionClass: this.regionClass,
parentEl: _.partial(_.result, this, 'el')
};
_buildOptions(definition, name) {
const options = normalizeRegion(definition, this.regionClass);

return _.reduce(regionDefinitions, (regions, definition, name) => {
regions[name] = buildRegion(definition, defaults);
this._addRegion(regions[name], name);
return regions;
}, {});
this.regions[name] = options;

return _.extend({}, options, {
el: this._findEl(options.el)
});
},

_addRegion(region, name) {
this.triggerMethod('before:add:region', this, name, region);
// Add a single region, by name, to the View
addRegion(name, definition) {
if (!this._isRendered) {
this.render();
}
const options = this._buildOptions(definition, name);

region._parentView = this;
region._name = name;
return this._addRegion(options, name);
},

this._regions[name] = region;
// Add multiple region definitions
addRegions(regionDefinitions) {
if (!this._isRendered) {
this.render();
}
// Building the options first prevents querying regions
// from already attached children
const regionOptions = _.reduce(regionDefinitions, (opts, definition, name) => {
opts[name] = this._buildOptions(definition, name);
return opts;
}, {});

this.triggerMethod('add:region', this, name, region);
return _.map(regionOptions, this._addRegion.bind(this));
},

// Remove a single region from the View, by name
removeRegion(name) {
const region = this._regions[name];

this._removeRegion(region, name);

return region;
return this.getRegion(name).destroy();
},

// Remove all regions from the View
removeRegions() {
const regions = this._getRegions();

_.each(this._regions, this._removeRegion.bind(this));

_invoke(regions, 'destroy');
return regions;
},

_removeRegion(region, name) {
this.triggerMethod('before:remove:region', this, name, region);

region.destroy();

this.triggerMethod('remove:region', this, name, region);
},

// Called in a region's destroy
_removeReferences(name) {
delete this.regions[name];
Expand Down Expand Up @@ -134,7 +146,7 @@ export default {
},

_getRegions() {
return _.clone(this._regions);
return _.extend({}, this._regions);
},

// Get all regions
Expand All @@ -158,5 +170,4 @@ export default {
getChildView(name) {
return this.getRegion(name).currentView;
}

};
47 changes: 9 additions & 38 deletions src/mixins/ui.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import _ from 'underscore';
// allows for the use of the @ui. syntax within
// a given key for triggers and events
// swaps the @ui with the associated selector.
// Returns a new, non-mutated, parsed events hash.
const normalizeUIKeys = function(hash, ui) {
return _.reduce(hash, (memo, val, key) => {
const normalizedKey = normalizeUIString(key, ui);
memo[normalizedKey] = val;
return memo;
}, {});
};

const uiRegEx = /@ui\.[a-zA-Z-_$0-9]*/g;

Expand All @@ -21,30 +10,19 @@ const normalizeUIString = function(uiString, ui) {
});
};

// allows for the use of the @ui. syntax within
// a given value for regions
// swaps the @ui with the associated selector
const normalizeUIValues = function(hash, ui, property) {
_.each(hash, (val, key) => {
if (_.isString(val)) {
hash[key] = normalizeUIString(val, ui);
} else if (val) {
const propertyVal = val[property];
if (_.isString(propertyVal)) {
val[property] = normalizeUIString(propertyVal, ui);
}
}
});
return hash;
};

export default {

// normalize the keys of passed hash with the views `ui` selectors.
// `{"@ui.foo": "bar"}`
// allows for the use of the @ui. syntax within
// a given key for triggers and events
// swaps the @ui with the associated selector.
// Returns a new, non-mutated, parsed events hash.
normalizeUIKeys(hash) {
const uiBindings = this._getUIBindings();
return normalizeUIKeys(hash, uiBindings);
return _.reduce(hash, (memo, val, key) => {
const normalizedKey = normalizeUIString(key, uiBindings);
memo[normalizedKey] = val;
return memo;
}, {});
},

// normalize the passed string with the views `ui` selectors.
Expand All @@ -54,13 +32,6 @@ export default {
return normalizeUIString(uiString, uiBindings);
},

// normalize the values of passed hash with the views `ui` selectors.
// `{foo: "@ui.bar"}`
normalizeUIValues(hash, property) {
const uiBindings = this._getUIBindings();
return normalizeUIValues(hash, uiBindings, property);
},

_getUIBindings() {
const uiBindings = _.result(this, '_uiBindings');
return uiBindings || _.result(this, 'ui');
Expand Down
Loading