{ this.setState({date}) }} />
- )
- }
-}
-```
-* `flatpickr options`: you can pass all `flatpickr parameters` to `props.options`
-* All flatpickr [hooks][hooks] can be passed as a react prop, or to `props.options`
-
-```jsx
-
-```
-
-### Themes
-Please import themes directly from the `flatpickr` dependency. In most cases, you should just be able to `import 'flatpickr/dist/themes/theme.css'`, but in some cases npm or yarn may install `flatpickr` in `node_modules/react-flatpickr/node_modules/flatpickr`. If that happens, removing your `node_modules` dir and reinstalling should put flatpickr in the root `node_modules` dir, or you can import from `react-flatpickr/node_modules/flatpickr` manually.
-
-## License
-MIT
-
-[npm-img]: https://img.shields.io/npm/v/react-flatpickr.svg?style=flat-square
-[npm-url]: https://npmjs.org/package/react-flatpickr
-[travis-img]: https://img.shields.io/travis/coderhaoxin/react-flatpickr.svg?style=flat-square
-[travis-url]: https://travis-ci.org/coderhaoxin/react-flatpickr
-[codecov-img]: https://img.shields.io/codecov/c/github/coderhaoxin/react-flatpickr.svg?style=flat-square
-[codecov-url]: https://codecov.io/github/coderhaoxin/react-flatpickr?branch=master
-[license-img]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square
-[license-url]: http://opensource.org/licenses/MIT
-[david-img]: https://img.shields.io/david/coderhaoxin/react-flatpickr.svg?style=flat-square
-[david-url]: https://david-dm.org/coderhaoxin/react-flatpickr
-[hooks]: https://chmln.github.io/flatpickr/events/#hooks
diff --git a/src/plugins/date-picker/components/react-flatpickr/build/index.js b/src/plugins/date-picker/components/react-flatpickr/build/index.js
deleted file mode 100644
index b3c6bdbe..00000000
--- a/src/plugins/date-picker/components/react-flatpickr/build/index.js
+++ /dev/null
@@ -1,168 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _react = require('react');
-
-var _react2 = _interopRequireDefault(_react);
-
-var _propTypes = require('prop-types');
-
-var _propTypes2 = _interopRequireDefault(_propTypes);
-
-var _flatpickr = require('flatpickr');
-
-var _flatpickr2 = _interopRequireDefault(_flatpickr);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
-
-var hooks = ['onChange', 'onOpen', 'onClose', 'onMonthChange', 'onYearChange', 'onReady', 'onValueUpdate', 'onDayCreate'];
-var hookPropType = _propTypes2.default.oneOfType([_propTypes2.default.func, _propTypes2.default.arrayOf(_propTypes2.default.func)]);
-
-var DateTimePicker = function (_Component) {
- _inherits(DateTimePicker, _Component);
-
- function DateTimePicker() {
- _classCallCheck(this, DateTimePicker);
-
- return _possibleConstructorReturn(this, (DateTimePicker.__proto__ || Object.getPrototypeOf(DateTimePicker)).apply(this, arguments));
- }
-
- _createClass(DateTimePicker, [{
- key: 'componentWillReceiveProps',
- value: function componentWillReceiveProps(props) {
- var _this2 = this;
-
- var options = props.options;
-
- var prevOptions = this.props.options;
-
- // Add prop hooks to options
- hooks.forEach(function (hook) {
- if (props.hasOwnProperty(hook)) {
- options[hook] = props[hook];
- }
- // Add prev ones too so we can compare against them later
- if (_this2.props.hasOwnProperty(hook)) {
- prevOptions[hook] = _this2.props[hook];
- }
- });
-
- var optionsKeys = Object.getOwnPropertyNames(options);
-
- for (var index = optionsKeys.length - 1; index >= 0; index--) {
- var key = optionsKeys[index];
- var value = options[key];
-
- if (value !== prevOptions[key]) {
- // Hook handlers must be set as an array
- if (hooks.indexOf(key) !== -1 && !Array.isArray(value)) {
- value = [value];
- }
-
- this.flatpickr.set(key, value);
- }
- }
-
- if (props.hasOwnProperty('value') && props.value !== this.props.value) {
- this.flatpickr.setDate(props.value, false);
- }
- }
- }, {
- key: 'componentDidMount',
- value: function componentDidMount() {
- var _this3 = this;
-
- var options = _extends({
- onClose: function onClose() {
- _this3.node.blur && _this3.node.blur();
- }
- }, this.props.options);
-
- // Add prop hooks to options
- hooks.forEach(function (hook) {
- if (_this3.props[hook]) {
- options[hook] = _this3.props[hook];
- }
- });
-
- this.flatpickr = new _flatpickr2.default(this.node, options);
-
- if (this.props.hasOwnProperty('value')) {
- this.flatpickr.setDate(this.props.value, false);
- }
- }
- }, {
- key: 'componentWillUnmount',
- value: function componentWillUnmount() {
- this.flatpickr.destroy();
- }
- }, {
- key: 'render',
- value: function render() {
- var _this4 = this;
-
- // eslint-disable-next-line no-unused-vars
- var _props = this.props,
- options = _props.options,
- defaultValue = _props.defaultValue,
- value = _props.value,
- children = _props.children,
- props = _objectWithoutProperties(_props, ['options', 'defaultValue', 'value', 'children']);
-
- // Don't pass hooks to dom node
-
-
- hooks.forEach(function (hook) {
- delete props[hook];
- });
-
- return options.wrap ? _react2.default.createElement(
- 'div',
- _extends({}, props, { ref: function ref(node) {
- _this4.node = node;
- } }),
- children
- ) : _react2.default.createElement('input', _extends({}, props, { defaultValue: defaultValue,
- ref: function ref(node) {
- _this4.node = node;
- } }));
- }
- }]);
-
- return DateTimePicker;
-}(_react.Component);
-
-DateTimePicker.propTypes = {
- defaultValue: _propTypes2.default.string,
- options: _propTypes2.default.object,
- onChange: hookPropType,
- onOpen: hookPropType,
- onClose: hookPropType,
- onMonthChange: hookPropType,
- onYearChange: hookPropType,
- onReady: hookPropType,
- onValueUpdate: hookPropType,
- onDayCreate: hookPropType,
- value: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.array, _propTypes2.default.object, _propTypes2.default.number]),
- children: _propTypes2.default.node,
- className: _propTypes2.default.string
-};
-DateTimePicker.defaultProps = {
- options: {}
-};
-exports.default = DateTimePicker;
\ No newline at end of file
diff --git a/src/plugins/date-picker/components/react-flatpickr/lib/index.js b/src/plugins/date-picker/components/react-flatpickr/lib/index.js
deleted file mode 100644
index ebec978f..00000000
--- a/src/plugins/date-picker/components/react-flatpickr/lib/index.js
+++ /dev/null
@@ -1,131 +0,0 @@
-
-import React, { Component } from 'react'
-import PropTypes from 'prop-types'
-import Flatpickr from 'flatpickr'
-
-const hooks = [
- 'onChange',
- 'onOpen',
- 'onClose',
- 'onMonthChange',
- 'onYearChange',
- 'onReady',
- 'onValueUpdate',
- 'onDayCreate'
-]
-const hookPropType = PropTypes.oneOfType([
- PropTypes.func,
- PropTypes.arrayOf(PropTypes.func)
-])
-
-class DateTimePicker extends Component {
- static propTypes = {
- defaultValue: PropTypes.string,
- options: PropTypes.object,
- onChange: hookPropType,
- onOpen: hookPropType,
- onClose: hookPropType,
- onMonthChange: hookPropType,
- onYearChange: hookPropType,
- onReady: hookPropType,
- onValueUpdate: hookPropType,
- onDayCreate: hookPropType,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.array,
- PropTypes.object,
- PropTypes.number
- ]),
- children: PropTypes.node,
- className: PropTypes.string
- }
-
- static defaultProps = {
- options: {}
- }
-
- componentWillReceiveProps(props) {
- const { options } = props
- const prevOptions = this.props.options
-
- // Add prop hooks to options
- hooks.forEach(hook => {
- if (props.hasOwnProperty(hook)) {
- options[hook] = props[hook]
- }
- // Add prev ones too so we can compare against them later
- if (this.props.hasOwnProperty(hook)) {
- prevOptions[hook] = this.props[hook]
- }
- })
-
- const optionsKeys = Object.getOwnPropertyNames(options)
-
- for (let index = optionsKeys.length - 1; index >= 0; index--) {
- const key = optionsKeys[index]
- let value = options[key]
-
- if (value !== prevOptions[key]) {
- // Hook handlers must be set as an array
- if (hooks.indexOf(key) !== -1 && !Array.isArray(value)) {
- value = [value]
- }
-
- this.flatpickr.set(key, value)
- }
- }
-
- if (props.hasOwnProperty('value') && props.value !== this.props.value) {
- this.flatpickr.setDate(props.value, false)
- }
- }
-
- componentDidMount() {
- const options = {
- onClose: () => {
- this.node.blur && this.node.blur()
- },
- ...this.props.options
- }
-
- // Add prop hooks to options
- hooks.forEach(hook => {
- if (this.props[hook]) {
- options[hook] = this.props[hook]
- }
- })
-
- this.flatpickr = new Flatpickr(this.node, options)
-
- if (this.props.hasOwnProperty('value')) {
- this.flatpickr.setDate(this.props.value, false)
- }
- }
-
- componentWillUnmount() {
- this.flatpickr.destroy()
- }
-
- render() {
- // eslint-disable-next-line no-unused-vars
- const { options, defaultValue, value, children, ...props } = this.props
-
- // Don't pass hooks to dom node
- hooks.forEach(hook => {
- delete props[hook]
- })
-
- return options.wrap
- ? (
- { this.node = node }}>
- { children }
-
- )
- : (
- { this.node = node }} />
- )
- }
-}
-
-export default DateTimePicker
diff --git a/src/plugins/date-picker/components/react-flatpickr/package.json b/src/plugins/date-picker/components/react-flatpickr/package.json
deleted file mode 100644
index 4d9263fb..00000000
--- a/src/plugins/date-picker/components/react-flatpickr/package.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "name": "react-flatpickr",
- "version": "3.7.1",
- "description": "flatpickr for React",
- "main": "build/index.js",
- "scripts": {
- "example": "npm run build && webpack --config example/webpack.js",
- "dev": "echo 'Please open the example directory to view the example' && webpack-dev-server --config example/webpack.js",
- "lint": "eslint lib && eslint example",
- "build": "babel lib --out-dir build",
- "prepublishOnly": "npm run build",
- "test": "echo todo"
- },
- "repository": "coderhaoxin/react-flatpickr",
- "keywords": [
- "flatpickr",
- "react"
- ],
- "files": [
- "build/",
- "lib/"
- ],
- "author": "haoxin",
- "license": "MIT",
- "dependencies": {
- "flatpickr": "^4.3.2",
- "prop-types": "^15.5.10"
- },
- "devDependencies": {
- "babel-cli": "^6.22.2",
- "babel-core": "^6.22.1",
- "babel-eslint": "^8.0.1",
- "babel-loader": "^7.1.2",
- "babel-preset-es2015": "^6.22.0",
- "babel-preset-react": "^6.22.0",
- "babel-preset-stage-0": "^6.22.0",
- "css-loader": "^0.28.7",
- "eslint": "^4.8.0",
- "eslint-config-ok": "github:haoxins/eslint-config",
- "react": "^16.0.0",
- "react-dom": "^16.0.0",
- "style-loader": "^0.19.0",
- "webpack": "^3.6.0",
- "webpack-dev-server": "^2.8.2"
- },
- "eslintConfig": {
- "extends": [
- "ok"
- ]
- }
-}
diff --git a/src/plugins/date-picker/index.tsx b/src/plugins/date-picker/index.tsx
index fc2aac44..78efb7da 100644
--- a/src/plugins/date-picker/index.tsx
+++ b/src/plugins/date-picker/index.tsx
@@ -1,333 +1,299 @@
-import * as React from "react";
-import "./style.css";
-
-// Flatpickr Datepicker
-import Flatpickr from './components/react-flatpickr';
-import './flatpickr.css';
+/* Node modules */
+import React, { FC, useState } from "react";
+import Flatpickr from "react-flatpickr";
-// languages
-import l10n from './langHelper';
-import moment from 'moment';
-
-import { MessageComponentProps, MessagePlugin, MessagePluginFactory } from "../../common/interfaces/message-plugin";
-import { createMessagePlugin, registerMessagePlugin } from "../helper";
+/* Custom modules */
+import l10n from "./langHelper";
+import { registerMessagePlugin } from "../helper";
import { IMessage } from "../../common/interfaces/message";
+import {
+ MessageComponentProps,
+ MessagePluginFactory,
+} from "../../common/interfaces/message-plugin";
-const datePickerDaySelector = ".flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange, .flatpickr-day.selected.inRange, .flatpickr-day.startRange.inRange, .flatpickr-day.endRange.inRange, .flatpickr-day.selected:focus, .flatpickr-day.startRange:focus, .flatpickr-day.endRange:focus, .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover, .flatpickr-day.selected.prevMonthDay, .flatpickr-day.startRange.prevMonthDay, .flatpickr-day.endRange.prevMonthDay, .flatpickr-day.selected.nextMonthDay, .flatpickr-day.startRange.nextMonthDay, .flatpickr-day.endRange.nextMonthDay";
-
-interface IState {
- msg: string,
-}
+/* CSS */
+import "./style.css";
+import "./flatpickr.css";
+import { formatDate, transformNamedDate } from "./utils";
+const datePickerDaySelector =
+ ".flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange, .flatpickr-day.selected.inRange, .flatpickr-day.startRange.inRange, .flatpickr-day.endRange.inRange, .flatpickr-day.selected:focus, .flatpickr-day.startRange:focus, .flatpickr-day.endRange:focus, .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover, .flatpickr-day.selected.prevMonthDay, .flatpickr-day.startRange.prevMonthDay, .flatpickr-day.endRange.prevMonthDay, .flatpickr-day.selected.nextMonthDay, .flatpickr-day.startRange.nextMonthDay, .flatpickr-day.endRange.nextMonthDay";
/**
* Transforms regional locales to flatpicks internal locale key
*/
const getFlatpickrLocaleId = (locale: string) => {
- switch (locale) {
- case 'us':
- case 'gb':
- case 'au':
- case 'ca':
- return 'en';
- }
+ switch (locale) {
+ case "us":
+ case "gb":
+ case "au":
+ case "ca":
+ return "en";
+ }
- return locale;
-}
-
-/**
- * Transforms regional locales to flatpicks internal locale key
- */
-const getMomemtLocaleId = (locale: string) => {
- switch (locale) {
- case 'au':
- return 'en-au';
- case 'ca':
- return 'en-ca';
- case 'gb':
- return 'en-gb';
- case 'us':
- return 'en';
- }
-
- return locale;
-}
+ return locale;
+};
const datePickerPlugin: MessagePluginFactory = ({ styled }) => {
-
-
- const DatePickerRoot = styled.div(({ theme }) => ({
- display: "flex",
- flexDirection: "column",
- flexGrow: 1,
- [datePickerDaySelector]: {
- background: theme.primaryGradient,
- color: theme.primaryContrastColor,
- }
- }));
-
- const Button = styled.button(({ theme }) => ({
- backgroundColor: theme.greyColor,
- color: theme.greyContrastColor,
-
- cursor: "pointer",
- border: "none",
-
- height: 40,
-
- padding: `${theme.unitSize}px ${theme.unitSize * 2}px`,
- borderRadius: theme.unitSize * 2,
- }));
-
- const PrimaryButton = styled(Button)(({ theme }) => ({
- background: theme.primaryGradient,
- color: theme.primaryContrastColor,
- }));
-
- const OutlinedButton = styled(Button)(({ theme }) => ({
- backgroundColor: 'transparent',
- border: `1px solid ${theme.primaryColor}`,
- color: theme.primaryColor
- }));
-
- const SubmitButton = styled(PrimaryButton)(({ theme }) => ({
- flexGrow: 2,
- marginLeft: theme.unitSize * 2
- }));
-
- const CancelButton = styled(Button)(({ theme }) => ({
- flexGrow: 1
- }));
-
- const OpenDatepickerButton = styled(OutlinedButton)(({ theme }) => ({
- '&[disabled]': {
- borderColor: theme.greyColor,
- color: theme.greyColor,
- cursor: 'default'
- },
- '&:focus':{
- outline: 'none',
- boxShadow: `0 0 3px 1px ${theme.primaryWeakColor}`
- }
- }));
-
- const Padding = styled.div(({ theme }) => ({
- paddingTop: theme.unitSize,
- paddingBottom: theme.unitSize,
- paddingLeft: theme.unitSize * 2,
- paddingRight: theme.unitSize * 2
- }));
-
- const Header = styled(Padding)(({ theme }) => ({
- background: theme.primaryGradient,
- color: theme.primaryContrastColor,
- flexGrow: 1,
- display: 'flex',
- alignItems: 'center',
- fontWeight: 'bolder',
- boxShadow: theme.shadow,
- zIndex: 2
- }));
-
- const Content = styled(Padding)(({ theme }) => ({
- display: 'flex',
- justifyContent: 'center',
- }))
-
- const Footer = styled(Padding)(({ theme }) => ({
- display: 'flex',
- justifyContent: 'space-between',
- alignItems: 'center',
- backgroundColor: 'white',
- boxShadow: theme.shadow,
- }));
-
- const processedMessages: Set = new Set();
-
- class DatePicker extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- msg: "",
- };
- }
-
- handleSubmit = () => {
- const { message } = this.props
-
- // close plugin if user didn't choose a date
- if (this.state.msg.length > 0) {
- if (message.source === 'bot')
- processedMessages.add(message.traceId);
-
- setTimeout(() => {
- this.props.onSendMessage(this.state.msg), {
- _plugin: "date-picker",
- date: this.state.msg,
- }
- }, 300);
- } else {
- this.props.onDismissFullscreen();
- }
-
- };
-
- handleAbort = () => {
- const { message } = this.props;
-
- this.props.onDismissFullscreen();
- }
-
- static isWeekendDate(date: string) {
- const isoWeekday = moment(date).isoWeekday();
-
- switch (isoWeekday) {
- // 6 is saturday
- case 6:
- // 7 is sunday
- case 7:
- return true;
- }
-
- return false;
- }
-
- static transformNamedDate(namedDate: string) {
- switch (namedDate) {
- case "today":
- return moment().format('YYYY-MM-DD');
-
- case "tomorrow":
- return moment().add(1, 'days').format('YYYY-MM-DD');
-
- case "yesterday":
- return moment().add(-1, 'days').format('YYYY-MM-DD');
- }
-
- return namedDate
- }
-
- static getOptionsFromMessage(message: IMessage) {
- const { data } = message.data._plugin;
-
- const dateFormat = data.dateFormat || 'YYYY-MM-DD';
- const defaultDate = DatePicker.transformNamedDate(data.defaultDate)
- || DatePicker.transformNamedDate(data.minDate)
- || undefined;
-
- const localeId = data.locale || 'us';
- const momentLocaleId = getMomemtLocaleId(localeId);
- const flatpickrLocaleId = getFlatpickrLocaleId(localeId);
- let locale = l10n[flatpickrLocaleId];
- const enableTime = !!data.enableTime;
- const timeTemp = data.time_24hr ? 'H:i' : 'h:i'; //12-hour format without AM/PM
- const timeWithSeconds = data.enableSeconds ? `${timeTemp}:S` : timeTemp;
- const timeFormat = data.time_24hr ? timeWithSeconds :`${timeWithSeconds} K` //12-hour format with AM/PM
-
- if ( localeId === 'gb' ) locale = { ...locale, firstDayOfWeek: 1 };
- const options = {
- defaultHour: data.defaultHour || 12,
- defaultMinute: data.defaultMinute || 0,
- enableSeconds: data.enableSeconds || false,
- hourIncrement: data.hourIncrement || 1,
- minuteIncrement: data.minuteIncrement || 5,
- noCalendar: data.noCalendar || false,
- weekNumbers: data.weekNumbers || false,
- dateFormat: enableTime ? `${dateFormat} ${timeFormat}` : dateFormat,
- defaultDate,
- disable: [] as string[],
- enable: [] as string[],
- enableTime,
- event: data.eventName,
- inline: true,
- locale,
- maxDate: DatePicker.transformNamedDate(data.maxDate) || '',
- minDate: DatePicker.transformNamedDate(data.minDate) || '',
- mode: data.mode || 'single',
- static: true,
- time_24hr: data.time_24hr || false,
- parseDate: dateString => moment(dateString).toDate(),
- // if no custom formatting is defined, apply default formatting
- formatDate: !data.dateFormat
- ? date => moment(date).locale(momentLocaleId).format(enableTime ? 'L LT' : 'L')
- : undefined
- };
-
- const mask: string[] = [...(data.enable_disable || [])]
- // add special rule for weekends
- .map(dateString => {
- if (dateString === 'weekends')
- return DatePicker.isWeekendDate
-
- return dateString;
- })
- // resolve relative date names like today, tomorrow or yesterday
- .map(DatePicker.transformNamedDate);
-
- if (!!data.wantDisable) {
- // add date mask as blacklist
- options.disable = mask;
- } else if (mask.length > 0) {
-
- // add date mask as whitelist
- options.enable = mask;
- }
-
- return options;
- }
-
- render() {
- const { onSendMessage, message, config, attributes, isFullscreen, onSetFullscreen } = this.props;
-
-
- let dateButtonText = message.data._plugin.data.openPickerButtonText || 'pick date';
- let cancelButtonText = message.data._plugin.data.cancelButtonText || 'cancel';
- let submitButtonText = message.data._plugin.data.submitButtonText || 'submit';
-
- const options = DatePicker.getOptionsFromMessage(message);
-
- let datepickerWasOpen = false;
- if (message.source === 'bot') {
- datepickerWasOpen = processedMessages.has(message.traceId);
- }
-
- if (!isFullscreen) {
- if (datepickerWasOpen) {
- return {dateButtonText}
- }
-
- return {dateButtonText}
- }
-
- return (
-
-
-
- { this.setState({ msg }) }}
- options={
- options
- }
- />
-
-
-
- );
- }
- }
-
- const plugin = {
- match: "date-picker",
- component: DatePicker
- }
-
- return plugin;
-}
+ const DatePickerRoot = styled.div(({ theme }) => ({
+ display: "flex",
+ flexDirection: "column",
+ flexGrow: 1,
+ [datePickerDaySelector]: {
+ background: theme.primaryGradient,
+ color: theme.primaryContrastColor,
+ },
+ }));
+
+ const Button = styled.button(({ theme }) => ({
+ backgroundColor: theme.greyColor,
+ color: theme.greyContrastColor,
+
+ cursor: "pointer",
+ border: "none",
+
+ height: 40,
+
+ padding: `${theme.unitSize}px ${theme.unitSize * 2}px`,
+ borderRadius: theme.unitSize * 2,
+ }));
+
+ const PrimaryButton = styled(Button)(({ theme }) => ({
+ background: theme.primaryGradient,
+ color: theme.primaryContrastColor,
+ }));
+
+ const OutlinedButton = styled(Button)(({ theme }) => ({
+ backgroundColor: "transparent",
+ border: `1px solid ${theme.primaryColor}`,
+ color: theme.primaryColor,
+ }));
+
+ const SubmitButton = styled(PrimaryButton)(({ theme }) => ({
+ flexGrow: 2,
+ marginLeft: theme.unitSize * 2,
+ }));
+
+ const CancelButton = styled(Button)(({ theme }) => ({
+ flexGrow: 1,
+ }));
+
+ const OpenDatepickerButton = styled(OutlinedButton)(({ theme }) => ({
+ "&[disabled]": {
+ borderColor: theme.greyColor,
+ color: theme.greyColor,
+ cursor: "default",
+ },
+ "&:focus": {
+ outline: "none",
+ boxShadow: `0 0 3px 1px ${theme.primaryWeakColor}`,
+ },
+ }));
+
+ const Padding = styled.div(({ theme }) => ({
+ paddingTop: theme.unitSize,
+ paddingBottom: theme.unitSize,
+ paddingLeft: theme.unitSize * 2,
+ paddingRight: theme.unitSize * 2,
+ }));
+
+ const Header = styled(Padding)(({ theme }) => ({
+ background: theme.primaryGradient,
+ color: theme.primaryContrastColor,
+ flexGrow: 1,
+ display: "flex",
+ alignItems: "center",
+ fontWeight: "bolder",
+ boxShadow: theme.shadow,
+ zIndex: 2,
+ }));
+
+ const Content = styled(Padding)(({ theme }) => ({
+ display: "flex",
+ justifyContent: "center",
+ }));
+
+ const Footer = styled(Padding)(({ theme }) => ({
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ backgroundColor: "white",
+ boxShadow: theme.shadow,
+ }));
+
+ const processedMessages: Set = new Set();
+
+ const DatePicker: FC = props => {
+ const {
+ onSendMessage,
+ message,
+ config,
+ attributes,
+ isFullscreen,
+ onSetFullscreen,
+ onDismissFullscreen,
+ } = props;
+
+ const [msg, setMsg] = useState("");
+
+ const handleSubmit = () => {
+ // close plugin if user didn't choose a date
+ if (msg.length > 0) {
+ if (message.source === "bot") processedMessages.add(message.traceId);
+
+ setTimeout(() => {
+ onSendMessage(msg),
+ {
+ _plugin: "date-picker",
+ date: msg,
+ };
+ }, 300);
+ } else {
+ onDismissFullscreen && onDismissFullscreen();
+ }
+ };
+
+ const handleAbort = () => {
+ onDismissFullscreen && onDismissFullscreen();
+ };
+
+ const isWeekendDate = (date: string) => {
+ const isoWeekday = ((new Date(date).getDay() + 6) % 7) + 1;
+
+ switch (isoWeekday) {
+ // 6 is saturday
+ case 6:
+ // 7 is sunday
+ case 7:
+ return true;
+ }
+
+ return false;
+ };
+
+ const getOptionsFromMessage = (message: IMessage) => {
+ const { data } = message.data._plugin;
+ const dateFormat = data.dateFormat || "YYYY-MM-DD";
+ const defaultDate =
+ transformNamedDate(data.defaultDate) || transformNamedDate(data.minDate) || "";
+
+ const localeId = data.locale || "us";
+ const flatpickrLocaleId = getFlatpickrLocaleId(localeId);
+ let locale = l10n[flatpickrLocaleId];
+ const enableTime = !!data.enableTime;
+ const timeTemp = data.time_24hr ? "H:i" : "h:i"; //12-hour format without AM/PM
+ const timeWithSeconds = data.enableSeconds ? `${timeTemp}:S` : timeTemp;
+ const timeFormat = data.time_24hr ? timeWithSeconds : `${timeWithSeconds} K`; //12-hour format with AM/PM
+
+ if (localeId === "gb") locale = { ...locale, firstDayOfWeek: 1 };
+ const options = {
+ defaultHour: data.defaultHour || 12,
+ defaultMinute: data.defaultMinute || 0,
+ enableSeconds: data.enableSeconds || false,
+ hourIncrement: data.hourIncrement || 1,
+ minuteIncrement: data.minuteIncrement || 5,
+ noCalendar: data.noCalendar || false,
+ weekNumbers: data.weekNumbers || false,
+ dateFormat: enableTime ? `${dateFormat} ${timeFormat}` : dateFormat,
+ defaultDate,
+ disable: [] as string[],
+ enable: [] as string[],
+ enableTime,
+ event: data.eventName,
+ inline: true,
+ locale,
+ maxDate: transformNamedDate(data.maxDate) || "",
+ minDate: transformNamedDate(data.minDate) || "",
+ mode: data.mode || "single",
+ static: true,
+ time_24hr: data.time_24hr || false,
+ parseDate: dateString => new Date(dateString),
+ // if no custom formatting is defined, apply default formatting. es-PA is MM/dd/yyyy
+ formatDate: !data.dateFormat
+ ? date => formatDate(date, enableTime, flatpickrLocaleId)
+ : undefined,
+ };
+
+ const mask: string[] = [...(data.enable_disable || [])]
+ // add special rule for weekends
+ .map(dateString => {
+ if (dateString === "weekends") return isWeekendDate;
+
+ return dateString;
+ })
+ // resolve relative date names like today, tomorrow or yesterday
+ .map(transformNamedDate);
+
+ if (!!data.wantDisable) {
+ // add date mask as blacklist
+ options.disable = mask;
+ } else if (mask.length > 0) {
+ // add date mask as whitelist
+ options.enable = mask;
+ }
+
+ return options;
+ };
+
+ const dateButtonText = message.data._plugin.data.openPickerButtonText || "pick date";
+ const cancelButtonText = message.data._plugin.data.cancelButtonText || "cancel";
+ const submitButtonText = message.data._plugin.data.submitButtonText || "submit";
+
+ const options = getOptionsFromMessage(message);
+
+ let datepickerWasOpen = false;
+ if (message.source === "bot") {
+ datepickerWasOpen = processedMessages.has(message.traceId);
+ }
+
+ if (!isFullscreen) {
+ if (datepickerWasOpen) {
+ return (
+
+ {dateButtonText}
+
+ );
+ }
+
+ return (
+
+ {dateButtonText}
+
+ );
+ }
+
+ return (
+
+
+
+ {
+ setMsg(msg);
+ }}
+ options={options}
+ />
+
+
+
+ );
+ };
+
+ const plugin = {
+ match: "date-picker",
+ component: DatePicker,
+ };
+
+ return plugin;
+};
registerMessagePlugin(datePickerPlugin);
diff --git a/src/plugins/date-picker/utils.ts b/src/plugins/date-picker/utils.ts
new file mode 100644
index 00000000..5e1147a0
--- /dev/null
+++ b/src/plugins/date-picker/utils.ts
@@ -0,0 +1,46 @@
+export const transformNamedDate = (namedDate: string) => {
+ switch (namedDate) {
+ case "today":
+ // fr-CA is one of the few locales with a day format of YYYY-MM-DD
+ return new Intl.DateTimeFormat("fr-CA").format(new Date());
+
+ case "tomorrow":
+ return new Intl.DateTimeFormat("fr-CA").format(
+ new Date().setDate(new Date().getDate() + 1),
+ );
+
+ case "yesterday":
+ return new Intl.DateTimeFormat("fr-CA").format(
+ new Date().setDate(new Date().getDate() - 1),
+ );
+ }
+
+ return namedDate;
+};
+
+export const formatDate = (date: Date, enableTime: boolean, locale: string) => {
+ return new Intl.DateTimeFormat(
+ locale,
+ enableTime
+ ? {
+ hour: "2-digit",
+ minute: "2-digit",
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ }
+ : {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ },
+ )
+ .format(date)
+ .replace(",", "")
+ .replace("am", "AM")
+ .replace("a.m.", "AM")
+ .replace("a. m.", "AM")
+ .replace("pm", "PM")
+ .replace("p.m.", "PM")
+ .replace("p. m.", "PM");
+};
\ No newline at end of file