├── .babelrc.js ├── .circleci └── config.yml ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .vscode └── settings.json ├── LICENSE ├── dist ├── index.js ├── index.js.map ├── index.m.js ├── index.m.js.map ├── index.umd.js └── index.umd.js.map ├── images ├── datetime-days-of-week.png ├── datetime-picker-black-fr.png ├── datetime-picker-formats.png └── datetime-picker.png ├── karma.conf.js ├── package.json ├── playground ├── public │ ├── index.html │ └── style.css ├── src │ ├── controllers │ │ └── flatpickr_controller.js │ └── index.js └── webpack.config.js ├── readme.md ├── rollup.config.js ├── spec ├── controllers │ ├── flatpickr_controller.js │ └── flatpickr_hook_controller.js ├── fixtures │ ├── index-custom.html │ └── index.html ├── flatpickr_controller_events_spec.js ├── flatpickr_controller_spec.js ├── flatpickr_controller_targets_spec.js ├── flatpickr_custom_element_spec.js ├── helpers.js ├── mapping_spec.js └── utils_spec.js ├── src ├── config_options.js ├── elements.js ├── events.js ├── index.js ├── strftime_mapping.js └── utils.js └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | test: { 4 | plugins: [ 5 | [ 6 | 'istanbul', 7 | { 8 | exclude: ['spec/**/*.js'] 9 | } 10 | ] 11 | ] 12 | } 13 | }, 14 | presets: [ 15 | [ 16 | '@babel/preset-env', 17 | { 18 | targets: { 19 | node: 'current' 20 | } 21 | } 22 | ] 23 | ], 24 | plugins: [ 25 | '@babel/plugin-proposal-class-properties', 26 | '@babel/plugin-transform-classes', 27 | '@babel/plugin-proposal-object-rest-spread' 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:latest-browsers 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | # working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | - run: yarn codecov 39 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | mocha: true 6 | }, 7 | plugins: ['mocha'], 8 | extends: ['plugin:mocha/recommended'], 9 | globals: { 10 | Atomics: 'readonly', 11 | SharedArrayBuffer: 'readonly' 12 | }, 13 | parser: 'babel-eslint', 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | sourceType: 'module' 17 | }, 18 | rules: { 19 | 'space-before-function-paren': 'off', 20 | 'no-unused-expressions': 'off', 21 | 'mocha/no-setup-in-describe': 'off', 22 | 'mocha/no-top-level-hooks': 'off', 23 | 'mocha/no-hooks-for-single-case': 'off' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | yarn-error.log 3 | .DS_Store 4 | notes.md 5 | /coverage 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .DS_Store 3 | .gitignore 4 | .yarn.lock 5 | /.git 6 | /node_modules 7 | /playground 8 | /coverage 9 | /.circleci 10 | /images 11 | /spec 12 | karma.conf.js 13 | rollup.config.js 14 | .babelrc.js 15 | .eslintrc.js 16 | .prettierrc.json 17 | /.vscode -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "printWidth": 120, 5 | "semi": false, 6 | "singleQuote": true, 7 | "tabWidth": 2, 8 | "trailingComma": "none", 9 | "useTabs": false, 10 | "overrides": [ 11 | { 12 | "files": "*.json", 13 | "options": { 14 | "printWidth": 200 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Adrien POLY 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 4 | 5 | var stimulus = require('stimulus'); 6 | var flatpickr = _interopDefault(require('flatpickr')); 7 | 8 | function _classCallCheck(instance, Constructor) { 9 | if (!(instance instanceof Constructor)) { 10 | throw new TypeError("Cannot call a class as a function"); 11 | } 12 | } 13 | 14 | function _defineProperties(target, props) { 15 | for (var i = 0; i < props.length; i++) { 16 | var descriptor = props[i]; 17 | descriptor.enumerable = descriptor.enumerable || false; 18 | descriptor.configurable = true; 19 | if ("value" in descriptor) descriptor.writable = true; 20 | Object.defineProperty(target, descriptor.key, descriptor); 21 | } 22 | } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { 25 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 26 | if (staticProps) _defineProperties(Constructor, staticProps); 27 | return Constructor; 28 | } 29 | 30 | function _defineProperty(obj, key, value) { 31 | if (key in obj) { 32 | Object.defineProperty(obj, key, { 33 | value: value, 34 | enumerable: true, 35 | configurable: true, 36 | writable: true 37 | }); 38 | } else { 39 | obj[key] = value; 40 | } 41 | 42 | return obj; 43 | } 44 | 45 | function ownKeys(object, enumerableOnly) { 46 | var keys = Object.keys(object); 47 | 48 | if (Object.getOwnPropertySymbols) { 49 | var symbols = Object.getOwnPropertySymbols(object); 50 | if (enumerableOnly) symbols = symbols.filter(function (sym) { 51 | return Object.getOwnPropertyDescriptor(object, sym).enumerable; 52 | }); 53 | keys.push.apply(keys, symbols); 54 | } 55 | 56 | return keys; 57 | } 58 | 59 | function _objectSpread2(target) { 60 | for (var i = 1; i < arguments.length; i++) { 61 | var source = arguments[i] != null ? arguments[i] : {}; 62 | 63 | if (i % 2) { 64 | ownKeys(Object(source), true).forEach(function (key) { 65 | _defineProperty(target, key, source[key]); 66 | }); 67 | } else if (Object.getOwnPropertyDescriptors) { 68 | Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); 69 | } else { 70 | ownKeys(Object(source)).forEach(function (key) { 71 | Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); 72 | }); 73 | } 74 | } 75 | 76 | return target; 77 | } 78 | 79 | function _inherits(subClass, superClass) { 80 | if (typeof superClass !== "function" && superClass !== null) { 81 | throw new TypeError("Super expression must either be null or a function"); 82 | } 83 | 84 | subClass.prototype = Object.create(superClass && superClass.prototype, { 85 | constructor: { 86 | value: subClass, 87 | writable: true, 88 | configurable: true 89 | } 90 | }); 91 | if (superClass) _setPrototypeOf(subClass, superClass); 92 | } 93 | 94 | function _getPrototypeOf(o) { 95 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { 96 | return o.__proto__ || Object.getPrototypeOf(o); 97 | }; 98 | return _getPrototypeOf(o); 99 | } 100 | 101 | function _setPrototypeOf(o, p) { 102 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 103 | o.__proto__ = p; 104 | return o; 105 | }; 106 | 107 | return _setPrototypeOf(o, p); 108 | } 109 | 110 | function _isNativeReflectConstruct() { 111 | if (typeof Reflect === "undefined" || !Reflect.construct) return false; 112 | if (Reflect.construct.sham) return false; 113 | if (typeof Proxy === "function") return true; 114 | 115 | try { 116 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); 117 | return true; 118 | } catch (e) { 119 | return false; 120 | } 121 | } 122 | 123 | function _assertThisInitialized(self) { 124 | if (self === void 0) { 125 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 126 | } 127 | 128 | return self; 129 | } 130 | 131 | function _possibleConstructorReturn(self, call) { 132 | if (call && (typeof call === "object" || typeof call === "function")) { 133 | return call; 134 | } 135 | 136 | return _assertThisInitialized(self); 137 | } 138 | 139 | function _createSuper(Derived) { 140 | return function () { 141 | var Super = _getPrototypeOf(Derived), 142 | result; 143 | 144 | if (_isNativeReflectConstruct()) { 145 | var NewTarget = _getPrototypeOf(this).constructor; 146 | 147 | result = Reflect.construct(Super, arguments, NewTarget); 148 | } else { 149 | result = Super.apply(this, arguments); 150 | } 151 | 152 | return _possibleConstructorReturn(this, result); 153 | }; 154 | } 155 | 156 | const kebabCase = string => string.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase(); 157 | const capitalize = string => { 158 | return string.charAt(0).toUpperCase() + string.slice(1); 159 | }; 160 | 161 | const booleanOptions = ['allowInput', 'altInput', 'animate', 'clickOpens', 'closeOnSelect', 'disableMobile', 'enableSeconds', 'enableTime', 'inline', 'noCalendar', 'shorthandCurrentMonth', 'static', 'time_24hr', 'weekNumbers', 'wrap']; 162 | const stringOptions = ['altInputClass', 'conjunction', 'mode', 'nextArrow', 'position', 'prevArrow', 'monthSelectorType']; 163 | const numberOptions = ['defaultHour', 'defaultMinute', 'defaultSeconds', 'hourIncrement', 'minuteIncrement', 'showMonths']; 164 | const arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']; 165 | const arrayOrStringOptions = ['defaultDate']; 166 | const dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']; 167 | const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']; 168 | const options = { 169 | string: stringOptions, 170 | boolean: booleanOptions, 171 | date: dateOptions, 172 | array: arrayOptions, 173 | number: numberOptions, 174 | arrayOrString: arrayOrStringOptions 175 | }; 176 | 177 | const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']; 178 | 179 | const elements = ['calendarContainer', 'currentYearElement', 'days', 'daysContainer', 'input', 'nextMonthNav', 'monthNav', 'prevMonthNav', 'rContainer', 'selectedDateElem', 'todayDateElem', 'weekdayContainer']; 180 | 181 | const mapping = { 182 | '%Y': 'Y', 183 | '%y': 'y', 184 | '%C': 'Y', 185 | '%m': 'm', 186 | '%-m': 'n', 187 | '%_m': 'n', 188 | '%B': 'F', 189 | '%^B': 'F', 190 | '%b': 'M', 191 | '%^b': 'M', 192 | '%h': 'M', 193 | '%^h': 'M', 194 | '%d': 'd', 195 | '%-d': 'j', 196 | '%e': 'j', 197 | '%H': 'H', 198 | '%k': 'H', 199 | '%I': 'h', 200 | '%l': 'h', 201 | '%-l': 'h', 202 | '%P': 'K', 203 | '%p': 'K', 204 | '%M': 'i', 205 | '%S': 'S', 206 | '%A': 'l', 207 | '%a': 'D', 208 | '%w': 'w' 209 | }; 210 | const strftimeRegex = new RegExp(Object.keys(mapping).join('|').replace(new RegExp('\\^', 'g'), '\\^'), 'g'); 211 | const convertDateFormat = format => { 212 | return format.replace(strftimeRegex, match => { 213 | return mapping[match]; 214 | }); 215 | }; 216 | 217 | let StimulusFlatpickr = /*#__PURE__*/function (_Controller) { 218 | _inherits(StimulusFlatpickr, _Controller); 219 | 220 | var _super = _createSuper(StimulusFlatpickr); 221 | 222 | function StimulusFlatpickr() { 223 | _classCallCheck(this, StimulusFlatpickr); 224 | 225 | return _super.apply(this, arguments); 226 | } 227 | 228 | _createClass(StimulusFlatpickr, [{ 229 | key: "initialize", 230 | value: function initialize() { 231 | this.config = {}; 232 | } 233 | }, { 234 | key: "connect", 235 | value: function connect() { 236 | this._initializeEvents(); 237 | 238 | this._initializeOptions(); 239 | 240 | this._initializeDateFormats(); 241 | 242 | this.fp = flatpickr(this.flatpickrElement, _objectSpread2({}, this.config)); 243 | 244 | this._initializeElements(); 245 | } 246 | }, { 247 | key: "disconnect", 248 | value: function disconnect() { 249 | const value = this.inputTarget.value; 250 | this.fp.destroy(); 251 | this.inputTarget.value = value; 252 | } 253 | }, { 254 | key: "_initializeEvents", 255 | value: function _initializeEvents() { 256 | events.forEach(event => { 257 | if (this[event]) { 258 | const hook = `on${capitalize(event)}`; 259 | this.config[hook] = this[event].bind(this); 260 | } 261 | }); 262 | } 263 | }, { 264 | key: "_initializeOptions", 265 | value: function _initializeOptions() { 266 | Object.keys(options).forEach(optionType => { 267 | const optionsCamelCase = options[optionType]; 268 | optionsCamelCase.forEach(option => { 269 | const optionKebab = kebabCase(option); 270 | 271 | if (this.data.has(optionKebab)) { 272 | this.config[option] = this[`_${optionType}`](optionKebab); 273 | } 274 | }); 275 | }); 276 | 277 | this._handleDaysOfWeek(); 278 | } 279 | }, { 280 | key: "_handleDaysOfWeek", 281 | value: function _handleDaysOfWeek() { 282 | if (this.config.disableDaysOfWeek) { 283 | this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek); 284 | this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]; 285 | } 286 | 287 | if (this.config.enableDaysOfWeek) { 288 | this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek); 289 | this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]; 290 | } 291 | } 292 | }, { 293 | key: "_validateDaysOfWeek", 294 | value: function _validateDaysOfWeek(days) { 295 | if (Array.isArray(days)) { 296 | return days.map(day => parseInt(day)); 297 | } else { 298 | console.error('days of week must be a valid array'); 299 | return []; 300 | } 301 | } 302 | }, { 303 | key: "_disable", 304 | value: function _disable(date) { 305 | const disabledDays = this.config.disableDaysOfWeek; 306 | return disabledDays.includes(date.getDay()); 307 | } 308 | }, { 309 | key: "_enable", 310 | value: function _enable(date) { 311 | const enabledDays = this.config.enableDaysOfWeek; 312 | return enabledDays.includes(date.getDay()); 313 | } 314 | }, { 315 | key: "_initializeDateFormats", 316 | value: function _initializeDateFormats() { 317 | dateFormats.forEach(dateFormat => { 318 | if (this.data.has(dateFormat)) { 319 | this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat)); 320 | } 321 | }); 322 | } 323 | }, { 324 | key: "_initializeElements", 325 | value: function _initializeElements() { 326 | elements.forEach(element => { 327 | this[`${element}Target`] = this.fp[element]; 328 | }); 329 | } 330 | }, { 331 | key: "_string", 332 | value: function _string(option) { 333 | return this.data.get(option); 334 | } 335 | }, { 336 | key: "_date", 337 | value: function _date(option) { 338 | return this.data.get(option); 339 | } 340 | }, { 341 | key: "_boolean", 342 | value: function _boolean(option) { 343 | return !(this.data.get(option) == '0' || this.data.get(option) == 'false'); 344 | } 345 | }, { 346 | key: "_array", 347 | value: function _array(option) { 348 | return JSON.parse(this.data.get(option)); 349 | } 350 | }, { 351 | key: "_number", 352 | value: function _number(option) { 353 | return parseInt(this.data.get(option)); 354 | } 355 | }, { 356 | key: "_arrayOrString", 357 | value: function _arrayOrString(option) { 358 | const val = this.data.get(option); 359 | 360 | try { 361 | return JSON.parse(val); 362 | } catch (e) { 363 | return val; 364 | } 365 | } 366 | }, { 367 | key: "flatpickrElement", 368 | get: function () { 369 | return this.hasInstanceTarget && this.instanceTarget || this.element; 370 | } 371 | }]); 372 | 373 | return StimulusFlatpickr; 374 | }(stimulus.Controller); 375 | 376 | _defineProperty(StimulusFlatpickr, "targets", ['instance']); 377 | 378 | module.exports = StimulusFlatpickr; 379 | //# sourceMappingURL=index.js.map 380 | -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":["../src/utils.js","../src/config_options.js","../src/events.js","../src/elements.js","../src/strftime_mapping.js","../src/index.js"],"sourcesContent":["export const kebabCase = string =>\n string\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n\nexport const capitalize = string => {\n return string.charAt(0).toUpperCase() + string.slice(1);\n};\n","const booleanOptions = [\n 'allowInput',\n 'altInput',\n 'animate',\n 'clickOpens',\n 'closeOnSelect',\n 'disableMobile',\n 'enableSeconds',\n 'enableTime',\n 'inline',\n 'noCalendar',\n 'shorthandCurrentMonth',\n 'static',\n 'time_24hr',\n 'weekNumbers',\n 'wrap'\n]\n\nconst stringOptions = [\n 'altInputClass',\n 'conjunction',\n 'mode',\n 'nextArrow',\n 'position',\n 'prevArrow',\n 'monthSelectorType'\n]\n\nconst numberOptions = [\n 'defaultHour',\n 'defaultMinute',\n 'defaultSeconds',\n 'hourIncrement',\n 'minuteIncrement',\n 'showMonths'\n]\n\nconst arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']\n\nconst arrayOrStringOptions = ['defaultDate']\n\nconst dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']\n\nexport const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']\n\nexport const options = {\n string: stringOptions,\n boolean: booleanOptions,\n date: dateOptions,\n array: arrayOptions,\n number: numberOptions,\n arrayOrString: arrayOrStringOptions\n}\n","export const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']\n","export const elements = [\n 'calendarContainer',\n 'currentYearElement',\n 'days',\n 'daysContainer',\n 'input',\n 'nextMonthNav',\n 'monthNav',\n 'prevMonthNav',\n 'rContainer',\n 'selectedDateElem',\n 'todayDateElem',\n 'weekdayContainer'\n]\n","export const mapping = {\n '%Y': 'Y',\n '%y': 'y',\n '%C': 'Y',\n '%m': 'm',\n '%-m': 'n',\n '%_m': 'n',\n '%B': 'F',\n '%^B': 'F',\n '%b': 'M',\n '%^b': 'M',\n '%h': 'M',\n '%^h': 'M',\n '%d': 'd',\n '%-d': 'j',\n '%e': 'j',\n '%H': 'H',\n '%k': 'H',\n '%I': 'h',\n '%l': 'h',\n '%-l': 'h',\n '%P': 'K',\n '%p': 'K',\n '%M': 'i',\n '%S': 'S',\n '%A': 'l',\n '%a': 'D',\n '%w': 'w'\n}\n\nconst strftimeRegex = new RegExp(\n Object.keys(mapping)\n .join('|')\n .replace(new RegExp('\\\\^', 'g'), '\\\\^'),\n 'g'\n)\n\nexport const convertDateFormat = (format) => {\n return format.replace(strftimeRegex, (match) => {\n return mapping[match]\n })\n}\n","import { Controller } from 'stimulus'\nimport flatpickr from 'flatpickr'\nimport { kebabCase, capitalize } from './utils'\nimport { options, dateFormats } from './config_options'\nimport { events } from './events'\nimport { elements } from './elements'\nimport { convertDateFormat } from './strftime_mapping'\n\nclass StimulusFlatpickr extends Controller {\n static targets = ['instance']\n\n initialize() {\n this.config = {}\n }\n\n connect() {\n this._initializeEvents()\n this._initializeOptions()\n this._initializeDateFormats()\n\n this.fp = flatpickr(this.flatpickrElement, {\n ...this.config\n })\n\n this._initializeElements()\n }\n\n disconnect() {\n const value = this.inputTarget.value\n this.fp.destroy()\n this.inputTarget.value = value\n }\n\n _initializeEvents() {\n events.forEach((event) => {\n if (this[event]) {\n const hook = `on${capitalize(event)}`\n this.config[hook] = this[event].bind(this)\n }\n })\n }\n\n _initializeOptions() {\n Object.keys(options).forEach((optionType) => {\n const optionsCamelCase = options[optionType]\n optionsCamelCase.forEach((option) => {\n const optionKebab = kebabCase(option)\n if (this.data.has(optionKebab)) {\n this.config[option] = this[`_${optionType}`](optionKebab)\n }\n })\n })\n this._handleDaysOfWeek()\n }\n\n _handleDaysOfWeek() {\n if (this.config.disableDaysOfWeek) {\n this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek)\n this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]\n }\n\n if (this.config.enableDaysOfWeek) {\n this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek)\n this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]\n }\n }\n\n _validateDaysOfWeek(days) {\n if (Array.isArray(days)) {\n return days.map((day) => parseInt(day))\n } else {\n console.error('days of week must be a valid array')\n return []\n }\n }\n\n _disable(date) {\n const disabledDays = this.config.disableDaysOfWeek\n return disabledDays.includes(date.getDay())\n }\n\n _enable(date) {\n const enabledDays = this.config.enableDaysOfWeek\n return enabledDays.includes(date.getDay())\n }\n\n _initializeDateFormats() {\n dateFormats.forEach((dateFormat) => {\n if (this.data.has(dateFormat)) {\n this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat))\n }\n })\n }\n\n _initializeElements() {\n elements.forEach((element) => {\n this[`${element}Target`] = this.fp[element]\n })\n }\n\n _string(option) {\n return this.data.get(option)\n }\n\n _date(option) {\n return this.data.get(option)\n }\n\n _boolean(option) {\n return !(this.data.get(option) == '0' || this.data.get(option) == 'false')\n }\n\n _array(option) {\n return JSON.parse(this.data.get(option))\n }\n\n _number(option) {\n return parseInt(this.data.get(option))\n }\n\n _arrayOrString(option) {\n const val = this.data.get(option)\n try {\n return JSON.parse(val)\n } catch (e) {\n return val\n }\n }\n\n get flatpickrElement() {\n return (this.hasInstanceTarget && this.instanceTarget) || this.element\n }\n}\n\nexport default StimulusFlatpickr\n"],"names":["kebabCase","string","replace","toLowerCase","capitalize","charAt","toUpperCase","slice","booleanOptions","stringOptions","numberOptions","arrayOptions","arrayOrStringOptions","dateOptions","dateFormats","options","boolean","date","array","number","arrayOrString","events","elements","mapping","strftimeRegex","RegExp","Object","keys","join","convertDateFormat","format","match","StimulusFlatpickr","config","_initializeEvents","_initializeOptions","_initializeDateFormats","fp","flatpickr","flatpickrElement","_initializeElements","value","inputTarget","destroy","forEach","event","hook","bind","optionType","optionsCamelCase","option","optionKebab","data","has","_handleDaysOfWeek","disableDaysOfWeek","_validateDaysOfWeek","disable","_disable","enableDaysOfWeek","enable","_enable","days","Array","isArray","map","day","parseInt","console","error","disabledDays","includes","getDay","enabledDays","dateFormat","get","element","JSON","parse","val","e","hasInstanceTarget","instanceTarget","Controller"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/index.m.js: -------------------------------------------------------------------------------- 1 | import { Controller } from 'stimulus'; 2 | import flatpickr from 'flatpickr'; 3 | 4 | function _classCallCheck(instance, Constructor) { 5 | if (!(instance instanceof Constructor)) { 6 | throw new TypeError("Cannot call a class as a function"); 7 | } 8 | } 9 | 10 | function _defineProperties(target, props) { 11 | for (var i = 0; i < props.length; i++) { 12 | var descriptor = props[i]; 13 | descriptor.enumerable = descriptor.enumerable || false; 14 | descriptor.configurable = true; 15 | if ("value" in descriptor) descriptor.writable = true; 16 | Object.defineProperty(target, descriptor.key, descriptor); 17 | } 18 | } 19 | 20 | function _createClass(Constructor, protoProps, staticProps) { 21 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 22 | if (staticProps) _defineProperties(Constructor, staticProps); 23 | return Constructor; 24 | } 25 | 26 | function _defineProperty(obj, key, value) { 27 | if (key in obj) { 28 | Object.defineProperty(obj, key, { 29 | value: value, 30 | enumerable: true, 31 | configurable: true, 32 | writable: true 33 | }); 34 | } else { 35 | obj[key] = value; 36 | } 37 | 38 | return obj; 39 | } 40 | 41 | function ownKeys(object, enumerableOnly) { 42 | var keys = Object.keys(object); 43 | 44 | if (Object.getOwnPropertySymbols) { 45 | var symbols = Object.getOwnPropertySymbols(object); 46 | if (enumerableOnly) symbols = symbols.filter(function (sym) { 47 | return Object.getOwnPropertyDescriptor(object, sym).enumerable; 48 | }); 49 | keys.push.apply(keys, symbols); 50 | } 51 | 52 | return keys; 53 | } 54 | 55 | function _objectSpread2(target) { 56 | for (var i = 1; i < arguments.length; i++) { 57 | var source = arguments[i] != null ? arguments[i] : {}; 58 | 59 | if (i % 2) { 60 | ownKeys(Object(source), true).forEach(function (key) { 61 | _defineProperty(target, key, source[key]); 62 | }); 63 | } else if (Object.getOwnPropertyDescriptors) { 64 | Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); 65 | } else { 66 | ownKeys(Object(source)).forEach(function (key) { 67 | Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); 68 | }); 69 | } 70 | } 71 | 72 | return target; 73 | } 74 | 75 | function _inherits(subClass, superClass) { 76 | if (typeof superClass !== "function" && superClass !== null) { 77 | throw new TypeError("Super expression must either be null or a function"); 78 | } 79 | 80 | subClass.prototype = Object.create(superClass && superClass.prototype, { 81 | constructor: { 82 | value: subClass, 83 | writable: true, 84 | configurable: true 85 | } 86 | }); 87 | if (superClass) _setPrototypeOf(subClass, superClass); 88 | } 89 | 90 | function _getPrototypeOf(o) { 91 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { 92 | return o.__proto__ || Object.getPrototypeOf(o); 93 | }; 94 | return _getPrototypeOf(o); 95 | } 96 | 97 | function _setPrototypeOf(o, p) { 98 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 99 | o.__proto__ = p; 100 | return o; 101 | }; 102 | 103 | return _setPrototypeOf(o, p); 104 | } 105 | 106 | function _isNativeReflectConstruct() { 107 | if (typeof Reflect === "undefined" || !Reflect.construct) return false; 108 | if (Reflect.construct.sham) return false; 109 | if (typeof Proxy === "function") return true; 110 | 111 | try { 112 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); 113 | return true; 114 | } catch (e) { 115 | return false; 116 | } 117 | } 118 | 119 | function _assertThisInitialized(self) { 120 | if (self === void 0) { 121 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 122 | } 123 | 124 | return self; 125 | } 126 | 127 | function _possibleConstructorReturn(self, call) { 128 | if (call && (typeof call === "object" || typeof call === "function")) { 129 | return call; 130 | } 131 | 132 | return _assertThisInitialized(self); 133 | } 134 | 135 | function _createSuper(Derived) { 136 | return function () { 137 | var Super = _getPrototypeOf(Derived), 138 | result; 139 | 140 | if (_isNativeReflectConstruct()) { 141 | var NewTarget = _getPrototypeOf(this).constructor; 142 | 143 | result = Reflect.construct(Super, arguments, NewTarget); 144 | } else { 145 | result = Super.apply(this, arguments); 146 | } 147 | 148 | return _possibleConstructorReturn(this, result); 149 | }; 150 | } 151 | 152 | const kebabCase = string => string.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase(); 153 | const capitalize = string => { 154 | return string.charAt(0).toUpperCase() + string.slice(1); 155 | }; 156 | 157 | const booleanOptions = ['allowInput', 'altInput', 'animate', 'clickOpens', 'closeOnSelect', 'disableMobile', 'enableSeconds', 'enableTime', 'inline', 'noCalendar', 'shorthandCurrentMonth', 'static', 'time_24hr', 'weekNumbers', 'wrap']; 158 | const stringOptions = ['altInputClass', 'conjunction', 'mode', 'nextArrow', 'position', 'prevArrow', 'monthSelectorType']; 159 | const numberOptions = ['defaultHour', 'defaultMinute', 'defaultSeconds', 'hourIncrement', 'minuteIncrement', 'showMonths']; 160 | const arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']; 161 | const arrayOrStringOptions = ['defaultDate']; 162 | const dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']; 163 | const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']; 164 | const options = { 165 | string: stringOptions, 166 | boolean: booleanOptions, 167 | date: dateOptions, 168 | array: arrayOptions, 169 | number: numberOptions, 170 | arrayOrString: arrayOrStringOptions 171 | }; 172 | 173 | const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']; 174 | 175 | const elements = ['calendarContainer', 'currentYearElement', 'days', 'daysContainer', 'input', 'nextMonthNav', 'monthNav', 'prevMonthNav', 'rContainer', 'selectedDateElem', 'todayDateElem', 'weekdayContainer']; 176 | 177 | const mapping = { 178 | '%Y': 'Y', 179 | '%y': 'y', 180 | '%C': 'Y', 181 | '%m': 'm', 182 | '%-m': 'n', 183 | '%_m': 'n', 184 | '%B': 'F', 185 | '%^B': 'F', 186 | '%b': 'M', 187 | '%^b': 'M', 188 | '%h': 'M', 189 | '%^h': 'M', 190 | '%d': 'd', 191 | '%-d': 'j', 192 | '%e': 'j', 193 | '%H': 'H', 194 | '%k': 'H', 195 | '%I': 'h', 196 | '%l': 'h', 197 | '%-l': 'h', 198 | '%P': 'K', 199 | '%p': 'K', 200 | '%M': 'i', 201 | '%S': 'S', 202 | '%A': 'l', 203 | '%a': 'D', 204 | '%w': 'w' 205 | }; 206 | const strftimeRegex = new RegExp(Object.keys(mapping).join('|').replace(new RegExp('\\^', 'g'), '\\^'), 'g'); 207 | const convertDateFormat = format => { 208 | return format.replace(strftimeRegex, match => { 209 | return mapping[match]; 210 | }); 211 | }; 212 | 213 | let StimulusFlatpickr = /*#__PURE__*/function (_Controller) { 214 | _inherits(StimulusFlatpickr, _Controller); 215 | 216 | var _super = _createSuper(StimulusFlatpickr); 217 | 218 | function StimulusFlatpickr() { 219 | _classCallCheck(this, StimulusFlatpickr); 220 | 221 | return _super.apply(this, arguments); 222 | } 223 | 224 | _createClass(StimulusFlatpickr, [{ 225 | key: "initialize", 226 | value: function initialize() { 227 | this.config = {}; 228 | } 229 | }, { 230 | key: "connect", 231 | value: function connect() { 232 | this._initializeEvents(); 233 | 234 | this._initializeOptions(); 235 | 236 | this._initializeDateFormats(); 237 | 238 | this.fp = flatpickr(this.flatpickrElement, _objectSpread2({}, this.config)); 239 | 240 | this._initializeElements(); 241 | } 242 | }, { 243 | key: "disconnect", 244 | value: function disconnect() { 245 | const value = this.inputTarget.value; 246 | this.fp.destroy(); 247 | this.inputTarget.value = value; 248 | } 249 | }, { 250 | key: "_initializeEvents", 251 | value: function _initializeEvents() { 252 | events.forEach(event => { 253 | if (this[event]) { 254 | const hook = `on${capitalize(event)}`; 255 | this.config[hook] = this[event].bind(this); 256 | } 257 | }); 258 | } 259 | }, { 260 | key: "_initializeOptions", 261 | value: function _initializeOptions() { 262 | Object.keys(options).forEach(optionType => { 263 | const optionsCamelCase = options[optionType]; 264 | optionsCamelCase.forEach(option => { 265 | const optionKebab = kebabCase(option); 266 | 267 | if (this.data.has(optionKebab)) { 268 | this.config[option] = this[`_${optionType}`](optionKebab); 269 | } 270 | }); 271 | }); 272 | 273 | this._handleDaysOfWeek(); 274 | } 275 | }, { 276 | key: "_handleDaysOfWeek", 277 | value: function _handleDaysOfWeek() { 278 | if (this.config.disableDaysOfWeek) { 279 | this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek); 280 | this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]; 281 | } 282 | 283 | if (this.config.enableDaysOfWeek) { 284 | this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek); 285 | this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]; 286 | } 287 | } 288 | }, { 289 | key: "_validateDaysOfWeek", 290 | value: function _validateDaysOfWeek(days) { 291 | if (Array.isArray(days)) { 292 | return days.map(day => parseInt(day)); 293 | } else { 294 | console.error('days of week must be a valid array'); 295 | return []; 296 | } 297 | } 298 | }, { 299 | key: "_disable", 300 | value: function _disable(date) { 301 | const disabledDays = this.config.disableDaysOfWeek; 302 | return disabledDays.includes(date.getDay()); 303 | } 304 | }, { 305 | key: "_enable", 306 | value: function _enable(date) { 307 | const enabledDays = this.config.enableDaysOfWeek; 308 | return enabledDays.includes(date.getDay()); 309 | } 310 | }, { 311 | key: "_initializeDateFormats", 312 | value: function _initializeDateFormats() { 313 | dateFormats.forEach(dateFormat => { 314 | if (this.data.has(dateFormat)) { 315 | this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat)); 316 | } 317 | }); 318 | } 319 | }, { 320 | key: "_initializeElements", 321 | value: function _initializeElements() { 322 | elements.forEach(element => { 323 | this[`${element}Target`] = this.fp[element]; 324 | }); 325 | } 326 | }, { 327 | key: "_string", 328 | value: function _string(option) { 329 | return this.data.get(option); 330 | } 331 | }, { 332 | key: "_date", 333 | value: function _date(option) { 334 | return this.data.get(option); 335 | } 336 | }, { 337 | key: "_boolean", 338 | value: function _boolean(option) { 339 | return !(this.data.get(option) == '0' || this.data.get(option) == 'false'); 340 | } 341 | }, { 342 | key: "_array", 343 | value: function _array(option) { 344 | return JSON.parse(this.data.get(option)); 345 | } 346 | }, { 347 | key: "_number", 348 | value: function _number(option) { 349 | return parseInt(this.data.get(option)); 350 | } 351 | }, { 352 | key: "_arrayOrString", 353 | value: function _arrayOrString(option) { 354 | const val = this.data.get(option); 355 | 356 | try { 357 | return JSON.parse(val); 358 | } catch (e) { 359 | return val; 360 | } 361 | } 362 | }, { 363 | key: "flatpickrElement", 364 | get: function () { 365 | return this.hasInstanceTarget && this.instanceTarget || this.element; 366 | } 367 | }]); 368 | 369 | return StimulusFlatpickr; 370 | }(Controller); 371 | 372 | _defineProperty(StimulusFlatpickr, "targets", ['instance']); 373 | 374 | export default StimulusFlatpickr; 375 | //# sourceMappingURL=index.m.js.map 376 | -------------------------------------------------------------------------------- /dist/index.m.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.m.js","sources":["../src/utils.js","../src/config_options.js","../src/events.js","../src/elements.js","../src/strftime_mapping.js","../src/index.js"],"sourcesContent":["export const kebabCase = string =>\n string\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n\nexport const capitalize = string => {\n return string.charAt(0).toUpperCase() + string.slice(1);\n};\n","const booleanOptions = [\n 'allowInput',\n 'altInput',\n 'animate',\n 'clickOpens',\n 'closeOnSelect',\n 'disableMobile',\n 'enableSeconds',\n 'enableTime',\n 'inline',\n 'noCalendar',\n 'shorthandCurrentMonth',\n 'static',\n 'time_24hr',\n 'weekNumbers',\n 'wrap'\n]\n\nconst stringOptions = [\n 'altInputClass',\n 'conjunction',\n 'mode',\n 'nextArrow',\n 'position',\n 'prevArrow',\n 'monthSelectorType'\n]\n\nconst numberOptions = [\n 'defaultHour',\n 'defaultMinute',\n 'defaultSeconds',\n 'hourIncrement',\n 'minuteIncrement',\n 'showMonths'\n]\n\nconst arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']\n\nconst arrayOrStringOptions = ['defaultDate']\n\nconst dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']\n\nexport const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']\n\nexport const options = {\n string: stringOptions,\n boolean: booleanOptions,\n date: dateOptions,\n array: arrayOptions,\n number: numberOptions,\n arrayOrString: arrayOrStringOptions\n}\n","export const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']\n","export const elements = [\n 'calendarContainer',\n 'currentYearElement',\n 'days',\n 'daysContainer',\n 'input',\n 'nextMonthNav',\n 'monthNav',\n 'prevMonthNav',\n 'rContainer',\n 'selectedDateElem',\n 'todayDateElem',\n 'weekdayContainer'\n]\n","export const mapping = {\n '%Y': 'Y',\n '%y': 'y',\n '%C': 'Y',\n '%m': 'm',\n '%-m': 'n',\n '%_m': 'n',\n '%B': 'F',\n '%^B': 'F',\n '%b': 'M',\n '%^b': 'M',\n '%h': 'M',\n '%^h': 'M',\n '%d': 'd',\n '%-d': 'j',\n '%e': 'j',\n '%H': 'H',\n '%k': 'H',\n '%I': 'h',\n '%l': 'h',\n '%-l': 'h',\n '%P': 'K',\n '%p': 'K',\n '%M': 'i',\n '%S': 'S',\n '%A': 'l',\n '%a': 'D',\n '%w': 'w'\n}\n\nconst strftimeRegex = new RegExp(\n Object.keys(mapping)\n .join('|')\n .replace(new RegExp('\\\\^', 'g'), '\\\\^'),\n 'g'\n)\n\nexport const convertDateFormat = (format) => {\n return format.replace(strftimeRegex, (match) => {\n return mapping[match]\n })\n}\n","import { Controller } from 'stimulus'\nimport flatpickr from 'flatpickr'\nimport { kebabCase, capitalize } from './utils'\nimport { options, dateFormats } from './config_options'\nimport { events } from './events'\nimport { elements } from './elements'\nimport { convertDateFormat } from './strftime_mapping'\n\nclass StimulusFlatpickr extends Controller {\n static targets = ['instance']\n\n initialize() {\n this.config = {}\n }\n\n connect() {\n this._initializeEvents()\n this._initializeOptions()\n this._initializeDateFormats()\n\n this.fp = flatpickr(this.flatpickrElement, {\n ...this.config\n })\n\n this._initializeElements()\n }\n\n disconnect() {\n const value = this.inputTarget.value\n this.fp.destroy()\n this.inputTarget.value = value\n }\n\n _initializeEvents() {\n events.forEach((event) => {\n if (this[event]) {\n const hook = `on${capitalize(event)}`\n this.config[hook] = this[event].bind(this)\n }\n })\n }\n\n _initializeOptions() {\n Object.keys(options).forEach((optionType) => {\n const optionsCamelCase = options[optionType]\n optionsCamelCase.forEach((option) => {\n const optionKebab = kebabCase(option)\n if (this.data.has(optionKebab)) {\n this.config[option] = this[`_${optionType}`](optionKebab)\n }\n })\n })\n this._handleDaysOfWeek()\n }\n\n _handleDaysOfWeek() {\n if (this.config.disableDaysOfWeek) {\n this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek)\n this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]\n }\n\n if (this.config.enableDaysOfWeek) {\n this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek)\n this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]\n }\n }\n\n _validateDaysOfWeek(days) {\n if (Array.isArray(days)) {\n return days.map((day) => parseInt(day))\n } else {\n console.error('days of week must be a valid array')\n return []\n }\n }\n\n _disable(date) {\n const disabledDays = this.config.disableDaysOfWeek\n return disabledDays.includes(date.getDay())\n }\n\n _enable(date) {\n const enabledDays = this.config.enableDaysOfWeek\n return enabledDays.includes(date.getDay())\n }\n\n _initializeDateFormats() {\n dateFormats.forEach((dateFormat) => {\n if (this.data.has(dateFormat)) {\n this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat))\n }\n })\n }\n\n _initializeElements() {\n elements.forEach((element) => {\n this[`${element}Target`] = this.fp[element]\n })\n }\n\n _string(option) {\n return this.data.get(option)\n }\n\n _date(option) {\n return this.data.get(option)\n }\n\n _boolean(option) {\n return !(this.data.get(option) == '0' || this.data.get(option) == 'false')\n }\n\n _array(option) {\n return JSON.parse(this.data.get(option))\n }\n\n _number(option) {\n return parseInt(this.data.get(option))\n }\n\n _arrayOrString(option) {\n const val = this.data.get(option)\n try {\n return JSON.parse(val)\n } catch (e) {\n return val\n }\n }\n\n get flatpickrElement() {\n return (this.hasInstanceTarget && this.instanceTarget) || this.element\n }\n}\n\nexport default StimulusFlatpickr\n"],"names":["kebabCase","string","replace","toLowerCase","capitalize","charAt","toUpperCase","slice","booleanOptions","stringOptions","numberOptions","arrayOptions","arrayOrStringOptions","dateOptions","dateFormats","options","boolean","date","array","number","arrayOrString","events","elements","mapping","strftimeRegex","RegExp","Object","keys","join","convertDateFormat","format","match","StimulusFlatpickr","config","_initializeEvents","_initializeOptions","_initializeDateFormats","fp","flatpickr","flatpickrElement","_initializeElements","value","inputTarget","destroy","forEach","event","hook","bind","optionType","optionsCamelCase","option","optionKebab","data","has","_handleDaysOfWeek","disableDaysOfWeek","_validateDaysOfWeek","disable","_disable","enableDaysOfWeek","enable","_enable","days","Array","isArray","map","day","parseInt","console","error","disabledDays","includes","getDay","enabledDays","dateFormat","get","element","JSON","parse","val","e","hasInstanceTarget","instanceTarget","Controller"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/index.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('stimulus'), require('flatpickr')) : 3 | typeof define === 'function' && define.amd ? define(['stimulus', 'flatpickr'], factory) : 4 | (global = global || self, global.StimulusFlatpikcr = factory(global.Stimulus, global.Flatpickr)); 5 | }(this, (function (stimulus, flatpickr) { 'use strict'; 6 | 7 | flatpickr = flatpickr && Object.prototype.hasOwnProperty.call(flatpickr, 'default') ? flatpickr['default'] : flatpickr; 8 | 9 | function _classCallCheck(instance, Constructor) { 10 | if (!(instance instanceof Constructor)) { 11 | throw new TypeError("Cannot call a class as a function"); 12 | } 13 | } 14 | 15 | function _defineProperties(target, props) { 16 | for (var i = 0; i < props.length; i++) { 17 | var descriptor = props[i]; 18 | descriptor.enumerable = descriptor.enumerable || false; 19 | descriptor.configurable = true; 20 | if ("value" in descriptor) descriptor.writable = true; 21 | Object.defineProperty(target, descriptor.key, descriptor); 22 | } 23 | } 24 | 25 | function _createClass(Constructor, protoProps, staticProps) { 26 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 27 | if (staticProps) _defineProperties(Constructor, staticProps); 28 | return Constructor; 29 | } 30 | 31 | function _defineProperty(obj, key, value) { 32 | if (key in obj) { 33 | Object.defineProperty(obj, key, { 34 | value: value, 35 | enumerable: true, 36 | configurable: true, 37 | writable: true 38 | }); 39 | } else { 40 | obj[key] = value; 41 | } 42 | 43 | return obj; 44 | } 45 | 46 | function ownKeys(object, enumerableOnly) { 47 | var keys = Object.keys(object); 48 | 49 | if (Object.getOwnPropertySymbols) { 50 | var symbols = Object.getOwnPropertySymbols(object); 51 | if (enumerableOnly) symbols = symbols.filter(function (sym) { 52 | return Object.getOwnPropertyDescriptor(object, sym).enumerable; 53 | }); 54 | keys.push.apply(keys, symbols); 55 | } 56 | 57 | return keys; 58 | } 59 | 60 | function _objectSpread2(target) { 61 | for (var i = 1; i < arguments.length; i++) { 62 | var source = arguments[i] != null ? arguments[i] : {}; 63 | 64 | if (i % 2) { 65 | ownKeys(Object(source), true).forEach(function (key) { 66 | _defineProperty(target, key, source[key]); 67 | }); 68 | } else if (Object.getOwnPropertyDescriptors) { 69 | Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); 70 | } else { 71 | ownKeys(Object(source)).forEach(function (key) { 72 | Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); 73 | }); 74 | } 75 | } 76 | 77 | return target; 78 | } 79 | 80 | function _inherits(subClass, superClass) { 81 | if (typeof superClass !== "function" && superClass !== null) { 82 | throw new TypeError("Super expression must either be null or a function"); 83 | } 84 | 85 | subClass.prototype = Object.create(superClass && superClass.prototype, { 86 | constructor: { 87 | value: subClass, 88 | writable: true, 89 | configurable: true 90 | } 91 | }); 92 | if (superClass) _setPrototypeOf(subClass, superClass); 93 | } 94 | 95 | function _getPrototypeOf(o) { 96 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { 97 | return o.__proto__ || Object.getPrototypeOf(o); 98 | }; 99 | return _getPrototypeOf(o); 100 | } 101 | 102 | function _setPrototypeOf(o, p) { 103 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 104 | o.__proto__ = p; 105 | return o; 106 | }; 107 | 108 | return _setPrototypeOf(o, p); 109 | } 110 | 111 | function _isNativeReflectConstruct() { 112 | if (typeof Reflect === "undefined" || !Reflect.construct) return false; 113 | if (Reflect.construct.sham) return false; 114 | if (typeof Proxy === "function") return true; 115 | 116 | try { 117 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); 118 | return true; 119 | } catch (e) { 120 | return false; 121 | } 122 | } 123 | 124 | function _assertThisInitialized(self) { 125 | if (self === void 0) { 126 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 127 | } 128 | 129 | return self; 130 | } 131 | 132 | function _possibleConstructorReturn(self, call) { 133 | if (call && (typeof call === "object" || typeof call === "function")) { 134 | return call; 135 | } 136 | 137 | return _assertThisInitialized(self); 138 | } 139 | 140 | function _createSuper(Derived) { 141 | return function () { 142 | var Super = _getPrototypeOf(Derived), 143 | result; 144 | 145 | if (_isNativeReflectConstruct()) { 146 | var NewTarget = _getPrototypeOf(this).constructor; 147 | 148 | result = Reflect.construct(Super, arguments, NewTarget); 149 | } else { 150 | result = Super.apply(this, arguments); 151 | } 152 | 153 | return _possibleConstructorReturn(this, result); 154 | }; 155 | } 156 | 157 | const kebabCase = string => string.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase(); 158 | const capitalize = string => { 159 | return string.charAt(0).toUpperCase() + string.slice(1); 160 | }; 161 | 162 | const booleanOptions = ['allowInput', 'altInput', 'animate', 'clickOpens', 'closeOnSelect', 'disableMobile', 'enableSeconds', 'enableTime', 'inline', 'noCalendar', 'shorthandCurrentMonth', 'static', 'time_24hr', 'weekNumbers', 'wrap']; 163 | const stringOptions = ['altInputClass', 'conjunction', 'mode', 'nextArrow', 'position', 'prevArrow', 'monthSelectorType']; 164 | const numberOptions = ['defaultHour', 'defaultMinute', 'defaultSeconds', 'hourIncrement', 'minuteIncrement', 'showMonths']; 165 | const arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']; 166 | const arrayOrStringOptions = ['defaultDate']; 167 | const dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']; 168 | const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']; 169 | const options = { 170 | string: stringOptions, 171 | boolean: booleanOptions, 172 | date: dateOptions, 173 | array: arrayOptions, 174 | number: numberOptions, 175 | arrayOrString: arrayOrStringOptions 176 | }; 177 | 178 | const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']; 179 | 180 | const elements = ['calendarContainer', 'currentYearElement', 'days', 'daysContainer', 'input', 'nextMonthNav', 'monthNav', 'prevMonthNav', 'rContainer', 'selectedDateElem', 'todayDateElem', 'weekdayContainer']; 181 | 182 | const mapping = { 183 | '%Y': 'Y', 184 | '%y': 'y', 185 | '%C': 'Y', 186 | '%m': 'm', 187 | '%-m': 'n', 188 | '%_m': 'n', 189 | '%B': 'F', 190 | '%^B': 'F', 191 | '%b': 'M', 192 | '%^b': 'M', 193 | '%h': 'M', 194 | '%^h': 'M', 195 | '%d': 'd', 196 | '%-d': 'j', 197 | '%e': 'j', 198 | '%H': 'H', 199 | '%k': 'H', 200 | '%I': 'h', 201 | '%l': 'h', 202 | '%-l': 'h', 203 | '%P': 'K', 204 | '%p': 'K', 205 | '%M': 'i', 206 | '%S': 'S', 207 | '%A': 'l', 208 | '%a': 'D', 209 | '%w': 'w' 210 | }; 211 | const strftimeRegex = new RegExp(Object.keys(mapping).join('|').replace(new RegExp('\\^', 'g'), '\\^'), 'g'); 212 | const convertDateFormat = format => { 213 | return format.replace(strftimeRegex, match => { 214 | return mapping[match]; 215 | }); 216 | }; 217 | 218 | let StimulusFlatpickr = /*#__PURE__*/function (_Controller) { 219 | _inherits(StimulusFlatpickr, _Controller); 220 | 221 | var _super = _createSuper(StimulusFlatpickr); 222 | 223 | function StimulusFlatpickr() { 224 | _classCallCheck(this, StimulusFlatpickr); 225 | 226 | return _super.apply(this, arguments); 227 | } 228 | 229 | _createClass(StimulusFlatpickr, [{ 230 | key: "initialize", 231 | value: function initialize() { 232 | this.config = {}; 233 | } 234 | }, { 235 | key: "connect", 236 | value: function connect() { 237 | this._initializeEvents(); 238 | 239 | this._initializeOptions(); 240 | 241 | this._initializeDateFormats(); 242 | 243 | this.fp = flatpickr(this.flatpickrElement, _objectSpread2({}, this.config)); 244 | 245 | this._initializeElements(); 246 | } 247 | }, { 248 | key: "disconnect", 249 | value: function disconnect() { 250 | const value = this.inputTarget.value; 251 | this.fp.destroy(); 252 | this.inputTarget.value = value; 253 | } 254 | }, { 255 | key: "_initializeEvents", 256 | value: function _initializeEvents() { 257 | events.forEach(event => { 258 | if (this[event]) { 259 | const hook = `on${capitalize(event)}`; 260 | this.config[hook] = this[event].bind(this); 261 | } 262 | }); 263 | } 264 | }, { 265 | key: "_initializeOptions", 266 | value: function _initializeOptions() { 267 | Object.keys(options).forEach(optionType => { 268 | const optionsCamelCase = options[optionType]; 269 | optionsCamelCase.forEach(option => { 270 | const optionKebab = kebabCase(option); 271 | 272 | if (this.data.has(optionKebab)) { 273 | this.config[option] = this[`_${optionType}`](optionKebab); 274 | } 275 | }); 276 | }); 277 | 278 | this._handleDaysOfWeek(); 279 | } 280 | }, { 281 | key: "_handleDaysOfWeek", 282 | value: function _handleDaysOfWeek() { 283 | if (this.config.disableDaysOfWeek) { 284 | this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek); 285 | this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]; 286 | } 287 | 288 | if (this.config.enableDaysOfWeek) { 289 | this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek); 290 | this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]; 291 | } 292 | } 293 | }, { 294 | key: "_validateDaysOfWeek", 295 | value: function _validateDaysOfWeek(days) { 296 | if (Array.isArray(days)) { 297 | return days.map(day => parseInt(day)); 298 | } else { 299 | console.error('days of week must be a valid array'); 300 | return []; 301 | } 302 | } 303 | }, { 304 | key: "_disable", 305 | value: function _disable(date) { 306 | const disabledDays = this.config.disableDaysOfWeek; 307 | return disabledDays.includes(date.getDay()); 308 | } 309 | }, { 310 | key: "_enable", 311 | value: function _enable(date) { 312 | const enabledDays = this.config.enableDaysOfWeek; 313 | return enabledDays.includes(date.getDay()); 314 | } 315 | }, { 316 | key: "_initializeDateFormats", 317 | value: function _initializeDateFormats() { 318 | dateFormats.forEach(dateFormat => { 319 | if (this.data.has(dateFormat)) { 320 | this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat)); 321 | } 322 | }); 323 | } 324 | }, { 325 | key: "_initializeElements", 326 | value: function _initializeElements() { 327 | elements.forEach(element => { 328 | this[`${element}Target`] = this.fp[element]; 329 | }); 330 | } 331 | }, { 332 | key: "_string", 333 | value: function _string(option) { 334 | return this.data.get(option); 335 | } 336 | }, { 337 | key: "_date", 338 | value: function _date(option) { 339 | return this.data.get(option); 340 | } 341 | }, { 342 | key: "_boolean", 343 | value: function _boolean(option) { 344 | return !(this.data.get(option) == '0' || this.data.get(option) == 'false'); 345 | } 346 | }, { 347 | key: "_array", 348 | value: function _array(option) { 349 | return JSON.parse(this.data.get(option)); 350 | } 351 | }, { 352 | key: "_number", 353 | value: function _number(option) { 354 | return parseInt(this.data.get(option)); 355 | } 356 | }, { 357 | key: "_arrayOrString", 358 | value: function _arrayOrString(option) { 359 | const val = this.data.get(option); 360 | 361 | try { 362 | return JSON.parse(val); 363 | } catch (e) { 364 | return val; 365 | } 366 | } 367 | }, { 368 | key: "flatpickrElement", 369 | get: function () { 370 | return this.hasInstanceTarget && this.instanceTarget || this.element; 371 | } 372 | }]); 373 | 374 | return StimulusFlatpickr; 375 | }(stimulus.Controller); 376 | 377 | _defineProperty(StimulusFlatpickr, "targets", ['instance']); 378 | 379 | return StimulusFlatpickr; 380 | 381 | }))); 382 | //# sourceMappingURL=index.umd.js.map 383 | -------------------------------------------------------------------------------- /dist/index.umd.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.umd.js","sources":["../src/utils.js","../src/config_options.js","../src/events.js","../src/elements.js","../src/strftime_mapping.js","../src/index.js"],"sourcesContent":["export const kebabCase = string =>\n string\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n\nexport const capitalize = string => {\n return string.charAt(0).toUpperCase() + string.slice(1);\n};\n","const booleanOptions = [\n 'allowInput',\n 'altInput',\n 'animate',\n 'clickOpens',\n 'closeOnSelect',\n 'disableMobile',\n 'enableSeconds',\n 'enableTime',\n 'inline',\n 'noCalendar',\n 'shorthandCurrentMonth',\n 'static',\n 'time_24hr',\n 'weekNumbers',\n 'wrap'\n]\n\nconst stringOptions = [\n 'altInputClass',\n 'conjunction',\n 'mode',\n 'nextArrow',\n 'position',\n 'prevArrow',\n 'monthSelectorType'\n]\n\nconst numberOptions = [\n 'defaultHour',\n 'defaultMinute',\n 'defaultSeconds',\n 'hourIncrement',\n 'minuteIncrement',\n 'showMonths'\n]\n\nconst arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek']\n\nconst arrayOrStringOptions = ['defaultDate']\n\nconst dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now']\n\nexport const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat']\n\nexport const options = {\n string: stringOptions,\n boolean: booleanOptions,\n date: dateOptions,\n array: arrayOptions,\n number: numberOptions,\n arrayOrString: arrayOrStringOptions\n}\n","export const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate']\n","export const elements = [\n 'calendarContainer',\n 'currentYearElement',\n 'days',\n 'daysContainer',\n 'input',\n 'nextMonthNav',\n 'monthNav',\n 'prevMonthNav',\n 'rContainer',\n 'selectedDateElem',\n 'todayDateElem',\n 'weekdayContainer'\n]\n","export const mapping = {\n '%Y': 'Y',\n '%y': 'y',\n '%C': 'Y',\n '%m': 'm',\n '%-m': 'n',\n '%_m': 'n',\n '%B': 'F',\n '%^B': 'F',\n '%b': 'M',\n '%^b': 'M',\n '%h': 'M',\n '%^h': 'M',\n '%d': 'd',\n '%-d': 'j',\n '%e': 'j',\n '%H': 'H',\n '%k': 'H',\n '%I': 'h',\n '%l': 'h',\n '%-l': 'h',\n '%P': 'K',\n '%p': 'K',\n '%M': 'i',\n '%S': 'S',\n '%A': 'l',\n '%a': 'D',\n '%w': 'w'\n}\n\nconst strftimeRegex = new RegExp(\n Object.keys(mapping)\n .join('|')\n .replace(new RegExp('\\\\^', 'g'), '\\\\^'),\n 'g'\n)\n\nexport const convertDateFormat = (format) => {\n return format.replace(strftimeRegex, (match) => {\n return mapping[match]\n })\n}\n","import { Controller } from 'stimulus'\nimport flatpickr from 'flatpickr'\nimport { kebabCase, capitalize } from './utils'\nimport { options, dateFormats } from './config_options'\nimport { events } from './events'\nimport { elements } from './elements'\nimport { convertDateFormat } from './strftime_mapping'\n\nclass StimulusFlatpickr extends Controller {\n static targets = ['instance']\n\n initialize() {\n this.config = {}\n }\n\n connect() {\n this._initializeEvents()\n this._initializeOptions()\n this._initializeDateFormats()\n\n this.fp = flatpickr(this.flatpickrElement, {\n ...this.config\n })\n\n this._initializeElements()\n }\n\n disconnect() {\n const value = this.inputTarget.value\n this.fp.destroy()\n this.inputTarget.value = value\n }\n\n _initializeEvents() {\n events.forEach((event) => {\n if (this[event]) {\n const hook = `on${capitalize(event)}`\n this.config[hook] = this[event].bind(this)\n }\n })\n }\n\n _initializeOptions() {\n Object.keys(options).forEach((optionType) => {\n const optionsCamelCase = options[optionType]\n optionsCamelCase.forEach((option) => {\n const optionKebab = kebabCase(option)\n if (this.data.has(optionKebab)) {\n this.config[option] = this[`_${optionType}`](optionKebab)\n }\n })\n })\n this._handleDaysOfWeek()\n }\n\n _handleDaysOfWeek() {\n if (this.config.disableDaysOfWeek) {\n this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek)\n this.config.disable = [...(this.config.disable || []), this._disable.bind(this)]\n }\n\n if (this.config.enableDaysOfWeek) {\n this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek)\n this.config.enable = [...(this.config.enable || []), this._enable.bind(this)]\n }\n }\n\n _validateDaysOfWeek(days) {\n if (Array.isArray(days)) {\n return days.map((day) => parseInt(day))\n } else {\n console.error('days of week must be a valid array')\n return []\n }\n }\n\n _disable(date) {\n const disabledDays = this.config.disableDaysOfWeek\n return disabledDays.includes(date.getDay())\n }\n\n _enable(date) {\n const enabledDays = this.config.enableDaysOfWeek\n return enabledDays.includes(date.getDay())\n }\n\n _initializeDateFormats() {\n dateFormats.forEach((dateFormat) => {\n if (this.data.has(dateFormat)) {\n this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat))\n }\n })\n }\n\n _initializeElements() {\n elements.forEach((element) => {\n this[`${element}Target`] = this.fp[element]\n })\n }\n\n _string(option) {\n return this.data.get(option)\n }\n\n _date(option) {\n return this.data.get(option)\n }\n\n _boolean(option) {\n return !(this.data.get(option) == '0' || this.data.get(option) == 'false')\n }\n\n _array(option) {\n return JSON.parse(this.data.get(option))\n }\n\n _number(option) {\n return parseInt(this.data.get(option))\n }\n\n _arrayOrString(option) {\n const val = this.data.get(option)\n try {\n return JSON.parse(val)\n } catch (e) {\n return val\n }\n }\n\n get flatpickrElement() {\n return (this.hasInstanceTarget && this.instanceTarget) || this.element\n }\n}\n\nexport default StimulusFlatpickr\n"],"names":["kebabCase","string","replace","toLowerCase","capitalize","charAt","toUpperCase","slice","booleanOptions","stringOptions","numberOptions","arrayOptions","arrayOrStringOptions","dateOptions","dateFormats","options","boolean","date","array","number","arrayOrString","events","elements","mapping","strftimeRegex","RegExp","Object","keys","join","convertDateFormat","format","match","StimulusFlatpickr","config","_initializeEvents","_initializeOptions","_initializeDateFormats","fp","flatpickr","flatpickrElement","_initializeElements","value","inputTarget","destroy","forEach","event","hook","bind","optionType","optionsCamelCase","option","optionKebab","data","has","_handleDaysOfWeek","disableDaysOfWeek","_validateDaysOfWeek","disable","_disable","enableDaysOfWeek","enable","_enable","days","Array","isArray","map","day","parseInt","console","error","disabledDays","includes","getDay","enabledDays","dateFormat","get","element","JSON","parse","val","e","hasInstanceTarget","instanceTarget","Controller"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAO,MAAMA,SAAS,GAAGC,MAAM,IAC7BA,MAAM,CACHC,OADH,CACW,iBADX,EAC8B,OAD9B,EAEGA,OAFH,CAEW,SAFX,EAEsB,GAFtB,EAGGC,WAHH,EADK;EAMA,MAAMC,UAAU,GAAGH,MAAM,IAAI;EAClC,SAAOA,MAAM,CAACI,MAAP,CAAc,CAAd,EAAiBC,WAAjB,KAAiCL,MAAM,CAACM,KAAP,CAAa,CAAb,CAAxC;EACD,CAFM;;ECNP,MAAMC,cAAc,GAAG,CACrB,YADqB,EAErB,UAFqB,EAGrB,SAHqB,EAIrB,YAJqB,EAKrB,eALqB,EAMrB,eANqB,EAOrB,eAPqB,EAQrB,YARqB,EASrB,QATqB,EAUrB,YAVqB,EAWrB,uBAXqB,EAYrB,QAZqB,EAarB,WAbqB,EAcrB,aAdqB,EAerB,MAfqB,CAAvB;EAkBA,MAAMC,aAAa,GAAG,CACpB,eADoB,EAEpB,aAFoB,EAGpB,MAHoB,EAIpB,WAJoB,EAKpB,UALoB,EAMpB,WANoB,EAOpB,mBAPoB,CAAtB;EAUA,MAAMC,aAAa,GAAG,CACpB,aADoB,EAEpB,eAFoB,EAGpB,gBAHoB,EAIpB,eAJoB,EAKpB,iBALoB,EAMpB,YANoB,CAAtB;EASA,MAAMC,YAAY,GAAG,CAAC,SAAD,EAAY,QAAZ,EAAsB,mBAAtB,EAA2C,kBAA3C,CAArB;EAEA,MAAMC,oBAAoB,GAAG,CAAC,aAAD,CAA7B;EAEA,MAAMC,WAAW,GAAG,CAAC,SAAD,EAAY,SAAZ,EAAuB,SAAvB,EAAkC,SAAlC,EAA6C,KAA7C,CAApB;AAEA,EAAO,MAAMC,WAAW,GAAG,CAAC,WAAD,EAAc,gBAAd,EAAgC,YAAhC,CAApB;AAEP,EAAO,MAAMC,OAAO,GAAG;EACrBd,EAAAA,MAAM,EAAEQ,aADa;EAErBO,EAAAA,OAAO,EAAER,cAFY;EAGrBS,EAAAA,IAAI,EAAEJ,WAHe;EAIrBK,EAAAA,KAAK,EAAEP,YAJc;EAKrBQ,EAAAA,MAAM,EAAET,aALa;EAMrBU,EAAAA,aAAa,EAAER;EANM,CAAhB;;EC7CA,MAAMS,MAAM,GAAG,CAAC,QAAD,EAAW,MAAX,EAAmB,OAAnB,EAA4B,aAA5B,EAA2C,YAA3C,EAAyD,OAAzD,EAAkE,aAAlE,EAAiF,WAAjF,CAAf;;ECAA,MAAMC,QAAQ,GAAG,CACtB,mBADsB,EAEtB,oBAFsB,EAGtB,MAHsB,EAItB,eAJsB,EAKtB,OALsB,EAMtB,cANsB,EAOtB,UAPsB,EAQtB,cARsB,EAStB,YATsB,EAUtB,kBAVsB,EAWtB,eAXsB,EAYtB,kBAZsB,CAAjB;;ECAA,MAAMC,OAAO,GAAG;EACrB,QAAM,GADe;EAErB,QAAM,GAFe;EAGrB,QAAM,GAHe;EAIrB,QAAM,GAJe;EAKrB,SAAO,GALc;EAMrB,SAAO,GANc;EAOrB,QAAM,GAPe;EAQrB,SAAO,GARc;EASrB,QAAM,GATe;EAUrB,SAAO,GAVc;EAWrB,QAAM,GAXe;EAYrB,SAAO,GAZc;EAarB,QAAM,GAbe;EAcrB,SAAO,GAdc;EAerB,QAAM,GAfe;EAgBrB,QAAM,GAhBe;EAiBrB,QAAM,GAjBe;EAkBrB,QAAM,GAlBe;EAmBrB,QAAM,GAnBe;EAoBrB,SAAO,GApBc;EAqBrB,QAAM,GArBe;EAsBrB,QAAM,GAtBe;EAuBrB,QAAM,GAvBe;EAwBrB,QAAM,GAxBe;EAyBrB,QAAM,GAzBe;EA0BrB,QAAM,GA1Be;EA2BrB,QAAM;EA3Be,CAAhB;EA8BP,MAAMC,aAAa,GAAG,IAAIC,MAAJ,CACpBC,MAAM,CAACC,IAAP,CAAYJ,OAAZ,EACGK,IADH,CACQ,GADR,EAEG1B,OAFH,CAEW,IAAIuB,MAAJ,CAAW,KAAX,EAAkB,GAAlB,CAFX,EAEmC,KAFnC,CADoB,EAIpB,GAJoB,CAAtB;AAOA,EAAO,MAAMI,iBAAiB,GAAIC,MAAD,IAAY;EAC3C,SAAOA,MAAM,CAAC5B,OAAP,CAAesB,aAAf,EAA+BO,KAAD,IAAW;EAC9C,WAAOR,OAAO,CAACQ,KAAD,CAAd;EACD,GAFM,CAAP;EAGD,CAJM;;MC7BDC;;;;;;;;;;;;;mCAGS;EACX,WAAKC,MAAL,GAAc,EAAd;EACD;;;gCAES;EACR,WAAKC,iBAAL;;EACA,WAAKC,kBAAL;;EACA,WAAKC,sBAAL;;EAEA,WAAKC,EAAL,GAAUC,SAAS,CAAC,KAAKC,gBAAN,qBACd,KAAKN,MADS,EAAnB;;EAIA,WAAKO,mBAAL;EACD;;;mCAEY;EACX,YAAMC,KAAK,GAAG,KAAKC,WAAL,CAAiBD,KAA/B;EACA,WAAKJ,EAAL,CAAQM,OAAR;EACA,WAAKD,WAAL,CAAiBD,KAAjB,GAAyBA,KAAzB;EACD;;;0CAEmB;EAClBpB,MAAAA,MAAM,CAACuB,OAAP,CAAgBC,KAAD,IAAW;EACxB,YAAI,KAAKA,KAAL,CAAJ,EAAiB;EACf,gBAAMC,IAAI,GAAI,KAAI1C,UAAU,CAACyC,KAAD,CAAQ,EAApC;EACA,eAAKZ,MAAL,CAAYa,IAAZ,IAAoB,KAAKD,KAAL,EAAYE,IAAZ,CAAiB,IAAjB,CAApB;EACD;EACF,OALD;EAMD;;;2CAEoB;EACnBrB,MAAAA,MAAM,CAACC,IAAP,CAAYZ,OAAZ,EAAqB6B,OAArB,CAA8BI,UAAD,IAAgB;EAC3C,cAAMC,gBAAgB,GAAGlC,OAAO,CAACiC,UAAD,CAAhC;EACAC,QAAAA,gBAAgB,CAACL,OAAjB,CAA0BM,MAAD,IAAY;EACnC,gBAAMC,WAAW,GAAGnD,SAAS,CAACkD,MAAD,CAA7B;;EACA,cAAI,KAAKE,IAAL,CAAUC,GAAV,CAAcF,WAAd,CAAJ,EAAgC;EAC9B,iBAAKlB,MAAL,CAAYiB,MAAZ,IAAsB,KAAM,IAAGF,UAAW,EAApB,EAAuBG,WAAvB,CAAtB;EACD;EACF,SALD;EAMD,OARD;;EASA,WAAKG,iBAAL;EACD;;;0CAEmB;EAClB,UAAI,KAAKrB,MAAL,CAAYsB,iBAAhB,EAAmC;EACjC,aAAKtB,MAAL,CAAYsB,iBAAZ,GAAgC,KAAKC,mBAAL,CAAyB,KAAKvB,MAAL,CAAYsB,iBAArC,CAAhC;EACA,aAAKtB,MAAL,CAAYwB,OAAZ,GAAsB,CAAC,IAAI,KAAKxB,MAAL,CAAYwB,OAAZ,IAAuB,EAA3B,CAAD,EAAiC,KAAKC,QAAL,CAAcX,IAAd,CAAmB,IAAnB,CAAjC,CAAtB;EACD;;EAED,UAAI,KAAKd,MAAL,CAAY0B,gBAAhB,EAAkC;EAChC,aAAK1B,MAAL,CAAY0B,gBAAZ,GAA+B,KAAKH,mBAAL,CAAyB,KAAKvB,MAAL,CAAY0B,gBAArC,CAA/B;EACA,aAAK1B,MAAL,CAAY2B,MAAZ,GAAqB,CAAC,IAAI,KAAK3B,MAAL,CAAY2B,MAAZ,IAAsB,EAA1B,CAAD,EAAgC,KAAKC,OAAL,CAAad,IAAb,CAAkB,IAAlB,CAAhC,CAArB;EACD;EACF;;;0CAEmBe,MAAM;EACxB,UAAIC,KAAK,CAACC,OAAN,CAAcF,IAAd,CAAJ,EAAyB;EACvB,eAAOA,IAAI,CAACG,GAAL,CAAUC,GAAD,IAASC,QAAQ,CAACD,GAAD,CAA1B,CAAP;EACD,OAFD,MAEO;EACLE,QAAAA,OAAO,CAACC,KAAR,CAAc,oCAAd;EACA,eAAO,EAAP;EACD;EACF;;;+BAEQpD,MAAM;EACb,YAAMqD,YAAY,GAAG,KAAKrC,MAAL,CAAYsB,iBAAjC;EACA,aAAOe,YAAY,CAACC,QAAb,CAAsBtD,IAAI,CAACuD,MAAL,EAAtB,CAAP;EACD;;;8BAEOvD,MAAM;EACZ,YAAMwD,WAAW,GAAG,KAAKxC,MAAL,CAAY0B,gBAAhC;EACA,aAAOc,WAAW,CAACF,QAAZ,CAAqBtD,IAAI,CAACuD,MAAL,EAArB,CAAP;EACD;;;+CAEwB;EACvB1D,MAAAA,WAAW,CAAC8B,OAAZ,CAAqB8B,UAAD,IAAgB;EAClC,YAAI,KAAKtB,IAAL,CAAUC,GAAV,CAAcqB,UAAd,CAAJ,EAA+B;EAC7B,eAAKzC,MAAL,CAAYyC,UAAZ,IAA0B7C,iBAAiB,CAAC,KAAKuB,IAAL,CAAUuB,GAAV,CAAcD,UAAd,CAAD,CAA3C;EACD;EACF,OAJD;EAKD;;;4CAEqB;EACpBpD,MAAAA,QAAQ,CAACsB,OAAT,CAAkBgC,OAAD,IAAa;EAC5B,aAAM,GAAEA,OAAQ,QAAhB,IAA2B,KAAKvC,EAAL,CAAQuC,OAAR,CAA3B;EACD,OAFD;EAGD;;;8BAEO1B,QAAQ;EACd,aAAO,KAAKE,IAAL,CAAUuB,GAAV,CAAczB,MAAd,CAAP;EACD;;;4BAEKA,QAAQ;EACZ,aAAO,KAAKE,IAAL,CAAUuB,GAAV,CAAczB,MAAd,CAAP;EACD;;;+BAEQA,QAAQ;EACf,aAAO,EAAE,KAAKE,IAAL,CAAUuB,GAAV,CAAczB,MAAd,KAAyB,GAAzB,IAAgC,KAAKE,IAAL,CAAUuB,GAAV,CAAczB,MAAd,KAAyB,OAA3D,CAAP;EACD;;;6BAEMA,QAAQ;EACb,aAAO2B,IAAI,CAACC,KAAL,CAAW,KAAK1B,IAAL,CAAUuB,GAAV,CAAczB,MAAd,CAAX,CAAP;EACD;;;8BAEOA,QAAQ;EACd,aAAOiB,QAAQ,CAAC,KAAKf,IAAL,CAAUuB,GAAV,CAAczB,MAAd,CAAD,CAAf;EACD;;;qCAEcA,QAAQ;EACrB,YAAM6B,GAAG,GAAG,KAAK3B,IAAL,CAAUuB,GAAV,CAAczB,MAAd,CAAZ;;EACA,UAAI;EACF,eAAO2B,IAAI,CAACC,KAAL,CAAWC,GAAX,CAAP;EACD,OAFD,CAEE,OAAOC,CAAP,EAAU;EACV,eAAOD,GAAP;EACD;EACF;;;uBAEsB;EACrB,aAAQ,KAAKE,iBAAL,IAA0B,KAAKC,cAAhC,IAAmD,KAAKN,OAA/D;EACD;;;;IA3H6BO;;kBAA1BnD,8BACa,CAAC,UAAD;;;;;;;;"} -------------------------------------------------------------------------------- /images/datetime-days-of-week.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienpoly/stimulus-flatpickr/cf9c7e7f08043999c2a7ca62001a3f61be075ca2/images/datetime-days-of-week.png -------------------------------------------------------------------------------- /images/datetime-picker-black-fr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienpoly/stimulus-flatpickr/cf9c7e7f08043999c2a7ca62001a3f61be075ca2/images/datetime-picker-black-fr.png -------------------------------------------------------------------------------- /images/datetime-picker-formats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienpoly/stimulus-flatpickr/cf9c7e7f08043999c2a7ca62001a3f61be075ca2/images/datetime-picker-formats.png -------------------------------------------------------------------------------- /images/datetime-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienpoly/stimulus-flatpickr/cf9c7e7f08043999c2a7ca62001a3f61be075ca2/images/datetime-picker.png -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Sep 03 2018 21:41:15 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path that will be used to resolve all patterns (eg. files, exclude) 7 | basePath: '', 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['mocha', 'sinon-chai', 'fixture'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | 'spec/*.js', 16 | 'spec/fixtures/*.html', 17 | { 18 | pattern: '**/*.js.map', 19 | included: false 20 | } 21 | ], 22 | 23 | // list of files / patterns to exclude 24 | exclude: [], 25 | 26 | // preprocess matching files before serving them to the browser 27 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 28 | preprocessors: { 29 | 'spec/**/*.js': ['webpack', 'sourcemap'], 30 | 'src/**/*.js': ['webpack', 'sourcemap'], 31 | 'spec/fixtures/*.html': ['html2js'] 32 | }, 33 | 34 | webpack: { 35 | mode: 'development', 36 | module: { 37 | rules: [ 38 | { 39 | test: /\.js$/, 40 | exclude: [/node_modules/], 41 | use: ['babel-loader'] 42 | }, 43 | { 44 | test: /\.css$/, 45 | use: ['style-loader', 'css-loader'] 46 | } 47 | ] 48 | } 49 | }, 50 | 51 | client: { 52 | captureConsole: true, 53 | chai: { 54 | includeStack: true 55 | }, 56 | clearContext: false 57 | }, 58 | 59 | // test results reporter to use 60 | // possible values: 'dots', 'progress' 61 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 62 | reporters: ['mocha', 'coverage'], 63 | 64 | coverageReporter: { 65 | reporters: [ 66 | { 67 | type: 'lcov' 68 | }, 69 | { 70 | type: 'text-summary' 71 | }, 72 | { 73 | type: 'text' 74 | } 75 | ] 76 | }, 77 | // web server port 78 | port: 9876, 79 | 80 | // enable / disable colors in the output (reporters and logs) 81 | colors: true, 82 | 83 | // level of logging 84 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 85 | logLevel: config.LOG_INFO, 86 | 87 | // enable / disable watching file and executing tests whenever any file changes 88 | autoWatch: true, 89 | 90 | // start these browsers 91 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 92 | browsers: ['ChromeHeadless'], 93 | // browsers: ['Chrome'], 94 | 95 | // Continuous Integration mode 96 | // if true, Karma captures browsers, runs the tests and exits 97 | singleRun: true, 98 | 99 | // Concurrency level 100 | // how many browser should be started simultaneous 101 | concurrency: 5 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stimulus-flatpickr", 3 | "version": "1.4.0", 4 | "description": "A Stimulus Wrapper for Flatpickr library", 5 | "keywords": [ 6 | "stimulus", 7 | "stimulusjs", 8 | "flatpickr" 9 | ], 10 | "main": "dist/index.js", 11 | "umd:main": "dist/index.umd.js", 12 | "module": "dist/index.m.js", 13 | "source": "src/index.js", 14 | "amdName": "StimulusFlatpickr", 15 | "author": "@adrienpoly", 16 | "license": "MIT", 17 | "external": "stimulus, flatpickr", 18 | "scripts": { 19 | "start:playground": "webpack-dev-server --mode development --open --config playground/webpack.config.js --hot", 20 | "build": "rollup -c && yarn test", 21 | "dev": "rollup -wc", 22 | "test": "NODE_ENV=test karma start", 23 | "test:watch": "yarn test --auto-watch --no-single-run", 24 | "test:headless": "yarn test --auto-watch --no-single-run --browsers ChromeHeadless", 25 | "codecov": "codecov", 26 | "release": "np" 27 | }, 28 | "peerDependencies": { 29 | "flatpickr": ">=4.6.2", 30 | "stimulus": ">=1.1.1" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "^7.6.0", 34 | "@babel/plugin-proposal-class-properties": "^7.3.4", 35 | "@babel/plugin-proposal-object-rest-spread": "^7.5.5", 36 | "@babel/plugin-transform-classes": "^7.3.4", 37 | "@babel/plugin-transform-spread": "^7.2.2", 38 | "@babel/preset-env": "^7.6.0", 39 | "babel-eslint": "^10.0.3", 40 | "babel-loader": "^8.0.5", 41 | "babel-plugin-istanbul": "^5.1.1", 42 | "chai": "^4.2.0", 43 | "chai-dom": "^1.8.1", 44 | "codecov": "^3.1.0", 45 | "css-loader": "^1.0.0", 46 | "eslint": "^6.3.0", 47 | "eslint-plugin-import": "^2.18.2", 48 | "eslint-plugin-mocha": "^6.1.0", 49 | "eslint-plugin-node": "^10.0.0", 50 | "eslint-plugin-promise": "^4.2.1", 51 | "eslint-plugin-standard": "^4.0.1", 52 | "flatpickr": "^4.6.3", 53 | "karma": "^4.0.1", 54 | "karma-chai": "^0.1.0", 55 | "karma-chrome-launcher": "^2.2.0", 56 | "karma-coverage": "^2.0.1", 57 | "karma-fixture": "^0.2.6", 58 | "karma-html2js-preprocessor": "^1.1.0", 59 | "karma-mocha": "^1.3.0", 60 | "karma-mocha-reporter": "^2.2.5", 61 | "karma-rollup-preprocessor": "^7.0.2", 62 | "karma-sinon-chai": "^2.0.2", 63 | "karma-sourcemap-loader": "^0.3.7", 64 | "karma-webpack": "^4.0.2", 65 | "minimist": "^1.2.5", 66 | "mocha": "^5.2.0", 67 | "np": "^5.1.3", 68 | "npm-run-all": "^4.1.3", 69 | "rollup": "^1.20.3", 70 | "rollup-plugin-babel": "^4.3.2", 71 | "rollup-plugin-filesize": "^6.0.1", 72 | "rollup-plugin-node-resolve": "^5.2.0", 73 | "sinon": "^6.1.4", 74 | "sinon-chai": "^3.2.0", 75 | "size-plugin": "^1.0.1", 76 | "stimulus": "^1.1.1", 77 | "style-loader": "^0.23.0", 78 | "webpack": "^4.39.3", 79 | "webpack-cli": "^3.2.3", 80 | "webpack-dev-middleware": "^3.6.1", 81 | "webpack-dev-server": "^3.2.1" 82 | }, 83 | "repository": { 84 | "type": "git", 85 | "url": "https://github.com/adrienpoly/stimulus-flatpickr.git" 86 | }, 87 | "resolutions": { 88 | "minimist": "^1.2.5" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /playground/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stimulus Flatpickr package Playground 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Stimulus Flatpickr Playground

15 | 16 |

Basic

17 |
18 | 19 |
20 |

Date time

21 |
22 | 29 |
30 | 31 |

Human-friendly Dates

32 |
33 | 41 |
42 | 43 |
44 |         <input
45 |           type="text"
46 |           placeholder="Select Date.."
47 |           data-controller="flatpickr"
48 |           data-flatpickr-date-format="Y-m-d"
49 |           data-flatpickr-alt-format="F j, Y"
50 |           data-flatpickr-alt-input="true"
51 |         />
52 |       
53 | 54 |

minDate and maxDate

55 |
56 | 57 |
58 |
59 |         <input 
60 |           type="text" 
61 |           placeholder="Select Date.." 
62 |           data-controller="flatpickr" 
63 |           data-flatpickr-min-date="2020-01" 
64 |         />
65 |       
66 | 67 |

Flatpickr + external elements

68 |

if you need to wrap custom elements inside the controller you can use two solutions:

69 |
1) Wrap: true option
70 |
71 | 72 | 73 | 74 |
75 | 76 |
77 |         <div data-controller="flatpickr" data-flatpickr-wrap="true">
78 |           <input type="text" placeholder="Select Date.." data-input />
79 |           <button title="toggle" data-toggle>open</button>
80 |           <button title="clear" data-clear>clear</button>
81 |         </div>
82 |       
83 | 84 |
2) Instance target
85 |
86 | 87 |
88 | 89 |
90 |         <div data-controller="flatpickr">
91 |           <input type="text" placeholder="Select Date.." data-target="flatpickr.instance" />
92 |         </div>
93 |       
94 |
95 | 96 | 97 | -------------------------------------------------------------------------------- /playground/public/style.css: -------------------------------------------------------------------------------- 1 | /* Sakura.css v1.0.0 2 | * ================ 3 | * Minimal css theme. 4 | * Project: https://github.com/oxalorg/sakura 5 | */ 6 | /* Body */ 7 | html { 8 | font-size: 62.5%; 9 | font-family: 'Roboto', sans-serif; 10 | } 11 | 12 | body { 13 | font-size: 1.8rem; 14 | line-height: 1.618; 15 | margin: auto; 16 | color: #4a4a4a; 17 | background-color: #fff; 18 | padding: 13px; 19 | } 20 | 21 | .container { 22 | max-width: 960px; 23 | margin: auto; 24 | min-height: 100wv; 25 | } 26 | 27 | @media (max-width: 684px) { 28 | body { 29 | font-size: 1.53rem; 30 | } 31 | } 32 | 33 | @media (max-width: 382px) { 34 | body { 35 | font-size: 1.35rem; 36 | } 37 | } 38 | 39 | h1, 40 | h2, 41 | h3, 42 | h4, 43 | h5, 44 | h6 { 45 | color: #5893ff; 46 | line-height: 1.1; 47 | font-family: 'Libre Franklin', sans-serif; 48 | font-weight: 700; 49 | overflow-wrap: break-word; 50 | word-wrap: break-word; 51 | -ms-word-break: break-all; 52 | word-break: break-word; 53 | -ms-hyphens: auto; 54 | -moz-hyphens: auto; 55 | -webkit-hyphens: auto; 56 | hyphens: auto; 57 | } 58 | 59 | h1 { 60 | font-size: 2.35em; 61 | } 62 | 63 | h2 { 64 | font-size: 2em; 65 | } 66 | 67 | h3 { 68 | color: #4a4a4a; 69 | font-weight: 300; 70 | font-size: 1.75em; 71 | } 72 | 73 | h4 { 74 | color: #4a4a4a; 75 | font-size: 1.5em; 76 | } 77 | 78 | h5 { 79 | color: #4a4a4a; 80 | font-size: 1.25em; 81 | } 82 | 83 | h6 { 84 | color: #4a4a4a; 85 | font-size: 1em; 86 | } 87 | 88 | small, 89 | sub, 90 | sup { 91 | font-size: 75%; 92 | } 93 | 94 | hr { 95 | border-color: #2c8898; 96 | } 97 | 98 | a { 99 | text-decoration: none; 100 | color: #2c8898; 101 | } 102 | a:hover { 103 | color: #982c61; 104 | border-bottom: 2px solid #4a4a4a; 105 | } 106 | 107 | ul { 108 | padding-left: 1.4em; 109 | } 110 | 111 | li { 112 | margin-bottom: 0.4em; 113 | } 114 | 115 | blockquote { 116 | font-style: italic; 117 | margin-left: 1.5em; 118 | padding-left: 1em; 119 | border-left: 3px solid #2c8898; 120 | } 121 | 122 | img { 123 | max-width: 100%; 124 | } 125 | 126 | /* Pre and Code */ 127 | pre { 128 | background-color: #f1f1f1; 129 | display: block; 130 | padding: 1em; 131 | overflow-x: auto; 132 | } 133 | 134 | code { 135 | font-size: 0.9em; 136 | padding: 0 0.5em; 137 | background-color: #f1f1f1; 138 | white-space: pre-wrap; 139 | } 140 | 141 | pre > code { 142 | padding: 0; 143 | background-color: transparent; 144 | white-space: pre; 145 | } 146 | 147 | /* Tables */ 148 | table { 149 | text-align: justify; 150 | width: 100%; 151 | border-collapse: collapse; 152 | } 153 | 154 | td, 155 | th { 156 | padding: 0.5em; 157 | border-bottom: 1px solid #f1f1f1; 158 | } 159 | 160 | /* Buttons, forms and input */ 161 | input, 162 | textarea { 163 | border: 1px solid #4a4a4a; 164 | } 165 | input:focus, 166 | textarea:focus { 167 | border: 1px solid #2c8898; 168 | } 169 | 170 | textarea { 171 | width: 100%; 172 | } 173 | 174 | .button, 175 | button, 176 | input[type='submit'], 177 | input[type='reset'], 178 | input[type='button'] { 179 | display: inline-block; 180 | padding: 5px 10px; 181 | text-align: center; 182 | text-decoration: none; 183 | white-space: nowrap; 184 | background-color: #2c8898; 185 | color: #f9f9f9; 186 | border-radius: 1px; 187 | border: 1px solid #2c8898; 188 | cursor: pointer; 189 | box-sizing: border-box; 190 | } 191 | .button[disabled], 192 | button[disabled], 193 | input[type='submit'][disabled], 194 | input[type='reset'][disabled], 195 | input[type='button'][disabled] { 196 | cursor: default; 197 | opacity: 0.5; 198 | } 199 | .button:focus, 200 | .button:hover, 201 | button:focus, 202 | button:hover, 203 | input[type='submit']:focus, 204 | input[type='submit']:hover, 205 | input[type='reset']:focus, 206 | input[type='reset']:hover, 207 | input[type='button']:focus, 208 | input[type='button']:hover { 209 | background-color: #982c61; 210 | border-color: #982c61; 211 | color: #f9f9f9; 212 | outline: 0; 213 | } 214 | 215 | textarea, 216 | select, 217 | input[type] { 218 | font-size: 1.5rem; 219 | color: #4a4a4a; 220 | padding: 6px 10px; 221 | /* The 6px vertically centers text on FF, ignored by Webkit */ 222 | margin-bottom: 10px; 223 | background-color: #f1f1f1; 224 | border: 1px solid #f1f1f1; 225 | border-radius: 4px; 226 | box-shadow: none; 227 | box-sizing: border-box; 228 | } 229 | textarea:focus, 230 | select:focus, 231 | input[type]:focus { 232 | border: 1px solid #2c8898; 233 | outline: 0; 234 | } 235 | 236 | input[type='checkbox']:focus { 237 | outline: 1px dotted #2c8898; 238 | } 239 | 240 | label, 241 | legend, 242 | fieldset { 243 | display: block; 244 | margin-bottom: 0.5rem; 245 | font-weight: 600; 246 | } 247 | -------------------------------------------------------------------------------- /playground/src/controllers/flatpickr_controller.js: -------------------------------------------------------------------------------- 1 | import Flatpickr from '../../../src/index.js' 2 | 3 | export default class extends Flatpickr {} 4 | -------------------------------------------------------------------------------- /playground/src/index.js: -------------------------------------------------------------------------------- 1 | import { Application } from 'stimulus' 2 | import FlatpickrController from './controllers/flatpickr_controller' 3 | 4 | const application = Application.start() 5 | application.register('flatpickr', FlatpickrController) 6 | -------------------------------------------------------------------------------- /playground/webpack.config.js: -------------------------------------------------------------------------------- 1 | // this webpack configuration is only used for the playground test site with yarn start 2 | const path = require('path') 3 | 4 | module.exports = { 5 | entry: { 6 | bundle: './playground/src/index.js' 7 | }, 8 | 9 | output: { 10 | filename: '[name].js', 11 | path: path.resolve(__dirname, 'playground/public') 12 | }, 13 | 14 | devServer: { 15 | contentBase: './playground/public', 16 | watchContentBase: true 17 | }, 18 | 19 | devtool: 'source-map', 20 | 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.js$/, 25 | exclude: [/node_modules/], 26 | use: [{ loader: 'babel-loader' }] 27 | } 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

📆 Stimulus-Flatpickr Wrapper

2 |

3 | 4 | npm version 5 | 6 | 7 | CircleCi build status 8 | 9 | 10 | Coverage 11 | 12 |

13 | 14 |

15 | Modest yet powerful wrapper of Flatpickr for Stimulus
16 | Only ~1kb 17 |

18 | 19 |
20 | 21 | - **Simple**: create advanced datepickers with less code 22 | - **Backend Friendly**: easily pass backend information to the datepicker (locals, availabilities, date formats etc) 23 | - **strftime friendly**: [converts automatically strftime](#date-and-time-formats) formats to flatpickr formating tokens 24 | - **Disable days of week**: easily disable days of week (ie: all sundays) 25 | - **Turbolinks**: make all your datepickers compatible with Turbolinks by design 26 | - **Getters**: all Flatpickr elements are available as [targets](#elements) 27 | - **Events/hooks**: all flatpickr [events/hooks](#callbacks) are directly available in your Stimulus Controller. 28 | - **Example**: [detailed example](#example) for adavanced usage of flatpickr 29 | - **MIT Licensed**: free for personal and commercial use 30 | 31 | ## A modest wrapper of Flatpickr for Stimulus 32 | 33 | By using this wrapper of [Flatpickr](http://flatpickr.js.org/) for [Stimulus](https://stimulusjs.org/) you can make all configurations for the Datepicker directly with the `data-attributes` of the HTML. This makes it very handy to create datepicker with server generate html and pass information from the backend to the datepicker. 34 | 35 | Here is a simple example: 36 | 37 | ```erb 38 | <%= form_with model: Appointement.new, authenticity_token: true do |f| %> 39 | <%= f.text_field :start_time, 40 | data: { 41 | controller: "flatpickr", 42 | flatpickr_min_date: Time.zone.now #disables past dates 43 | } %> 44 | <% end %> 45 | ``` 46 | 47 |

48 | 👇👇👇👇👇👇 49 |

50 |

51 | datetime picker result 52 |

53 | 54 | ## Example 55 | 56 | An example of a Rails app showcasing 57 | 58 | - localization of the datepicker 🌍 59 | - localization of the date formats 🌍 60 | - availabilities in the date picker 📅 61 | - Fully boosted with Turbolinks 🚀 62 | 63 | is available here : [Rails Stimulus Flatpickr](https://github.com/adrienpoly/rails_stimulus_flatpickr) 64 | 65 | ## Install 66 | 67 | This assumes that you have [Stimulus](https://stimulusjs.org/handbook/installing) already installed. For Rails(5.1+) app please refer this doc (https://github.com/rails/webpacker/blob/master/docs/integrations.md#stimulus) to get started with Stimulus. 68 | 69 | In your project just add the `flatpickr` and `stimulus-flatpickr` package. 70 | 71 | ```bash 72 | yarn add flatpickr 73 | yarn add stimulus-flatpickr 74 | ``` 75 | or 76 | 77 | ```bash 78 | npm i flatpickr 79 | npm i stimulus-flatpickr 80 | ``` 81 | Note: Do not use both `yarn` and `npm` to install packages, this might lead to an error: `...It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files` 82 | 83 | ### Using importmap 84 | 85 | ```bash 86 | ./bin/importmap pin flatpickr stimulus-flatpickr@beta 87 | ``` 88 | 89 | ## Basic usage 90 | 91 | If you only need to convert an input field in a DateTime picker, you just need to register a standard Stimulus controller and add some markup to your input field. 92 | 93 | ### Register a Flatpickr Controller 94 | 95 | manually register a new Stimulus controller in your main JS entry point. 96 | 97 | ```js 98 | // ./packs/application.js 99 | import { Application } from 'stimulus' 100 | import { definitionsFromContext } from 'stimulus/webpack-helpers' 101 | 102 | const application = Application.start() 103 | const context = require.context('../controllers', true, /\.js$/) 104 | application.load(definitionsFromContext(context)) 105 | 106 | // import Flatpickr 107 | import Flatpickr from 'stimulus-flatpickr' 108 | 109 | // Import style for flatpickr 110 | require("flatpickr/dist/flatpickr.css") 111 | 112 | // Manually register Flatpickr as a stimulus controller 113 | application.register('flatpickr', Flatpickr) 114 | ``` 115 | Note: 116 | * **Setup**: By Manually registering Flatpickr controller, you don't need to create a `flatpickr_controller.js` file. However, To add custom behavior you will have to create the `flatpickr_controller.js` file. Read more details about it below. 117 | * **Style**: You can always choose different theme for calender by requiring different `.css` file. You can find them inside your app's root directory `node_modules/flatpickr/dist/themes` 118 | * **Deployment**: In Production environment, include `<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>` in your `application.html.erb` file in order to load the calendar style. 119 | 120 | 121 | ### Using it with Rails 122 | 123 | You can now create forms and input fields easily by adding a `data-controller="flatpickr"` attribute to the input fields and pass [options](https://flatpickr.js.org/options/) with the Stimulus Controller states : `data-flatpickr-the-option`. 124 | 125 | ```erb 126 | <%= form_with model: Appointement.new, authenticity_token: true do |f| %> 127 | <%= f.text_field :start_time, 128 | data: { 129 | controller: "flatpickr", 130 | flatpickr_date_format: "Y-m-d", 131 | flatpickr_min_date: Time.zone.now 132 | } %> 133 | <% end %> 134 | ``` 135 | 136 |

137 | 👇👇👇👇👇👇 138 |

139 |

140 | datetime picker result 141 |

142 | 143 | ### Options & conventions 144 | 145 | All options for Flatpickr can be found [here](https://flatpickr.js.org/options/). 146 | 147 | All options are in `camelCase` (JS) and must be converted to `lower_snake_case` in the `data-attribute`. `lower_snake_case` is automatically converted to `kebab-case` when rails render the HTML. 148 | 149 | ```erb 150 | <%= f.text_field :start_time, 151 | data: { 152 | controller: "flatpickr", 153 | flatpickr_enable_time: true 154 | } 155 | } %> 156 | ``` 157 | 158 | will output this HTML: 159 | 160 | ```html 161 | 162 | ``` 163 | 164 | ### HTML markup 165 | 166 | If you are not using Rails or simply wants to markup your HTML directly, simply add a `html data-controller="flatpickr"` to your input field and some options `html data-flatpickr-some-option="value"` options must be converted from `camelCase` to `kebab-case` 167 | 168 | ## Advanced Usage 169 | 170 | If you need more than just displaying the standard DateTime picker, then you can extend the `stimulus-flatpickr` wrapper controller. This is necessary when you need to: 171 | 172 | - set a custom language 173 | - create custom callbacks 174 | - perform JS business logic 175 | 176 | **Skip basics installation steps from above!** 177 | 178 | ### Extends the controller 179 | 180 | create a new Stimulus controller that will inherit from `stimulus-flatpickr` 181 | 182 | ```js 183 | // ./controllers/flatpickr_controller.js 184 | // import stimulus-flatpickr wrapper controller to extend it 185 | import Flatpickr from 'stimulus-flatpickr' 186 | 187 | // you can also import a translation file 188 | import { French } from 'flatpickr/dist/l10n/fr.js' 189 | 190 | // import a theme (could be in your main CSS entry too...) 191 | import 'flatpickr/dist/themes/dark.css' 192 | 193 | // create a new Stimulus controller by extending stimulus-flatpickr wrapper controller 194 | export default class extends Flatpickr { 195 | initialize() { 196 | // sets your language (you can also set some global setting for all time pickers) 197 | this.config = { 198 | locale: French 199 | } 200 | } 201 | 202 | // all flatpickr hooks are available as callbacks in your Stimulus controller 203 | change(selectedDates, dateStr, instance) { 204 | console.log('the callback returns the selected dates', selectedDates) 205 | console.log('but returns it also as a string', dateStr) 206 | console.log('and the flatpickr instance', instance) 207 | } 208 | } 209 | ``` 210 | 211 | ### Global settings for all datepickers 212 | 213 | As we have seen just above you can easily from your rails `erb` code pass the flatpickr options. This is great for passing dynamic options that might change (ie enableDate, dateFormat etc). 214 | 215 | If all your datepickers share some global settings you can define them in your `initialize()` or `connect()` function. 216 | 217 | ```js 218 | initialize() { 219 | //global options 220 | this.config = { 221 | enableTime: true, 222 | time_24hr: true 223 | }; 224 | } 225 | ``` 226 | 227 | or with `connect()` 228 | 229 | ```js 230 | connect() { 231 | //global options 232 | this.config = { 233 | ...this.config, //spread options in case some where defined in initialize 234 | enableTime: true, 235 | time_24hr: true 236 | }; 237 | 238 | //always call super.connect() 239 | super.connect(); 240 | } 241 | ``` 242 | 243 | ### HTML markup 244 | 245 | Then in the same way as above you can now create forms and input fields easily by adding a `data-controller="flatpickr"` attribute to the input fields and pass [options](https://flatpickr.js.org/options/) with the Stimulus Controller states : `data-flatpick-the-option`. 246 | 247 | ```erb 248 | <%= form_with model: Appointement.new, authenticity_token: true do |f| %> 249 | <%= f.text_field :start_time, 250 | data: { 251 | controller: "flatpickr", 252 | flatpickr_date_format: "Y-m-d", 253 | flatpickr_min_date: Time.zone.now } 254 | %> 255 | <% end %> 256 | ``` 257 | 258 |

259 | 👇👇👇👇👇👇 260 |

261 |

262 | datetime picker result 263 |

264 | 265 | ### Date and Time formats 266 | 267 | Flatpickr has custom [formatting tokens](https://flatpickr.js.org/formatting/). in Rails (and other backends) formats are based on `strftime` standard. 268 | 269 | This package automatically converts `strftime` datetime formats to the nearest Flatpickr format. 270 | 271 | With this solution, it becomes handy to localize your date formats. `t("date.formats.long")` outputs `"%B %d, %Y"`for the local `:en` and it outputs `"%e %B %Y"` for the locale `:fr`. 272 | 273 | ```erb 274 | <%= form_with model: appointment do |f| %> 275 | <%= f.text_field :start_at, 276 | data: { 277 | controller: "flatpickr", 278 | flatpickr_alt_format: t("date.formats.long"), 279 | flatpickr_alt_input: true, 280 | flatpickr_min_date: Time.zone.now, 281 | } %> 282 | <% end %> 283 | ``` 284 | 285 |

286 | 👇👇👇👇👇👇 287 |

288 |

289 | datetime picker result 290 |

291 | 292 | ### Enable/Disable days of week 293 | 294 | With Flatpickr to disable certain days of the week, you need to use the disable js function. Obviously passing a function through data-attributes is not easy 😄. 295 | 296 | The wrapper introduce two new configuration options: 297 | 298 | - `disableDaysOfWeek`: pass an array of days to disable (all others are enabled) 299 | - `enableDaysOfWeek`: pass an array of days to enable (all others are disabled) 300 | 301 | 302 | 305 | 308 | 309 | 321 | 326 | 327 |
303 | Code 304 | 306 | Result 307 |
310 |
311 |    <%= form_with model: Appointement.new, authenticity_token: true do |f| %>
312 |      <%= f.text_field :start_time,
313 |        data: {
314 |          controller: "flatpickr",
315 |          flatpickr_disable_days_of_week: [5,6], #disables saturdays and sundays
316 |          flatpickr_disable: ["2018-09-25", "2018-09-26"] #disables individual dates
317 |        } %>
318 |    <% end %>
319 |    
320 |
322 |

323 | datetime picker result days of week 324 |

325 |
328 | 329 | ### Callbacks 330 | 331 | All Flatpickr [events/hooks](https://flatpickr.js.org/events/) are available as callbacks in the extended controller as demonstrated above for the `onChange` hook. 332 | 333 | Just add the function to your Stimulus Controller in `camelCase` without `on`. 334 | 335 | `onChange` -> `change(){}` 336 | 337 | ### Instance and its methods 338 | 339 | You can access the flatpickr instance from your Stimulus controller by calling `this.fp`. Also, the instance methods are available through this instance call. 340 | 341 | ```javascript 342 | yourFunction () { 343 | // ... 344 | this.fp.clear() 345 | this.fp.close() 346 | } 347 | ``` 348 | 349 | ### Custom elements 350 | 351 | If you want to display additional information on the calendar, you can wrap the Flatpickr controller arround custom elements. You can use the predefined target `instance` to attach the input element to the date picker. 352 | 353 | Example: 354 | 355 | ```html 356 |
357 | 358 | 359 | 360 | 361 |
362 | ``` 363 | 364 | In the stimulus controller, add the target: 365 | ```js 366 | static targets = ['custom'] 367 | 368 | yourFunction () { 369 | //... 370 | this.customTarget 371 | } 372 | ``` 373 | 374 | ### Getters 375 | 376 | #### Elements 377 | 378 | In your controller you can access the Flapickr [elements](https://flatpickr.js.org/instance-methods-properties-elements/#elements) using some Stimulus like targets. 379 | 380 | `this.calendarContainerTarget` : Self-explanatory. This is the div.flatpickr-calendar element. 381 | 382 | `this.currentYearElementTarget`: The input holding the current year. 383 | 384 | `this.daysTarget` : The container for all the day elements. 385 | 386 | `this.daysContainerTarget` : The container for all the day elements. 387 | 388 | `this.inputTarget` : The text input element associated with flatpickr. 389 | 390 | `this.nextMonthNavTarget` : The “right arrow” element responsible for incrementing the current month. 391 | 392 | `this.monthNavTarget` : The container with the month navigation. 393 | 394 | `this.prevMonthNavTarget` : The “left arrow” element responsible for decrementing the current month. 395 | 396 | `this.selectedDateElemTarget`: the selected date element. 397 | 398 | `this.todayDateElemTarget`: today element. 399 | 400 | `this.weekdayContainerTarget`: the container we all the days of the week. 401 | 402 | ## Overriding connect & disconnect 403 | 404 | if you need to override the connect function in the extended controller, you need to call `super` 405 | 406 | ```js 407 | connect(){ 408 | // ... 409 | // define global settings as explained in the global settings section before super 410 | // ... 411 | 412 | // always call super.connect() 413 | super.connect(); 414 | 415 | // ... 416 | // Your code can access this.fp flatpickr instance 417 | // ... 418 | } 419 | ``` 420 | 421 | ## Internationalization 422 | 423 | To handle multiple language to translate your datepicker and convert the date formats, you can have a look at the [example app](https://github.com/adrienpoly/rails_stimulus_flatpickr). `stimulus-flatpickr` makes it straight forward to handle locales. 424 | 425 | ## CSS 426 | 427 | This wrapper does not include any CSS. Flatpickr CSS should be loaded separately from the main Flatpickr package as you would normally do. 428 | 429 | ## Contributing 430 | 431 | Bug reports and pull requests are welcome. 432 | 433 | **To contribute:** 434 | 435 | Fork the project. 436 | 437 | Install dependencies 438 | 439 | `$ yarn install` 440 | 441 | Start the test watcher 442 | 443 | `$ yarn test:watch` 444 | 445 | Running one-off test runs can be done with: 446 | 447 | `$ yarn test` 448 | 449 | You can test locally also the results with the playground project [./playground](./playground) 450 | 451 | `$ yarn start:playground` 452 | 453 | **Then :** 454 | 455 | 👍 Write some tests 456 | 457 | 💪 Add your feature 458 | 459 | 🚀 Send a PR 460 | 461 | ## License 462 | 463 | This package is available as open source under the terms of the MIT License. 464 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import filesize from 'rollup-plugin-filesize' 2 | import resolve from 'rollup-plugin-node-resolve' 3 | import babel from 'rollup-plugin-babel' 4 | 5 | const pkg = require('./package.json') 6 | 7 | export default { 8 | input: 'src/index.js', 9 | external: ['stimulus', 'flatpickr'], 10 | output: [ 11 | { 12 | file: 'dist/index.js', 13 | format: 'cjs', 14 | sourcemap: true 15 | }, 16 | { 17 | file: 'dist/index.m.js', 18 | format: 'es', 19 | sourcemap: true 20 | }, 21 | { 22 | file: 'dist/index.umd.js', 23 | format: 'umd', 24 | name: pkg.amdName, 25 | sourcemap: true, 26 | globals: { 27 | stimulus: 'Stimulus', 28 | flatpickr: 'Flatpickr' 29 | } 30 | } 31 | ], 32 | plugins: [resolve(), babel(), filesize()] 33 | } 34 | -------------------------------------------------------------------------------- /spec/controllers/flatpickr_controller.js: -------------------------------------------------------------------------------- 1 | import Flatpickr from '../../src/index' 2 | 3 | export default class extends Flatpickr {} 4 | -------------------------------------------------------------------------------- /spec/controllers/flatpickr_hook_controller.js: -------------------------------------------------------------------------------- 1 | import Flatpickr from '../../src/index' 2 | 3 | export default class extends Flatpickr { 4 | change() {} 5 | 6 | open() {} 7 | 8 | close() {} 9 | 10 | monthChange() {} 11 | 12 | yearChange() {} 13 | 14 | ready() {} 15 | 16 | valueUpdate() {} 17 | 18 | dayCreate() {} 19 | } 20 | -------------------------------------------------------------------------------- /spec/fixtures/index-custom.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /spec/fixtures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /spec/flatpickr_controller_events_spec.js: -------------------------------------------------------------------------------- 1 | // import Flatpickr from './controllers/flatpickr_hook_controller' 2 | import { Application } from 'stimulus' 3 | import StimulusFlatpickr from './controllers/flatpickr_hook_controller' 4 | import 'flatpickr/dist/flatpickr.css' 5 | import { registerController, nextFrame, fixtureQuerySelector, addFlatpickrOption } from './helpers' 6 | import chai, { expect } from 'chai' 7 | import chaiDom from 'chai-dom' 8 | import sinon from 'sinon' 9 | import sinonChai from 'sinon-chai' 10 | var controller, spy 11 | const application = Application.start() 12 | 13 | chai.use(chaiDom) 14 | chai.use(sinonChai) 15 | 16 | describe('Flatpickr Controller EVENTS tests', function() { 17 | before('initialize controller', async function() { 18 | fixture.load('index.html') 19 | this.sandbox = sinon.createSandbox() 20 | this.sandbox.stub(StimulusFlatpickr.prototype, 'open') 21 | this.sandbox.stub(StimulusFlatpickr.prototype, 'close') 22 | this.sandbox.stub(StimulusFlatpickr.prototype, 'ready') 23 | this.sandbox.stub(StimulusFlatpickr.prototype, 'monthChange') 24 | this.sandbox.stub(StimulusFlatpickr.prototype, 'yearChange') 25 | this.spyDayCreate = this.sandbox.stub(StimulusFlatpickr.prototype, 'dayCreate') 26 | this.spyValueUpdate = this.sandbox.stub(StimulusFlatpickr.prototype, 'valueUpdate') 27 | this.spyChange = this.sandbox.stub(StimulusFlatpickr.prototype, 'change') 28 | application.register('datepicker', StimulusFlatpickr) 29 | await nextFrame() 30 | controller = application.controllers[0] 31 | }) 32 | 33 | describe('Initial state', function() { 34 | it('Stimulus Flatpickr controller READY function is called', function() { 35 | expect(controller.ready).has.been.calledOnce 36 | // expect(this.spyDayCreate.getCalls().length).to.be.at.least(28) 37 | }) 38 | }) 39 | 40 | describe('When focus', function() { 41 | it('Stimulus Flatpickr controller OPEN function is called', function() { 42 | const input = fixtureQuerySelector('.flatpickr-input') 43 | input.dispatchEvent(new Event('focus')) 44 | expect(controller.open).has.been.calledOnce 45 | }) 46 | }) 47 | 48 | describe('When set a new date', function() { 49 | it('Stimulus Flatpickr controller valueUpdate & change functions are called once', function() { 50 | controller.fp.setDate('2018-10-15', true, 'Y-m-d') 51 | expect(controller.valueUpdate).has.been.calledOnce 52 | expect(this.spyValueUpdate.getCall(0).args[1]).to.match(/2018-10-15/) 53 | expect(controller.change).has.been.calledOnce 54 | expect(this.spyChange.getCall(0).args[1]).to.match(/2018-10-15/) 55 | }) 56 | }) 57 | 58 | describe('When input focus out', function() { 59 | it('Stimulus Flatpickr controller CLOSE function is called once', function() { 60 | const otherInput = fixtureQuerySelector('#other-input') 61 | otherInput.dispatchEvent(new Event('focus')) 62 | expect(controller.close).has.been.calledOnce 63 | }) 64 | }) 65 | 66 | describe('When change month', function() { 67 | it('Stimulus Flatpickr controller monthChange() function is called once', function() { 68 | controller.fp.changeMonth(1) 69 | expect(controller.monthChange).has.been.calledOnce 70 | }) 71 | }) 72 | 73 | describe('When change year', function() { 74 | it('Stimulus Flatpickr controller yearChange() function is called once', function() { 75 | controller.fp.changeYear(1) 76 | expect(controller.yearChange).has.been.calledOnce 77 | }) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /spec/flatpickr_controller_spec.js: -------------------------------------------------------------------------------- 1 | import { Application } from 'stimulus' 2 | import chai, { expect } from 'chai' 3 | import chaiDom from 'chai-dom' 4 | 5 | import StimulusFlatpickr from './controllers/flatpickr_controller' 6 | import { nextFrame, fixtureQuerySelector, flatpickrCalendar, addFlatpickrOption, beforeEachSuite } from './helpers' 7 | 8 | import 'flatpickr/dist/flatpickr.css' 9 | 10 | var controller 11 | 12 | const application = Application.start() 13 | 14 | chai.use(chaiDom) 15 | 16 | describe('Flatpickr Controller tests', function() { 17 | beforeEachSuite('initialize controller', async function() { 18 | fixture.load('index.html') 19 | application.register('datepicker', StimulusFlatpickr) 20 | await nextFrame() 21 | controller = application.controllers[0] 22 | }) 23 | 24 | describe('Initial state', function() { 25 | it('Stimulus flatpickr controller is initialized', function() { 26 | expect(controller).to.exist 27 | }) 28 | 29 | it('can find an input with the class "flatpickr-input"', function() { 30 | expect(fixtureQuerySelector('.flatpickr-input')).to.exist 31 | }) 32 | 33 | it('can find a div in the document with the class "flatpickr-calendar"', function() { 34 | expect(flatpickrCalendar()).to.exist 35 | }) 36 | 37 | it('does not have the class open', function() { 38 | expect(flatpickrCalendar()).not.to.have.class('open') 39 | }) 40 | }) 41 | 42 | describe('When input focus', function() { 43 | it('Flatpickr has the open class', async function() { 44 | const input = fixtureQuerySelector('.flatpickr-input') 45 | input.dispatchEvent(new Event('focus')) 46 | await nextFrame() 47 | 48 | expect(flatpickrCalendar()).to.have.class('open') 49 | }) 50 | }) 51 | 52 | describe('When input focus out', function() { 53 | it('Flatpickr does not have open class', function() { 54 | controller.fp.open() 55 | const otherInput = fixtureQuerySelector('#other-input') 56 | otherInput.dispatchEvent(new Event('focus')) 57 | 58 | expect(flatpickrCalendar()).not.to.have.class('open') 59 | }) 60 | }) 61 | 62 | describe('Flatpickr options with time enabled', function() { 63 | context('set enableTime false option', function() { 64 | it('cannot set time', async function() { 65 | addFlatpickrOption('EnableTime', 'false', controller) 66 | await nextFrame() 67 | 68 | expect(flatpickrCalendar()).not.to.have.class('hasTime') 69 | expect(flatpickrCalendar()).not.to.contain('.flatpickr-time') 70 | }) 71 | }) 72 | 73 | context('set enableTime true option', function() { 74 | it('can set time', async function() { 75 | addFlatpickrOption('EnableTime', 'true', controller) 76 | await nextFrame() 77 | 78 | expect(flatpickrCalendar()).to.have.class('hasTime') 79 | expect(flatpickrCalendar()).to.contain('.flatpickr-time') 80 | }) 81 | }) 82 | 83 | context('add time_24hr true option', function() { 84 | it('am pm are not visible', async function() { 85 | addFlatpickrOption('Time-24hr', 'true', controller) 86 | await nextFrame() 87 | 88 | expect(flatpickrCalendar()).to.have.class('hasTime') 89 | expect(flatpickrCalendar()).not.to.contain('.flatpickr-am-pm') 90 | }) 91 | }) 92 | 93 | context('add enableSeconds false option', function() { 94 | it('cannot set seconds', async function() { 95 | addFlatpickrOption('EnableSeconds', 'false', controller) 96 | await nextFrame() 97 | 98 | expect(flatpickrCalendar()).not.to.contain('.flatpickr-second') 99 | }) 100 | }) 101 | 102 | context('add enableSeconds true option', function() { 103 | it('can set seconds', async function() { 104 | addFlatpickrOption('EnableSeconds', 'true', controller) 105 | await nextFrame() 106 | 107 | expect(flatpickrCalendar()).to.contain('.flatpickr-time') 108 | expect(flatpickrCalendar()).to.contain('.flatpickr-second') 109 | }) 110 | }) 111 | }) 112 | 113 | describe('Flatpickr options with time Disabled', function() { 114 | context('add multiMonth 2 option', function() { 115 | it('can see two months', async function() { 116 | addFlatpickrOption('EnableTime', 'false', controller) 117 | addFlatpickrOption('ShowMonths', 2, controller) 118 | await nextFrame() 119 | 120 | expect(flatpickrCalendar()).to.have.class('multiMonth') 121 | }) 122 | }) 123 | 124 | context('add AltFormat %Y-%m-%d option', function() { 125 | it('can see new input field', async function() { 126 | addFlatpickrOption('AltFormat', '%B %d, %Y', controller) 127 | addFlatpickrOption('DefaultDate', '2018-10-15', controller) 128 | await nextFrame() 129 | 130 | expect(controller.selectedDateElemTarget).to.have.attribute('aria-label', 'October 15, 2018') 131 | expect(controller.inputTarget).to.have.value('2018-10-15') 132 | }) 133 | 134 | it('base dateFormat remains the same', function() { 135 | expect(controller.fp.config.dateFormat).to.equal('Y-m-d') 136 | }) 137 | }) 138 | 139 | context('add range mode', function() { 140 | it('calendar has the range class', function() { 141 | addFlatpickrOption('Mode', 'range', controller) 142 | expect(flatpickrCalendar()).to.have.class('rangeMode') 143 | }) 144 | 145 | it('can set preloaded range dates', function() { 146 | addFlatpickrOption('Mode', 'range', controller) 147 | addFlatpickrOption('DefaultDate', ['2018-10-15', '2018-10-20'], controller) 148 | 149 | expect(controller.inputTarget).to.have.value('2018-10-15 to 2018-10-20') 150 | }) 151 | }) 152 | }) 153 | 154 | describe('Flatpickr disable dates options', function() { 155 | before(async function() { 156 | controller.fp.setDate('2018-10-15') 157 | addFlatpickrOption('DateFormat', 'Y-m-d', controller) 158 | addFlatpickrOption('DefaultDate', '2018-10-15', controller) 159 | await nextFrame() 160 | }) 161 | 162 | context('set min date', function() { 163 | it('dates before min date are disabled', async function() { 164 | addFlatpickrOption('MinDate', '2018-10-03', controller) 165 | await nextFrame() 166 | 167 | expect(document.querySelector('span[aria-label="October 2, 2018"]')).to.have.class('flatpickr-disabled') 168 | }) 169 | }) 170 | 171 | context('disable dates', function() { 172 | it('disabled individual dates are disabled', async function() { 173 | addFlatpickrOption('Disable', ['2018-10-14', '2018-10-17'], controller) 174 | await nextFrame() 175 | 176 | expect(document.querySelector('span[aria-label="October 14, 2018"]')).to.have.class('flatpickr-disabled') 177 | }) 178 | 179 | it('add disable days of week all sundays are disabled', async function() { 180 | addFlatpickrOption('DisableDaysOfWeek', [6], controller) 181 | await nextFrame() 182 | 183 | expect(document.querySelector('span[aria-label="October 6, 2018"]')).to.have.class('flatpickr-disabled') 184 | expect(document.querySelector('span[aria-label="October 13, 2018"]')).to.have.class('flatpickr-disabled') 185 | }) 186 | }) 187 | }) 188 | 189 | describe('Flatpickr enable dates options', function() { 190 | before(async function() { 191 | controller.fp.setDate('2018-10-15') 192 | addFlatpickrOption('DateFormat', 'Y-m-d', controller) 193 | addFlatpickrOption('DefaultDate', '2018-10-15', controller) 194 | await nextFrame() 195 | }) 196 | 197 | it('enable individual dates are enabled', async function() { 198 | addFlatpickrOption('Enable', ['2018-10-14', '2018-10-17'], controller) 199 | addFlatpickrOption('DefaultDate', '2018-10-14', controller) 200 | await nextFrame() 201 | 202 | expect(document.querySelector('span[aria-label="October 14, 2018"]')).not.to.have.class('flatpickr-disabled') 203 | }) 204 | 205 | it('add enable days of week only mondays are enabled', async function() { 206 | addFlatpickrOption('EnableDaysOfWeek', [1], controller) 207 | await nextFrame() 208 | 209 | expect(document.querySelector('span[aria-label="October 8, 2018"]')).not.to.have.class('flatpickr-disabled') 210 | expect(document.querySelector('span[aria-label="October 15, 2018"]')).not.to.have.class('flatpickr-disabled') 211 | expect(document.querySelector('span[aria-label="October 14, 2018"]')).not.to.have.class('flatpickr-disabled') 212 | }) 213 | }) 214 | 215 | describe('Flatpickr enable days of week only', function() { 216 | it('add enable days of week', async function() { 217 | addFlatpickrOption('EnableDaysOfWeek', [1], controller) 218 | await nextFrame() 219 | 220 | const enabledDays = controller.daysTarget.querySelectorAll('span.flatpickr-day:not(.flatpickr-disabled)') 221 | expect(enabledDays.length).to.be.within(4, 6) 222 | }) 223 | }) 224 | 225 | describe('Flatpickr disable days of week only', function() { 226 | it('add disable days of week', async function() { 227 | addFlatpickrOption('DisableDaysOfWeek', [1], controller) 228 | await nextFrame() 229 | 230 | const disabledDays = controller.daysTarget.querySelectorAll('span.flatpickr-day.flatpickr-disabled') 231 | expect(disabledDays.length).to.be.within(4, 6) 232 | }) 233 | }) 234 | 235 | describe('Flatpickr disable days test with invalid entries', function() { 236 | context("don't provide an array", function() { 237 | it('it still work and no days are disabled', async function() { 238 | addFlatpickrOption('DisableDaysOfWeek', 1, controller) 239 | await nextFrame() 240 | 241 | const disabledDays = controller.daysTarget.querySelectorAll('span.flatpickr-day.flatpickr-disabled') 242 | expect(controller).to.exist 243 | expect(disabledDays.length).to.equal(0) 244 | }) 245 | }) 246 | 247 | context('provide an array of string', function() { 248 | it('it is the same as an array of integer', async function() { 249 | addFlatpickrOption('DisableDaysOfWeek', ['1'], controller) 250 | await nextFrame() 251 | 252 | const disabledDays = controller.daysTarget.querySelectorAll('span.flatpickr-day.flatpickr-disabled') 253 | expect(controller).to.exist 254 | expect(disabledDays.length).to.be.within(4, 6) 255 | }) 256 | }) 257 | }) 258 | 259 | describe('Flatpickr enable days test with invalid entries', function() { 260 | context("don't provide an array", function() { 261 | it('it still work and all days are disabled', async function() { 262 | addFlatpickrOption('EnableDaysOfWeek', 1, controller) 263 | await nextFrame() 264 | 265 | const disabledDays = controller.daysTarget.querySelectorAll('span.flatpickr-day.flatpickr-disabled') 266 | expect(controller).to.exist 267 | expect(disabledDays.length).to.be.at.least(30) 268 | }) 269 | }) 270 | }) 271 | 272 | describe('Boolean options can be true/false or 1/0', function() { 273 | it('accpet 1 as a true value', async function() { 274 | addFlatpickrOption('EnableTime', 1, controller) 275 | await nextFrame() 276 | 277 | expect(controller._boolean('enable-time')).to.be.true 278 | }) 279 | 280 | it('accept "true" as a true value', async function() { 281 | addFlatpickrOption('EnableTime', 'true', controller) 282 | await nextFrame() 283 | 284 | expect(controller._boolean('enable-time')).to.be.true 285 | }) 286 | 287 | it('accept 0 as a false value', async function() { 288 | addFlatpickrOption('EnableTime', 0, controller) 289 | await nextFrame() 290 | 291 | expect(controller._boolean('enable-time')).to.be.false 292 | }) 293 | 294 | it('accept "false" as a false value', async function() { 295 | addFlatpickrOption('EnableTime', 'false', controller) 296 | await nextFrame() 297 | 298 | expect(controller._boolean('enable-time')).to.be.false 299 | }) 300 | }) 301 | }) 302 | -------------------------------------------------------------------------------- /spec/flatpickr_controller_targets_spec.js: -------------------------------------------------------------------------------- 1 | import StimulusFlatpickr from './controllers/flatpickr_controller' 2 | import { Application } from 'stimulus' 3 | import 'flatpickr/dist/flatpickr.css' 4 | import { nextFrame } from './helpers' 5 | import chai, { expect } from 'chai' 6 | import chaiDom from 'chai-dom' 7 | var controller 8 | const application = Application.start() 9 | 10 | const elementsSelectors = { 11 | calendarContainer: '.flatpickr-calendar', 12 | currentYearElement: '.cur-year', 13 | days: '.dayContainer', 14 | daysContainer: '.flatpickr-days', 15 | input: '.flatpickr-input', 16 | monthNav: '.flatpickr-months', 17 | nextMonthNav: '.flatpickr-next-month', 18 | prevMonthNav: '.flatpickr-prev-month', 19 | rContainer: '.flatpickr-rContainer', 20 | todayDateElem: '.flatpickr-day.today', 21 | weekdayContainer: '.flatpickr-weekdays' 22 | } 23 | 24 | chai.use(chaiDom) 25 | 26 | describe('Flatpickr Controller Target tests', function() { 27 | before('initialize controller', async function() { 28 | fixture.load('index.html') 29 | application.register('datepicker', StimulusFlatpickr) 30 | await nextFrame() 31 | controller = application.controllers[0] 32 | // controller = await registerController('flatpickr', Flatpickr) 33 | // await addFlatpickrOption('DefaultDate', new Date(), controller) 34 | // await Promise.resolve() 35 | }) 36 | 37 | Object.keys(elementsSelectors).forEach((element) => { 38 | const selector = elementsSelectors[element] 39 | it(`${element}Target`, async function() { 40 | expect(controller[`${element}Target`]).to.match(selector) 41 | expect(controller[`${element}Target`]).to.exist 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /spec/flatpickr_custom_element_spec.js: -------------------------------------------------------------------------------- 1 | import { Application } from 'stimulus' 2 | import chai, { expect } from 'chai' 3 | import chaiDom from 'chai-dom' 4 | 5 | import StimulusFlatpickr from './controllers/flatpickr_controller' 6 | import { nextFrame, beforeEachSuite } from './helpers' 7 | 8 | import 'flatpickr/dist/flatpickr.css' 9 | 10 | var controller 11 | 12 | const application = Application.start() 13 | 14 | chai.use(chaiDom) 15 | 16 | describe('Flatpickr Custom element Controller tests', function() { 17 | beforeEachSuite('initialize controller', async function() { 18 | fixture.load('index-custom.html') 19 | application.register('datepicker', StimulusFlatpickr) 20 | await nextFrame() 21 | controller = application.controllers[0] 22 | }) 23 | 24 | describe('Initial state', function() { 25 | it('Stimulus flatpickr controller is initialized', function() { 26 | expect(controller).to.exist 27 | }) 28 | 29 | it('flatpickr instance exists', function() { 30 | expect(controller.fp).to.exist 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /spec/helpers.js: -------------------------------------------------------------------------------- 1 | import { Application } from 'stimulus' 2 | 3 | const nextFrame = async () => { 4 | return new Promise((resolve) => requestAnimationFrame(resolve)) 5 | } 6 | 7 | async function registerController(element, controllerType) { 8 | const stimulusApp = Application.start() 9 | stimulusApp.register(element, controllerType) 10 | 11 | return new Promise((resolve) => 12 | setTimeout(() => { 13 | resolve(stimulusApp.controllers[0]) 14 | }) 15 | ) 16 | } 17 | 18 | function flatpickrCalendar() { 19 | return document.querySelector('.flatpickr-calendar') 20 | } 21 | 22 | function addFlatpickrOption(option, value, controller) { 23 | const flatpickr = fixture.el.querySelector('#datepicker') 24 | flatpickr.dataset[`datepicker${option}`] = typeof value === 'object' ? JSON.stringify(value) : value 25 | controller.connect() 26 | // await nextFrame() 27 | } 28 | 29 | function fixtureQuerySelector(selector) { 30 | return fixture.el.querySelector(selector) 31 | } 32 | 33 | function calendarQuerySelector(selector) { 34 | return document.querySelector(`.flatpickr-calendar ${selector}`) 35 | } 36 | 37 | function resetDataAttributes(controller) { 38 | const attributes = controller.element.dataset 39 | Object.keys(attributes).forEach((attribute) => { 40 | delete controller.element.dataset[attribute] 41 | }) 42 | controller.element.dataset.controller = 'flatpickr' 43 | controller.connect() 44 | } 45 | 46 | function beforeEachSuite(title, fn) { 47 | before(title, function() { 48 | const suites = this.test.parent.suites || [] 49 | suites.forEach((s) => { 50 | s.beforeAll(fn) 51 | const hook = s._beforeAll.pop() 52 | s._beforeAll.unshift(hook) 53 | }) 54 | }) 55 | } 56 | 57 | export { 58 | registerController, 59 | fixtureQuerySelector, 60 | calendarQuerySelector, 61 | flatpickrCalendar, 62 | addFlatpickrOption, 63 | resetDataAttributes, 64 | beforeEachSuite, 65 | nextFrame 66 | } 67 | -------------------------------------------------------------------------------- /spec/mapping_spec.js: -------------------------------------------------------------------------------- 1 | import { convertDateFormat } from '../src/strftime_mapping' 2 | import { expect } from 'chai' 3 | 4 | const testDateFormats = { 5 | '%Y-%m-%d': 'Y-m-d', 6 | '%B %d, %Y': 'F d, Y', 7 | '%b %d': 'M d', 8 | '%d/%m/%Y': 'd/m/Y', 9 | '%e %b': 'j M', 10 | '%e %B %Y': 'j F Y', 11 | '%A %m/%d/%y, %-l:%M': 'l m/d/y, h:i' 12 | } 13 | 14 | const mapping = { 15 | '%Y': 'Y', 16 | '%y': 'y', 17 | '%C': 'Y', 18 | '%m': 'm', 19 | '%-m': 'n', 20 | '%_m': 'n', 21 | '%B': 'F', 22 | '%^B': 'F', 23 | '%b': 'M', 24 | '%^b': 'M', 25 | '%h': 'M', 26 | '%^h': 'M', 27 | '%d': 'd', 28 | '%-d': 'j', 29 | '%e': 'j', 30 | '%H': 'H', 31 | '%k': 'H', 32 | '%I': 'h', 33 | '%l': 'h', 34 | '%-l': 'h', 35 | '%P': 'K', 36 | '%p': 'K', 37 | '%M': 'i', 38 | '%S': 'S', 39 | '%A': 'l', 40 | '%a': 'D', 41 | '%w': 'w' 42 | } 43 | 44 | describe('strftime date conversion to Flatpickr tokens', function () { 45 | context('convertDateFormat', function () { 46 | it('random string without % should not be changed', function () { 47 | const testString = 'eznlen =:=:;:::; =:;fzlefnlzief &é&ééçà(!345345345)' 48 | expect(convertDateFormat(testString)).to.equal(testString) 49 | }) 50 | 51 | Object.keys(testDateFormats).forEach((strftimeDateFormat) => { 52 | const flatpickrDateFormat = testDateFormats[strftimeDateFormat] 53 | it(`strftime format ${strftimeDateFormat} to be converted to ${flatpickrDateFormat}`, function () { 54 | expect(convertDateFormat(strftimeDateFormat)).to.equal(flatpickrDateFormat) 55 | }) 56 | }) 57 | 58 | it('strftime format to be converted to', function () { 59 | Object.keys(mapping).forEach((strftimeDateFormat) => { 60 | const flatpickrDateFormat = mapping[strftimeDateFormat] 61 | expect(convertDateFormat(strftimeDateFormat)).to.equal(flatpickrDateFormat) 62 | }) 63 | }) 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /spec/utils_spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { kebabCase, capitalize } from "../src/utils"; 3 | 4 | describe("Utils", function() { 5 | describe("kebabCase", function() { 6 | it("test standard conversion", function() { 7 | expect(kebabCase("enableTime")).to.equal("enable-time"); 8 | expect(kebabCase("EnableTime")).to.equal("enable-time"); 9 | }); 10 | 11 | it("test edge case such as time_24hr", function() { 12 | expect(kebabCase("time_24hr")).to.equal("time-24hr"); 13 | }); 14 | }); 15 | 16 | describe("capitalize", function() { 17 | it("test standard conversion", function() { 18 | expect(capitalize("enableTime")).to.equal("EnableTime"); 19 | }); 20 | 21 | it("test edge case such as time_24hr", function() { 22 | expect(capitalize("time_24hr")).to.equal("Time_24hr"); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/config_options.js: -------------------------------------------------------------------------------- 1 | const booleanOptions = [ 2 | 'allowInput', 3 | 'altInput', 4 | 'animate', 5 | 'clickOpens', 6 | 'closeOnSelect', 7 | 'disableMobile', 8 | 'enableSeconds', 9 | 'enableTime', 10 | 'inline', 11 | 'noCalendar', 12 | 'shorthandCurrentMonth', 13 | 'static', 14 | 'time_24hr', 15 | 'weekNumbers', 16 | 'wrap' 17 | ] 18 | 19 | const stringOptions = [ 20 | 'altInputClass', 21 | 'conjunction', 22 | 'mode', 23 | 'nextArrow', 24 | 'position', 25 | 'prevArrow', 26 | 'monthSelectorType' 27 | ] 28 | 29 | const numberOptions = [ 30 | 'defaultHour', 31 | 'defaultMinute', 32 | 'defaultSeconds', 33 | 'hourIncrement', 34 | 'minuteIncrement', 35 | 'showMonths' 36 | ] 37 | 38 | const arrayOptions = ['disable', 'enable', 'disableDaysOfWeek', 'enableDaysOfWeek'] 39 | 40 | const arrayOrStringOptions = ['defaultDate'] 41 | 42 | const dateOptions = ['maxDate', 'minDate', 'maxTime', 'minTime', 'now'] 43 | 44 | export const dateFormats = ['altFormat', 'ariaDateFormat', 'dateFormat'] 45 | 46 | export const options = { 47 | string: stringOptions, 48 | boolean: booleanOptions, 49 | date: dateOptions, 50 | array: arrayOptions, 51 | number: numberOptions, 52 | arrayOrString: arrayOrStringOptions 53 | } 54 | -------------------------------------------------------------------------------- /src/elements.js: -------------------------------------------------------------------------------- 1 | export const elements = [ 2 | 'calendarContainer', 3 | 'currentYearElement', 4 | 'days', 5 | 'daysContainer', 6 | 'input', 7 | 'nextMonthNav', 8 | 'monthNav', 9 | 'prevMonthNav', 10 | 'rContainer', 11 | 'selectedDateElem', 12 | 'todayDateElem', 13 | 'weekdayContainer' 14 | ] 15 | -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | export const events = ['change', 'open', 'close', 'monthChange', 'yearChange', 'ready', 'valueUpdate', 'dayCreate'] 2 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Controller } from 'stimulus' 2 | import flatpickr from 'flatpickr' 3 | import { kebabCase, capitalize } from './utils' 4 | import { options, dateFormats } from './config_options' 5 | import { events } from './events' 6 | import { elements } from './elements' 7 | import { convertDateFormat } from './strftime_mapping' 8 | 9 | class StimulusFlatpickr extends Controller { 10 | static targets = ['instance'] 11 | 12 | initialize() { 13 | this.config = {} 14 | } 15 | 16 | connect() { 17 | this._initializeEvents() 18 | this._initializeOptions() 19 | this._initializeDateFormats() 20 | 21 | this.fp = flatpickr(this.flatpickrElement, { 22 | ...this.config 23 | }) 24 | 25 | this._initializeElements() 26 | } 27 | 28 | disconnect() { 29 | const value = this.inputTarget.value 30 | this.fp.destroy() 31 | this.inputTarget.value = value 32 | } 33 | 34 | _initializeEvents() { 35 | events.forEach((event) => { 36 | if (this[event]) { 37 | const hook = `on${capitalize(event)}` 38 | this.config[hook] = this[event].bind(this) 39 | } 40 | }) 41 | } 42 | 43 | _initializeOptions() { 44 | Object.keys(options).forEach((optionType) => { 45 | const optionsCamelCase = options[optionType] 46 | optionsCamelCase.forEach((option) => { 47 | const optionKebab = kebabCase(option) 48 | if (this.data.has(optionKebab)) { 49 | this.config[option] = this[`_${optionType}`](optionKebab) 50 | } 51 | }) 52 | }) 53 | this._handleDaysOfWeek() 54 | } 55 | 56 | _handleDaysOfWeek() { 57 | if (this.config.disableDaysOfWeek) { 58 | this.config.disableDaysOfWeek = this._validateDaysOfWeek(this.config.disableDaysOfWeek) 59 | this.config.disable = [...(this.config.disable || []), this._disable.bind(this)] 60 | } 61 | 62 | if (this.config.enableDaysOfWeek) { 63 | this.config.enableDaysOfWeek = this._validateDaysOfWeek(this.config.enableDaysOfWeek) 64 | this.config.enable = [...(this.config.enable || []), this._enable.bind(this)] 65 | } 66 | } 67 | 68 | _validateDaysOfWeek(days) { 69 | if (Array.isArray(days)) { 70 | return days.map((day) => parseInt(day)) 71 | } else { 72 | console.error('days of week must be a valid array') 73 | return [] 74 | } 75 | } 76 | 77 | _disable(date) { 78 | const disabledDays = this.config.disableDaysOfWeek 79 | return disabledDays.includes(date.getDay()) 80 | } 81 | 82 | _enable(date) { 83 | const enabledDays = this.config.enableDaysOfWeek 84 | return enabledDays.includes(date.getDay()) 85 | } 86 | 87 | _initializeDateFormats() { 88 | dateFormats.forEach((dateFormat) => { 89 | if (this.data.has(dateFormat)) { 90 | this.config[dateFormat] = convertDateFormat(this.data.get(dateFormat)) 91 | } 92 | }) 93 | } 94 | 95 | _initializeElements() { 96 | elements.forEach((element) => { 97 | this[`${element}Target`] = this.fp[element] 98 | }) 99 | } 100 | 101 | _string(option) { 102 | return this.data.get(option) 103 | } 104 | 105 | _date(option) { 106 | return this.data.get(option) 107 | } 108 | 109 | _boolean(option) { 110 | return !(this.data.get(option) == '0' || this.data.get(option) == 'false') 111 | } 112 | 113 | _array(option) { 114 | return JSON.parse(this.data.get(option)) 115 | } 116 | 117 | _number(option) { 118 | return parseInt(this.data.get(option)) 119 | } 120 | 121 | _arrayOrString(option) { 122 | const val = this.data.get(option) 123 | try { 124 | return JSON.parse(val) 125 | } catch (e) { 126 | return val 127 | } 128 | } 129 | 130 | get flatpickrElement() { 131 | return (this.hasInstanceTarget && this.instanceTarget) || this.element 132 | } 133 | } 134 | 135 | export default StimulusFlatpickr 136 | -------------------------------------------------------------------------------- /src/strftime_mapping.js: -------------------------------------------------------------------------------- 1 | export const mapping = { 2 | '%Y': 'Y', 3 | '%y': 'y', 4 | '%C': 'Y', 5 | '%m': 'm', 6 | '%-m': 'n', 7 | '%_m': 'n', 8 | '%B': 'F', 9 | '%^B': 'F', 10 | '%b': 'M', 11 | '%^b': 'M', 12 | '%h': 'M', 13 | '%^h': 'M', 14 | '%d': 'd', 15 | '%-d': 'j', 16 | '%e': 'j', 17 | '%H': 'H', 18 | '%k': 'H', 19 | '%I': 'h', 20 | '%l': 'h', 21 | '%-l': 'h', 22 | '%P': 'K', 23 | '%p': 'K', 24 | '%M': 'i', 25 | '%S': 'S', 26 | '%A': 'l', 27 | '%a': 'D', 28 | '%w': 'w' 29 | } 30 | 31 | const strftimeRegex = new RegExp( 32 | Object.keys(mapping) 33 | .join('|') 34 | .replace(new RegExp('\\^', 'g'), '\\^'), 35 | 'g' 36 | ) 37 | 38 | export const convertDateFormat = (format) => { 39 | return format.replace(strftimeRegex, (match) => { 40 | return mapping[match] 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export const kebabCase = string => 2 | string 3 | .replace(/([a-z])([A-Z])/g, "$1-$2") 4 | .replace(/[\s_]+/g, "-") 5 | .toLowerCase(); 6 | 7 | export const capitalize = string => { 8 | return string.charAt(0).toUpperCase() + string.slice(1); 9 | }; 10 | --------------------------------------------------------------------------------