@nrk/core-datepicker
Advanced tools
Comparing version 2.1.0 to 3.0.0
'use strict'; | ||
var name = "@nrk/core-datepicker"; | ||
var version = "2.0.3"; | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
function _getPrototypeOf(o) { | ||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { | ||
return o.__proto__ || Object.getPrototypeOf(o); | ||
}; | ||
return _getPrototypeOf(o); | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
function isNativeReflectConstruct() { | ||
if (typeof Reflect === "undefined" || !Reflect.construct) return false; | ||
if (Reflect.construct.sham) return false; | ||
if (typeof Proxy === "function") return true; | ||
try { | ||
Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
function _construct(Parent, args, Class) { | ||
if (isNativeReflectConstruct()) { | ||
_construct = Reflect.construct; | ||
} else { | ||
_construct = function _construct(Parent, args, Class) { | ||
var a = [null]; | ||
a.push.apply(a, args); | ||
var Constructor = Function.bind.apply(Parent, a); | ||
var instance = new Constructor(); | ||
if (Class) _setPrototypeOf(instance, Class.prototype); | ||
return instance; | ||
}; | ||
} | ||
return _construct.apply(null, arguments); | ||
} | ||
function _isNativeFunction(fn) { | ||
return Function.toString.call(fn).indexOf("[native code]") !== -1; | ||
} | ||
function _wrapNativeSuper(Class) { | ||
var _cache = typeof Map === "function" ? new Map() : undefined; | ||
_wrapNativeSuper = function _wrapNativeSuper(Class) { | ||
if (Class === null || !_isNativeFunction(Class)) return Class; | ||
if (typeof Class !== "function") { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
if (typeof _cache !== "undefined") { | ||
if (_cache.has(Class)) return _cache.get(Class); | ||
_cache.set(Class, Wrapper); | ||
} | ||
function Wrapper() { | ||
return _construct(Class, arguments, _getPrototypeOf(this).constructor); | ||
} | ||
Wrapper.prototype = Object.create(Class.prototype, { | ||
constructor: { | ||
value: Wrapper, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
return _setPrototypeOf(Wrapper, Class); | ||
}; | ||
return _wrapNativeSuper(Class); | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return self; | ||
} | ||
function _possibleConstructorReturn(self, call) { | ||
if (call && (typeof call === "object" || typeof call === "function")) { | ||
return call; | ||
} | ||
return _assertThisInitialized(self); | ||
} | ||
var IS_BROWSER = typeof window !== 'undefined'; | ||
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed | ||
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); | ||
var HAS_EVENT_OPTIONS = (function (has) { | ||
if ( has === void 0 ) has = false; | ||
try { window.addEventListener('test', null, { get passive () { has = true; } }); } catch (e) {} | ||
return has | ||
})(); | ||
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE | ||
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) { | ||
window.Element.prototype.toggleAttribute = function (name) { | ||
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name); | ||
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, ''); | ||
return force; | ||
}; | ||
} | ||
/** | ||
* addEvent | ||
* @param {String} uuid An unique ID of the event to bind - ensurnes single instance | ||
* @param {String} type The type of event to bind | ||
* @param {Function} handler The function to call on event | ||
* @param {Boolean|Object} options useCapture or options object for addEventListener. Defaults to false | ||
* addStyle | ||
* @param {String} nodeName An unique ID of the event to bind - ensurnes single instance | ||
* @param {String} css The css to inject | ||
*/ | ||
function addEvent (uuid, type, handler, options) { | ||
if ( options === void 0 ) options = false; | ||
if (typeof window === 'undefined' || window[uuid = uuid + "-" + type]) { return } // Ensure single instance | ||
if (!HAS_EVENT_OPTIONS && typeof options === 'object') { options = Boolean(options.capture); } // Fix unsupported options | ||
var node = (type === 'resize' || type === 'load') ? window : document; | ||
node.addEventListener(window[uuid] = type, handler, options); | ||
function addStyle(nodeName, css) { | ||
var key = "style-".concat(nodeName.toLowerCase()); | ||
var min = css.replace(/\/\*[^!][^*]*\*\//g, '').replace(/\s*(^|[:;,{}]|$)\s*/g, '$1'); | ||
document.getElementById(key) || document.head.insertAdjacentHTML('afterbegin', "<style id=\"".concat(key, "\">").concat(min, "</style>")); | ||
} | ||
/** | ||
@@ -37,7 +168,36 @@ * escapeHTML | ||
*/ | ||
var ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', '/': '/', '\'': ''' }; | ||
function escapeHTML (str) { | ||
return String(str || '').replace(/[&<>"'/]/g, function (char) { return ESCAPE_MAP[char]; }) | ||
var ESCAPE_MAP = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
'/': '/', | ||
'\'': ''' | ||
}; | ||
function escapeHTML(str) { | ||
return String(str || '').replace(/[&<>"'/]/g, function (_char) { | ||
return ESCAPE_MAP[_char]; | ||
}); | ||
} | ||
/** | ||
* closest | ||
* @param {Element} element Element to traverse up from | ||
* @param {String} selector A selector to search for matching parents or element itself | ||
* @return {Element|null} Element which is the closest ancestor matching selector | ||
*/ | ||
var closest = function () { | ||
var proto = typeof window === 'undefined' ? {} : window.Element.prototype; | ||
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector; | ||
return proto.closest ? function (el, css) { | ||
return el.closest(css); | ||
} : function (el, css) { | ||
for (; el; el = el.parentElement) { | ||
if (match.call(el, css)) return el; | ||
} | ||
return null; | ||
}; | ||
}(); | ||
/** | ||
@@ -50,26 +210,28 @@ * dispatchEvent - with infinite loop prevention | ||
*/ | ||
var IGNORE = 'prevent_recursive_dispatch_maximum_callstack'; | ||
function dispatchEvent (element, name, detail) { | ||
if ( detail === void 0 ) detail = {}; | ||
var ignore = "" + IGNORE + name; | ||
function dispatchEvent(element, name) { | ||
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name); | ||
var event; | ||
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one | ||
else element[ignore] = true; // Add name to dispatching ignore | ||
if (element[ignore]) { return true } // We are already processing this event, so skip sending a new one | ||
else { element[ignore] = true; } // Add name to dispatching ignore | ||
if (typeof window.CustomEvent === 'function') { | ||
event = new window.CustomEvent(name, { bubbles: true, cancelable: true, detail: detail }); | ||
event = new window.CustomEvent(name, { | ||
bubbles: true, | ||
cancelable: true, | ||
detail: detail | ||
}); | ||
} else { | ||
event = document.createEvent('CustomEvent'); | ||
event.initCustomEvent(name, true, true, detail); | ||
} | ||
// IE reports incorrect event.defaultPrevented | ||
} // IE reports incorrect event.defaultPrevented | ||
// but correct return value on element.dispatchEvent | ||
var result = element.dispatchEvent(event); | ||
element[ignore] = null; // Remove name from dispatching ignore | ||
return result // Follow W3C standard for return value | ||
return result; // Follow W3C standard for return value | ||
} | ||
/** | ||
@@ -80,14 +242,16 @@ * queryAll | ||
*/ | ||
function queryAll (elements, context) { | ||
if ( context === void 0 ) context = document; | ||
function queryAll(elements) { | ||
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document; | ||
if (elements) { | ||
if (elements.nodeType) { return [elements] } | ||
if (typeof elements === 'string') { return [].slice.call(context.querySelectorAll(elements)) } | ||
if (elements.length) { return [].slice.call(elements) } | ||
if (elements.nodeType) return [elements]; | ||
if (typeof elements === 'string') return [].slice.call(context.querySelectorAll(elements)); | ||
if (elements.length) return [].slice.call(elements); | ||
} | ||
return [] | ||
return []; | ||
} | ||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
@@ -99,121 +263,304 @@ function createCommonjsModule(fn, module) { | ||
var index_min = createCommonjsModule(function (module, exports) { | ||
!function(e,t){module.exports=t();}(commonjsGlobal,function(){var D={year:"FullYear",month:"Month",week:"Date",day:"Date",hour:"Hours",minute:"Minutes",second:"Seconds"},w=/([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g,M=/([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/,p=/([\dh]+):([\dm]+):?([\ds]+)?/;return function(e,t){if(isFinite(e)){ return new Date(Number(e)); }var n=String(e).toLowerCase(),r=new Date(isFinite(t)&&-1===n.indexOf("now")?Number(t):Date.now()),a=n.match(M)||[],o=a[1];void 0===o&&(o="y");var i=a[2];void 0===i&&(i="m");var s=a[3];void 0===s&&(s="d");var d=n.match(p)||[],u=d[1];void 0===u&&(u="h");var h=d[2];void 0===h&&(h="m");var m=d[3];void 0===m&&(m="s");var c={year:o,month:i,day:s,hour:u,minute:h,second:m};Object.keys(c).forEach(function(e){var t="month"===e?1:0,a=String(r["get"+D[e]]()+t);c[e]=c[e].replace(/[^-\d]+/g,function(e,t,n){return t?a.substr(a.length-n.length+t,e.length):a.substr(0,Math.max(0,a.length-n.length+e.length))})-t;});var f=new Date(c.year,Math.min(12,c.month+1),0).getDate();for(r.setFullYear(c.year,Math.min(11,c.month),Math.max(1,Math.min(f,c.day))),r.setHours(Math.min(24,c.hour),Math.min(59,c.minute),Math.min(59,c.second));null!==(c=w.exec(n));){var g=c[2],l=String(c[1]).replace(/\s/g,"")*("week"===g?7:1),v=c.slice(2).indexOf(c[0]),y=r.getDate();g?r["set"+D[g]](r["get"+D[g]]()+l):r.setDate(r.getDate()-(r.getDay()||7)+v),"month"!==g&&"year"!==g||y===r.getDate()||r.setDate(0);}return r}}); | ||
!function (e, t) { | ||
module.exports = t(); | ||
}(commonjsGlobal, function () { | ||
var D = { | ||
year: "FullYear", | ||
month: "Month", | ||
week: "Date", | ||
day: "Date", | ||
hour: "Hours", | ||
minute: "Minutes", | ||
second: "Seconds" | ||
}, | ||
w = /([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g, | ||
M = /([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/, | ||
p = /([\dh]+):([\dm]+):?([\ds]+)?/; | ||
return function (e, t) { | ||
if (isFinite(e)) return new Date(Number(e)); | ||
var n = String(e).toLowerCase(), | ||
r = new Date(isFinite(t) && -1 === n.indexOf("now") ? Number(t) : Date.now()), | ||
a = n.match(M) || [], | ||
o = a[1]; | ||
void 0 === o && (o = "y"); | ||
var i = a[2]; | ||
void 0 === i && (i = "m"); | ||
var s = a[3]; | ||
void 0 === s && (s = "d"); | ||
var d = n.match(p) || [], | ||
u = d[1]; | ||
void 0 === u && (u = "h"); | ||
var h = d[2]; | ||
void 0 === h && (h = "m"); | ||
var m = d[3]; | ||
void 0 === m && (m = "s"); | ||
var c = { | ||
year: o, | ||
month: i, | ||
day: s, | ||
hour: u, | ||
minute: h, | ||
second: m | ||
}; | ||
Object.keys(c).forEach(function (e) { | ||
var t = "month" === e ? 1 : 0, | ||
a = String(r["get" + D[e]]() + t); | ||
c[e] = c[e].replace(/[^-\d]+/g, function (e, t, n) { | ||
return t ? a.substr(a.length - n.length + t, e.length) : a.substr(0, Math.max(0, a.length - n.length + e.length)); | ||
}) - t; | ||
}); | ||
var f = new Date(c.year, Math.min(12, c.month + 1), 0).getDate(); | ||
for (r.setFullYear(c.year, Math.min(11, c.month), Math.max(1, Math.min(f, c.day))), r.setHours(Math.min(24, c.hour), Math.min(59, c.minute), Math.min(59, c.second)); null !== (c = w.exec(n));) { | ||
var g = c[2], | ||
l = String(c[1]).replace(/\s/g, "") * ("week" === g ? 7 : 1), | ||
v = c.slice(2).indexOf(c[0]), | ||
y = r.getDate(); | ||
g ? r["set" + D[g]](r["get" + D[g]]() + l) : r.setDate(r.getDate() - (r.getDay() || 7) + v), "month" !== g && "year" !== g || y === r.getDate() || r.setDate(0); | ||
} | ||
return r; | ||
}; | ||
}); | ||
}); | ||
var ATTR = 'data-core-datepicker'; | ||
var UUID = ("data-" + name + "-" + version).replace(/\W+/g, '-'); // Strip invalid attribute characters | ||
var KEY_CODES = { 33: '-1month', 34: '+1month', 35: 'y-m-99', 36: 'y-m-1', 37: '-1day', 38: '-1week', 39: '+1day', 40: '+1week' }; | ||
var MASK = { year: '*-m-d', month: 'y-*-d', day: 'y-m-*', hour: '*:m', minute: 'h:*', second: 'h:m:*', timestamp: '*' }; | ||
var MS_IN_MINUTES = 60000; | ||
var MASK = { | ||
year: '*-m-d', | ||
month: 'y-*-d', | ||
day: 'y-m-*', | ||
hour: '*:m', | ||
minute: 'h:*', | ||
second: 'h:m:*', | ||
timestamp: '*', | ||
"null": '*' | ||
}; | ||
var KEYS = { | ||
33: '-1month', | ||
34: '+1month', | ||
35: 'y-m-99', | ||
36: 'y-m-1', | ||
37: '-1day', | ||
38: '-1week', | ||
39: '+1day', | ||
40: '+1week' | ||
}; | ||
var MONTHS = 'januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember'; | ||
var DAYS = 'man,tirs,ons,tors,fre,lør,søn'; | ||
function datepicker (elements, date) { // date can be String, Timestamp or Date | ||
return queryAll(elements).map(function (element) { | ||
var prevDate = index_min(element.getAttribute(UUID) || date); | ||
var nextDate = index_min(typeof date === 'undefined' ? prevDate : date, prevDate); | ||
var disable = function () { return false; }; | ||
var CoreDatepicker = | ||
/*#__PURE__*/ | ||
function (_HTMLElement) { | ||
_inherits(CoreDatepicker, _HTMLElement); | ||
dispatchEvent(element, 'datepicker.render', { nextDate: nextDate, prevDate: prevDate, disable: function (fn) { return (disable = fn); } }); | ||
if (disable(nextDate)) { nextDate = prevDate; } // Jump back to prev date if next is disabled | ||
function CoreDatepicker() { | ||
_classCallCheck(this, CoreDatepicker); | ||
var isUpdate = prevDate.getTime() === nextDate.getTime() || dispatchEvent(element, 'datepicker.change', { prevDate: prevDate, nextDate: nextDate }); | ||
var next = isUpdate ? nextDate : index_min(element.getAttribute(UUID) || Date.now()); // dispatchEvent can change attributes to parse prevDate again | ||
var json = new Date(next.getTime() - next.getTimezoneOffset() * MS_IN_MINUTES).toJSON().match(/\d+/g); | ||
var unit = { year: next.getFullYear(), month: json[1], day: json[2], hour: json[3], minute: json[4], second: json[5], timestamp: next.getTime() }; | ||
return _possibleConstructorReturn(this, _getPrototypeOf(CoreDatepicker).apply(this, arguments)); | ||
} | ||
element.setAttribute(UUID, unit.timestamp); | ||
queryAll('button').forEach(function (el) { return button(el, next, disable, element); }); | ||
queryAll('select', element).concat(queryAll(("select[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return select(el, next, disable); }); | ||
queryAll('input', element).concat(queryAll(("input[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return input(el, next, disable, unit); }); | ||
queryAll('table', element).concat(queryAll(("table[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return table(el, next, disable); }); | ||
_createClass(CoreDatepicker, [{ | ||
key: "connectedCallback", | ||
value: function connectedCallback() { | ||
var _this = this; | ||
return element | ||
}) | ||
} | ||
this._date = this.date; // Store for later comparison and speeding up things | ||
// Expose API and config | ||
datepicker.parse = index_min; | ||
datepicker.months = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember']; | ||
datepicker.days = ['man', 'tirs', 'ons', 'tors', 'fre', 'lør', 'søn']; | ||
document.addEventListener('click', this); | ||
document.addEventListener('change', this); | ||
document.addEventListener('keydown', this); | ||
setTimeout(function () { | ||
return _this.attributeChangedCallback(); | ||
}); // Render after children is parsed | ||
addEvent(UUID, 'change', onChange); | ||
addEvent(UUID, 'click', function (ref) { | ||
var target = ref.target; | ||
addStyle(this.nodeName, "".concat(this.nodeName, "{display:block}")); // default to display block | ||
} | ||
}, { | ||
key: "disconnectedCallback", | ||
value: function disconnectedCallback() { | ||
this._date = this._disabled = null; // Garbage collection | ||
for (var el = target; el; el = el.parentElement) { | ||
if (el.nodeName === 'BUTTON') { return onChange({ target: el }) } | ||
} | ||
}); | ||
addEvent(UUID, 'keydown', function (event) { | ||
if (event.ctrlKey || event.metaKey || event.shitKey || event.altKey || !KEY_CODES[event.keyCode]) { return } | ||
for (var el = event.target, table = (void 0); el; el = el.parentElement) { | ||
if (!table && el.nodeName === 'TABLE') { table = el; } // Store table while traversing DOM parents | ||
if (table && el.hasAttribute(UUID)) { // Only listen to keyCodes inside table inside datepicker | ||
datepicker(el, KEY_CODES[event.keyCode]); | ||
table.querySelector(("[" + ATTR + "-selected=\"true\"]")).focus(); | ||
event.preventDefault(); | ||
break | ||
document.removeEventListener('click', this); | ||
document.removeEventListener('change', this); | ||
document.removeEventListener('keydown', this); | ||
} | ||
} | ||
}); | ||
}, { | ||
key: "attributeChangedCallback", | ||
value: function attributeChangedCallback() { | ||
if (!this._date) return; // Only render after connectedCallback | ||
function onChange (ref) { | ||
var target = ref.target; | ||
if (this.disabled(this.date) && !this.disabled(this._date)) return this.date = this._date; // Jump back | ||
for (var el = target, table = (void 0); el; el = el.parentElement) { | ||
var elem = document.getElementById(el.getAttribute(ATTR)) || el; | ||
var mask = elem.hasAttribute(UUID) && (MASK[target.getAttribute((UUID + "-type"))] || '*'); | ||
if (this.diff(this.date)) dispatchEvent(this, 'datepicker.change', this._date = this.date); | ||
forEach('button', this, button); | ||
forEach('select', this, select); | ||
forEach('input', this, input); | ||
forEach('table', this, table); | ||
} | ||
}, { | ||
key: "handleEvent", | ||
value: function handleEvent(event) { | ||
if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.type === 'keydown' && !KEYS[event.keyCode]) return; | ||
if (!this.contains(event.target) && !closest(event.target, "[for=\"".concat(this.id, "\"]"))) return; | ||
if (event.type === 'change') this.date = MASK[event.target.getAttribute('data-type')].replace('*', event.target.value);else if (event.type === 'click') { | ||
var _button = closest(event.target, 'button[value]'); | ||
if (!table && el.nodeName === 'TABLE') { table = el; } // Store table while traversing DOM parents | ||
if (mask) { | ||
var nextDate = mask.replace('*', target.value); | ||
var isUpdate = !elem.contains(table) || dispatchEvent(elem, 'datepicker.click.day', { | ||
currentTarget: target, | ||
relatedTarget: table, | ||
prevDate: index_min(elem.getAttribute(UUID)), | ||
nextDate: index_min(nextDate) | ||
}); | ||
var _table = closest(event.target, 'table'); | ||
return isUpdate && datepicker(elem, nextDate) | ||
if (_button) this.date = _button.value; | ||
if (_button && _table) dispatchEvent(this, 'datepicker.click.day'); | ||
} else if (event.type === 'keydown' && closest(event.target, 'table')) { | ||
this.date = KEYS[event.keyCode]; | ||
this.querySelector('[autofocus]').focus(); | ||
event.preventDefault(); // Prevent scrolling | ||
} | ||
} | ||
} | ||
}, { | ||
key: "diff", | ||
value: function diff(val) { | ||
return this.parse(val).getTime() - this.timestamp; | ||
} | ||
}, { | ||
key: "parse", | ||
value: function parse(val, from) { | ||
return index_min(val, from || this._date); | ||
} | ||
}, { | ||
key: "disabled", | ||
get: function get() { | ||
return this._disabled || Function.prototype; | ||
}, | ||
set: function set(fn) { | ||
var _this2 = this; | ||
this._disabled = typeof fn === 'function' ? function (val) { | ||
return fn(_this2.parse(val), _this2); | ||
} : function () { | ||
return fn; | ||
}; // Auto parse dates | ||
this.attributeChangedCallback(); // Re-render | ||
} | ||
}, { | ||
key: "timestamp", | ||
get: function get() { | ||
return String(this._date.getTime()); | ||
} | ||
}, { | ||
key: "year", | ||
get: function get() { | ||
return String(this._date.getFullYear()); | ||
} // Stringify for consistency and for truthy '0' | ||
}, { | ||
key: "month", | ||
get: function get() { | ||
return pad(this._date.getMonth() + 1); | ||
} | ||
}, { | ||
key: "day", | ||
get: function get() { | ||
return pad(this._date.getDate()); | ||
} | ||
}, { | ||
key: "hour", | ||
get: function get() { | ||
return pad(this._date.getHours()); | ||
} | ||
}, { | ||
key: "minute", | ||
get: function get() { | ||
return pad(this._date.getMinutes()); | ||
} | ||
}, { | ||
key: "second", | ||
get: function get() { | ||
return pad(this._date.getSeconds()); | ||
} | ||
}, { | ||
key: "date", | ||
get: function get() { | ||
return index_min(this.getAttribute('timestamp') || this._date || Date.now()); | ||
}, | ||
set: function set(val) { | ||
return this.setAttribute('timestamp', this.parse(val).getTime()); | ||
} | ||
}, { | ||
key: "months", | ||
set: function set(val) { | ||
this.setAttribute('months', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('months') || MONTHS).split(/\s*,\s*/); | ||
} | ||
}, { | ||
key: "days", | ||
set: function set(val) { | ||
this.setAttribute('days', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('days') || DAYS).split(/\s*,\s*/); | ||
} | ||
}], [{ | ||
key: "observedAttributes", | ||
get: function get() { | ||
return ['timestamp', 'months', 'days']; | ||
} | ||
}]); | ||
return CoreDatepicker; | ||
}(_wrapNativeSuper(HTMLElement)); | ||
var pad = function pad(val) { | ||
return "0".concat(val).slice(-2); | ||
}; | ||
var forEach = function forEach(css, self, fn) { | ||
return [].forEach.call(document.getElementsByTagName(css), function (el) { | ||
if (self.contains(el) || self.id === el.getAttribute(self.external)) fn(self, el, self._date); | ||
}); | ||
}; | ||
function button(self, el) { | ||
el.disabled = self.disabled(el.value); | ||
} | ||
function input (el, date, disable, unit) { | ||
var type = el.getAttribute((UUID + "-type")) || el.getAttribute('type'); | ||
function input(self, el) { | ||
var type = el.getAttribute('data-type') || el.getAttribute('type'); | ||
if (type === 'radio' || type === 'checkbox') { | ||
var val = index_min(el.value, date); | ||
el.disabled = disable(val); | ||
el.checked = val.getTime() === date.getTime(); | ||
} else if (unit[type]) { | ||
el.disabled = self.disabled(el.value); | ||
el.checked = !self.diff(el.value); | ||
} else if (MASK[type]) { | ||
el.setAttribute('type', 'number'); // Set input type to number | ||
el.setAttribute((UUID + "-type"), type); // And store original input type | ||
el.value = unit[type]; | ||
el.setAttribute('data-type', type); // And store original type | ||
el.value = self[type]; | ||
} | ||
} | ||
function table (table, date, disable) { | ||
function table(self, table) { | ||
if (!table.firstElementChild) { | ||
table.innerHTML = "\n <caption></caption><thead><tr><th>" + (datepicker.days.map(escapeHTML).join('</th><th>')) + "</th></tr></thead>\n <tbody>" + (Array(7).join(("<tr>" + (Array(8).join("<td><button type=\"button\"></button></td>")) + "</tr>"))) + "</tbody>"; | ||
table.innerHTML = "\n <caption></caption><thead><tr><th>".concat(self.days.map(escapeHTML).join('</th><th>'), "</th></tr></thead>\n <tbody>").concat(Array(7).join("<tr>".concat(Array(8).join('<td><button type="button"></button></td>'), "</tr>")), "</tbody>"); | ||
} | ||
var today = new Date(); | ||
var day = index_min('y-m-1 mon', date); // Monday in first week of month | ||
table.caption.textContent = (escapeHTML(datepicker.months[date.getMonth()])) + ", " + (date.getFullYear()); | ||
var month = self.date.getMonth(); | ||
var day = self.parse('y-m-1 mon'); // Monday in first week of month | ||
table.caption.textContent = "".concat(self.months[month], ", ").concat(self.year); | ||
queryAll('button', table).forEach(function (button) { | ||
var isToday = day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear(); | ||
var isSelected = day.getTime() === date.getTime(); | ||
var isSelected = !self.diff(day); | ||
var dayInMonth = day.getDate(); | ||
var month = day.getMonth(); | ||
var dayMonth = day.getMonth(); | ||
button.textContent = dayInMonth; // Set textContent instead of innerHTML avoids reflow | ||
button.textContent = dayInMonth; // Set textContent instead of innerHTML avoids reflow | ||
button.value = (day.getFullYear()) + "-" + (month + 1) + "-" + dayInMonth; | ||
button.disabled = disable(day); | ||
button.setAttribute('tabindex', isSelected - 1); | ||
button.setAttribute((ATTR + "-selected"), isSelected); | ||
button.setAttribute((ATTR + "-adjacent"), date.getMonth() !== month); | ||
button.setAttribute('aria-label', (dayInMonth + ". " + (datepicker.months[month]))); | ||
button[isToday ? 'setAttribute' : 'removeAttribute']('aria-current', 'date'); | ||
button[isSelected ? 'setAttribute' : 'removeAttribute']('autofocus', ''); | ||
button.value = "".concat(day.getFullYear(), "-").concat(dayMonth + 1, "-").concat(dayInMonth); | ||
button.disabled = self.disabled(day); | ||
button.tabIndex = isSelected - 1; | ||
button.setAttribute("data-adjacent", month !== dayMonth); | ||
button.setAttribute('aria-label', "".concat(dayInMonth, ". ").concat(self.months[dayMonth])); | ||
button.setAttribute('aria-current', day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear() && 'date'); | ||
button.toggleAttribute('autofocus', isSelected); | ||
day.setDate(dayInMonth + 1); | ||
@@ -223,21 +570,15 @@ }); | ||
function button (el, date, disable, picker) { | ||
if (el.getAttribute(ATTR) === picker.id || picker.contains(el)) { | ||
el.disabled = disable(index_min(el.value, date)); | ||
} | ||
} | ||
function select (select, date, disable) { | ||
function select(self, select) { | ||
if (!select.firstElementChild) { | ||
select.innerHTML = datepicker.months.map(function (name, month) { return ("<option value=\"y-" + (month + 1) + "-d\">" + (escapeHTML(name)) + "</option>"); } | ||
).join(''); | ||
select.innerHTML = self.months.map(function (name, month) { | ||
return "<option value=\"y-".concat(month + 1, "-d\">").concat(escapeHTML(name), "</option>"); | ||
}).join(''); | ||
} | ||
queryAll(select.children).forEach(function (option) { | ||
var val = index_min(option.value, date); | ||
option.disabled = disable(val); | ||
option.selected = val.getTime() === date.getTime(); | ||
option.disabled = self.disabled(option.value); | ||
option.selected = !self.diff(option.value); | ||
}); | ||
} | ||
module.exports = datepicker; | ||
module.exports = CoreDatepicker; |
@@ -1,118 +0,122 @@ | ||
import { name, version } from './package.json' | ||
import { addEvent, escapeHTML, dispatchEvent, queryAll } from '../utils' | ||
import { addStyle, closest, escapeHTML, dispatchEvent, queryAll } from '../utils' | ||
import parse from '@nrk/simple-date-parse' | ||
const ATTR = 'data-core-datepicker' | ||
const UUID = `data-${name}-${version}`.replace(/\W+/g, '-') // Strip invalid attribute characters | ||
const KEY_CODES = { 33: '-1month', 34: '+1month', 35: 'y-m-99', 36: 'y-m-1', 37: '-1day', 38: '-1week', 39: '+1day', 40: '+1week' } | ||
const MASK = { year: '*-m-d', month: 'y-*-d', day: 'y-m-*', hour: '*:m', minute: 'h:*', second: 'h:m:*', timestamp: '*' } | ||
const MS_IN_MINUTES = 60000 | ||
const MASK = { year: '*-m-d', month: 'y-*-d', day: 'y-m-*', hour: '*:m', minute: 'h:*', second: 'h:m:*', timestamp: '*', null: '*' } | ||
const KEYS = { 33: '-1month', 34: '+1month', 35: 'y-m-99', 36: 'y-m-1', 37: '-1day', 38: '-1week', 39: '+1day', 40: '+1week' } | ||
const MONTHS = 'januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember' | ||
const DAYS = 'man,tirs,ons,tors,fre,lør,søn' | ||
export default function datepicker (elements, date) { // date can be String, Timestamp or Date | ||
return queryAll(elements).map((element) => { | ||
const prevDate = parse(element.getAttribute(UUID) || date) | ||
let nextDate = parse(typeof date === 'undefined' ? prevDate : date, prevDate) | ||
let disable = () => false | ||
export default class CoreDatepicker extends HTMLElement { | ||
static get observedAttributes () { return ['timestamp', 'months', 'days'] } | ||
dispatchEvent(element, 'datepicker.render', { nextDate, prevDate, disable: (fn) => (disable = fn) }) | ||
if (disable(nextDate)) nextDate = prevDate // Jump back to prev date if next is disabled | ||
connectedCallback () { | ||
this._date = this.date // Store for later comparison and speeding up things | ||
document.addEventListener('click', this) | ||
document.addEventListener('change', this) | ||
document.addEventListener('keydown', this) | ||
setTimeout(() => this.attributeChangedCallback()) // Render after children is parsed | ||
addStyle(this.nodeName, `${this.nodeName}{display:block}`) // default to display block | ||
} | ||
disconnectedCallback () { | ||
this._date = this._disabled = null // Garbage collection | ||
document.removeEventListener('click', this) | ||
document.removeEventListener('change', this) | ||
document.removeEventListener('keydown', this) | ||
} | ||
attributeChangedCallback () { | ||
if (!this._date) return // Only render after connectedCallback | ||
if (this.disabled(this.date) && !this.disabled(this._date)) return (this.date = this._date) // Jump back | ||
if (this.diff(this.date)) dispatchEvent(this, 'datepicker.change', this._date = this.date) | ||
const isUpdate = prevDate.getTime() === nextDate.getTime() || dispatchEvent(element, 'datepicker.change', { prevDate, nextDate }) | ||
const next = isUpdate ? nextDate : parse(element.getAttribute(UUID) || Date.now()) // dispatchEvent can change attributes to parse prevDate again | ||
const json = new Date(next.getTime() - next.getTimezoneOffset() * MS_IN_MINUTES).toJSON().match(/\d+/g) | ||
const unit = { year: next.getFullYear(), month: json[1], day: json[2], hour: json[3], minute: json[4], second: json[5], timestamp: next.getTime() } | ||
forEach('button', this, button) | ||
forEach('select', this, select) | ||
forEach('input', this, input) | ||
forEach('table', this, table) | ||
} | ||
handleEvent (event) { | ||
if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || (event.type === 'keydown' && !KEYS[event.keyCode])) return | ||
if (!this.contains(event.target) && !closest(event.target, `[for="${this.id}"]`)) return | ||
if (event.type === 'change') this.date = MASK[event.target.getAttribute('data-type')].replace('*', event.target.value) | ||
else if (event.type === 'click') { | ||
const button = closest(event.target, 'button[value]') | ||
const table = closest(event.target, 'table') | ||
if (button) this.date = button.value | ||
if (button && table) dispatchEvent(this, 'datepicker.click.day') | ||
} else if (event.type === 'keydown' && closest(event.target, 'table')) { | ||
this.date = KEYS[event.keyCode] | ||
this.querySelector('[autofocus]').focus() | ||
event.preventDefault() // Prevent scrolling | ||
} | ||
} | ||
diff (val) { return this.parse(val).getTime() - this.timestamp } | ||
parse (val, from) { return parse(val, from || this._date) } | ||
element.setAttribute(UUID, unit.timestamp) | ||
queryAll('button').forEach((el) => button(el, next, disable, element)) | ||
queryAll('select', element).concat(queryAll(`select[${ATTR}="${element.id}"]`)).forEach((el) => select(el, next, disable)) | ||
queryAll('input', element).concat(queryAll(`input[${ATTR}="${element.id}"]`)).forEach((el) => input(el, next, disable, unit)) | ||
queryAll('table', element).concat(queryAll(`table[${ATTR}="${element.id}"]`)).forEach((el) => table(el, next, disable)) | ||
get disabled () { return this._disabled || Function.prototype } | ||
set disabled (fn) { | ||
this._disabled = typeof fn === 'function' ? (val) => fn(this.parse(val), this) : () => fn // Auto parse dates | ||
this.attributeChangedCallback() // Re-render | ||
} | ||
return element | ||
}) | ||
get timestamp () { return String(this._date.getTime()) } | ||
get year () { return String(this._date.getFullYear()) } // Stringify for consistency and for truthy '0' | ||
get month () { return pad(this._date.getMonth() + 1) } | ||
get day () { return pad(this._date.getDate()) } | ||
get hour () { return pad(this._date.getHours()) } | ||
get minute () { return pad(this._date.getMinutes()) } | ||
get second () { return pad(this._date.getSeconds()) } | ||
get date () { return parse(this.getAttribute('timestamp') || this._date || Date.now()) } | ||
set date (val) { return this.setAttribute('timestamp', this.parse(val).getTime()) } | ||
set months (val) { this.setAttribute('months', [].concat(val).join(',')) } | ||
get months () { return (this.getAttribute('months') || MONTHS).split(/\s*,\s*/) } | ||
set days (val) { this.setAttribute('days', [].concat(val).join(',')) } | ||
get days () { return (this.getAttribute('days') || DAYS).split(/\s*,\s*/) } | ||
} | ||
// Expose API and config | ||
datepicker.parse = parse | ||
datepicker.months = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'] | ||
datepicker.days = ['man', 'tirs', 'ons', 'tors', 'fre', 'lør', 'søn'] | ||
addEvent(UUID, 'change', onChange) | ||
addEvent(UUID, 'click', ({ target }) => { | ||
for (let el = target; el; el = el.parentElement) { | ||
if (el.nodeName === 'BUTTON') return onChange({ target: el }) | ||
} | ||
const pad = (val) => `0${val}`.slice(-2) | ||
const forEach = (css, self, fn) => [].forEach.call(document.getElementsByTagName(css), (el) => { | ||
if (self.contains(el) || self.id === el.getAttribute(self.external)) fn(self, el, self._date) | ||
}) | ||
addEvent(UUID, 'keydown', (event) => { | ||
if (event.ctrlKey || event.metaKey || event.shitKey || event.altKey || !KEY_CODES[event.keyCode]) return | ||
for (let el = event.target, table; el; el = el.parentElement) { | ||
if (!table && el.nodeName === 'TABLE') table = el // Store table while traversing DOM parents | ||
if (table && el.hasAttribute(UUID)) { // Only listen to keyCodes inside table inside datepicker | ||
datepicker(el, KEY_CODES[event.keyCode]) | ||
table.querySelector(`[${ATTR}-selected="true"]`).focus() | ||
event.preventDefault() | ||
break | ||
} | ||
} | ||
}) | ||
function onChange ({ target }) { | ||
for (let el = target, table; el; el = el.parentElement) { | ||
const elem = document.getElementById(el.getAttribute(ATTR)) || el | ||
const mask = elem.hasAttribute(UUID) && (MASK[target.getAttribute(`${UUID}-type`)] || '*') | ||
function button (self, el) { | ||
el.disabled = self.disabled(el.value) | ||
} | ||
if (!table && el.nodeName === 'TABLE') table = el // Store table while traversing DOM parents | ||
if (mask) { | ||
const nextDate = mask.replace('*', target.value) | ||
const isUpdate = !elem.contains(table) || dispatchEvent(elem, 'datepicker.click.day', { | ||
currentTarget: target, | ||
relatedTarget: table, | ||
prevDate: parse(elem.getAttribute(UUID)), | ||
nextDate: parse(nextDate) | ||
}) | ||
function input (self, el) { | ||
const type = el.getAttribute('data-type') || el.getAttribute('type') | ||
return isUpdate && datepicker(elem, nextDate) | ||
} | ||
} | ||
} | ||
function input (el, date, disable, unit) { | ||
const type = el.getAttribute(`${UUID}-type`) || el.getAttribute('type') | ||
if (type === 'radio' || type === 'checkbox') { | ||
const val = parse(el.value, date) | ||
el.disabled = disable(val) | ||
el.checked = val.getTime() === date.getTime() | ||
} else if (unit[type]) { | ||
el.disabled = self.disabled(el.value) | ||
el.checked = !self.diff(el.value) | ||
} else if (MASK[type]) { | ||
el.setAttribute('type', 'number') // Set input type to number | ||
el.setAttribute(`${UUID}-type`, type) // And store original input type | ||
el.value = unit[type] | ||
el.setAttribute('data-type', type) // And store original type | ||
el.value = self[type] | ||
} | ||
} | ||
function table (table, date, disable) { | ||
function table (self, table) { | ||
if (!table.firstElementChild) { | ||
table.innerHTML = ` | ||
<caption></caption><thead><tr><th>${datepicker.days.map(escapeHTML).join('</th><th>')}</th></tr></thead> | ||
<tbody>${Array(7).join(`<tr>${Array(8).join(`<td><button type="button"></button></td>`)}</tr>`)}</tbody>` | ||
<caption></caption><thead><tr><th>${self.days.map(escapeHTML).join('</th><th>')}</th></tr></thead> | ||
<tbody>${Array(7).join(`<tr>${Array(8).join('<td><button type="button"></button></td>')}</tr>`)}</tbody>` | ||
} | ||
const today = new Date() | ||
let day = parse('y-m-1 mon', date) // Monday in first week of month | ||
table.caption.textContent = `${escapeHTML(datepicker.months[date.getMonth()])}, ${date.getFullYear()}` | ||
const month = self.date.getMonth() | ||
const day = self.parse('y-m-1 mon') // Monday in first week of month | ||
table.caption.textContent = `${self.months[month]}, ${self.year}` | ||
queryAll('button', table).forEach((button) => { | ||
const isToday = day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear() | ||
const isSelected = day.getTime() === date.getTime() | ||
const isSelected = !self.diff(day) | ||
const dayInMonth = day.getDate() | ||
const month = day.getMonth() | ||
const dayMonth = day.getMonth() | ||
button.textContent = dayInMonth // Set textContent instead of innerHTML avoids reflow | ||
button.value = `${day.getFullYear()}-${month + 1}-${dayInMonth}` | ||
button.disabled = disable(day) | ||
button.setAttribute('tabindex', isSelected - 1) | ||
button.setAttribute(`${ATTR}-selected`, isSelected) | ||
button.setAttribute(`${ATTR}-adjacent`, date.getMonth() !== month) | ||
button.setAttribute('aria-label', `${dayInMonth}. ${datepicker.months[month]}`) | ||
button[isToday ? 'setAttribute' : 'removeAttribute']('aria-current', 'date') | ||
button[isSelected ? 'setAttribute' : 'removeAttribute']('autofocus', '') | ||
button.value = `${day.getFullYear()}-${dayMonth + 1}-${dayInMonth}` | ||
button.disabled = self.disabled(day) | ||
button.tabIndex = isSelected - 1 | ||
button.setAttribute(`data-adjacent`, month !== dayMonth) | ||
button.setAttribute('aria-label', `${dayInMonth}. ${self.months[dayMonth]}`) | ||
button.setAttribute('aria-current', day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear() && 'date') | ||
button.toggleAttribute('autofocus', isSelected) | ||
day.setDate(dayInMonth + 1) | ||
@@ -122,11 +126,5 @@ }) | ||
function button (el, date, disable, picker) { | ||
if (el.getAttribute(ATTR) === picker.id || picker.contains(el)) { | ||
el.disabled = disable(parse(el.value, date)) | ||
} | ||
} | ||
function select (select, date, disable) { | ||
function select (self, select) { | ||
if (!select.firstElementChild) { | ||
select.innerHTML = datepicker.months.map((name, month) => | ||
select.innerHTML = self.months.map((name, month) => | ||
`<option value="y-${month + 1}-d">${escapeHTML(name)}</option>` | ||
@@ -137,6 +135,5 @@ ).join('') | ||
queryAll(select.children).forEach((option) => { | ||
const val = parse(option.value, date) | ||
option.disabled = disable(val) | ||
option.selected = val.getTime() === date.getTime() | ||
option.disabled = self.disabled(option.value) | ||
option.selected = !self.diff(option.value) | ||
}) | ||
} |
@@ -1,51 +0,4 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import coreDatepicker from './core-datepicker' | ||
import { exclude } from '../utils' | ||
import CoreDatepicker from './core-datepicker.js' | ||
import { elementToReact } from '../utils.js' | ||
export default class Datepicker extends React.Component { | ||
static get defaultProps () { return { date: null, disable: null, onRender: null, onChange: null, onClickDay: null } } | ||
static set months (months) { coreDatepicker.months = months } | ||
static set days (days) { coreDatepicker.days = days } | ||
static get months () { return coreDatepicker.months } | ||
static get days () { return coreDatepicker.days } | ||
static parse (...args) { return coreDatepicker.parse(...args) } | ||
constructor (props) { | ||
super(props) | ||
this.onClickDay = this.onClickDay.bind(this) | ||
this.onChange = this.onChange.bind(this) | ||
this.onRender = this.onRender.bind(this) | ||
} | ||
componentDidMount () { | ||
this.el.addEventListener('datepicker.click.day', this.onClickDay) | ||
this.el.addEventListener('datepicker.change', this.onChange) | ||
this.el.addEventListener('datepicker.render', this.onRender) | ||
coreDatepicker(this.el, this.props.date) | ||
} | ||
componentDidUpdate () { coreDatepicker(this.el, this.props.date) } | ||
componentWillUnmount () { | ||
this.el.removeEventListener('datepicker.click.day', this.onClickDay) | ||
this.el.removeEventListener('datepicker.change', this.onChange) | ||
this.el.removeEventListener('datepicker.render', this.onRender) | ||
} | ||
onClickDay (event) { this.props.onClickDay && this.props.onClickDay(event) } | ||
onChange (event) { this.props.onChange && this.props.onChange(event) } | ||
onRender (event) { this.props.disable && event.detail.disable(this.props.disable) } | ||
render () { | ||
const props = exclude(this.props, Datepicker.defaultProps, { ref: el => (this.el = el) }) | ||
return React.createElement('div', props, this.props.children) | ||
} | ||
} | ||
Datepicker.propTypes = { | ||
disable: PropTypes.func, | ||
onRender: PropTypes.func, | ||
onChange: PropTypes.func, | ||
onClickDay: PropTypes.func, | ||
date: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.number, | ||
PropTypes.instanceOf(Date) | ||
]) | ||
} | ||
export default elementToReact(CoreDatepicker, 'datepicker.change', 'datepicker.click.day', 'disabled', 'months', 'days') |
@@ -1,3 +0,683 @@ | ||
/*! @nrk/core-datepicker v2.0.3 - Copyright (c) 2017-2019 NRK */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("prop-types")):"function"==typeof define&&define.amd?define(["react","prop-types"],t):(e=e||self).CoreDatepicker=t(e.React,e.PropTypes)}(this,function(a,e){"use strict";a=a&&a.hasOwnProperty("default")?a.default:a,e=e&&e.hasOwnProperty("default")?e.default:e;var t="undefined"!=typeof window,o=(t&&/(android)/i.test(navigator.userAgent),t&&/iPad|iPhone|iPod/.test(String(navigator.platform)),function(e){void 0===e&&(e=!1);try{window.addEventListener("test",null,{get passive(){e=!0}})}catch(e){}return e}());function n(e,t,n,r){(void 0===r&&(r=!1),"undefined"==typeof window||window[e=e+"-"+t])||(o||"object"!=typeof r||(r=Boolean(r.capture)),("resize"===t||"load"===t?window:document).addEventListener(window[e]=t,n,r))}var r={"&":"&","<":"<",">":">",'"':""","/":"/","'":"'"};function s(e){return String(e||"").replace(/[&<>"'/]/g,function(e){return r[e]})}var u="prevent_recursive_dispatch_maximum_callstack";function d(e,t,n){void 0===n&&(n={});var r,o=""+u+t;if(e[o])return!0;e[o]=!0,"function"==typeof window.CustomEvent?r=new window.CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n}):(r=document.createEvent("CustomEvent")).initCustomEvent(t,!0,!0,n);var i=e.dispatchEvent(r);return e[o]=null,i}function c(e,t){if(void 0===t&&(t=document),e){if(e.nodeType)return[e];if("string"==typeof e)return[].slice.call(t.querySelectorAll(e));if(e.length)return[].slice.call(e)}return[]}"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var i,l=(function(e,t){var v,b,k,w;e.exports=(v={year:"FullYear",month:"Month",week:"Date",day:"Date",hour:"Hours",minute:"Minutes",second:"Seconds"},b=/([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g,k=/([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/,w=/([\dh]+):([\dm]+):?([\ds]+)?/,function(e,t){if(isFinite(e))return new Date(Number(e));var n=String(e).toLowerCase(),o=new Date(isFinite(t)&&-1===n.indexOf("now")?Number(t):Date.now()),r=n.match(k)||[],i=r[1];void 0===i&&(i="y");var a=r[2];void 0===a&&(a="m");var u=r[3];void 0===u&&(u="d");var d=n.match(w)||[],s=d[1];void 0===s&&(s="h");var c=d[2];void 0===c&&(c="m");var l=d[3];void 0===l&&(l="s");var p={year:i,month:a,day:u,hour:s,minute:c,second:l};Object.keys(p).forEach(function(e){var t="month"===e?1:0,r=String(o["get"+v[e]]()+t);p[e]=p[e].replace(/[^-\d]+/g,function(e,t,n){return t?r.substr(r.length-n.length+t,e.length):r.substr(0,Math.max(0,r.length-n.length+e.length))})-t});var h=new Date(p.year,Math.min(12,p.month+1),0).getDate();for(o.setFullYear(p.year,Math.min(11,p.month),Math.max(1,Math.min(h,p.day))),o.setHours(Math.min(24,p.hour),Math.min(59,p.minute),Math.min(59,p.second));null!==(p=b.exec(n));){var f=p[2],m=String(p[1]).replace(/\s/g,"")*("week"===f?7:1),y=p.slice(2).indexOf(p[0]),g=o.getDate();f?o["set"+v[f]](o["get"+v[f]]()+m):o.setDate(o.getDate()-(o.getDay()||7)+y),"month"!==f&&"year"!==f||g===o.getDate()||o.setDate(0)}return o})}(i={exports:{}},i.exports),i.exports),p="data-core-datepicker",h="data-@nrk/core-datepicker-2.0.3".replace(/\W+/g,"-"),f={33:"-1month",34:"+1month",35:"y-m-99",36:"y-m-1",37:"-1day",38:"-1week",39:"+1day",40:"+1week"},m={year:"*-m-d",month:"y-*-d",day:"y-m-*",hour:"*:m",minute:"h:*",second:"h:m:*",timestamp:"*"},y=6e4;function g(e,o){return c(e).map(function(i){var e=l(i.getAttribute(h)||o),t=l(void 0===o?e:o,e),a=function(){return!1};d(i,"datepicker.render",{nextDate:t,prevDate:e,disable:function(e){return a=e}}),a(t)&&(t=e);var u=e.getTime()===t.getTime()||d(i,"datepicker.change",{prevDate:e,nextDate:t})?t:l(i.getAttribute(h)||Date.now()),n=new Date(u.getTime()-u.getTimezoneOffset()*y).toJSON().match(/\d+/g),r={year:u.getFullYear(),month:n[1],day:n[2],hour:n[3],minute:n[4],second:n[5],timestamp:u.getTime()};return i.setAttribute(h,r.timestamp),c("button").forEach(function(e){return n=u,r=a,o=i,void(((t=e).getAttribute(p)===o.id||o.contains(t))&&(t.disabled=r(l(t.value,n))));var t,n,r,o}),c("select",i).concat(c("select["+p+'="'+i.id+'"]')).forEach(function(e){return function(e,n,r){e.firstElementChild||(e.innerHTML=g.months.map(function(e,t){return'<option value="y-'+(t+1)+'-d">'+s(e)+"</option>"}).join(""));c(e.children).forEach(function(e){var t=l(e.value,n);e.disabled=r(t),e.selected=t.getTime()===n.getTime()})}(e,u,a)}),c("input",i).concat(c("input["+p+'="'+i.id+'"]')).forEach(function(e){return function(e,t,n,r){var o=e.getAttribute(h+"-type")||e.getAttribute("type");if("radio"===o||"checkbox"===o){var i=l(e.value,t);e.disabled=n(i),e.checked=i.getTime()===t.getTime()}else r[o]&&(e.setAttribute("type","number"),e.setAttribute(h+"-type",o),e.value=r[o])}(e,u,a,r)}),c("table",i).concat(c("table["+p+'="'+i.id+'"]')).forEach(function(e){return function(e,i,a){e.firstElementChild||(e.innerHTML="\n <caption></caption><thead><tr><th>"+g.days.map(s).join("</th><th>")+"</th></tr></thead>\n <tbody>"+Array(7).join("<tr>"+Array(8).join('<td><button type="button"></button></td>')+"</tr>")+"</tbody>");var u=new Date,d=l("y-m-1 mon",i);e.caption.textContent=s(g.months[i.getMonth()])+", "+i.getFullYear(),c("button",e).forEach(function(e){var t=d.getDate()===u.getDate()&&d.getMonth()===u.getMonth()&&d.getFullYear()===u.getFullYear(),n=d.getTime()===i.getTime(),r=d.getDate(),o=d.getMonth();e.textContent=r,e.value=d.getFullYear()+"-"+(o+1)+"-"+r,e.disabled=a(d),e.setAttribute("tabindex",n-1),e.setAttribute(p+"-selected",n),e.setAttribute(p+"-adjacent",i.getMonth()!==o),e.setAttribute("aria-label",r+". "+g.months[o]),e[t?"setAttribute":"removeAttribute"]("aria-current","date"),e[n?"setAttribute":"removeAttribute"]("autofocus",""),d.setDate(r+1)})}(e,u,a)}),i})}function v(e){for(var t=e.target,n=t,r=void 0;n;n=n.parentElement){var o=document.getElementById(n.getAttribute(p))||n,i=o.hasAttribute(h)&&(m[t.getAttribute(h+"-type")]||"*");if(r||"TABLE"!==n.nodeName||(r=n),i){var a=i.replace("*",t.value);return(!o.contains(r)||d(o,"datepicker.click.day",{currentTarget:t,relatedTarget:r,prevDate:l(o.getAttribute(h)),nextDate:l(a)}))&&g(o,a)}}}g.parse=l,g.months=["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],g.days=["man","tirs","ons","tors","fre","lør","søn"],n(h,"change",v),n(h,"click",function(e){for(var t=e.target;t;t=t.parentElement)if("BUTTON"===t.nodeName)return v({target:t})}),n(h,"keydown",function(e){if(!(e.ctrlKey||e.metaKey||e.shitKey||e.altKey)&&f[e.keyCode])for(var t=e.target,n=void 0;t;t=t.parentElement)if(n||"TABLE"!==t.nodeName||(n=t),n&&t.hasAttribute(h)){g(t,f[e.keyCode]),n.querySelector("["+p+'-selected="true"]').focus(),e.preventDefault();break}});var b=function(t){function i(e){t.call(this,e),this.onClickDay=this.onClickDay.bind(this),this.onChange=this.onChange.bind(this),this.onRender=this.onRender.bind(this)}t&&(i.__proto__=t),(i.prototype=Object.create(t&&t.prototype)).constructor=i;var e={defaultProps:{configurable:!0},months:{configurable:!0},days:{configurable:!0}};return e.defaultProps.get=function(){return{date:null,disable:null,onRender:null,onChange:null,onClickDay:null}},e.months.set=function(e){g.months=e},e.days.set=function(e){g.days=e},e.months.get=function(){return g.months},e.days.get=function(){return g.days},i.parse=function(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return g.parse.apply(g,e)},i.prototype.componentDidMount=function(){this.el.addEventListener("datepicker.click.day",this.onClickDay),this.el.addEventListener("datepicker.change",this.onChange),this.el.addEventListener("datepicker.render",this.onRender),g(this.el,this.props.date)},i.prototype.componentDidUpdate=function(){g(this.el,this.props.date)},i.prototype.componentWillUnmount=function(){this.el.removeEventListener("datepicker.click.day",this.onClickDay),this.el.removeEventListener("datepicker.change",this.onChange),this.el.removeEventListener("datepicker.render",this.onRender)},i.prototype.onClickDay=function(e){this.props.onClickDay&&this.props.onClickDay(e)},i.prototype.onChange=function(e){this.props.onChange&&this.props.onChange(e)},i.prototype.onRender=function(e){this.props.disable&&e.detail.disable(this.props.disable)},i.prototype.render=function(){var n,r,e,t=this,o=(n=this.props,r=i.defaultProps,void 0===(e={ref:function(e){return t.el=e}})&&(e={}),Object.keys(n).reduce(function(e,t){return r.hasOwnProperty(t)||(e[t]=n[t]),e},e));return a.createElement("div",o,this.props.children)},Object.defineProperties(i,e),i}(a.Component);return b.propTypes={disable:e.func,onRender:e.func,onChange:e.func,onClickDay:e.func,date:e.oneOfType([e.string,e.number,e.instanceOf(Date)])},b}); | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) : | ||
typeof define === 'function' && define.amd ? define(['react'], factory) : | ||
(global = global || self, global.CoreDatepicker = factory(global.React)); | ||
}(this, function (React) { 'use strict'; | ||
React = React && React.hasOwnProperty('default') ? React['default'] : React; | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
function _getPrototypeOf(o) { | ||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { | ||
return o.__proto__ || Object.getPrototypeOf(o); | ||
}; | ||
return _getPrototypeOf(o); | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
function isNativeReflectConstruct() { | ||
if (typeof Reflect === "undefined" || !Reflect.construct) return false; | ||
if (Reflect.construct.sham) return false; | ||
if (typeof Proxy === "function") return true; | ||
try { | ||
Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
function _construct(Parent, args, Class) { | ||
if (isNativeReflectConstruct()) { | ||
_construct = Reflect.construct; | ||
} else { | ||
_construct = function _construct(Parent, args, Class) { | ||
var a = [null]; | ||
a.push.apply(a, args); | ||
var Constructor = Function.bind.apply(Parent, a); | ||
var instance = new Constructor(); | ||
if (Class) _setPrototypeOf(instance, Class.prototype); | ||
return instance; | ||
}; | ||
} | ||
return _construct.apply(null, arguments); | ||
} | ||
function _isNativeFunction(fn) { | ||
return Function.toString.call(fn).indexOf("[native code]") !== -1; | ||
} | ||
function _wrapNativeSuper(Class) { | ||
var _cache = typeof Map === "function" ? new Map() : undefined; | ||
_wrapNativeSuper = function _wrapNativeSuper(Class) { | ||
if (Class === null || !_isNativeFunction(Class)) return Class; | ||
if (typeof Class !== "function") { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
if (typeof _cache !== "undefined") { | ||
if (_cache.has(Class)) return _cache.get(Class); | ||
_cache.set(Class, Wrapper); | ||
} | ||
function Wrapper() { | ||
return _construct(Class, arguments, _getPrototypeOf(this).constructor); | ||
} | ||
Wrapper.prototype = Object.create(Class.prototype, { | ||
constructor: { | ||
value: Wrapper, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
return _setPrototypeOf(Wrapper, Class); | ||
}; | ||
return _wrapNativeSuper(Class); | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return self; | ||
} | ||
function _possibleConstructorReturn(self, call) { | ||
if (call && (typeof call === "object" || typeof call === "function")) { | ||
return call; | ||
} | ||
return _assertThisInitialized(self); | ||
} | ||
var IS_BROWSER = typeof window !== 'undefined'; | ||
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed | ||
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE | ||
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) { | ||
window.Element.prototype.toggleAttribute = function (name) { | ||
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name); | ||
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, ''); | ||
return force; | ||
}; | ||
} | ||
/** | ||
* addStyle | ||
* @param {String} nodeName An unique ID of the event to bind - ensurnes single instance | ||
* @param {String} css The css to inject | ||
*/ | ||
function addStyle(nodeName, css) { | ||
var key = "style-".concat(nodeName.toLowerCase()); | ||
var min = css.replace(/\/\*[^!][^*]*\*\//g, '').replace(/\s*(^|[:;,{}]|$)\s*/g, '$1'); | ||
document.getElementById(key) || document.head.insertAdjacentHTML('afterbegin', "<style id=\"".concat(key, "\">").concat(min, "</style>")); | ||
} | ||
/** | ||
* escapeHTML | ||
* @param {String} str A string with potential html tokens | ||
* @return {String} Escaped HTML string according to OWASP recommendation | ||
*/ | ||
var ESCAPE_MAP = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
'/': '/', | ||
'\'': ''' | ||
}; | ||
function escapeHTML(str) { | ||
return String(str || '').replace(/[&<>"'/]/g, function (_char) { | ||
return ESCAPE_MAP[_char]; | ||
}); | ||
} | ||
/** | ||
* closest | ||
* @param {Element} element Element to traverse up from | ||
* @param {String} selector A selector to search for matching parents or element itself | ||
* @return {Element|null} Element which is the closest ancestor matching selector | ||
*/ | ||
var closest = function () { | ||
var proto = typeof window === 'undefined' ? {} : window.Element.prototype; | ||
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector; | ||
return proto.closest ? function (el, css) { | ||
return el.closest(css); | ||
} : function (el, css) { | ||
for (; el; el = el.parentElement) { | ||
if (match.call(el, css)) return el; | ||
} | ||
return null; | ||
}; | ||
}(); | ||
/** | ||
* dispatchEvent - with infinite loop prevention | ||
* @param {Element} elem The target object | ||
* @param {String} name The source object(s) | ||
* @param {Object} detail Detail object (bubbles and cancelable is set to true) | ||
* @return {Boolean} Whether the event was canceled | ||
*/ | ||
function dispatchEvent(element, name) { | ||
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name); | ||
var event; | ||
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one | ||
else element[ignore] = true; // Add name to dispatching ignore | ||
if (typeof window.CustomEvent === 'function') { | ||
event = new window.CustomEvent(name, { | ||
bubbles: true, | ||
cancelable: true, | ||
detail: detail | ||
}); | ||
} else { | ||
event = document.createEvent('CustomEvent'); | ||
event.initCustomEvent(name, true, true, detail); | ||
} // IE reports incorrect event.defaultPrevented | ||
// but correct return value on element.dispatchEvent | ||
var result = element.dispatchEvent(event); | ||
element[ignore] = null; // Remove name from dispatching ignore | ||
return result; // Follow W3C standard for return value | ||
} | ||
function elementToReact(elementClass) { | ||
for (var _len = arguments.length, attr = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
attr[_key - 1] = arguments[_key]; | ||
} | ||
var name = elementClass.name || String(elementClass).match(/function ([^(]+)/)[1]; // String match for IE11 | ||
var tag = "".concat(name.replace(/\W+/, '-'), "-").concat(getUUID()).toLowerCase(); | ||
if (IS_BROWSER && !window.customElements.get(tag)) window.customElements.define(tag, elementClass); | ||
return ( | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inherits(_class, _React$Component); | ||
function _class(props) { | ||
var _this; | ||
_classCallCheck(this, _class); | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(_class).call(this, props)); | ||
_this.ref = function (el) { | ||
return _this.el = el; | ||
}; | ||
attr.forEach(function (k) { | ||
var on = "on".concat(k.replace(/(^|\.)./g, function (m) { | ||
return m.slice(-1).toUpperCase(); | ||
})); // input.filter => onInputFilter | ||
_this[k] = function (event) { | ||
return _this.props[on] && _this.props[on](event); | ||
}; | ||
}); | ||
return _this; | ||
} | ||
_createClass(_class, [{ | ||
key: "componentDidMount", | ||
value: function componentDidMount() { | ||
var _this2 = this; | ||
attr.forEach(function (k) { | ||
return _this2.props[k] ? _this2.el[k] = _this2.props[k] : _this2.el.addEventListener(k, _this2[k]); | ||
}); | ||
} | ||
}, { | ||
key: "componentDidUpdate", | ||
value: function componentDidUpdate(prev) { | ||
var _this3 = this; | ||
attr.forEach(function (k) { | ||
return prev[k] !== _this3.props[k] && (_this3.el[k] = _this3.props[k]); | ||
}); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
var _this4 = this; | ||
attr.forEach(function (k) { | ||
return _this4.el.removeEventListener(k, _this4[k]); | ||
}); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this5 = this; | ||
// Convert React props to CustomElement props https://github.com/facebook/react/issues/12810 | ||
return React.createElement(tag, Object.keys(this.props).reduce(function (props, k) { | ||
if (k === 'className') props["class"] = _this5.props[k]; // Fixes className for custom elements | ||
else if (_this5.props[k] === true) props[k] = ''; // Fixes boolean attributes | ||
else if (_this5.props[k] !== false) props[k] = _this5.props[k]; | ||
return props; | ||
}, { | ||
ref: this.ref | ||
})); | ||
} | ||
}]); | ||
return _class; | ||
}(React.Component) | ||
); | ||
} | ||
/** | ||
* getUUID | ||
* @return {String} A generated unique ID | ||
*/ | ||
function getUUID() { | ||
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5); | ||
} | ||
/** | ||
* queryAll | ||
* @param {String|NodeList|Array|Element} elements A CSS selector string, nodeList, element array, or single element | ||
* @return {Element[]} Array of elements | ||
*/ | ||
function queryAll(elements) { | ||
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document; | ||
if (elements) { | ||
if (elements.nodeType) return [elements]; | ||
if (typeof elements === 'string') return [].slice.call(context.querySelectorAll(elements)); | ||
if (elements.length) return [].slice.call(elements); | ||
} | ||
return []; | ||
} | ||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var index_min = createCommonjsModule(function (module, exports) { | ||
!function (e, t) { | ||
module.exports = t(); | ||
}(commonjsGlobal, function () { | ||
var D = { | ||
year: "FullYear", | ||
month: "Month", | ||
week: "Date", | ||
day: "Date", | ||
hour: "Hours", | ||
minute: "Minutes", | ||
second: "Seconds" | ||
}, | ||
w = /([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g, | ||
M = /([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/, | ||
p = /([\dh]+):([\dm]+):?([\ds]+)?/; | ||
return function (e, t) { | ||
if (isFinite(e)) return new Date(Number(e)); | ||
var n = String(e).toLowerCase(), | ||
r = new Date(isFinite(t) && -1 === n.indexOf("now") ? Number(t) : Date.now()), | ||
a = n.match(M) || [], | ||
o = a[1]; | ||
void 0 === o && (o = "y"); | ||
var i = a[2]; | ||
void 0 === i && (i = "m"); | ||
var s = a[3]; | ||
void 0 === s && (s = "d"); | ||
var d = n.match(p) || [], | ||
u = d[1]; | ||
void 0 === u && (u = "h"); | ||
var h = d[2]; | ||
void 0 === h && (h = "m"); | ||
var m = d[3]; | ||
void 0 === m && (m = "s"); | ||
var c = { | ||
year: o, | ||
month: i, | ||
day: s, | ||
hour: u, | ||
minute: h, | ||
second: m | ||
}; | ||
Object.keys(c).forEach(function (e) { | ||
var t = "month" === e ? 1 : 0, | ||
a = String(r["get" + D[e]]() + t); | ||
c[e] = c[e].replace(/[^-\d]+/g, function (e, t, n) { | ||
return t ? a.substr(a.length - n.length + t, e.length) : a.substr(0, Math.max(0, a.length - n.length + e.length)); | ||
}) - t; | ||
}); | ||
var f = new Date(c.year, Math.min(12, c.month + 1), 0).getDate(); | ||
for (r.setFullYear(c.year, Math.min(11, c.month), Math.max(1, Math.min(f, c.day))), r.setHours(Math.min(24, c.hour), Math.min(59, c.minute), Math.min(59, c.second)); null !== (c = w.exec(n));) { | ||
var g = c[2], | ||
l = String(c[1]).replace(/\s/g, "") * ("week" === g ? 7 : 1), | ||
v = c.slice(2).indexOf(c[0]), | ||
y = r.getDate(); | ||
g ? r["set" + D[g]](r["get" + D[g]]() + l) : r.setDate(r.getDate() - (r.getDay() || 7) + v), "month" !== g && "year" !== g || y === r.getDate() || r.setDate(0); | ||
} | ||
return r; | ||
}; | ||
}); | ||
}); | ||
var MASK = { | ||
year: '*-m-d', | ||
month: 'y-*-d', | ||
day: 'y-m-*', | ||
hour: '*:m', | ||
minute: 'h:*', | ||
second: 'h:m:*', | ||
timestamp: '*', | ||
"null": '*' | ||
}; | ||
var KEYS = { | ||
33: '-1month', | ||
34: '+1month', | ||
35: 'y-m-99', | ||
36: 'y-m-1', | ||
37: '-1day', | ||
38: '-1week', | ||
39: '+1day', | ||
40: '+1week' | ||
}; | ||
var MONTHS = 'januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember'; | ||
var DAYS = 'man,tirs,ons,tors,fre,lør,søn'; | ||
var CoreDatepicker = | ||
/*#__PURE__*/ | ||
function (_HTMLElement) { | ||
_inherits(CoreDatepicker, _HTMLElement); | ||
function CoreDatepicker() { | ||
_classCallCheck(this, CoreDatepicker); | ||
return _possibleConstructorReturn(this, _getPrototypeOf(CoreDatepicker).apply(this, arguments)); | ||
} | ||
_createClass(CoreDatepicker, [{ | ||
key: "connectedCallback", | ||
value: function connectedCallback() { | ||
var _this = this; | ||
this._date = this.date; // Store for later comparison and speeding up things | ||
document.addEventListener('click', this); | ||
document.addEventListener('change', this); | ||
document.addEventListener('keydown', this); | ||
setTimeout(function () { | ||
return _this.attributeChangedCallback(); | ||
}); // Render after children is parsed | ||
addStyle(this.nodeName, "".concat(this.nodeName, "{display:block}")); // default to display block | ||
} | ||
}, { | ||
key: "disconnectedCallback", | ||
value: function disconnectedCallback() { | ||
this._date = this._disabled = null; // Garbage collection | ||
document.removeEventListener('click', this); | ||
document.removeEventListener('change', this); | ||
document.removeEventListener('keydown', this); | ||
} | ||
}, { | ||
key: "attributeChangedCallback", | ||
value: function attributeChangedCallback() { | ||
if (!this._date) return; // Only render after connectedCallback | ||
if (this.disabled(this.date) && !this.disabled(this._date)) return this.date = this._date; // Jump back | ||
if (this.diff(this.date)) dispatchEvent(this, 'datepicker.change', this._date = this.date); | ||
forEach('button', this, button); | ||
forEach('select', this, select); | ||
forEach('input', this, input); | ||
forEach('table', this, table); | ||
} | ||
}, { | ||
key: "handleEvent", | ||
value: function handleEvent(event) { | ||
if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.type === 'keydown' && !KEYS[event.keyCode]) return; | ||
if (!this.contains(event.target) && !closest(event.target, "[for=\"".concat(this.id, "\"]"))) return; | ||
if (event.type === 'change') this.date = MASK[event.target.getAttribute('data-type')].replace('*', event.target.value);else if (event.type === 'click') { | ||
var _button = closest(event.target, 'button[value]'); | ||
var _table = closest(event.target, 'table'); | ||
if (_button) this.date = _button.value; | ||
if (_button && _table) dispatchEvent(this, 'datepicker.click.day'); | ||
} else if (event.type === 'keydown' && closest(event.target, 'table')) { | ||
this.date = KEYS[event.keyCode]; | ||
this.querySelector('[autofocus]').focus(); | ||
event.preventDefault(); // Prevent scrolling | ||
} | ||
} | ||
}, { | ||
key: "diff", | ||
value: function diff(val) { | ||
return this.parse(val).getTime() - this.timestamp; | ||
} | ||
}, { | ||
key: "parse", | ||
value: function parse(val, from) { | ||
return index_min(val, from || this._date); | ||
} | ||
}, { | ||
key: "disabled", | ||
get: function get() { | ||
return this._disabled || Function.prototype; | ||
}, | ||
set: function set(fn) { | ||
var _this2 = this; | ||
this._disabled = typeof fn === 'function' ? function (val) { | ||
return fn(_this2.parse(val), _this2); | ||
} : function () { | ||
return fn; | ||
}; // Auto parse dates | ||
this.attributeChangedCallback(); // Re-render | ||
} | ||
}, { | ||
key: "timestamp", | ||
get: function get() { | ||
return String(this._date.getTime()); | ||
} | ||
}, { | ||
key: "year", | ||
get: function get() { | ||
return String(this._date.getFullYear()); | ||
} // Stringify for consistency and for truthy '0' | ||
}, { | ||
key: "month", | ||
get: function get() { | ||
return pad(this._date.getMonth() + 1); | ||
} | ||
}, { | ||
key: "day", | ||
get: function get() { | ||
return pad(this._date.getDate()); | ||
} | ||
}, { | ||
key: "hour", | ||
get: function get() { | ||
return pad(this._date.getHours()); | ||
} | ||
}, { | ||
key: "minute", | ||
get: function get() { | ||
return pad(this._date.getMinutes()); | ||
} | ||
}, { | ||
key: "second", | ||
get: function get() { | ||
return pad(this._date.getSeconds()); | ||
} | ||
}, { | ||
key: "date", | ||
get: function get() { | ||
return index_min(this.getAttribute('timestamp') || this._date || Date.now()); | ||
}, | ||
set: function set(val) { | ||
return this.setAttribute('timestamp', this.parse(val).getTime()); | ||
} | ||
}, { | ||
key: "months", | ||
set: function set(val) { | ||
this.setAttribute('months', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('months') || MONTHS).split(/\s*,\s*/); | ||
} | ||
}, { | ||
key: "days", | ||
set: function set(val) { | ||
this.setAttribute('days', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('days') || DAYS).split(/\s*,\s*/); | ||
} | ||
}], [{ | ||
key: "observedAttributes", | ||
get: function get() { | ||
return ['timestamp', 'months', 'days']; | ||
} | ||
}]); | ||
return CoreDatepicker; | ||
}(_wrapNativeSuper(HTMLElement)); | ||
var pad = function pad(val) { | ||
return "0".concat(val).slice(-2); | ||
}; | ||
var forEach = function forEach(css, self, fn) { | ||
return [].forEach.call(document.getElementsByTagName(css), function (el) { | ||
if (self.contains(el) || self.id === el.getAttribute(self.external)) fn(self, el, self._date); | ||
}); | ||
}; | ||
function button(self, el) { | ||
el.disabled = self.disabled(el.value); | ||
} | ||
function input(self, el) { | ||
var type = el.getAttribute('data-type') || el.getAttribute('type'); | ||
if (type === 'radio' || type === 'checkbox') { | ||
el.disabled = self.disabled(el.value); | ||
el.checked = !self.diff(el.value); | ||
} else if (MASK[type]) { | ||
el.setAttribute('type', 'number'); // Set input type to number | ||
el.setAttribute('data-type', type); // And store original type | ||
el.value = self[type]; | ||
} | ||
} | ||
function table(self, table) { | ||
if (!table.firstElementChild) { | ||
table.innerHTML = "\n <caption></caption><thead><tr><th>".concat(self.days.map(escapeHTML).join('</th><th>'), "</th></tr></thead>\n <tbody>").concat(Array(7).join("<tr>".concat(Array(8).join('<td><button type="button"></button></td>'), "</tr>")), "</tbody>"); | ||
} | ||
var today = new Date(); | ||
var month = self.date.getMonth(); | ||
var day = self.parse('y-m-1 mon'); // Monday in first week of month | ||
table.caption.textContent = "".concat(self.months[month], ", ").concat(self.year); | ||
queryAll('button', table).forEach(function (button) { | ||
var isSelected = !self.diff(day); | ||
var dayInMonth = day.getDate(); | ||
var dayMonth = day.getMonth(); | ||
button.textContent = dayInMonth; // Set textContent instead of innerHTML avoids reflow | ||
button.value = "".concat(day.getFullYear(), "-").concat(dayMonth + 1, "-").concat(dayInMonth); | ||
button.disabled = self.disabled(day); | ||
button.tabIndex = isSelected - 1; | ||
button.setAttribute("data-adjacent", month !== dayMonth); | ||
button.setAttribute('aria-label', "".concat(dayInMonth, ". ").concat(self.months[dayMonth])); | ||
button.setAttribute('aria-current', day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear() && 'date'); | ||
button.toggleAttribute('autofocus', isSelected); | ||
day.setDate(dayInMonth + 1); | ||
}); | ||
} | ||
function select(self, select) { | ||
if (!select.firstElementChild) { | ||
select.innerHTML = self.months.map(function (name, month) { | ||
return "<option value=\"y-".concat(month + 1, "-d\">").concat(escapeHTML(name), "</option>"); | ||
}).join(''); | ||
} | ||
queryAll(select.children).forEach(function (option) { | ||
option.disabled = self.disabled(option.value); | ||
option.selected = !self.diff(option.value); | ||
}); | ||
} | ||
var coreDatepicker = elementToReact(CoreDatepicker, 'datepicker.change', 'datepicker.click.day', 'disabled', 'months', 'days'); | ||
return coreDatepicker; | ||
})); | ||
//# sourceMappingURL=core-datepicker.jsx.js.map |
@@ -1,3 +0,3 @@ | ||
/*! @nrk/core-datepicker v2.0.3 - Copyright (c) 2017-2019 NRK */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).coreDatepicker=t()}(this,function(){"use strict";var e="undefined"!=typeof window,a=(e&&/(android)/i.test(navigator.userAgent),e&&/iPad|iPhone|iPod/.test(String(navigator.platform)),function(e){void 0===e&&(e=!1);try{window.addEventListener("test",null,{get passive(){e=!0}})}catch(e){}return e}());function t(e,t,n,r){(void 0===r&&(r=!1),"undefined"==typeof window||window[e=e+"-"+t])||(a||"object"!=typeof r||(r=Boolean(r.capture)),("resize"===t||"load"===t?window:document).addEventListener(window[e]=t,n,r))}var n={"&":"&","<":"<",">":">",'"':""","/":"/","'":"'"};function c(e){return String(e||"").replace(/[&<>"'/]/g,function(e){return n[e]})}var o="prevent_recursive_dispatch_maximum_callstack";function d(e,t,n){void 0===n&&(n={});var r,a=""+o+t;if(e[a])return!0;e[a]=!0,"function"==typeof window.CustomEvent?r=new window.CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n}):(r=document.createEvent("CustomEvent")).initCustomEvent(t,!0,!0,n);var i=e.dispatchEvent(r);return e[a]=null,i}function s(e,t){if(void 0===t&&(t=document),e){if(e.nodeType)return[e];if("string"==typeof e)return[].slice.call(t.querySelectorAll(e));if(e.length)return[].slice.call(e)}return[]}"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var r,l=(function(e,t){var y,b,w,D;e.exports=(y={year:"FullYear",month:"Month",week:"Date",day:"Date",hour:"Hours",minute:"Minutes",second:"Seconds"},b=/([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g,w=/([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/,D=/([\dh]+):([\dm]+):?([\ds]+)?/,function(e,t){if(isFinite(e))return new Date(Number(e));var n=String(e).toLowerCase(),a=new Date(isFinite(t)&&-1===n.indexOf("now")?Number(t):Date.now()),r=n.match(w)||[],i=r[1];void 0===i&&(i="y");var o=r[2];void 0===o&&(o="m");var u=r[3];void 0===u&&(u="d");var d=n.match(D)||[],c=d[1];void 0===c&&(c="h");var s=d[2];void 0===s&&(s="m");var l=d[3];void 0===l&&(l="s");var m={year:i,month:o,day:u,hour:c,minute:s,second:l};Object.keys(m).forEach(function(e){var t="month"===e?1:0,r=String(a["get"+y[e]]()+t);m[e]=m[e].replace(/[^-\d]+/g,function(e,t,n){return t?r.substr(r.length-n.length+t,e.length):r.substr(0,Math.max(0,r.length-n.length+e.length))})-t});var f=new Date(m.year,Math.min(12,m.month+1),0).getDate();for(a.setFullYear(m.year,Math.min(11,m.month),Math.max(1,Math.min(f,m.day))),a.setHours(Math.min(24,m.hour),Math.min(59,m.minute),Math.min(59,m.second));null!==(m=b.exec(n));){var h=m[2],g=String(m[1]).replace(/\s/g,"")*("week"===h?7:1),v=m.slice(2).indexOf(m[0]),p=a.getDate();h?a["set"+y[h]](a["get"+y[h]]()+g):a.setDate(a.getDate()-(a.getDay()||7)+v),"month"!==h&&"year"!==h||p===a.getDate()||a.setDate(0)}return a})}(r={exports:{}},r.exports),r.exports),m="data-core-datepicker",f="data-@nrk/core-datepicker-2.0.3".replace(/\W+/g,"-"),i={33:"-1month",34:"+1month",35:"y-m-99",36:"y-m-1",37:"-1day",38:"-1week",39:"+1day",40:"+1week"},u={year:"*-m-d",month:"y-*-d",day:"y-m-*",hour:"*:m",minute:"h:*",second:"h:m:*",timestamp:"*"},h=6e4;function g(e,a){return s(e).map(function(i){var e=l(i.getAttribute(f)||a),t=l(void 0===a?e:a,e),o=function(){return!1};d(i,"datepicker.render",{nextDate:t,prevDate:e,disable:function(e){return o=e}}),o(t)&&(t=e);var u=e.getTime()===t.getTime()||d(i,"datepicker.change",{prevDate:e,nextDate:t})?t:l(i.getAttribute(f)||Date.now()),n=new Date(u.getTime()-u.getTimezoneOffset()*h).toJSON().match(/\d+/g),r={year:u.getFullYear(),month:n[1],day:n[2],hour:n[3],minute:n[4],second:n[5],timestamp:u.getTime()};return i.setAttribute(f,r.timestamp),s("button").forEach(function(e){return n=u,r=o,a=i,void(((t=e).getAttribute(m)===a.id||a.contains(t))&&(t.disabled=r(l(t.value,n))));var t,n,r,a}),s("select",i).concat(s("select["+m+'="'+i.id+'"]')).forEach(function(e){return function(e,n,r){e.firstElementChild||(e.innerHTML=g.months.map(function(e,t){return'<option value="y-'+(t+1)+'-d">'+c(e)+"</option>"}).join(""));s(e.children).forEach(function(e){var t=l(e.value,n);e.disabled=r(t),e.selected=t.getTime()===n.getTime()})}(e,u,o)}),s("input",i).concat(s("input["+m+'="'+i.id+'"]')).forEach(function(e){return function(e,t,n,r){var a=e.getAttribute(f+"-type")||e.getAttribute("type");if("radio"===a||"checkbox"===a){var i=l(e.value,t);e.disabled=n(i),e.checked=i.getTime()===t.getTime()}else r[a]&&(e.setAttribute("type","number"),e.setAttribute(f+"-type",a),e.value=r[a])}(e,u,o,r)}),s("table",i).concat(s("table["+m+'="'+i.id+'"]')).forEach(function(e){return function(e,i,o){e.firstElementChild||(e.innerHTML="\n <caption></caption><thead><tr><th>"+g.days.map(c).join("</th><th>")+"</th></tr></thead>\n <tbody>"+Array(7).join("<tr>"+Array(8).join('<td><button type="button"></button></td>')+"</tr>")+"</tbody>");var u=new Date,d=l("y-m-1 mon",i);e.caption.textContent=c(g.months[i.getMonth()])+", "+i.getFullYear(),s("button",e).forEach(function(e){var t=d.getDate()===u.getDate()&&d.getMonth()===u.getMonth()&&d.getFullYear()===u.getFullYear(),n=d.getTime()===i.getTime(),r=d.getDate(),a=d.getMonth();e.textContent=r,e.value=d.getFullYear()+"-"+(a+1)+"-"+r,e.disabled=o(d),e.setAttribute("tabindex",n-1),e.setAttribute(m+"-selected",n),e.setAttribute(m+"-adjacent",i.getMonth()!==a),e.setAttribute("aria-label",r+". "+g.months[a]),e[t?"setAttribute":"removeAttribute"]("aria-current","date"),e[n?"setAttribute":"removeAttribute"]("autofocus",""),d.setDate(r+1)})}(e,u,o)}),i})}function v(e){for(var t=e.target,n=t,r=void 0;n;n=n.parentElement){var a=document.getElementById(n.getAttribute(m))||n,i=a.hasAttribute(f)&&(u[t.getAttribute(f+"-type")]||"*");if(r||"TABLE"!==n.nodeName||(r=n),i){var o=i.replace("*",t.value);return(!a.contains(r)||d(a,"datepicker.click.day",{currentTarget:t,relatedTarget:r,prevDate:l(a.getAttribute(f)),nextDate:l(o)}))&&g(a,o)}}}return g.parse=l,g.months=["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],g.days=["man","tirs","ons","tors","fre","lør","søn"],t(f,"change",v),t(f,"click",function(e){for(var t=e.target;t;t=t.parentElement)if("BUTTON"===t.nodeName)return v({target:t})}),t(f,"keydown",function(e){if(!(e.ctrlKey||e.metaKey||e.shitKey||e.altKey)&&i[e.keyCode])for(var t=e.target,n=void 0;t;t=t.parentElement)if(n||"TABLE"!==t.nodeName||(n=t),n&&t.hasAttribute(f)){g(t,i[e.keyCode]),n.querySelector("["+m+'-selected="true"]').focus(),e.preventDefault();break}}),g}); | ||
/*! @nrk/core-datepicker v3.0.0 - Copyright (c) 2017-2019 NRK */ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).coreDatepicker=e()}(this,function(){"use strict";function o(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function a(t){return(a=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function u(t,e){return(u=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function i(t,e,n){return(i=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}()?Reflect.construct:function(t,e,n){var r=[null];r.push.apply(r,e);var i=new(Function.bind.apply(t,r));return n&&u(i,n.prototype),i}).apply(null,arguments)}function c(t){var r="function"==typeof Map?new Map:void 0;return(c=function(t){if(null===t||(e=t,-1===Function.toString.call(e).indexOf("[native code]")))return t;var e;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==r){if(r.has(t))return r.get(t);r.set(t,n)}function n(){return i(t,arguments,a(this).constructor)}return n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),u(n,t)})(t)}function s(t,e){return!e||"object"!=typeof e&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}var t="undefined"!=typeof window;t&&/(android)/i.test(navigator.userAgent),t&&/iPad|iPhone|iPod/.test(String(navigator.platform));t&&!window.Element.prototype.toggleAttribute&&(window.Element.prototype.toggleAttribute=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:!this.hasAttribute(t);return!e===this.hasAttribute(t)&&this[e?"setAttribute":"removeAttribute"](t,""),e});var e={"&":"&","<":"<",">":">",'"':""","/":"/","'":"'"};function n(t){return String(t||"").replace(/[&<>"'/]/g,function(t){return e[t]})}var r,d,l=(r="undefined"==typeof window?{}:window.Element.prototype,d=r.matches||r.msMatchesSelector||r.webkitMatchesSelector,r.closest?function(t,e){return t.closest(e)}:function(t,e){for(;t;t=t.parentElement)if(d.call(t,e))return t;return null});function f(t,e){var n,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{},i="prevent_recursive_dispatch_maximum_callstack".concat(e);if(t[i])return!0;t[i]=!0,"function"==typeof window.CustomEvent?n=new window.CustomEvent(e,{bubbles:!0,cancelable:!0,detail:r}):(n=document.createEvent("CustomEvent")).initCustomEvent(e,!0,!0,r);var o=t.dispatchEvent(n);return t[i]=null,o}function h(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:document;if(t){if(t.nodeType)return[t];if("string"==typeof t)return[].slice.call(e.querySelectorAll(t));if(t.length)return[].slice.call(t)}return[]}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var y,p=(function(t,e){var b,v,w,k;t.exports=(b={year:"FullYear",month:"Month",week:"Date",day:"Date",hour:"Hours",minute:"Minutes",second:"Seconds"},v=/([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g,w=/([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/,k=/([\dh]+):([\dm]+):?([\ds]+)?/,function(t,e){if(isFinite(t))return new Date(Number(t));var n=String(t).toLowerCase(),i=new Date(isFinite(e)&&-1===n.indexOf("now")?Number(e):Date.now()),r=n.match(w)||[],o=r[1];void 0===o&&(o="y");var a=r[2];void 0===a&&(a="m");var u=r[3];void 0===u&&(u="d");var c=n.match(k)||[],s=c[1];void 0===s&&(s="h");var d=c[2];void 0===d&&(d="m");var l=c[3];void 0===l&&(l="s");var f={year:o,month:a,day:u,hour:s,minute:d,second:l};Object.keys(f).forEach(function(t){var e="month"===t?1:0,r=String(i["get"+b[t]]()+e);f[t]=f[t].replace(/[^-\d]+/g,function(t,e,n){return e?r.substr(r.length-n.length+e,t.length):r.substr(0,Math.max(0,r.length-n.length+t.length))})-e});var h=new Date(f.year,Math.min(12,f.month+1),0).getDate();for(i.setFullYear(f.year,Math.min(11,f.month),Math.max(1,Math.min(h,f.day))),i.setHours(Math.min(24,f.hour),Math.min(59,f.minute),Math.min(59,f.second));null!==(f=v.exec(n));){var y=f[2],p=String(f[1]).replace(/\s/g,"")*("week"===y?7:1),m=f.slice(2).indexOf(f[0]),g=i.getDate();y?i["set"+b[y]](i["get"+b[y]]()+p):i.setDate(i.getDate()-(i.getDay()||7)+m),"month"!==y&&"year"!==y||g===i.getDate()||i.setDate(0)}return i})}(y={exports:{}},y.exports),y.exports),m={year:"*-m-d",month:"y-*-d",day:"y-m-*",hour:"*:m",minute:"h:*",second:"h:m:*",timestamp:"*",null:"*"},g={33:"-1month",34:"+1month",35:"y-m-99",36:"y-m-1",37:"-1day",38:"-1week",39:"+1day",40:"+1week"},b=function(t){function e(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),s(this,a(e).apply(this,arguments))}var n,r,i;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&u(t,e)}(e,c(HTMLElement)),n=e,i=[{key:"observedAttributes",get:function(){return["timestamp","months","days"]}}],(r=[{key:"connectedCallback",value:function(){var t,e,n,r,i=this;this._date=this.date,document.addEventListener("click",this),document.addEventListener("change",this),document.addEventListener("keydown",this),setTimeout(function(){return i.attributeChangedCallback()}),t=this.nodeName,e="".concat(this.nodeName,"{display:block}"),n="style-".concat(t.toLowerCase()),r=e.replace(/\/\*[^!][^*]*\*\//g,"").replace(/\s*(^|[:;,{}]|$)\s*/g,"$1"),document.getElementById(n)||document.head.insertAdjacentHTML("afterbegin",'<style id="'.concat(n,'">').concat(r,"</style>"))}},{key:"disconnectedCallback",value:function(){this._date=this._disabled=null,document.removeEventListener("click",this),document.removeEventListener("change",this),document.removeEventListener("keydown",this)}},{key:"attributeChangedCallback",value:function(){if(this._date){if(this.disabled(this.date)&&!this.disabled(this._date))return this.date=this._date;this.diff(this.date)&&f(this,"datepicker.change",this._date=this.date),w("button",this,k),w("select",this,A),w("input",this,E),w("table",this,_)}}},{key:"handleEvent",value:function(t){if(!(t.defaultPrevented||t.ctrlKey||t.metaKey||t.shiftKey||t.altKey||"keydown"===t.type&&!g[t.keyCode])&&(this.contains(t.target)||l(t.target,'[for="'.concat(this.id,'"]'))))if("change"===t.type)this.date=m[t.target.getAttribute("data-type")].replace("*",t.target.value);else if("click"===t.type){var e=l(t.target,"button[value]"),n=l(t.target,"table");e&&(this.date=e.value),e&&n&&f(this,"datepicker.click.day")}else"keydown"===t.type&&l(t.target,"table")&&(this.date=g[t.keyCode],this.querySelector("[autofocus]").focus(),t.preventDefault())}},{key:"diff",value:function(t){return this.parse(t).getTime()-this.timestamp}},{key:"parse",value:function(t,e){return p(t,e||this._date)}},{key:"disabled",get:function(){return this._disabled||Function.prototype},set:function(e){var n=this;this._disabled="function"==typeof e?function(t){return e(n.parse(t),n)}:function(){return e},this.attributeChangedCallback()}},{key:"timestamp",get:function(){return String(this._date.getTime())}},{key:"year",get:function(){return String(this._date.getFullYear())}},{key:"month",get:function(){return v(this._date.getMonth()+1)}},{key:"day",get:function(){return v(this._date.getDate())}},{key:"hour",get:function(){return v(this._date.getHours())}},{key:"minute",get:function(){return v(this._date.getMinutes())}},{key:"second",get:function(){return v(this._date.getSeconds())}},{key:"date",get:function(){return p(this.getAttribute("timestamp")||this._date||Date.now())},set:function(t){return this.setAttribute("timestamp",this.parse(t).getTime())}},{key:"months",set:function(t){this.setAttribute("months",[].concat(t).join(","))},get:function(){return(this.getAttribute("months")||"januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember").split(/\s*,\s*/)}},{key:"days",set:function(t){this.setAttribute("days",[].concat(t).join(","))},get:function(){return(this.getAttribute("days")||"man,tirs,ons,tors,fre,lør,søn").split(/\s*,\s*/)}}])&&o(n.prototype,r),i&&o(n,i),e}(),v=function(t){return"0".concat(t).slice(-2)},w=function(t,e,n){return[].forEach.call(document.getElementsByTagName(t),function(t){(e.contains(t)||e.id===t.getAttribute(e.external))&&n(e,t,e._date)})};function k(t,e){e.disabled=t.disabled(e.value)}function E(t,e){var n=e.getAttribute("data-type")||e.getAttribute("type");"radio"===n||"checkbox"===n?(e.disabled=t.disabled(e.value),e.checked=!t.diff(e.value)):m[n]&&(e.setAttribute("type","number"),e.setAttribute("data-type",n),e.value=t[n])}function _(i,t){t.firstElementChild||(t.innerHTML="\n <caption></caption><thead><tr><th>".concat(i.days.map(n).join("</th><th>"),"</th></tr></thead>\n <tbody>").concat(Array(7).join("<tr>".concat(Array(8).join('<td><button type="button"></button></td>'),"</tr>")),"</tbody>"));var o=new Date,a=i.date.getMonth(),u=i.parse("y-m-1 mon");t.caption.textContent="".concat(i.months[a],", ").concat(i.year),h("button",t).forEach(function(t){var e=!i.diff(u),n=u.getDate(),r=u.getMonth();t.textContent=n,t.value="".concat(u.getFullYear(),"-").concat(r+1,"-").concat(n),t.disabled=i.disabled(u),t.tabIndex=e-1,t.setAttribute("data-adjacent",a!==r),t.setAttribute("aria-label","".concat(n,". ").concat(i.months[r])),t.setAttribute("aria-current",u.getDate()===o.getDate()&&u.getMonth()===o.getMonth()&&u.getFullYear()===o.getFullYear()&&"date"),t.toggleAttribute("autofocus",e),u.setDate(n+1)})}function A(e,t){t.firstElementChild||(t.innerHTML=e.months.map(function(t,e){return'<option value="y-'.concat(e+1,'-d">').concat(n(t),"</option>")}).join("")),h(t.children).forEach(function(t){t.disabled=e.disabled(t.value),t.selected=!e.diff(t.value)})}return b}),window.customElements.define("core-datepicker",coreDatepicker); | ||
//# sourceMappingURL=core-datepicker.min.js.map |
@@ -1,241 +0,212 @@ | ||
const coreDatepicker = require('./core-datepicker.min') | ||
import test from 'ava' | ||
import path from 'path' | ||
import puppeteer from 'puppeteer' | ||
const standardHTML = ` | ||
<div class="my-datepicker"> | ||
<input type="timestamp" /> | ||
<select></select> | ||
<table></table> | ||
</div> | ||
` | ||
async function withPage (t, run) { | ||
const browser = await puppeteer.launch() | ||
const page = await browser.newPage() | ||
page.on('console', msg => console.log(msg._text)) | ||
await page.addScriptTag({ path: path.join(__dirname, 'core-datepicker.min.js') }) | ||
try { | ||
await run(t, page) | ||
} finally { | ||
await page.close() | ||
await browser.close() | ||
} | ||
} | ||
const customSelectHTML = ` | ||
<div class="my-datepicker"> | ||
<input type="timestamp" /> | ||
<select> | ||
<option value="2016-m-d">Set year to 2016</option> | ||
<option value="19yy-1-1">Back 100 years and set to January 1st.</option> | ||
<option value="1985-12-19">December 19, 1985</option> | ||
</select> | ||
<select></select> | ||
<table></table> | ||
</div> | ||
` | ||
test('sets up properties', withPage, async (t, page) => { | ||
const date = new Date('2019-04-30T10:44:56') | ||
await page.setContent(`<core-datepicker timestamp="${date.getTime()}"></core-datepicker>`) | ||
t.is(await page.$eval('core-datepicker', el => el.year), '2019') | ||
t.is(await page.$eval('core-datepicker', el => el.month), '04') | ||
t.is(await page.$eval('core-datepicker', el => el.day), '30') | ||
t.is(await page.$eval('core-datepicker', el => el.hour), '10') | ||
t.is(await page.$eval('core-datepicker', el => el.minute), '44') | ||
t.is(await page.$eval('core-datepicker', el => el.second), '56') | ||
t.is(await page.$eval('core-datepicker', el => el.date.getTime()), date.getTime()) | ||
t.is(await page.$eval('core-datepicker', el => el.days.join(',')), 'man,tirs,ons,tors,fre,lør,søn') | ||
t.is(await page.$eval('core-datepicker', el => el.months.join(',')), | ||
'januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember') | ||
}) | ||
describe('core-datepicker', () => { | ||
beforeEach(() => { | ||
coreDatepicker.months = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'] | ||
coreDatepicker.days = ['man', 'tirs', 'ons', 'tors', 'fre', 'lør', 'søn'] | ||
}) | ||
test('sets input values from timestamp', withPage, async (t, page) => { | ||
const date = new Date('2019-04-30T10:44:56') | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<input type="year"> | ||
<input type="month"> | ||
<input type="day"> | ||
<input type="hour"> | ||
<input type="minute"> | ||
<input type="second"> | ||
<input type="timestamp"> | ||
</core-datepicker> | ||
`) | ||
t.true(await page.$$eval('core-datepicker input', els => els.every(el => el.type === 'number'))) | ||
t.true(await page.$$eval('core-datepicker input', els => els.every(el => el.hasAttribute('data-type')))) | ||
t.is(await page.$eval('core-datepicker input[data-type="year"]', el => el.value), '2019') | ||
t.is(await page.$eval('core-datepicker input[data-type="month"]', el => el.value), '04') | ||
t.is(await page.$eval('core-datepicker input[data-type="day"]', el => el.value), '30') | ||
t.is(await page.$eval('core-datepicker input[data-type="hour"]', el => el.value), '10') | ||
t.is(await page.$eval('core-datepicker input[data-type="minute"]', el => el.value), '44') | ||
t.is(await page.$eval('core-datepicker input[data-type="second"]', el => el.value), '56') | ||
t.is(await page.$eval('core-datepicker input[data-type="timestamp"]', el => el.value), String(date.getTime())) | ||
}) | ||
it('should exists', () => { | ||
expect(coreDatepicker).toBeInstanceOf(Function) | ||
}) | ||
test('populates empty select with months', withPage, async (t, page) => { | ||
await page.setContent(` | ||
<core-datepicker> | ||
<select></select> | ||
</core-datepicker> | ||
`) | ||
await page.waitFor('core-datepicker select option') | ||
t.is(await page.$eval('core-datepicker select', el => el.childElementCount), 12) | ||
for (let i = 1; i <= 12; i++) { | ||
t.is(await page.$eval(`core-datepicker option:nth-child(${i})`, el => el.value), `y-${i}-d`) | ||
} | ||
t.is(await page.$eval('core-datepicker option:nth-child(1)', el => el.textContent), 'januar') | ||
t.is(await page.$eval('core-datepicker option:nth-child(2)', el => el.textContent), 'februar') | ||
t.is(await page.$eval('core-datepicker option:nth-child(3)', el => el.textContent), 'mars') | ||
t.is(await page.$eval('core-datepicker option:nth-child(4)', el => el.textContent), 'april') | ||
t.is(await page.$eval('core-datepicker option:nth-child(5)', el => el.textContent), 'mai') | ||
t.is(await page.$eval('core-datepicker option:nth-child(6)', el => el.textContent), 'juni') | ||
t.is(await page.$eval('core-datepicker option:nth-child(7)', el => el.textContent), 'juli') | ||
t.is(await page.$eval('core-datepicker option:nth-child(8)', el => el.textContent), 'august') | ||
t.is(await page.$eval('core-datepicker option:nth-child(9)', el => el.textContent), 'september') | ||
t.is(await page.$eval('core-datepicker option:nth-child(10)', el => el.textContent), 'oktober') | ||
t.is(await page.$eval('core-datepicker option:nth-child(11)', el => el.textContent), 'november') | ||
t.is(await page.$eval('core-datepicker option:nth-child(12)', el => el.textContent), 'desember') | ||
}) | ||
it('should dispatch render when datepicker is initialized ', () => { | ||
document.body.innerHTML = standardHTML | ||
const callback = jest.fn() | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
test('re-uses custom select', withPage, async (t, page) => { | ||
await page.setContent(` | ||
<core-datepicker> | ||
<select> | ||
<option>---</option> | ||
<option value="2016-m-d">Set year to 2016</option> | ||
<option value="19yy-1-1">Back 100 years and set to January 1st.</option> | ||
<option value="1985-12-19">December 19, 1985</option> | ||
</select> | ||
</core-datepicker> | ||
`) | ||
t.is(await page.$eval('core-datepicker select', el => el.childElementCount), 4) | ||
t.is(await page.$eval('core-datepicker option:nth-child(1)', el => el.value), '---') | ||
t.is(await page.$eval('core-datepicker option:nth-child(2)', el => el.value), '2016-m-d') | ||
t.is(await page.$eval('core-datepicker option:nth-child(3)', el => el.value), '19yy-1-1') | ||
}) | ||
datepickerEl.addEventListener('datepicker.render', callback) | ||
test('populates empty table', withPage, async (t, page) => { | ||
const date = new Date('2018-01-01') | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<table></table> | ||
</core-datepicker> | ||
`) | ||
await page.waitFor('core-datepicker table button') | ||
const days = await page.$eval('core-datepicker', el => el.days) | ||
t.is(days.length, 7) | ||
for (let i = 1; i <= 7; i++) { | ||
t.is(await page.$eval(`core-datepicker thead tr th:nth-child(${i})`, el => el.textContent), days[i - 1]) | ||
} | ||
t.is(await page.$$eval(`core-datepicker table td button[data-adjacent="false"]`, els => els.length), 31) | ||
t.is(await page.$eval('core-datepicker button[autofocus]', el => el.textContent), '1') | ||
}) | ||
coreDatepicker(datepickerEl) | ||
expect(callback).toBeCalled() | ||
}) | ||
test('marks today\'s date in table', withPage, async (t, page) => { | ||
const date = new Date() | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<table></table> | ||
</core-datepicker> | ||
`) | ||
await page.waitFor('core-datepicker table button') | ||
t.true(await page.$eval('core-datepicker button[aria-current="date"]', el => el !== null)) | ||
t.is(await page.$eval('core-datepicker button[aria-current="date"]', el => el.textContent), String(date.getDate())) | ||
}) | ||
it('should set selected date if date parameter is set', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const now = new Date() | ||
test('changes date on day clicked', withPage, async (t, page) => { | ||
const date = new Date('2018-01-01') | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<table></table> | ||
</core-datepicker> | ||
`) | ||
await page.waitFor('core-datepicker table button') | ||
t.is(await page.$eval('core-datepicker button[autofocus]', el => el.textContent), '1') | ||
await page.click('core-datepicker tbody tr:nth-child(2) td:nth-child(5) button') | ||
t.is(await page.$eval('core-datepicker button[autofocus]', el => el.textContent), '12') | ||
}) | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(datepickerEl.querySelector('input').value)).toEqual(now.getTime()) | ||
}) | ||
test('changes month names', withPage, async (t, page) => { | ||
const months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'oktober', 'november', 'december'] | ||
await page.setContent(` | ||
<core-datepicker months="${months.join()}"> | ||
<select></select> | ||
</core-datepicker>` | ||
) | ||
await page.waitFor('core-datepicker select option') | ||
t.is(await page.$eval('core-datepicker select', el => el.childElementCount), 12) | ||
for (let i = 1; i <= 12; i++) { | ||
t.is(await page.$eval(`core-datepicker option:nth-child(${i})`, el => el.textContent), months[i - 1]) | ||
} | ||
}) | ||
it('should change month names in select when replacing months property', () => { | ||
document.body.innerHTML = standardHTML | ||
const monthsEn = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'oktober', 'november', 'december'] | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
test('changes day names', withPage, async (t, page) => { | ||
const days = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu'] | ||
await page.setContent(` | ||
<core-datepicker days="${days.join()}"> | ||
<table></table> | ||
</core-datepicker>` | ||
) | ||
await page.waitFor('core-datepicker table thead') | ||
t.is(await page.$eval('core-datepicker table thead tr', el => el.childElementCount), 7) | ||
for (let i = 1; i <= 7; i++) { | ||
t.is(await page.$eval(`core-datepicker thead tr th:nth-child(${i})`, el => el.textContent), days[i - 1]) | ||
} | ||
}) | ||
coreDatepicker.months = monthsEn | ||
test('disables elements from function', withPage, async (t, page) => { | ||
const date = new Date('2018-01-01') | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<table></table> | ||
</core-datepicker> | ||
`) | ||
await page.evaluate(() => (document.querySelector('core-datepicker').disabled = date => date > new Date('2018-01-01'))) | ||
await page.waitFor('core-datepicker table button') | ||
coreDatepicker(datepickerEl) | ||
datepickerEl.querySelectorAll('select option').forEach((option, index) => { | ||
expect(option.textContent).toEqual(monthsEn[index]) | ||
}) | ||
}) | ||
t.false(await page.$eval('core-datepicker tbody tr:nth-child(1) td:nth-child(1) button', el => el.disabled)) | ||
t.true(await page.$$eval('core-datepicker tbody tr:nth-child(2) button', els => els.every(el => el.disabled))) | ||
t.true(await page.$$eval('core-datepicker tbody tr:nth-child(3) button', els => els.every(el => el.disabled))) | ||
t.true(await page.$$eval('core-datepicker tbody tr:nth-child(4) button', els => els.every(el => el.disabled))) | ||
}) | ||
it('should change days names in table when replacing days property', () => { | ||
document.body.innerHTML = standardHTML | ||
const daysTest = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu'] | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
coreDatepicker.days = daysTest | ||
coreDatepicker(datepickerEl) | ||
datepickerEl.querySelectorAll('thead tr th').forEach((option, index) => { | ||
expect(option.textContent).toEqual(daysTest[index]) | ||
test('triggers change event', withPage, async (t, page) => { | ||
const date = new Date('2018-01-01') | ||
const expected = new Date('2018-01-02') | ||
await page.setContent(`<core-datepicker timestamp="${date.getTime()}"></core-datepicker>`) | ||
const newDate = await page.evaluate(() => { | ||
return new Promise((resolve, reject) => { | ||
window.addEventListener('datepicker.change', ({ detail }) => resolve(detail.getTime())) | ||
document.querySelector('core-datepicker').date = new Date('2018-01-02') | ||
}) | ||
}) | ||
t.is(newDate, expected.getTime()) | ||
}) | ||
describe('<input />', () => { | ||
it('should set selected year if input is type year', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test year type | ||
inputEl.type = 'year' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getFullYear()) | ||
test('triggers click day event', withPage, async (t, page) => { | ||
const date = new Date('2018-01-01') | ||
await page.setContent(` | ||
<core-datepicker timestamp="${date.getTime()}"> | ||
<table></table> | ||
</core-datepicker>` | ||
) | ||
await page.evaluate(() => { | ||
return new Promise((resolve, reject) => { | ||
window.addEventListener('datepicker.click.day', resolve) | ||
document.querySelector('core-datepicker button').click() | ||
}) | ||
it('should set selected month if input is type month', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test month type | ||
inputEl.type = 'month' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getMonth() + 1) | ||
}) | ||
it('should set selected day if input is type day', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test day type | ||
inputEl.type = 'day' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getDate()) | ||
}) | ||
it('should set selected hour if input is type hour', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test hour type | ||
inputEl.type = 'hour' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getHours()) | ||
}) | ||
it('should set selected minute if input is type minute', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test minute type | ||
inputEl.type = 'minute' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getMinutes()) | ||
}) | ||
it('should set selected second if input is type second', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const inputEl = datepickerEl.querySelector('input') | ||
const now = new Date() | ||
// Test second type | ||
inputEl.type = 'second' | ||
coreDatepicker(datepickerEl, now.getTime()) | ||
expect(Number(inputEl.value)).toEqual(now.getSeconds()) | ||
}) | ||
}) | ||
describe('<select>', () => { | ||
it('should use custom select if specified and not overwrite options', () => { | ||
document.body.innerHTML = customSelectHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const selectEl = datepickerEl.querySelector('select') | ||
coreDatepicker(datepickerEl) | ||
expect(selectEl.childElementCount).toEqual(3) | ||
expect(selectEl.children[0].value).toEqual('2016-m-d') | ||
expect(selectEl.children[0].textContent).toEqual('Set year to 2016') | ||
}) | ||
it('should support multiple select (one custom and one default)', () => { | ||
document.body.innerHTML = customSelectHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const selectsEl = datepickerEl.querySelectorAll('select') | ||
coreDatepicker(datepickerEl) | ||
expect(selectsEl.length).toEqual(2) | ||
expect(selectsEl[0].children[0].textContent).toEqual('Set year to 2016') | ||
expect(selectsEl[1].children[0].textContent).toEqual(coreDatepicker.months[0]) | ||
}) | ||
}) | ||
describe('<table>', () => { | ||
it('should show days of the week in header', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
coreDatepicker(datepickerEl) | ||
const tableHeaders = datepickerEl.querySelectorAll('table th') | ||
expect(tableHeaders.length).toEqual(7) | ||
tableHeaders.forEach((el, i) => expect(el.textContent).toEqual(coreDatepicker.days[i])) | ||
}) | ||
it('should display the current month with all dates', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const nowJan = new Date('2018-01-01') // So we know the months has 31 days and starts on monday | ||
coreDatepicker(datepickerEl, nowJan) | ||
expect(datepickerEl.querySelectorAll('table button')[30].textContent).toEqual('31') | ||
}) | ||
it('should change date if a day is clicked', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const tableEl = datepickerEl.querySelector('table') | ||
// Fri Jan 12 2018 | ||
const current = new Date(1515774608994) | ||
coreDatepicker(datepickerEl, current) | ||
const jan1Btn = tableEl.querySelector('tbody tr td button') | ||
const jan12Btn = tableEl.querySelector('tbody tr:nth-child(2) td:nth-child(5) button') | ||
jan1Btn.click() | ||
expect(jan1Btn.getAttribute('data-core-datepicker-selected')).toEqual('true') | ||
expect(jan12Btn.getAttribute('data-core-datepicker-selected')).toEqual('false') | ||
}) | ||
it('should disable the dates specified by disable method', () => { | ||
document.body.innerHTML = standardHTML | ||
const datepickerEl = document.querySelector('.my-datepicker') | ||
const tableEl = datepickerEl.querySelector('table') | ||
// Fri Jan 12 2018 | ||
const current = new Date(1515774608994) | ||
datepickerEl.addEventListener('datepicker.render', (event) => { | ||
event.detail.disable((date) => date > current) | ||
}) | ||
coreDatepicker(datepickerEl, current) | ||
const currentBtn = tableEl.querySelector('tbody tr:nth-child(2) td:nth-child(5) button') | ||
const prevBtn = tableEl.querySelector('tbody tr:nth-child(2) td:nth-child(4) button') | ||
const nextBtn = tableEl.querySelector('tbody tr:nth-child(2) td:nth-child(6) button') | ||
expect(currentBtn.hasAttribute('disabled')).toBe(false) | ||
expect(prevBtn.hasAttribute('disabled')).toBe(false) | ||
expect(nextBtn.hasAttribute('disabled')).toBe(true) | ||
tableEl.querySelectorAll('tbody tr:nth-child(3) td') | ||
.forEach((td) => expect(td.children[0].hasAttribute('disabled')).toEqual(true)) | ||
}) | ||
}) | ||
t.pass() | ||
}) |
788
jsx.js
@@ -6,33 +6,163 @@ 'use strict'; | ||
var React = _interopDefault(require('react')); | ||
var PropTypes = _interopDefault(require('prop-types')); | ||
var name = "@nrk/core-datepicker"; | ||
var version = "2.0.3"; | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
function _getPrototypeOf(o) { | ||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { | ||
return o.__proto__ || Object.getPrototypeOf(o); | ||
}; | ||
return _getPrototypeOf(o); | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
function isNativeReflectConstruct() { | ||
if (typeof Reflect === "undefined" || !Reflect.construct) return false; | ||
if (Reflect.construct.sham) return false; | ||
if (typeof Proxy === "function") return true; | ||
try { | ||
Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
function _construct(Parent, args, Class) { | ||
if (isNativeReflectConstruct()) { | ||
_construct = Reflect.construct; | ||
} else { | ||
_construct = function _construct(Parent, args, Class) { | ||
var a = [null]; | ||
a.push.apply(a, args); | ||
var Constructor = Function.bind.apply(Parent, a); | ||
var instance = new Constructor(); | ||
if (Class) _setPrototypeOf(instance, Class.prototype); | ||
return instance; | ||
}; | ||
} | ||
return _construct.apply(null, arguments); | ||
} | ||
function _isNativeFunction(fn) { | ||
return Function.toString.call(fn).indexOf("[native code]") !== -1; | ||
} | ||
function _wrapNativeSuper(Class) { | ||
var _cache = typeof Map === "function" ? new Map() : undefined; | ||
_wrapNativeSuper = function _wrapNativeSuper(Class) { | ||
if (Class === null || !_isNativeFunction(Class)) return Class; | ||
if (typeof Class !== "function") { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
if (typeof _cache !== "undefined") { | ||
if (_cache.has(Class)) return _cache.get(Class); | ||
_cache.set(Class, Wrapper); | ||
} | ||
function Wrapper() { | ||
return _construct(Class, arguments, _getPrototypeOf(this).constructor); | ||
} | ||
Wrapper.prototype = Object.create(Class.prototype, { | ||
constructor: { | ||
value: Wrapper, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
return _setPrototypeOf(Wrapper, Class); | ||
}; | ||
return _wrapNativeSuper(Class); | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return self; | ||
} | ||
function _possibleConstructorReturn(self, call) { | ||
if (call && (typeof call === "object" || typeof call === "function")) { | ||
return call; | ||
} | ||
return _assertThisInitialized(self); | ||
} | ||
var IS_BROWSER = typeof window !== 'undefined'; | ||
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed | ||
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); | ||
var HAS_EVENT_OPTIONS = (function (has) { | ||
if ( has === void 0 ) has = false; | ||
try { window.addEventListener('test', null, { get passive () { has = true; } }); } catch (e) {} | ||
return has | ||
})(); | ||
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE | ||
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) { | ||
window.Element.prototype.toggleAttribute = function (name) { | ||
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name); | ||
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, ''); | ||
return force; | ||
}; | ||
} | ||
/** | ||
* addEvent | ||
* @param {String} uuid An unique ID of the event to bind - ensurnes single instance | ||
* @param {String} type The type of event to bind | ||
* @param {Function} handler The function to call on event | ||
* @param {Boolean|Object} options useCapture or options object for addEventListener. Defaults to false | ||
* addStyle | ||
* @param {String} nodeName An unique ID of the event to bind - ensurnes single instance | ||
* @param {String} css The css to inject | ||
*/ | ||
function addEvent (uuid, type, handler, options) { | ||
if ( options === void 0 ) options = false; | ||
if (typeof window === 'undefined' || window[uuid = uuid + "-" + type]) { return } // Ensure single instance | ||
if (!HAS_EVENT_OPTIONS && typeof options === 'object') { options = Boolean(options.capture); } // Fix unsupported options | ||
var node = (type === 'resize' || type === 'load') ? window : document; | ||
node.addEventListener(window[uuid] = type, handler, options); | ||
function addStyle(nodeName, css) { | ||
var key = "style-".concat(nodeName.toLowerCase()); | ||
var min = css.replace(/\/\*[^!][^*]*\*\//g, '').replace(/\s*(^|[:;,{}]|$)\s*/g, '$1'); | ||
document.getElementById(key) || document.head.insertAdjacentHTML('afterbegin', "<style id=\"".concat(key, "\">").concat(min, "</style>")); | ||
} | ||
/** | ||
@@ -43,22 +173,36 @@ * escapeHTML | ||
*/ | ||
var ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', '/': '/', '\'': ''' }; | ||
function escapeHTML (str) { | ||
return String(str || '').replace(/[&<>"'/]/g, function (char) { return ESCAPE_MAP[char]; }) | ||
var ESCAPE_MAP = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
'/': '/', | ||
'\'': ''' | ||
}; | ||
function escapeHTML(str) { | ||
return String(str || '').replace(/[&<>"'/]/g, function (_char) { | ||
return ESCAPE_MAP[_char]; | ||
}); | ||
} | ||
/** | ||
* exclude | ||
* @param {Object} target The target object | ||
* @param {Object} exclude The source to exclude keys from | ||
* @return {Object} The target object without keys found in source | ||
* closest | ||
* @param {Element} element Element to traverse up from | ||
* @param {String} selector A selector to search for matching parents or element itself | ||
* @return {Element|null} Element which is the closest ancestor matching selector | ||
*/ | ||
function exclude (target, exclude, include) { | ||
if ( include === void 0 ) include = {}; | ||
return Object.keys(target).reduce(function (acc, key) { | ||
if (!exclude.hasOwnProperty(key)) { acc[key] = target[key]; } | ||
return acc | ||
}, include) | ||
} | ||
var closest = function () { | ||
var proto = typeof window === 'undefined' ? {} : window.Element.prototype; | ||
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector; | ||
return proto.closest ? function (el, css) { | ||
return el.closest(css); | ||
} : function (el, css) { | ||
for (; el; el = el.parentElement) { | ||
if (match.call(el, css)) return el; | ||
} | ||
return null; | ||
}; | ||
}(); | ||
/** | ||
@@ -71,27 +215,122 @@ * dispatchEvent - with infinite loop prevention | ||
*/ | ||
var IGNORE = 'prevent_recursive_dispatch_maximum_callstack'; | ||
function dispatchEvent (element, name, detail) { | ||
if ( detail === void 0 ) detail = {}; | ||
var ignore = "" + IGNORE + name; | ||
function dispatchEvent(element, name) { | ||
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name); | ||
var event; | ||
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one | ||
else element[ignore] = true; // Add name to dispatching ignore | ||
if (element[ignore]) { return true } // We are already processing this event, so skip sending a new one | ||
else { element[ignore] = true; } // Add name to dispatching ignore | ||
if (typeof window.CustomEvent === 'function') { | ||
event = new window.CustomEvent(name, { bubbles: true, cancelable: true, detail: detail }); | ||
event = new window.CustomEvent(name, { | ||
bubbles: true, | ||
cancelable: true, | ||
detail: detail | ||
}); | ||
} else { | ||
event = document.createEvent('CustomEvent'); | ||
event.initCustomEvent(name, true, true, detail); | ||
} | ||
// IE reports incorrect event.defaultPrevented | ||
} // IE reports incorrect event.defaultPrevented | ||
// but correct return value on element.dispatchEvent | ||
var result = element.dispatchEvent(event); | ||
element[ignore] = null; // Remove name from dispatching ignore | ||
return result // Follow W3C standard for return value | ||
return result; // Follow W3C standard for return value | ||
} | ||
function elementToReact(elementClass) { | ||
for (var _len = arguments.length, attr = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
attr[_key - 1] = arguments[_key]; | ||
} | ||
var name = elementClass.name || String(elementClass).match(/function ([^(]+)/)[1]; // String match for IE11 | ||
var tag = "".concat(name.replace(/\W+/, '-'), "-").concat(getUUID()).toLowerCase(); | ||
if (IS_BROWSER && !window.customElements.get(tag)) window.customElements.define(tag, elementClass); | ||
return ( | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inherits(_class, _React$Component); | ||
function _class(props) { | ||
var _this; | ||
_classCallCheck(this, _class); | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(_class).call(this, props)); | ||
_this.ref = function (el) { | ||
return _this.el = el; | ||
}; | ||
attr.forEach(function (k) { | ||
var on = "on".concat(k.replace(/(^|\.)./g, function (m) { | ||
return m.slice(-1).toUpperCase(); | ||
})); // input.filter => onInputFilter | ||
_this[k] = function (event) { | ||
return _this.props[on] && _this.props[on](event); | ||
}; | ||
}); | ||
return _this; | ||
} | ||
_createClass(_class, [{ | ||
key: "componentDidMount", | ||
value: function componentDidMount() { | ||
var _this2 = this; | ||
attr.forEach(function (k) { | ||
return _this2.props[k] ? _this2.el[k] = _this2.props[k] : _this2.el.addEventListener(k, _this2[k]); | ||
}); | ||
} | ||
}, { | ||
key: "componentDidUpdate", | ||
value: function componentDidUpdate(prev) { | ||
var _this3 = this; | ||
attr.forEach(function (k) { | ||
return prev[k] !== _this3.props[k] && (_this3.el[k] = _this3.props[k]); | ||
}); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
var _this4 = this; | ||
attr.forEach(function (k) { | ||
return _this4.el.removeEventListener(k, _this4[k]); | ||
}); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this5 = this; | ||
// Convert React props to CustomElement props https://github.com/facebook/react/issues/12810 | ||
return React.createElement(tag, Object.keys(this.props).reduce(function (props, k) { | ||
if (k === 'className') props["class"] = _this5.props[k]; // Fixes className for custom elements | ||
else if (_this5.props[k] === true) props[k] = ''; // Fixes boolean attributes | ||
else if (_this5.props[k] !== false) props[k] = _this5.props[k]; | ||
return props; | ||
}, { | ||
ref: this.ref | ||
})); | ||
} | ||
}]); | ||
return _class; | ||
}(React.Component) | ||
); | ||
} | ||
/** | ||
* getUUID | ||
* @return {String} A generated unique ID | ||
*/ | ||
function getUUID() { | ||
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5); | ||
} | ||
/** | ||
* queryAll | ||
@@ -101,14 +340,16 @@ * @param {String|NodeList|Array|Element} elements A CSS selector string, nodeList, element array, or single element | ||
*/ | ||
function queryAll (elements, context) { | ||
if ( context === void 0 ) context = document; | ||
function queryAll(elements) { | ||
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document; | ||
if (elements) { | ||
if (elements.nodeType) { return [elements] } | ||
if (typeof elements === 'string') { return [].slice.call(context.querySelectorAll(elements)) } | ||
if (elements.length) { return [].slice.call(elements) } | ||
if (elements.nodeType) return [elements]; | ||
if (typeof elements === 'string') return [].slice.call(context.querySelectorAll(elements)); | ||
if (elements.length) return [].slice.call(elements); | ||
} | ||
return [] | ||
return []; | ||
} | ||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
@@ -120,121 +361,304 @@ function createCommonjsModule(fn, module) { | ||
var index_min = createCommonjsModule(function (module, exports) { | ||
!function(e,t){module.exports=t();}(commonjsGlobal,function(){var D={year:"FullYear",month:"Month",week:"Date",day:"Date",hour:"Hours",minute:"Minutes",second:"Seconds"},w=/([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g,M=/([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/,p=/([\dh]+):([\dm]+):?([\ds]+)?/;return function(e,t){if(isFinite(e)){ return new Date(Number(e)); }var n=String(e).toLowerCase(),r=new Date(isFinite(t)&&-1===n.indexOf("now")?Number(t):Date.now()),a=n.match(M)||[],o=a[1];void 0===o&&(o="y");var i=a[2];void 0===i&&(i="m");var s=a[3];void 0===s&&(s="d");var d=n.match(p)||[],u=d[1];void 0===u&&(u="h");var h=d[2];void 0===h&&(h="m");var m=d[3];void 0===m&&(m="s");var c={year:o,month:i,day:s,hour:u,minute:h,second:m};Object.keys(c).forEach(function(e){var t="month"===e?1:0,a=String(r["get"+D[e]]()+t);c[e]=c[e].replace(/[^-\d]+/g,function(e,t,n){return t?a.substr(a.length-n.length+t,e.length):a.substr(0,Math.max(0,a.length-n.length+e.length))})-t;});var f=new Date(c.year,Math.min(12,c.month+1),0).getDate();for(r.setFullYear(c.year,Math.min(11,c.month),Math.max(1,Math.min(f,c.day))),r.setHours(Math.min(24,c.hour),Math.min(59,c.minute),Math.min(59,c.second));null!==(c=w.exec(n));){var g=c[2],l=String(c[1]).replace(/\s/g,"")*("week"===g?7:1),v=c.slice(2).indexOf(c[0]),y=r.getDate();g?r["set"+D[g]](r["get"+D[g]]()+l):r.setDate(r.getDate()-(r.getDay()||7)+v),"month"!==g&&"year"!==g||y===r.getDate()||r.setDate(0);}return r}}); | ||
!function (e, t) { | ||
module.exports = t(); | ||
}(commonjsGlobal, function () { | ||
var D = { | ||
year: "FullYear", | ||
month: "Month", | ||
week: "Date", | ||
day: "Date", | ||
hour: "Hours", | ||
minute: "Minutes", | ||
second: "Seconds" | ||
}, | ||
w = /([+-]\s*\d+)\s*(second|minute|hour|day|week|month|year)|(mon)|(tue)|(wed)|(thu)|(fri)|(sat)|(sun)/g, | ||
M = /([-\dy]+)[-/.]([\dm]+)[-/.]([\dd]+)/, | ||
p = /([\dh]+):([\dm]+):?([\ds]+)?/; | ||
return function (e, t) { | ||
if (isFinite(e)) return new Date(Number(e)); | ||
var n = String(e).toLowerCase(), | ||
r = new Date(isFinite(t) && -1 === n.indexOf("now") ? Number(t) : Date.now()), | ||
a = n.match(M) || [], | ||
o = a[1]; | ||
void 0 === o && (o = "y"); | ||
var i = a[2]; | ||
void 0 === i && (i = "m"); | ||
var s = a[3]; | ||
void 0 === s && (s = "d"); | ||
var d = n.match(p) || [], | ||
u = d[1]; | ||
void 0 === u && (u = "h"); | ||
var h = d[2]; | ||
void 0 === h && (h = "m"); | ||
var m = d[3]; | ||
void 0 === m && (m = "s"); | ||
var c = { | ||
year: o, | ||
month: i, | ||
day: s, | ||
hour: u, | ||
minute: h, | ||
second: m | ||
}; | ||
Object.keys(c).forEach(function (e) { | ||
var t = "month" === e ? 1 : 0, | ||
a = String(r["get" + D[e]]() + t); | ||
c[e] = c[e].replace(/[^-\d]+/g, function (e, t, n) { | ||
return t ? a.substr(a.length - n.length + t, e.length) : a.substr(0, Math.max(0, a.length - n.length + e.length)); | ||
}) - t; | ||
}); | ||
var f = new Date(c.year, Math.min(12, c.month + 1), 0).getDate(); | ||
for (r.setFullYear(c.year, Math.min(11, c.month), Math.max(1, Math.min(f, c.day))), r.setHours(Math.min(24, c.hour), Math.min(59, c.minute), Math.min(59, c.second)); null !== (c = w.exec(n));) { | ||
var g = c[2], | ||
l = String(c[1]).replace(/\s/g, "") * ("week" === g ? 7 : 1), | ||
v = c.slice(2).indexOf(c[0]), | ||
y = r.getDate(); | ||
g ? r["set" + D[g]](r["get" + D[g]]() + l) : r.setDate(r.getDate() - (r.getDay() || 7) + v), "month" !== g && "year" !== g || y === r.getDate() || r.setDate(0); | ||
} | ||
return r; | ||
}; | ||
}); | ||
}); | ||
var ATTR = 'data-core-datepicker'; | ||
var UUID = ("data-" + name + "-" + version).replace(/\W+/g, '-'); // Strip invalid attribute characters | ||
var KEY_CODES = { 33: '-1month', 34: '+1month', 35: 'y-m-99', 36: 'y-m-1', 37: '-1day', 38: '-1week', 39: '+1day', 40: '+1week' }; | ||
var MASK = { year: '*-m-d', month: 'y-*-d', day: 'y-m-*', hour: '*:m', minute: 'h:*', second: 'h:m:*', timestamp: '*' }; | ||
var MS_IN_MINUTES = 60000; | ||
var MASK = { | ||
year: '*-m-d', | ||
month: 'y-*-d', | ||
day: 'y-m-*', | ||
hour: '*:m', | ||
minute: 'h:*', | ||
second: 'h:m:*', | ||
timestamp: '*', | ||
"null": '*' | ||
}; | ||
var KEYS = { | ||
33: '-1month', | ||
34: '+1month', | ||
35: 'y-m-99', | ||
36: 'y-m-1', | ||
37: '-1day', | ||
38: '-1week', | ||
39: '+1day', | ||
40: '+1week' | ||
}; | ||
var MONTHS = 'januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember'; | ||
var DAYS = 'man,tirs,ons,tors,fre,lør,søn'; | ||
function datepicker (elements, date) { // date can be String, Timestamp or Date | ||
return queryAll(elements).map(function (element) { | ||
var prevDate = index_min(element.getAttribute(UUID) || date); | ||
var nextDate = index_min(typeof date === 'undefined' ? prevDate : date, prevDate); | ||
var disable = function () { return false; }; | ||
var CoreDatepicker = | ||
/*#__PURE__*/ | ||
function (_HTMLElement) { | ||
_inherits(CoreDatepicker, _HTMLElement); | ||
dispatchEvent(element, 'datepicker.render', { nextDate: nextDate, prevDate: prevDate, disable: function (fn) { return (disable = fn); } }); | ||
if (disable(nextDate)) { nextDate = prevDate; } // Jump back to prev date if next is disabled | ||
function CoreDatepicker() { | ||
_classCallCheck(this, CoreDatepicker); | ||
var isUpdate = prevDate.getTime() === nextDate.getTime() || dispatchEvent(element, 'datepicker.change', { prevDate: prevDate, nextDate: nextDate }); | ||
var next = isUpdate ? nextDate : index_min(element.getAttribute(UUID) || Date.now()); // dispatchEvent can change attributes to parse prevDate again | ||
var json = new Date(next.getTime() - next.getTimezoneOffset() * MS_IN_MINUTES).toJSON().match(/\d+/g); | ||
var unit = { year: next.getFullYear(), month: json[1], day: json[2], hour: json[3], minute: json[4], second: json[5], timestamp: next.getTime() }; | ||
return _possibleConstructorReturn(this, _getPrototypeOf(CoreDatepicker).apply(this, arguments)); | ||
} | ||
element.setAttribute(UUID, unit.timestamp); | ||
queryAll('button').forEach(function (el) { return button(el, next, disable, element); }); | ||
queryAll('select', element).concat(queryAll(("select[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return select(el, next, disable); }); | ||
queryAll('input', element).concat(queryAll(("input[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return input(el, next, disable, unit); }); | ||
queryAll('table', element).concat(queryAll(("table[" + ATTR + "=\"" + (element.id) + "\"]"))).forEach(function (el) { return table(el, next, disable); }); | ||
_createClass(CoreDatepicker, [{ | ||
key: "connectedCallback", | ||
value: function connectedCallback() { | ||
var _this = this; | ||
return element | ||
}) | ||
} | ||
this._date = this.date; // Store for later comparison and speeding up things | ||
// Expose API and config | ||
datepicker.parse = index_min; | ||
datepicker.months = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember']; | ||
datepicker.days = ['man', 'tirs', 'ons', 'tors', 'fre', 'lør', 'søn']; | ||
document.addEventListener('click', this); | ||
document.addEventListener('change', this); | ||
document.addEventListener('keydown', this); | ||
setTimeout(function () { | ||
return _this.attributeChangedCallback(); | ||
}); // Render after children is parsed | ||
addEvent(UUID, 'change', onChange); | ||
addEvent(UUID, 'click', function (ref) { | ||
var target = ref.target; | ||
addStyle(this.nodeName, "".concat(this.nodeName, "{display:block}")); // default to display block | ||
} | ||
}, { | ||
key: "disconnectedCallback", | ||
value: function disconnectedCallback() { | ||
this._date = this._disabled = null; // Garbage collection | ||
for (var el = target; el; el = el.parentElement) { | ||
if (el.nodeName === 'BUTTON') { return onChange({ target: el }) } | ||
} | ||
}); | ||
addEvent(UUID, 'keydown', function (event) { | ||
if (event.ctrlKey || event.metaKey || event.shitKey || event.altKey || !KEY_CODES[event.keyCode]) { return } | ||
for (var el = event.target, table = (void 0); el; el = el.parentElement) { | ||
if (!table && el.nodeName === 'TABLE') { table = el; } // Store table while traversing DOM parents | ||
if (table && el.hasAttribute(UUID)) { // Only listen to keyCodes inside table inside datepicker | ||
datepicker(el, KEY_CODES[event.keyCode]); | ||
table.querySelector(("[" + ATTR + "-selected=\"true\"]")).focus(); | ||
event.preventDefault(); | ||
break | ||
document.removeEventListener('click', this); | ||
document.removeEventListener('change', this); | ||
document.removeEventListener('keydown', this); | ||
} | ||
} | ||
}); | ||
}, { | ||
key: "attributeChangedCallback", | ||
value: function attributeChangedCallback() { | ||
if (!this._date) return; // Only render after connectedCallback | ||
function onChange (ref) { | ||
var target = ref.target; | ||
if (this.disabled(this.date) && !this.disabled(this._date)) return this.date = this._date; // Jump back | ||
for (var el = target, table = (void 0); el; el = el.parentElement) { | ||
var elem = document.getElementById(el.getAttribute(ATTR)) || el; | ||
var mask = elem.hasAttribute(UUID) && (MASK[target.getAttribute((UUID + "-type"))] || '*'); | ||
if (this.diff(this.date)) dispatchEvent(this, 'datepicker.change', this._date = this.date); | ||
forEach('button', this, button); | ||
forEach('select', this, select); | ||
forEach('input', this, input); | ||
forEach('table', this, table); | ||
} | ||
}, { | ||
key: "handleEvent", | ||
value: function handleEvent(event) { | ||
if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.type === 'keydown' && !KEYS[event.keyCode]) return; | ||
if (!this.contains(event.target) && !closest(event.target, "[for=\"".concat(this.id, "\"]"))) return; | ||
if (event.type === 'change') this.date = MASK[event.target.getAttribute('data-type')].replace('*', event.target.value);else if (event.type === 'click') { | ||
var _button = closest(event.target, 'button[value]'); | ||
if (!table && el.nodeName === 'TABLE') { table = el; } // Store table while traversing DOM parents | ||
if (mask) { | ||
var nextDate = mask.replace('*', target.value); | ||
var isUpdate = !elem.contains(table) || dispatchEvent(elem, 'datepicker.click.day', { | ||
currentTarget: target, | ||
relatedTarget: table, | ||
prevDate: index_min(elem.getAttribute(UUID)), | ||
nextDate: index_min(nextDate) | ||
}); | ||
var _table = closest(event.target, 'table'); | ||
return isUpdate && datepicker(elem, nextDate) | ||
if (_button) this.date = _button.value; | ||
if (_button && _table) dispatchEvent(this, 'datepicker.click.day'); | ||
} else if (event.type === 'keydown' && closest(event.target, 'table')) { | ||
this.date = KEYS[event.keyCode]; | ||
this.querySelector('[autofocus]').focus(); | ||
event.preventDefault(); // Prevent scrolling | ||
} | ||
} | ||
} | ||
}, { | ||
key: "diff", | ||
value: function diff(val) { | ||
return this.parse(val).getTime() - this.timestamp; | ||
} | ||
}, { | ||
key: "parse", | ||
value: function parse(val, from) { | ||
return index_min(val, from || this._date); | ||
} | ||
}, { | ||
key: "disabled", | ||
get: function get() { | ||
return this._disabled || Function.prototype; | ||
}, | ||
set: function set(fn) { | ||
var _this2 = this; | ||
this._disabled = typeof fn === 'function' ? function (val) { | ||
return fn(_this2.parse(val), _this2); | ||
} : function () { | ||
return fn; | ||
}; // Auto parse dates | ||
this.attributeChangedCallback(); // Re-render | ||
} | ||
}, { | ||
key: "timestamp", | ||
get: function get() { | ||
return String(this._date.getTime()); | ||
} | ||
}, { | ||
key: "year", | ||
get: function get() { | ||
return String(this._date.getFullYear()); | ||
} // Stringify for consistency and for truthy '0' | ||
}, { | ||
key: "month", | ||
get: function get() { | ||
return pad(this._date.getMonth() + 1); | ||
} | ||
}, { | ||
key: "day", | ||
get: function get() { | ||
return pad(this._date.getDate()); | ||
} | ||
}, { | ||
key: "hour", | ||
get: function get() { | ||
return pad(this._date.getHours()); | ||
} | ||
}, { | ||
key: "minute", | ||
get: function get() { | ||
return pad(this._date.getMinutes()); | ||
} | ||
}, { | ||
key: "second", | ||
get: function get() { | ||
return pad(this._date.getSeconds()); | ||
} | ||
}, { | ||
key: "date", | ||
get: function get() { | ||
return index_min(this.getAttribute('timestamp') || this._date || Date.now()); | ||
}, | ||
set: function set(val) { | ||
return this.setAttribute('timestamp', this.parse(val).getTime()); | ||
} | ||
}, { | ||
key: "months", | ||
set: function set(val) { | ||
this.setAttribute('months', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('months') || MONTHS).split(/\s*,\s*/); | ||
} | ||
}, { | ||
key: "days", | ||
set: function set(val) { | ||
this.setAttribute('days', [].concat(val).join(',')); | ||
}, | ||
get: function get() { | ||
return (this.getAttribute('days') || DAYS).split(/\s*,\s*/); | ||
} | ||
}], [{ | ||
key: "observedAttributes", | ||
get: function get() { | ||
return ['timestamp', 'months', 'days']; | ||
} | ||
}]); | ||
return CoreDatepicker; | ||
}(_wrapNativeSuper(HTMLElement)); | ||
var pad = function pad(val) { | ||
return "0".concat(val).slice(-2); | ||
}; | ||
var forEach = function forEach(css, self, fn) { | ||
return [].forEach.call(document.getElementsByTagName(css), function (el) { | ||
if (self.contains(el) || self.id === el.getAttribute(self.external)) fn(self, el, self._date); | ||
}); | ||
}; | ||
function button(self, el) { | ||
el.disabled = self.disabled(el.value); | ||
} | ||
function input (el, date, disable, unit) { | ||
var type = el.getAttribute((UUID + "-type")) || el.getAttribute('type'); | ||
function input(self, el) { | ||
var type = el.getAttribute('data-type') || el.getAttribute('type'); | ||
if (type === 'radio' || type === 'checkbox') { | ||
var val = index_min(el.value, date); | ||
el.disabled = disable(val); | ||
el.checked = val.getTime() === date.getTime(); | ||
} else if (unit[type]) { | ||
el.disabled = self.disabled(el.value); | ||
el.checked = !self.diff(el.value); | ||
} else if (MASK[type]) { | ||
el.setAttribute('type', 'number'); // Set input type to number | ||
el.setAttribute((UUID + "-type"), type); // And store original input type | ||
el.value = unit[type]; | ||
el.setAttribute('data-type', type); // And store original type | ||
el.value = self[type]; | ||
} | ||
} | ||
function table (table, date, disable) { | ||
function table(self, table) { | ||
if (!table.firstElementChild) { | ||
table.innerHTML = "\n <caption></caption><thead><tr><th>" + (datepicker.days.map(escapeHTML).join('</th><th>')) + "</th></tr></thead>\n <tbody>" + (Array(7).join(("<tr>" + (Array(8).join("<td><button type=\"button\"></button></td>")) + "</tr>"))) + "</tbody>"; | ||
table.innerHTML = "\n <caption></caption><thead><tr><th>".concat(self.days.map(escapeHTML).join('</th><th>'), "</th></tr></thead>\n <tbody>").concat(Array(7).join("<tr>".concat(Array(8).join('<td><button type="button"></button></td>'), "</tr>")), "</tbody>"); | ||
} | ||
var today = new Date(); | ||
var day = index_min('y-m-1 mon', date); // Monday in first week of month | ||
table.caption.textContent = (escapeHTML(datepicker.months[date.getMonth()])) + ", " + (date.getFullYear()); | ||
var month = self.date.getMonth(); | ||
var day = self.parse('y-m-1 mon'); // Monday in first week of month | ||
table.caption.textContent = "".concat(self.months[month], ", ").concat(self.year); | ||
queryAll('button', table).forEach(function (button) { | ||
var isToday = day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear(); | ||
var isSelected = day.getTime() === date.getTime(); | ||
var isSelected = !self.diff(day); | ||
var dayInMonth = day.getDate(); | ||
var month = day.getMonth(); | ||
var dayMonth = day.getMonth(); | ||
button.textContent = dayInMonth; // Set textContent instead of innerHTML avoids reflow | ||
button.textContent = dayInMonth; // Set textContent instead of innerHTML avoids reflow | ||
button.value = (day.getFullYear()) + "-" + (month + 1) + "-" + dayInMonth; | ||
button.disabled = disable(day); | ||
button.setAttribute('tabindex', isSelected - 1); | ||
button.setAttribute((ATTR + "-selected"), isSelected); | ||
button.setAttribute((ATTR + "-adjacent"), date.getMonth() !== month); | ||
button.setAttribute('aria-label', (dayInMonth + ". " + (datepicker.months[month]))); | ||
button[isToday ? 'setAttribute' : 'removeAttribute']('aria-current', 'date'); | ||
button[isSelected ? 'setAttribute' : 'removeAttribute']('autofocus', ''); | ||
button.value = "".concat(day.getFullYear(), "-").concat(dayMonth + 1, "-").concat(dayInMonth); | ||
button.disabled = self.disabled(day); | ||
button.tabIndex = isSelected - 1; | ||
button.setAttribute("data-adjacent", month !== dayMonth); | ||
button.setAttribute('aria-label', "".concat(dayInMonth, ". ").concat(self.months[dayMonth])); | ||
button.setAttribute('aria-current', day.getDate() === today.getDate() && day.getMonth() === today.getMonth() && day.getFullYear() === today.getFullYear() && 'date'); | ||
button.toggleAttribute('autofocus', isSelected); | ||
day.setDate(dayInMonth + 1); | ||
@@ -244,83 +668,17 @@ }); | ||
function button (el, date, disable, picker) { | ||
if (el.getAttribute(ATTR) === picker.id || picker.contains(el)) { | ||
el.disabled = disable(index_min(el.value, date)); | ||
} | ||
} | ||
function select (select, date, disable) { | ||
function select(self, select) { | ||
if (!select.firstElementChild) { | ||
select.innerHTML = datepicker.months.map(function (name, month) { return ("<option value=\"y-" + (month + 1) + "-d\">" + (escapeHTML(name)) + "</option>"); } | ||
).join(''); | ||
select.innerHTML = self.months.map(function (name, month) { | ||
return "<option value=\"y-".concat(month + 1, "-d\">").concat(escapeHTML(name), "</option>"); | ||
}).join(''); | ||
} | ||
queryAll(select.children).forEach(function (option) { | ||
var val = index_min(option.value, date); | ||
option.disabled = disable(val); | ||
option.selected = val.getTime() === date.getTime(); | ||
option.disabled = self.disabled(option.value); | ||
option.selected = !self.diff(option.value); | ||
}); | ||
} | ||
var Datepicker = /*@__PURE__*/(function (superclass) { | ||
function Datepicker (props) { | ||
superclass.call(this, props); | ||
this.onClickDay = this.onClickDay.bind(this); | ||
this.onChange = this.onChange.bind(this); | ||
this.onRender = this.onRender.bind(this); | ||
} | ||
var coreDatepicker = elementToReact(CoreDatepicker, 'datepicker.change', 'datepicker.click.day', 'disabled', 'months', 'days'); | ||
if ( superclass ) Datepicker.__proto__ = superclass; | ||
Datepicker.prototype = Object.create( superclass && superclass.prototype ); | ||
Datepicker.prototype.constructor = Datepicker; | ||
var staticAccessors = { defaultProps: { configurable: true },months: { configurable: true },days: { configurable: true } }; | ||
staticAccessors.defaultProps.get = function () { return { date: null, disable: null, onRender: null, onChange: null, onClickDay: null } }; | ||
staticAccessors.months.set = function (months) { datepicker.months = months; }; | ||
staticAccessors.days.set = function (days) { datepicker.days = days; }; | ||
staticAccessors.months.get = function () { return datepicker.months }; | ||
staticAccessors.days.get = function () { return datepicker.days }; | ||
Datepicker.parse = function parse () { | ||
var args = [], len = arguments.length; | ||
while ( len-- ) args[ len ] = arguments[ len ]; | ||
return datepicker.parse.apply(datepicker, args) }; | ||
Datepicker.prototype.componentDidMount = function componentDidMount () { | ||
this.el.addEventListener('datepicker.click.day', this.onClickDay); | ||
this.el.addEventListener('datepicker.change', this.onChange); | ||
this.el.addEventListener('datepicker.render', this.onRender); | ||
datepicker(this.el, this.props.date); | ||
}; | ||
Datepicker.prototype.componentDidUpdate = function componentDidUpdate () { datepicker(this.el, this.props.date); }; | ||
Datepicker.prototype.componentWillUnmount = function componentWillUnmount () { | ||
this.el.removeEventListener('datepicker.click.day', this.onClickDay); | ||
this.el.removeEventListener('datepicker.change', this.onChange); | ||
this.el.removeEventListener('datepicker.render', this.onRender); | ||
}; | ||
Datepicker.prototype.onClickDay = function onClickDay (event) { this.props.onClickDay && this.props.onClickDay(event); }; | ||
Datepicker.prototype.onChange = function onChange (event) { this.props.onChange && this.props.onChange(event); }; | ||
Datepicker.prototype.onRender = function onRender (event) { this.props.disable && event.detail.disable(this.props.disable); }; | ||
Datepicker.prototype.render = function render () { | ||
var this$1 = this; | ||
var props = exclude(this.props, Datepicker.defaultProps, { ref: function (el) { return (this$1.el = el); } }); | ||
return React.createElement('div', props, this.props.children) | ||
}; | ||
Object.defineProperties( Datepicker, staticAccessors ); | ||
return Datepicker; | ||
}(React.Component)); | ||
Datepicker.propTypes = { | ||
disable: PropTypes.func, | ||
onRender: PropTypes.func, | ||
onChange: PropTypes.func, | ||
onClickDay: PropTypes.func, | ||
date: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.number, | ||
PropTypes.instanceOf(Date) | ||
]) | ||
}; | ||
module.exports = Datepicker; | ||
module.exports = coreDatepicker; |
@@ -5,3 +5,3 @@ { | ||
"author": "NRK <opensource@nrk.no> (https://www.nrk.no/)", | ||
"version": "2.1.0", | ||
"version": "3.0.0", | ||
"license": "MIT", | ||
@@ -15,6 +15,3 @@ "main": "core-datepicker.cjs.js", | ||
"@nrk/simple-date-parse": "1.0.3" | ||
}, | ||
"dependencies": { | ||
"prop-types": "15.7.2" | ||
} | ||
} |
401
readme.md
# Core Datepicker | ||
> `@nrk/core-datepicker` enhances all child `input`, `select` `table` and `button` elements with keyboard accessible functionality for selecting both dates and times. The interface and granularity of date refinement can easily be altered through markup. | ||
> `@nrk/core-datepicker` enhances all child `input`, `select` `table` and `button` elements with keyboard accessible functionality | ||
> for selecting both dates and times. The interface and granularity of date refinement can easily be altered through markup. | ||
## Installation | ||
```bash | ||
npm install @nrk/core-datepicker --save-exact | ||
``` | ||
```js | ||
import coreDatepicker from '@nrk/core-datepicker' // Vanilla JS | ||
import CoreDatepicker from '@nrk/core-datepicker/jsx' // React/Preact JSX | ||
``` | ||
<!-- <script src="https://unpkg.com/preact"></script> | ||
<script src="https://unpkg.com/preact-compat"></script> | ||
<script> | ||
window.React = preactCompat | ||
window.ReactDOM = preactCompat | ||
</script> --> | ||
<!--demo | ||
<script src="https://unpkg.com/@webcomponents/custom-elements"></script> | ||
<script src="core-toggle/core-toggle.min.js"></script> | ||
@@ -22,6 +19,6 @@ <script src="core-toggle/core-toggle.jsx.js"></script> | ||
<style> | ||
.my-datepicker { position: absolute; z-index: 3; padding: 1rem; background: #fff; box-shadow: 0 5px 9px rgba(0,0,0,.4) } | ||
.my-popup:not([hidden]) { display: block; position: absolute; z-index: 3; padding: 1rem; background: #fff; box-shadow: 0 5px 9px rgba(0,0,0,.4) } | ||
button[aria-current="date"] { border: 1px dashed } | ||
button[data-core-datepicker-adjacent="true"] { opacity: .3 } | ||
button[data-core-datepicker-selected="true"] { border: 2px solid } | ||
button[data-adjacent="true"] { opacity: .3 } | ||
button[autofocus] { border: 2px solid } | ||
:disabled { filter: brightness(.7) sepia(1) hue-rotate(-50deg) } | ||
@@ -31,3 +28,3 @@ </style> | ||
## Demo | ||
## Example | ||
@@ -37,61 +34,65 @@ ```html | ||
<button class="my-toggle">Velg dato</button> | ||
<div class="my-datepicker" id="my-datepicker" hidden> | ||
<input type="timestamp"> | ||
<fieldset> | ||
<legend>Navigasjon</legend> | ||
<button value="now">I dag</button> | ||
<button value="now - 1 day">I går</button> | ||
<button value="now + 1 day">I morgen</button> | ||
<button value="- 1 week">Tilbake en uke</button> | ||
<button value="+ 1 week">Fremover en uke</button> | ||
<button value="now tuesday - 1 week">Tirsdag sist uke</button> | ||
<button value="now + 10 years">Om ti år</button> | ||
<button value="yy00-01-01 - 100 years">Forrige århundre</button> | ||
</fieldset> | ||
<label> | ||
År | ||
<select> | ||
<option value="2016-m-d">2016</option> | ||
<option value="2017-m-d">2017</option> | ||
<option value="2018-m-d">2018</option> | ||
<option value="2019-m-d">2019</option> | ||
</select> | ||
</label> | ||
<label>Måned<select></select></label> | ||
<fieldset> | ||
<legend>Måned</legend> | ||
<label><input type="radio" name="my-months" value="y-1-d">Jan</label> | ||
<label><input type="radio" name="my-months" value="y-2-d">Feb</label> | ||
<label><input type="radio" name="my-months" value="y-3-d">Mars</label> | ||
<label><input type="radio" name="my-months" value="y-4-d">April</label> | ||
<label><input type="radio" name="my-months" value="y-5-d">Mai</label> | ||
<label><input type="radio" name="my-months" value="y-6-d">Juni</label> | ||
<label><input type="radio" name="my-months" value="y-7-d">Juli</label> | ||
<label><input type="radio" name="my-months" value="y-8-d">Aug</label> | ||
<label><input type="radio" name="my-months" value="y-9-d">Sep</label> | ||
<label><input type="radio" name="my-months" value="y-10-d">Okt</label> | ||
<label><input type="radio" name="my-months" value="y-11-d">Nov</label> | ||
<label><input type="radio" name="my-months" value="y-12-d">Des</label> | ||
</fieldset> | ||
<label><span>År</span><input type="year"></label> | ||
<label><span>Måned</span><input type="month"></label> | ||
<fieldset> | ||
<legend>Klokke</legend> | ||
<label>Time<input type="hour"></label> | ||
<label>Minutt<input type="minute"></label> | ||
<core-toggle popup hidden class="my-popup"> | ||
<core-datepicker id="my-datepicker" | ||
days="Mon,Tue,Wed,Thu,Fri,Sat,Sun" | ||
months="January,Febuary,March,April,May,June,July,August,September,October,November,December"> | ||
<input type="timestamp"> | ||
<fieldset> | ||
<legend>Navigasjon</legend> | ||
<button value="now">I dag</button> | ||
<button value="now - 1 day">I går</button> | ||
<button value="now + 1 day">I morgen</button> | ||
<button value="- 1 week">Tilbake en uke</button> | ||
<button value="+ 1 week">Fremover en uke</button> | ||
<button value="now tuesday - 1 week">Tirsdag sist uke</button> | ||
<button value="now + 10 years">Om ti år</button> | ||
<button value="yy00-01-01 - 100 years">Forrige århundre</button> | ||
</fieldset> | ||
<label> | ||
<span>Time</span> | ||
År | ||
<select> | ||
<option>--</option> | ||
<option value="11:m">11</option> | ||
<option value="12:m">12</option> | ||
<option value="13:m">13</option> | ||
<option value="2016-m-d">2016</option> | ||
<option value="2017-m-d">2017</option> | ||
<option value="2018-m-d">2018</option> | ||
<option value="2019-m-d">2019</option> | ||
</select> | ||
</label> | ||
</fieldset> | ||
<table></table> | ||
</div> | ||
<button data-core-datepicker="my-datepicker" value="now">Nå</button> | ||
<button data-core-datepicker="my-datepicker" value="+1 week">Neste uke</button> | ||
<select data-core-datepicker="my-datepicker"> | ||
<label>Måned<select></select></label> | ||
<fieldset> | ||
<legend>Måned</legend> | ||
<label><input type="radio" name="my-months" value="y-1-d">Jan</label> | ||
<label><input type="radio" name="my-months" value="y-2-d">Feb</label> | ||
<label><input type="radio" name="my-months" value="y-3-d">Mars</label> | ||
<label><input type="radio" name="my-months" value="y-4-d">April</label> | ||
<label><input type="radio" name="my-months" value="y-5-d">Mai</label> | ||
<label><input type="radio" name="my-months" value="y-6-d">Juni</label> | ||
<label><input type="radio" name="my-months" value="y-7-d">Juli</label> | ||
<label><input type="radio" name="my-months" value="y-8-d">Aug</label> | ||
<label><input type="radio" name="my-months" value="y-9-d">Sep</label> | ||
<label><input type="radio" name="my-months" value="y-10-d">Okt</label> | ||
<label><input type="radio" name="my-months" value="y-11-d">Nov</label> | ||
<label><input type="radio" name="my-months" value="y-12-d">Des</label> | ||
</fieldset> | ||
<label><span>År</span><input type="year"></label> | ||
<label><span>Måned</span><input type="month"></label> | ||
<fieldset> | ||
<legend>Klokke</legend> | ||
<label>Time<input type="hour"></label> | ||
<label>Minutt<input type="minute"></label> | ||
<label> | ||
<span>Time</span> | ||
<select> | ||
<option>--</option> | ||
<option value="11:m">11</option> | ||
<option value="12:m">12</option> | ||
<option value="13:m">13</option> | ||
</select> | ||
</label> | ||
</fieldset> | ||
<table></table> | ||
</core-datepicker> | ||
</core-toggle> | ||
<button for="my-datepicker" value="now">Nå</button> | ||
<button for="my-datepicker" value="+1 week">Neste uke</button> | ||
<select for="my-datepicker"> | ||
<option>Tid</option> | ||
@@ -102,15 +103,10 @@ <option value="11:m">11</option> | ||
</select> | ||
<table data-core-datepicker="my-datepicker"></table> | ||
<table for="my-datepicker"></table> | ||
<input type="text" id="my-datepicker-output"> | ||
<script> | ||
// Change labels of months | ||
coreDatepicker.months = ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] | ||
// Update GUI | ||
document.getElementById('my-datepicker').disabled = (date) => date > Date.now() | ||
// Change labels of days | ||
//coreDatepicker.days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] | ||
// Update GUI | ||
document.addEventListener('datepicker.render', function (event) { | ||
if (event.target.id !== 'my-datepicker') return | ||
event.detail.disable(function (date) { return date > Date.now() }) | ||
document.addEventListener('datepicker.click.day', function (event) { | ||
console.log(event) | ||
}) | ||
@@ -121,8 +117,5 @@ | ||
if (event.target.id !== 'my-datepicker') return | ||
document.getElementById('my-datepicker-output').value = event.detail.nextDate.toLocaleString() | ||
console.log(event) | ||
document.getElementById('my-datepicker-output').value = event.target.date.toLocaleString() | ||
}) | ||
// Initialize | ||
coreDatepicker('#my-datepicker') | ||
coreToggle('.my-toggle', {popup: true}) // Make popup | ||
</script> | ||
@@ -135,31 +128,28 @@ ``` | ||
<script type="text/jsx"> | ||
// Change labels | ||
CoreDatepicker.months = ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] | ||
CoreDatepicker.days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] | ||
class MyDate extends React.Component { | ||
constructor (props) { | ||
super(props) | ||
this.today = CoreDatepicker.parse('00:00') | ||
this.state = {date: new Date()} | ||
this.today = Date.now() - Date.now() % 864e3 | ||
this.state = { date: new Date() } | ||
this.onNow = this.onNow.bind(this) | ||
this.onChange = this.onChange.bind(this) | ||
} | ||
onNow () { this.setState({date: CoreDatepicker.parse('now')}) } | ||
onChange (event) { this.setState({date: event.detail.nextDate}) } | ||
onNow () { this.setState({ date: new Date() }) } | ||
onChange (event) { this.setState({ date: event.target.date }) } | ||
render () { | ||
return <CoreToggle popup={true}> | ||
return <div> | ||
<button>Velg dato JSX</button> | ||
<CoreDatepicker | ||
date={this.state.date} | ||
disable={(date) => date < this.today} | ||
onChange={this.onChange} | ||
className="my-datepicker"> | ||
<label>År<input type="year" /></label> | ||
<label>Måned<select></select></label> | ||
<table></table> | ||
</CoreDatepicker> | ||
<CoreToggle hidden popup className="my-popup"> | ||
<CoreDatepicker | ||
timestamp={this.state.date.getTime()} | ||
disabled={(date) => date <= this.today} | ||
onDatepickerChange={this.onChange}> | ||
<label>År<input type="year" /></label> | ||
<label>Måned<select></select></label> | ||
<table></table> | ||
</CoreDatepicker> | ||
</CoreToggle> | ||
<button onClick={this.onNow}>I dag JSX</button> | ||
<input type="text" readOnly value={this.state.date.toLocaleDateString()} /> | ||
</CoreToggle> | ||
</div> | ||
} | ||
@@ -172,4 +162,18 @@ } | ||
## Installation | ||
Using NPM provides own element namespace and extensibility. | ||
Recommended: | ||
```bash | ||
npm install @nrk/core-datepicker # Using NPM | ||
``` | ||
Using static registers the custom element with default name automatically: | ||
```html | ||
<script src="https://static.nrk.no/core-components/major/5/core-datepicker/core-datepicker.min.js"></script> <!-- Using static --> | ||
``` | ||
## Usage | ||
@@ -182,56 +186,73 @@ | ||
```html | ||
<div class="my-datepicker"> | ||
<!-- There are different behaviours depending on the type of <input>. --> | ||
<!-- When 'radio' or 'checkbox' is used, core-datepicker checks the value field --> | ||
<!-- to see if the date specified is matching the values of the <input>s. --> | ||
<!-- When any other type is used, core-datepicker sets the type to number --> | ||
<!-- and sets the date specified in the value field. --> | ||
<!-- NOTE: Other input types are not handled by core-datepicker to allow --> | ||
<!-- more customizability with other elements inside core-datepicker container --> | ||
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/> | ||
<core-datepicker | ||
timestamp="{String}" <!-- Optional. Sets date from UNIX timestamp --> | ||
months="{String}" <!-- Optional. Comma separated list of custom month names to be used. ("Jan,Feb,...") --> | ||
days="{String}"> <!-- Optional. Comma separated list of custom weekday names to be used ("Man,Tir,Ons,...") --> | ||
<!-- There are different behaviours depending on the type of <input>. --> | ||
<!-- When 'radio' or 'checkbox' is used, core-datepicker checks the value field --> | ||
<!-- to see if the date specified is matching the values of the <input>s. --> | ||
<!-- When any other type is used, core-datepicker sets the type to number --> | ||
<!-- and sets the date specified in the value field. --> | ||
<!-- NOTE: Other input types are not handled by core-datepicker to allow --> | ||
<!-- more customizability with other elements inside core-datepicker container --> | ||
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/> | ||
<!-- If an empty <select> is provided, core-datepicker will populate the select --> | ||
<!-- with months and automatically handle the date state when an option is chosen --> | ||
<select></select> | ||
<!-- If an empty <select> is provided, core-datepicker will populate the select --> | ||
<!-- with months and automatically handle the date state when an option is chosen --> | ||
<select></select> | ||
<!-- If you use a <select> that is already populated, core-datepicker will not --> | ||
<!-- modify it, but handle the dates specified in values --> | ||
<select> | ||
<option value="2016-m-d">Set year to 2016</option> | ||
<option value="19yy-1-1">Back 100 years and set to January 1st.</option> | ||
<option value="1985-12-19">December 19, 1985</option> | ||
</select> | ||
<!-- If you use a <select> that is already populated, core-datepicker will not --> | ||
<!-- modify it, but handle the dates specified in values --> | ||
<select> | ||
<option value="2016-m-d">Set year to 2016</option> | ||
<option value="19yy-1-1">Back 100 years and set to January 1st.</option> | ||
<option value="1985-12-19">December 19, 1985</option> | ||
</select> | ||
<!-- If an empty <table> is provided, core-datepicker will display all dates --> | ||
<!-- for the current/chosen month --> | ||
<table></table> | ||
<!-- If an empty <table> is provided, core-datepicker will display all dates --> | ||
<!-- for the current/chosen month --> | ||
<table></table> | ||
<!-- It is also possible to extend the datepicker with more features --> | ||
<!-- As shown in the example, it is possible to provide buttons that moves --> | ||
<!-- the date a certain amount of time --> | ||
<fieldset> | ||
<legend>Navigasjon</legend> | ||
<!-- Dates relative to today/now by using the keyword 'now' --> | ||
<button value="now">I dag</button> | ||
<button value="now - 1 day|week|month|year">I går/forrige uke/måned/år</button> | ||
<button value="now + 1 day|week|month|year">I morgen/neste uke/måned/år</button> | ||
<!-- It is also possible to extend the datepicker with more features --> | ||
<!-- As shown in the example, it is possible to provide buttons that moves --> | ||
<!-- the date a certain amount of time --> | ||
<fieldset> | ||
<legend>Navigasjon</legend> | ||
<!-- Dates relative to today/now by using the keyword 'now' --> | ||
<button value="now">I dag</button> | ||
<button value="now - 1 day|week|month|year">I går/forrige uke/måned/år</button> | ||
<button value="now + 1 day|week|month|year">I morgen/neste uke/måned/år</button> | ||
<!-- Semi-specific dates --> | ||
<!-- Will use the first two digits of the current year and set the two last --> | ||
<!-- digits of the year 0. Will set the date to 1st of January --> | ||
<button value="yy00-01-01">Start of current century</button> | ||
</fieldset> | ||
</div> | ||
<!-- Semi-specific dates --> | ||
<!-- Will use the first two digits of the current year and set the two last --> | ||
<!-- digits of the year 0. Will set the date to 1st of January --> | ||
<button value="yy00-01-01">Start of current century</button> | ||
</fieldset> | ||
</core-datepicker> | ||
``` | ||
```js | ||
import coreDatepicker from '@nrk/core-datepicker' | ||
import CoreDatepicker from '@nrk/core-datepicker' // Using NPM | ||
window.customElements.define('core-datepicker', CoreProgress) // Using NPM. Replace 'core-datepicker' with 'my-datepicker' to namespace | ||
coreDatepicker( | ||
String|Element|Elements, // Accepts a selector string, NodeList, Element or array of Elements | ||
String|Date // Specify the date which coreDatepicker should use. | ||
// e.g: | ||
'now + 2 days' // Will set the date to the day after tomorrow | ||
}) | ||
const myDatepicker = document.querySelector('core-datepicker') | ||
// Getters | ||
myDatepicker.date // Get date object | ||
myDatepicker.timestamp // Get timestamp | ||
myDatepicker.year // Get year | ||
myDatepicker.month // Get month | ||
myDatepicker.day // Get day | ||
myDatepicker.hour // Get hour | ||
myDatepicker.minute // Get minute | ||
myDatepicker.second // Get second | ||
// Setters | ||
myDatepicker.date = 'now' // Set date. Accepts simple-date-parse format or Date object | ||
myDatepicker.months = ['Jan', 'Feb', ...] // Set list of custom month names to be used | ||
myDatepicker.days = ['Man', 'Tir', ...] // Set list of custom weekday names to be used | ||
myDatepicker.disabled = Function|Boolean // Disable dates. If true disable all dates. Function receives each date, returns a boolean. | ||
// Methods | ||
myDatepicker.parse('fri') // Utility function for parsing time and dates. Really just @nrk/simple-date-parse | ||
``` | ||
@@ -244,7 +265,11 @@ | ||
<CoreDatepicker date={String|Date} onChange={function () {}} > | ||
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/> /* Same as with vanilla js */ | ||
<select></select> /* Same as with vanilla js */ | ||
<table></table> /* Same as with vanilla js */ | ||
<CoreDatepicker> | ||
<CoreDatepicker timestamp={String} // Optional. Sets date from timestamp | ||
months={String} // Optional. Comma separated list of custom month names to be used ("Jan,Feb,...") | ||
days={String}> // Optional. Comma separated list of custom weekday names to be used ("Man,Tir,Ons,...") | ||
onDatepickerChange={Function}> // Optional. See 'datepicker.change' | ||
onDatepickerClickDay={Function}> // Optional. See 'datepicker.click.day' | ||
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/> // Same as with vanilla js | ||
<select></select> // Same as with vanilla js | ||
<table></table> // Same as with vanilla js | ||
</CoreDatepicker> | ||
``` | ||
@@ -255,28 +280,11 @@ | ||
## Events | ||
Events run in the order `datepicker.click.day`\* → `datepicker.render` → `datepicker.change` | ||
<br><small>\* datepicker.click.day only fires if user clicked inside the month days grid</small> | ||
### datepicker.render | ||
`'datepicker.render'` event is fired on every render. The `datepicker.render` can be used to disable specific dates or limit timespan (read: max/min). The bubbles and can therefore be detected both from button element itself, or any parent element (read event delegation): | ||
```js | ||
document.addEventListener('datepicker.render', (event) => { | ||
event.target // The datepicker container | ||
event.detail.nextDate // The new date that triggered change | ||
event.detail.prevDate // The previous/current date | ||
event.detail.disable(function) // Pass a fuction to the disable parameter to visually dates | ||
// Example, disable all future dates: event.detail.disable((date) => date > Date.now()) | ||
}) | ||
``` | ||
### datepicker.change | ||
`'datepicker.change'` event is fired when date is changed by user or programatically (both for VanillaJS and React/Preact components). The `datepicker.change` event is cancelable, meaning you can use `event.preventDefault()` to cancel change. The event also bubbles, and can therefore be detected both from button element itself, or any parent element (read event delegation): | ||
Fired when date is changed by user or programatically: | ||
```js | ||
document.addEventListener('datepicker.change', (event) => { | ||
event.target // The datepicker container | ||
event.detail.nextDate // The new date that triggered change | ||
event.detail.prevDate // The previous/current date | ||
event.target // The datepicker | ||
event.detail // The new date that triggered change | ||
}) | ||
@@ -287,24 +295,10 @@ ``` | ||
`'datepicker.click.day'` event is fired if the user clicks a day in the month days grid. The `datepicker.click.day` runs before `datepicker.change`. The event is cancelable, meaning you can use `event.preventDefault()`. The event also bubbles, and can therefore be detected both from button element itself, or any parent element (read event delegation): | ||
Fired if the user clicks a day in the month days grid. The `datepicker.click.day` runs before `datepicker.change`: | ||
```js | ||
document.addEventListener('datepicker.click.day', (event) => { | ||
event.target // The datepicker container | ||
event.detail.currentTarget // The button clicked | ||
event.detail.relatedTarget // The table containing the button | ||
event.detail.nextDate // The new date that triggered change | ||
event.detail.prevDate // The previous/current date | ||
event.target // The datepicker | ||
}) | ||
``` | ||
## Methods | ||
### datepicker.parse | ||
A utility function for parsing time and dates. It's really just [`@nrk/simple-date-parse`](https://github.com/nrkno/simple-date-parse): | ||
```js | ||
coreDatepicker.parse('fri') | ||
``` | ||
## Properties | ||
@@ -315,11 +309,9 @@ | ||
```js | ||
//JS | ||
coreDatepicker.days = ['man', 'tir', 'ons', 'tor', 'fre', 'lør', 'søn'] // Change name of days | ||
coreDatepicker.months = ['jan', 'feb', ...] // Change name of months | ||
//JSX | ||
CoreDatepicker.days = ['man', 'tir', 'ons', 'tor', 'fre', 'lør', 'søn'] // Change name of days | ||
CoreDatepicker.months = ['jan', 'feb', ...] // Change name of months | ||
myDatepicker.days = ['man', 'tir', 'ons', 'tor', 'fre', 'lør', 'søn'] // Change name of days | ||
myDatepicker.months = ['jan', 'feb', ...] // Change name of months | ||
myDatepicker.disabled = (date) => date > Date.now() // Disable future dates | ||
myDatepicker.disabled = false // Enable all dates | ||
``` | ||
## Styling | ||
@@ -330,9 +322,10 @@ | ||
```css | ||
.my-datepicker /* Target datepicker container */ | ||
.my-datepicker input:checked /* Target selected checkbox/radio dates */ | ||
.my-datepicker input:disabled /* Target disabled checkbox/radio dates */ | ||
.my-datepicker button:disabled /* Target disabled dates */ | ||
.my-datepicker button[aria-current="date"] /* Target current date (today) in month view */ | ||
.my-datepicker button[data-core-datepicker-adjacent="true"] /* Target dates from next or previous month in the month view */ | ||
.my-datepicker button[data-core-datepicker-selected="true"] /* Target the chosen date in month view */ | ||
.my-datepicker /* Target datepicker container */ | ||
.my-datepicker input:checked /* Target selected checkbox/radio dates */ | ||
.my-datepicker input:disabled /* Target disabled checkbox/radio dates */ | ||
.my-datepicker button:disabled /* Target disabled dates */ | ||
.my-datepicker button[autofocus] /* Target the chosen date in month view */ | ||
.my-datepicker button[aria-current="date"] /* Target current date (today) in month view */ | ||
.my-datepicker button[data-adjacent="false"] /* Target date in current month in the month view */ | ||
.my-datepicker button[data-adjacent="true"] /* Target date in next or previous month in the month view */ | ||
``` |
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
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
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
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
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
167012
0
12
2037
0
319
2
- Removedprop-types@15.7.2
- Removedjs-tokens@4.0.0(transitive)
- Removedloose-envify@1.4.0(transitive)
- Removedobject-assign@4.1.1(transitive)
- Removedprop-types@15.7.2(transitive)
- Removedreact-is@16.13.1(transitive)