ember-responsive
Advanced tools
Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "ember-responsive", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"authors": [ | ||
@@ -18,10 +18,13 @@ "Jonathan Geiger <geiger@freshbooks.com>", | ||
"ignore": [ | ||
"**/.*", | ||
"lib", | ||
"node_modules", | ||
"bower_components", | ||
"test", | ||
"tests" | ||
"tests", | ||
"Gruntfile.js", | ||
"**/*.json", | ||
], | ||
"dependencies": { | ||
"ember": "~1.5.0" | ||
}, | ||
"devDependencies": { | ||
"ember": "~1.5.0", | ||
"qunit": "~1.14.0", | ||
@@ -28,0 +31,0 @@ "sinonjs": "~1.9.0" |
@@ -5,10 +5,2 @@ module.exports = function(grunt) { | ||
uglify: { | ||
dist: { | ||
files: { | ||
'dist/<%= pkg.name %>.min.js': ['lib/responsive.js', 'lib/*.js'] | ||
} | ||
} | ||
}, | ||
concat: { | ||
@@ -56,9 +48,20 @@ dist: { | ||
} | ||
} | ||
}, | ||
bump: { | ||
options: { | ||
files: ['package.json', 'bower.json'], | ||
commitFiles: ['package.json', 'bower.json', 'dist'], | ||
pushTo: 'origin', | ||
} | ||
}, | ||
clean: ['dist'] | ||
}); | ||
grunt.loadNpmTasks('grunt-contrib-uglify'); | ||
grunt.loadNpmTasks('grunt-contrib-concat'); | ||
grunt.loadNpmTasks('grunt-contrib-testem'); | ||
grunt.loadNpmTasks('grunt-contrib-yuidoc'); | ||
grunt.loadNpmTasks('grunt-contrib-clean'); | ||
grunt.loadNpmTasks('grunt-bump'); | ||
@@ -69,6 +72,12 @@ grunt.registerTask('test', 'Run tests using testem and PhantomJS', | ||
grunt.registerTask('dist', 'Create a distributable version', | ||
['doc', 'uglify:dist', 'concat:dist']); | ||
['doc', 'concat:dist']); | ||
grunt.registerTask('doc', 'Generate API documentation', | ||
['yuidoc:compile']); | ||
grunt.registerTask('release:minor', 'Generates and tags a minor release', | ||
['test', 'clean', 'dist', 'bump:minor']); | ||
grunt.registerTask('release:patch', 'Generates and tags a patch release', | ||
['test', 'clean', 'dist', 'bump:patch']); | ||
} |
@@ -1,79 +0,42 @@ | ||
/** | ||
* A helper method to initialize `Ember.Responsive`. | ||
* | ||
* This is the main entry point to `Ember.Responsive`, and all it does | ||
* is take care of returning an initializer that you can use when | ||
* spinning up your Ember app. Simply pass a list of "matchers"—named | ||
* media queries that you want your app to respond to—and you'll be | ||
* able to interact with them in your controllers, components, and | ||
* templates. | ||
* | ||
* **Initializing Ember.Responsive** | ||
* | ||
* ```javascript | ||
* var App = Ember.Application.create(); | ||
* | ||
* // Declare the initializer with the breakpoints that we care about. | ||
* App.initialize(Ember.Responsive.init({ | ||
* mobile: '(max-width: 768px)', | ||
* tablet: '(min-width: 769px) and (max-width: 992px)', | ||
* desktop: '(min-width: 993px) and (max-width: 1200px)', | ||
* jumbo: '(min-width: 1201px)', | ||
* }); | ||
* ``` | ||
* | ||
* **Accessing the media property** | ||
* | ||
* The media instance the initializer creates is available to all | ||
* controllers and components, so it's just a `get()` away. | ||
* | ||
* ```javascript | ||
* this.get('media.isMobile'); | ||
* ``` | ||
* | ||
* It's also, by extension, available in your templates. | ||
* | ||
* ```handlebars | ||
* \{{#if media.isDesktop}} | ||
* This is a desktop! | ||
* \{{/if}} | ||
* ``` | ||
* | ||
* Under the hood, this is simply passing your matchers along to a | ||
* new {{#crossLink "Ember.Responsive.Media"}}{{/crossLink}} | ||
* instance and injecting that instance into all of your controllers | ||
* and components. | ||
* | ||
* @static | ||
* @constructor | ||
* @module ember-responsive | ||
* @namespace Ember.Responsive | ||
* @class init | ||
* @param {object} matchers A hash of media queries to test. | ||
* @return {object} An Ember initializer. | ||
*/ | ||
Ember.Responsive.init = function(matchers) { | ||
return { | ||
/** | ||
* @property name | ||
* @type string | ||
* @default responsive | ||
*/ | ||
(function(Ember) { | ||
/** | ||
* An initializer that sets up `Ember.Responsive`. | ||
* | ||
* Refer to {{#crossLink "Ember.Application.responsive"}}{{/crossLink}} | ||
* for examples of how to configure this library before the initializer | ||
* before it's run by Ember. | ||
* | ||
* @static | ||
* @constructor | ||
* @module ember-responsive | ||
* @namespace Ember.Application | ||
* @class initializer | ||
* @param {object} matchers A hash of media queries to test. | ||
* @return {object} An Ember initializer. | ||
*/ | ||
Ember.Application.initializer({ | ||
/** | ||
* @property name | ||
* @type string | ||
* @default responsive | ||
*/ | ||
name: 'responsive', | ||
/** | ||
* @method initialize | ||
* @param Ember.Container container | ||
* @param Ember.Application app | ||
*/ | ||
/** | ||
* @method initialize | ||
* @param Ember.Container container | ||
* @param Ember.Application app | ||
*/ | ||
initialize: function(container, app) { | ||
var media = Ember.Responsive.Media.create({ | ||
matchers: matchers | ||
}); | ||
var responsive = app.constructor.responsive; | ||
app.register('responsive:media', media, { initialize: false }); | ||
app.inject('controller', 'media', 'responsive:media'); | ||
app.inject('component', 'media', 'responsive:media'); | ||
if (responsive.media) { | ||
app.register('responsive:media', responsive.media, { instantiate: false }); | ||
app.inject('controller', 'media', 'responsive:media'); | ||
app.inject('component', 'media', 'responsive:media'); | ||
app.inject('route', 'media', 'responsive:media'); | ||
app.inject('view', 'media', 'responsive:media'); | ||
} | ||
} | ||
}; | ||
}; | ||
}); | ||
})(window.Ember); |
308
lib/media.js
@@ -1,167 +0,157 @@ | ||
/** | ||
* Handles detecting and responding to media queries. | ||
* | ||
* The constructor takes a list of matchers, which are named media queries | ||
* that you can access on the created object. Take a look: | ||
* | ||
* ```javascript | ||
* m = Ember.Responsive.Media.create({ | ||
* matchers: { | ||
* all: 'all', | ||
* } | ||
* }); | ||
* | ||
* m.get('all.matches'); | ||
* m.get('isAll'); | ||
* // => true | ||
* ``` | ||
* | ||
* You can also declare matchers dynamically, which is useful for overriding | ||
* matchers at test time. For example, testing mobile can be easily mocked out: | ||
* | ||
* ```javascript | ||
* m = Ember.Responsive.Media.create(); | ||
* m.match('mobile', 'all'); | ||
* m.get('mobile.matches'); | ||
* // => true | ||
* ``` | ||
* | ||
* There are also convenience properties defined for determining | ||
* if there is a media type match | ||
* | ||
* ```javascript | ||
* m = Ember.Responsive.Media.create(); | ||
* m.match('mobile', 'all'); | ||
* m.get('isMobile'); | ||
* // => true | ||
* ``` | ||
* | ||
* You can also see a full list of matched media types. The class can | ||
* also return that list as a string of dasherized class names, which | ||
* is useful for placing on your container element. | ||
* | ||
* ```javascript | ||
* m = Ember.Responsive.Media.create(); | ||
* m.match('all', 'all'); | ||
* m.match('mobile', 'all'); | ||
* m.match('none', 'not all'); | ||
* m.get('matching') | ||
* // => Ember.Set(['all', 'mobile']); | ||
* m.get('classNames'); | ||
* // => 'media-all media-mobile' | ||
* ``` | ||
* | ||
* @module ember-responsive | ||
* @namespace Ember.Responsive | ||
* @class Media | ||
* @extends Ember.Object | ||
*/ | ||
Ember.Responsive.Media = Ember.Object.extend({ | ||
/** | ||
* A list of media queries that we want to test for. | ||
* | ||
* This is read on init so that you can pass a list of matchers | ||
* to the constructor. | ||
* | ||
* @attribute matchers | ||
* @type object | ||
* @default {} | ||
* @writeOnce | ||
*/ | ||
matchers: {}, | ||
(function(Ember) { | ||
/** | ||
* Handles detecting and responding to media queries. | ||
* | ||
* Generally speaking, you won't ever need to create an instance of this class | ||
* yourself, since `Ember.Responsive` takes care of creating and configuring it | ||
* for you. However, it is important to document how to interact with this | ||
* class—which can become important during test time in particular. With that | ||
* said, let's take a look at how to work with it. | ||
* | ||
* **Adding media query matchers** | ||
* | ||
* The first step to using the class is to add media queries that you | ||
* want it to listen to. Each media query has a name that you can | ||
* use to reference it by. | ||
* | ||
* ```javascript | ||
* media = Ember.Responsive.Media.create(); | ||
* media.match('mobile', '(max-width: 768px)'); | ||
* media.match('desktop', '(min-width: 769px)'); | ||
* ``` | ||
* | ||
* **Testing the media query matchers** | ||
* | ||
* Now that you've added a few matchers, you can access those media queries as | ||
* if they were properties on your object. The nice thing is that whenever the | ||
* media queries change, this class will automatically update the relevant | ||
* properties (and so will the rest of your application, thanks to the power | ||
* of two-way data-binding). | ||
* | ||
* ```javascript | ||
* media = Ember.Responsive.Media.create(); | ||
* media.match('mobile', '(max-width: 768px)'); | ||
* media.match('desktop', '(min-width: 769px)'); | ||
* | ||
* // There are convenient "isser" properties defined... | ||
* if (media.get('isMobile')) { | ||
* console.log('mobile!'); | ||
* } | ||
* | ||
* // As well as access to the matchMedia API... | ||
* if (media.get('desktop.matches')) { | ||
* console.log('desktop!'); | ||
* } | ||
* ``` | ||
* | ||
* **Retrieving a list of matching media queries** | ||
* | ||
* It's also nice to be able to see which media queries are matching, since | ||
* some applications might have many matches at the same time. | ||
* | ||
* ```javascript | ||
* media = Ember.Responsive.Media.create(); | ||
* media.match('desktop', 'all'); | ||
* media.match('mobile', 'all'); | ||
* | ||
* console.log(media.get('matches')); | ||
* // => Ember.Set(['desktop', 'mobile']); | ||
* ``` | ||
* | ||
* This class can also return that list as a string of dasherized class names, | ||
* which is useful for placing on your app's rootElement. By default, these | ||
* class names are prefixed with `media-`, so as not to clash with any other | ||
* classes your app might use. | ||
* | ||
* ```javascript | ||
* App.ApplicationView = Ember.View.extend({ | ||
* classNameBindings: ['media.classNames'] | ||
* }); | ||
* ``` | ||
* | ||
* @module ember-responsive | ||
* @namespace Ember.Responsive | ||
* @class Media | ||
* @extends Ember.Object | ||
*/ | ||
Ember.Responsive.Media = Ember.Object.extend({ | ||
/** | ||
* A set of matching matchers. | ||
* | ||
* @property matches | ||
* @type Ember.Set | ||
* @default Ember.Set | ||
*/ | ||
matches: function() { | ||
return new Ember.Set(); | ||
}.property(), | ||
/** | ||
* A set of matching matchers. | ||
* | ||
* @property matching | ||
* @type Ember.Set | ||
* @default Ember.Set | ||
*/ | ||
matching: function() { | ||
return new Ember.Set(); | ||
}.property(), | ||
/** | ||
* The matcher to use for testing media queries. | ||
* | ||
* @property matcher | ||
* @type matchMedia | ||
* @default window.matchMedia | ||
* @private | ||
*/ | ||
mql: window.matchMedia, | ||
/** | ||
* The matcher to use for testing media queries. | ||
* | ||
* @property matcher | ||
* @type matchMedia | ||
* @default window.matchMedia | ||
* @private | ||
*/ | ||
mql: window.matchMedia, | ||
/** | ||
* A string composed of all the matching matchers' names, turned into | ||
* friendly, dasherized class-names that are prefixed with `media-`. | ||
* | ||
* @property classNames | ||
* @type string | ||
*/ | ||
classNames: function() { | ||
return this.get('matches').map(function(name) { | ||
return 'media-' + name.dasherize(); | ||
}).join(' '); | ||
}.property('matches.@each'), | ||
/** | ||
* A string composed of all the matching matchers' names, turned into | ||
* friendly, dasherized class-names. | ||
* | ||
* @property classNames | ||
* @type string | ||
*/ | ||
classNames: function() { | ||
return this.get('matching').map(function(name) { | ||
return 'media-' + name.dasherize(); | ||
}).join(' '); | ||
}.property('matching.@each'), | ||
/** | ||
* Adds a new matcher to the list. | ||
* | ||
* After this method is called, you will be able to access the result | ||
* of the matcher as a property on this object. | ||
* | ||
* **Adding a new matcher** | ||
* | ||
* ```javascript | ||
* media = Ember.Responsive.Media.create(); | ||
* media.match('all', 'all'); | ||
* media.get('all'); | ||
* // => instanceof window.matchMedia | ||
* media.get('all.matches'); | ||
* // => true | ||
* ``` | ||
* | ||
* @param string name The name of the matcher | ||
* @param string query The media query to match against | ||
* @method match | ||
*/ | ||
match: function(name, query) { | ||
var matcher = this.get('mql')(query), | ||
isser = 'is' + name.classify(), | ||
_this = this; | ||
/** | ||
* Adds a new matcher to the list. | ||
* | ||
* After this method is called, you will be able to access the result | ||
* of the matcher as a property on this object. | ||
* | ||
* **Adding a new matcher** | ||
* | ||
* ```javascript | ||
* media = Ember.Responsive.Media.create(); | ||
* media.match('all', 'all'); | ||
* media.get('all'); | ||
* // => instanceof window.matchMedia | ||
* media.get('all.matches'); | ||
* // => true | ||
* ``` | ||
* | ||
* @param string name The name of the matcher | ||
* @param string query The media query to match against | ||
* @method match | ||
*/ | ||
match: function(name, query) { | ||
var matcher = this.get('mql')(query), | ||
isser = 'is' + name.classify(), | ||
_this = this; | ||
var listener = function(matcher) { | ||
_this.set(name, matcher); | ||
var listener = function(matcher) { | ||
_this.set(name, matcher); | ||
if (matcher.matches) { | ||
_this.get('matches').add(name); | ||
} else { | ||
_this.get('matches').remove(name); | ||
} | ||
}; | ||
if (matcher.matches) { | ||
_this.get('matching').add(name); | ||
} else { | ||
_this.get('matching').remove(name); | ||
} | ||
}; | ||
matcher.addListener(listener); | ||
listener(matcher); | ||
matcher.addListener(listener); | ||
listener(matcher); | ||
// Define a corresponding "isser" which is prettier to look at and type | ||
Ember.defineProperty(this, isser, Ember.computed(function() { | ||
return this.get(name).matches; | ||
}).property(name)); | ||
}, | ||
/** | ||
* Initializes the matchers declared at construction. | ||
* | ||
* @method initMatchers | ||
* @private | ||
*/ | ||
initMatchers: function() { | ||
var matchers = this.get('matchers'); | ||
for (var name in matchers) { | ||
if (matchers.hasOwnProperty(name)) { | ||
this.match(name, matchers[name]); | ||
} | ||
// Define a corresponding "isser" which is prettier to look at and type | ||
Ember.defineProperty(this, isser, Ember.computed(function() { | ||
return this.get(name).matches; | ||
}).property(name)); | ||
} | ||
}.observes('matchers').on('init'), | ||
}); | ||
}); | ||
})(window.Ember); |
@@ -1,7 +0,136 @@ | ||
/** | ||
* The main namespace for `Ember.Responsive`. | ||
* | ||
* @module ember-responsive | ||
* @main ember-responsive | ||
*/ | ||
Ember.Responsive = {}; | ||
(function(Ember) { | ||
/** | ||
* The main namespace for `Ember.Responsive`. | ||
* | ||
* @module ember-responsive | ||
* @main ember-responsive | ||
*/ | ||
Ember.Responsive = {}; | ||
/** | ||
* This is the main entry point to `Ember.Responsive`, and all it does | ||
* is take care of configuring this library to your specifications before | ||
* spinning up your Ember app. | ||
* | ||
* **Setting media for your application** | ||
* | ||
* Presently, you can easily configure your application's breakpoints | ||
* using media queries. | ||
* | ||
* ```javascript | ||
* var App = Ember.Application.extend(); | ||
* | ||
* // Declare the media that we care about. | ||
* App.responsive({ | ||
* media: { | ||
* mobile: '(max-width: 768px)', | ||
* tablet: '(min-width: 769px) and (max-width: 992px)', | ||
* desktop: '(min-width: 993px) and (max-width: 1200px)', | ||
* jumbo: '(min-width: 1201px)', | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* After you've gone through this process, you'll be able to access | ||
* the media queries in a few different places of your application: | ||
* | ||
* - All controllers, under the name `media`. | ||
* - All components, under the name `media`. | ||
* - All views, under the name `media`. | ||
* - All routes, under the name `media`. | ||
* - In the container, under the name `responsive:media`. | ||
* | ||
* **Binding media classes to your application view** | ||
* | ||
* `Ember.Responsive` makes it very easy to bind the active media queries as | ||
* class names on your app view (or any view, for that matter). This helps you | ||
* avoid having to duplicate media queries in your CSS and Ember code, and | ||
* makes writing responsive CSS much simpler. | ||
* | ||
* ```javascript | ||
* App.ApplicationView = Ember.View.extend({ | ||
* classNameBindings: ['media.classNames'] | ||
* }); | ||
* ``` | ||
* | ||
* **Referencing media queries in your app** | ||
* | ||
* `Ember.Responsive` also allows access to its API in controllers, components, | ||
* routes, and views, making it dead simple to only perform certain types of | ||
* work for certain devices. | ||
* | ||
* ```javascript | ||
* App.PostsRoute = Ember.Router.extend({ | ||
* model: function() { | ||
* if (this.get('media.isMobile')) { | ||
* // Return a smaller, less data-hungry result | ||
* } | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* **Referencing media queries in your templates** | ||
* | ||
* Since media queries are available in your controllers, components, | ||
* and views, they'll also be accessible in your templates. | ||
* | ||
* ```handlebars | ||
* \{{#if media.isMobile}} | ||
* Mobile view! | ||
* \{{/if}} | ||
* ``` | ||
* | ||
* **Mocking a particular media type in test** | ||
* | ||
* It's very easy to fake a particular view when testing as well. This | ||
* is useful when you want to be able to test different behaviours | ||
* for different types of devices. | ||
* | ||
* If you're writing an integration test, you'll probably want to mock | ||
* out the media type at the application level. | ||
* | ||
* ```javascript | ||
* App.responsive({ | ||
* media: { | ||
* mobile: 'all' // The 'all' media query will always match | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* But in unit tests, you can simply override the property that you're | ||
* referencing. | ||
* | ||
* ```javascript | ||
* controller.set('media.isMobile', true); | ||
* ``` | ||
* | ||
* Refer to the {{#crossLink "Ember.Responsive.Media"}}{{/crossLink}} | ||
* documentation for more examples of how to interact with the | ||
* aforementioned `media` object. | ||
* | ||
* @static | ||
* @module ember-responsive | ||
* @namespace Ember.Application | ||
* @class responsive | ||
* @param {object} config | ||
*/ | ||
Ember.Application.reopenClass({ | ||
responsive: function(config) { | ||
var media = config.media; | ||
if (!this.responsive.media) { | ||
this.responsive.media = Ember.Responsive.Media.create(); | ||
} | ||
if (media) { | ||
for (var name in media) { | ||
if (media.hasOwnProperty(name)) { | ||
this.responsive.media.match(name, media[name]); | ||
} | ||
} | ||
} | ||
return this; | ||
} | ||
}); | ||
})(window.Ember); |
{ | ||
"name": "ember-responsive", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Easy responsive layouts with Ember", | ||
@@ -34,3 +34,2 @@ "scripts": { | ||
"grunt": "^0.4.4", | ||
"grunt-contrib-uglify": "^0.4.0", | ||
"grunt-contrib-concat": "^0.4.0", | ||
@@ -40,4 +39,6 @@ "grunt-contrib-testem": "^0.5.15", | ||
"yuidocjs": "^0.3.49", | ||
"grunt-contrib-yuidoc": "^0.5.2" | ||
"grunt-contrib-yuidoc": "^0.5.2", | ||
"grunt-bump": "0.0.13", | ||
"grunt-contrib-clean": "^0.5.0" | ||
} | ||
} |
module('Ember.Responsive.Media'); | ||
test('matchers can be set at construction', function() { | ||
var subject = Ember.Responsive.Media.create({ | ||
matchers: { all: 'all' } | ||
}); | ||
equal(true, subject.get('all.matches')); | ||
}); | ||
test('matchers can be added dynamically', function() { | ||
@@ -25,3 +17,3 @@ var subject = Ember.Responsive.Media.create(); | ||
test('matching property returns matching matchers', function() { | ||
test('matches property returns matching matchers', function() { | ||
var subject = Ember.Responsive.Media.create(); | ||
@@ -32,3 +24,3 @@ subject.match('mobile', 'all'); | ||
deepEqual(['mobile', 'all'], subject.get('matching').toArray()); | ||
deepEqual(['mobile', 'all'], subject.get('matches').toArray()); | ||
}); | ||
@@ -35,0 +27,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
200093
42
2331
66
10
1
80
1