diff --git a/.gitignore b/.gitignore index beb828a..ecb68d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules npm-debug.log +package-lock.json diff --git a/readme.md b/readme.md index fc0fa53..3527427 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,18 @@ $ npm install vue-moment Vue.use(require('vue-moment')); ``` +...or add it to your component as a local filter: + +```js +import {moment} from 'vue-moment' + +export default { + filters: { + moment + } +} +``` + ## Usage Simply set `moment` as the filtering function and you're good to go. At least one argument is expected, which the filter assumes to be a `format` string if the argument doesn't match any of the other filtering methods. @@ -236,7 +248,7 @@ console.log(Vue.moment().locale()) //es ## this.$moment -`vue-moment` attaches the momentjs instance to your Vue app as `this.$moment`. +`vue-moment` attaches the momentjs instance to your Vue app as `this.$moment`. This allows you to call [the static methods momentjs provides](https://momentjs.com/docs/#/i18n/listing-months-weekdays/). diff --git a/tests/vue-moment.spec.js b/tests/vue-moment.spec.js index 9e460bd..51d35b3 100644 --- a/tests/vue-moment.spec.js +++ b/tests/vue-moment.spec.js @@ -33,6 +33,27 @@ const vmd = new Vue({ }, }).$mount(); +const vml = new Vue({ + template: `
+ {{ now | moment-local(...args) }} + {{ period | duration-local(...args) | duration-local(...formatter) }} +
`, + data() { + return { + now, + args: [ + 'YYYY-MM-DD', + ], + formatter: ['humanize', true], + period, + }; + }, + filters: { + 'moment-local': VueMoment.moment, + 'duration-local': VueMoment.duration + }, +}).$mount(); + describe('VueMoment', () => { describe('installing plugin', () => { it('loads prototype', () => { @@ -290,4 +311,14 @@ describe('VueMoment', () => { }); }); }); + + describe('local plugin', () => { + it('supports moment', () => { + expect(vml.$el.textContent).toContain(now.format('YYYY-MM-DD')); + }); + + it('supports duration', () => { + expect(vmd.$el.textContent).toContain('in a day'); + }); + }) }); diff --git a/vue-moment.js b/vue-moment.js index 27a1e32..305a763 100644 --- a/vue-moment.js +++ b/vue-moment.js @@ -1,232 +1,240 @@ -module.exports = { - install(Vue, options) { - const moment = options && options.moment ? options.moment : require('moment'); +const momentLocal = require('moment'); + +const createMomentFilter = moment => (...args) => { + args = Array.prototype.slice.call(args); + const input = args.shift(); + let date; + + if (Array.isArray(input) && typeof input[0] === 'string') { + // If input is array, assume we're being passed a format pattern to parse against. + // Format pattern will accept an array of potential formats to parse against. + // Date string should be at [0], format pattern(s) should be at [1] + date = moment(input[0], input[1], true); + } else if (typeof input === 'number') { + if (input.toString().length < 12) { + // If input is an integer with fewer than 12 digits, assume Unix seconds... + date = moment.unix(input); + } else { + // ..otherwise, assume milliseconds. + date = moment(input); + } + } else { + // Otherwise, throw the input at moment and see what happens... + date = moment(input); + } + + if (!input || !date.isValid()) { + // Log a warning if moment couldn't reconcile the input. Better than throwing an error? + console.warn('Could not build a valid `moment` object from input.'); + return input; + } + + function parse(...args) { + args = Array.prototype.slice.call(args); + const method = args.shift(); + + switch (method) { + case 'add': { + /* + * Mutates the original moment by adding time. + * http://momentjs.com/docs/#/manipulating/add/ + */ + + const addends = args.shift() + .split(',') + .map(Function.prototype.call, String.prototype.trim); + const obj = {}; + + for (let n = 0; n < addends.length; n++) { + const addend = addends[n].split(' '); + obj[addend[1]] = addend[0]; + } + date.add(obj); + break; + } - Object.defineProperties(Vue.prototype, { - $moment: { - get() { - return moment; - }, - }, - }); + case 'subtract': { + /* + * Mutates the original moment by subtracting time. + * http://momentjs.com/docs/#/manipulating/subtract/ + */ - Vue.moment = moment; + const subtrahends = args.shift() + .split(',') + .map(Function.prototype.call, String.prototype.trim); + const obj = {}; - Vue.filter('moment', (...args) => { - args = Array.prototype.slice.call(args); - const input = args.shift(); - let date; - - if (Array.isArray(input) && typeof input[0] === 'string') { - // If input is array, assume we're being passed a format pattern to parse against. - // Format pattern will accept an array of potential formats to parse against. - // Date string should be at [0], format pattern(s) should be at [1] - date = moment(input[0], input[1], true); - } else if (typeof input === 'number') { - if (input.toString().length < 12) { - // If input is an integer with fewer than 12 digits, assume Unix seconds... - date = moment.unix(input); - } else { - // ..otherwise, assume milliseconds. - date = moment(input); + for (let n = 0; n < subtrahends.length; n++) { + const subtrahend = subtrahends[n].split(' '); + obj[subtrahend[1]] = subtrahend[0]; } - } else { - // Otherwise, throw the input at moment and see what happens... - date = moment(input); + date.subtract(obj); + break; } - if (!input || !date.isValid()) { - // Log a warning if moment couldn't reconcile the input. Better than throwing an error? - console.warn('Could not build a valid `moment` object from input.'); - return input; + case 'from': { + /* + * Display a moment in relative time, either from now or from a specified date. + * http://momentjs.com/docs/#/displaying/fromnow/ + */ + + let from = 'now'; + let removeSuffix = false; + + if (args[0] === 'now') args.shift(); + // If valid, assume it is a date we want the output computed against. + if (moment(args[0]).isValid()) from = moment(args.shift()); + + if (args[0] === true) { + args.shift(); + removeSuffix = true; + } + + if (from !== 'now') { + date = date.from(from, removeSuffix); + } else { + date = date.fromNow(removeSuffix); + } + break; } - function parse(...args) { - args = Array.prototype.slice.call(args); - const method = args.shift(); - - switch (method) { - case 'add': { - /* - * Mutates the original moment by adding time. - * http://momentjs.com/docs/#/manipulating/add/ - */ - - const addends = args.shift() - .split(',') - .map(Function.prototype.call, String.prototype.trim); - const obj = {}; - - for (let n = 0; n < addends.length; n++) { - const addend = addends[n].split(' '); - obj[addend[1]] = addend[0]; - } - date.add(obj); - break; - } - - case 'subtract': { - /* - * Mutates the original moment by subtracting time. - * http://momentjs.com/docs/#/manipulating/subtract/ - */ - - const subtrahends = args.shift() - .split(',') - .map(Function.prototype.call, String.prototype.trim); - const obj = {}; - - for (let n = 0; n < subtrahends.length; n++) { - const subtrahend = subtrahends[n].split(' '); - obj[subtrahend[1]] = subtrahend[0]; - } - date.subtract(obj); - break; - } - - case 'from': { - /* - * Display a moment in relative time, either from now or from a specified date. - * http://momentjs.com/docs/#/displaying/fromnow/ - */ - - let from = 'now'; - let removeSuffix = false; - - if (args[0] === 'now') args.shift(); - // If valid, assume it is a date we want the output computed against. - if (moment(args[0]).isValid()) from = moment(args.shift()); - - if (args[0] === true) { - args.shift(); - removeSuffix = true; - } - - if (from !== 'now') { - date = date.from(from, removeSuffix); - } else { - date = date.fromNow(removeSuffix); - } - break; - } - - case 'diff': { - /* - * Mutates the original moment by doing a difference with another date. - * http://momentjs.com/docs/#/displaying/difference/ - */ - - let referenceTime = moment(); - let units = ''; - let float = false; - - if (moment(args[0]).isValid()) { - // If valid, assume it is a date we want the output computed against. - referenceTime = moment(args.shift()); - } else if (args[0] === null || args[0] === 'now') { - // If null or 'now', remove argument and proceed with default referenceTime. - args.shift(); - } - - if (args[0]) units = args.shift(); - - if (args[0] === true) float = args.shift(); - - date = date.diff(referenceTime, units, float); - break; - } - - case 'calendar': { - /* - * Formats a date with different strings depending on how close - * to a certain date (today by default) the date is. - * http://momentjs.com/docs/#/displaying/calendar-time/ - */ - - let referenceTime = moment(); - let formats = {}; - - if (moment(args[0]).isValid()) { - // If valid, assume it is a date we want the output computed against. - referenceTime = moment(args.shift()); - } else if (args[0] === null || args[0] === 'now') { - // If null or 'now', remove argument and proceed with default referenceTime. - args.shift(); - } - - if (typeof args[0] === 'object') formats = args.shift(); - - date = date.calendar(referenceTime, formats); - break; - } - - case 'utc': { - /* - * Mutates the original moment by converting to UTC - * https://momentjs.com/docs/#/manipulating/utc/ - */ - date.utc(); - break; - } - - case 'timezone': { - /* - * Mutates the original moment by converting to a new timezone. - * https://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/ - */ - date.tz(args.shift()); - break; - } - - default: { - /* - * Formats a date by taking a string of tokens and replacing - * them with their corresponding values. - * http://momentjs.com/docs/#/displaying/format/ - */ - - const format = method; - date = date.format(format); - } + case 'diff': { + /* + * Mutates the original moment by doing a difference with another date. + * http://momentjs.com/docs/#/displaying/difference/ + */ + + let referenceTime = moment(); + let units = ''; + let float = false; + + if (moment(args[0]).isValid()) { + // If valid, assume it is a date we want the output computed against. + referenceTime = moment(args.shift()); + } else if (args[0] === null || args[0] === 'now') { + // If null or 'now', remove argument and proceed with default referenceTime. + args.shift(); } - if (args.length) parse.apply(parse, args); + if (args[0]) units = args.shift(); + + if (args[0] === true) float = args.shift(); + + date = date.diff(referenceTime, units, float); + break; } - parse.apply(parse, args); + case 'calendar': { + /* + * Formats a date with different strings depending on how close + * to a certain date (today by default) the date is. + * http://momentjs.com/docs/#/displaying/calendar-time/ + */ + + let referenceTime = moment(); + let formats = {}; + + if (moment(args[0]).isValid()) { + // If valid, assume it is a date we want the output computed against. + referenceTime = moment(args.shift()); + } else if (args[0] === null || args[0] === 'now') { + // If null or 'now', remove argument and proceed with default referenceTime. + args.shift(); + } - return date; - }); + if (typeof args[0] === 'object') formats = args.shift(); - Vue.filter('duration', (...args) => { - /* - * Basic pass-through filter for leveraging moment.js's ability - * to manipulate and display durations. - * https://momentjs.com/docs/#/durations/ - */ - args = Array.prototype.slice.call(args); - const input = args.shift(); - const method = args.shift(); - - function createDuration(time) { - if (!Array.isArray(time)) time = [time]; - const result = moment.duration(...time); - if (!result.isValid()) console.warn('Could not build a valid `duration` object from input.'); - return result; + date = date.calendar(referenceTime, formats); + break; } - let duration = createDuration(input); - - if (method === 'add' || method === 'subtract') { - // Generates a duration object and either adds or subtracts it - // from our original duration. - const durationChange = createDuration(args); - duration[method](durationChange); - } else if (duration && duration[method]) { - // This gives a full proxy to moment.duration functions. - duration = duration[method](...args); + + case 'utc': { + /* + * Mutates the original moment by converting to UTC + * https://momentjs.com/docs/#/manipulating/utc/ + */ + date.utc(); + break; } - return duration; + case 'timezone': { + /* + * Mutates the original moment by converting to a new timezone. + * https://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/ + */ + date.tz(args.shift()); + break; + } + + default: { + /* + * Formats a date by taking a string of tokens and replacing + * them with their corresponding values. + * http://momentjs.com/docs/#/displaying/format/ + */ + + const format = method; + date = date.format(format); + } + } + + if (args.length) parse.apply(parse, args); + } + + parse.apply(parse, args); + + return date; +}; + +const createDurationFilter = moment => (...args) => { + /* + * Basic pass-through filter for leveraging moment.js's ability + * to manipulate and display durations. + * https://momentjs.com/docs/#/durations/ + */ + args = Array.prototype.slice.call(args); + const input = args.shift(); + const method = args.shift(); + + function createDuration(time) { + if (!Array.isArray(time)) time = [time]; + const result = moment.duration(...time); + if (!result.isValid()) console.warn('Could not build a valid `duration` object from input.'); + return result; + } + let duration = createDuration(input); + + if (method === 'add' || method === 'subtract') { + // Generates a duration object and either adds or subtracts it + // from our original duration. + const durationChange = createDuration(args); + duration[method](durationChange); + } else if (duration && duration[method]) { + // This gives a full proxy to moment.duration functions. + duration = duration[method](...args); + } + + return duration; +}; + +module.exports = { + install(Vue, options) { + const moment = options && options.moment ? options.moment : momentLocal; + + Object.defineProperties(Vue.prototype, { + $moment: { + get() { + return moment; + }, + }, }); + + Vue.moment = moment; + + Vue.filter('moment', createMomentFilter(moment)); + + Vue.filter('duration', createDurationFilter(moment)); }, + moment: createMomentFilter(momentLocal), + duration: createDurationFilter(momentLocal), };