├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── app
├── Calendar
│ ├── Calendar.js
│ ├── DayWrapper.js
│ ├── DefaultDay.js
│ ├── MonthComponent.js
│ ├── PositionsDay.js
│ ├── __tests__
│ │ └── DayPicker.spec.js
│ ├── helpers.js
│ └── styles.css
├── Example
│ ├── README.md
│ ├── build
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.3fbecb66.css
│ │ ├── main.3fbecb66.css.map
│ │ ├── main.e994af19.js
│ │ └── main.e994af19.js.map
│ ├── favicon.ico
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ └── index.js
│ └── yarn.lock
└── index.js
├── lib
└── app.min.js
├── package.json
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "targets": {
5 | "browsers": ["last 2 versions", "safari >= 7"]
6 | }
7 | }],
8 | ["es2015", {"modules": false}],
9 | "stage-0",
10 | "react"
11 | ],
12 | "env": {
13 | "test" : {
14 | "plugins":[
15 | ["module-resolver", {
16 | "root": ["./app"]
17 | }],
18 | "transform-es2015-modules-commonjs",
19 | "transform-runtime"
20 | ]
21 | },
22 | "development": {
23 | "plugins": ["transform-runtime"]
24 | },
25 | "production": {
26 | "plugins": [
27 | "transform-runtime"
28 | ]
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /bin/**
2 | /build/**
3 | /coverage/**
4 | /docs/**
5 | /jsdoc/**
6 | /templates/**
7 | /tests/bench/**
8 | /tests/fixtures/**
9 | /tests/performance/**
10 | /tmp/**
11 | test.js
12 | /webpack.config.babel.js
13 | /webpack.config.prod.js
14 | /undefinedclock-action-sync
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "es6": true,
5 | "jest": true,
6 | "browser": true
7 | },
8 | "parserOptions": {
9 | "ecmaversion": 6,
10 | "sourceType": "module",
11 | "ecmaFeatures": {
12 | "jsx": true,
13 | "experimentalObjectRestSpread": true
14 | }
15 | },
16 | "extends": ["eslint:recommended", "plugin:react/recommended", "standard"],
17 | "plugins": [
18 | "react"
19 | ],
20 | "rules": {
21 | "indent": [0, "tab"],
22 | "no-tabs": 0,
23 | "no-console": [2, {"allow": ["warn", "error"]}],
24 | "comma-dangle" : [2, "always-multiline"],
25 | "semi": [2, "never"],
26 | "no-extra-semi": 2,
27 | "jsx-quotes": [2, "prefer-single"],
28 | "react/jsx-boolean-value": [2, "always"],
29 | "react/jsx-closing-bracket-location": [2, {"selfClosing": "after-props", "nonEmpty": "after-props"}],
30 | "react/jsx-curly-spacing": [2, "never", {"allowMultiline": false}],
31 | "react/jsx-max-props-per-line": [2, {"maximum": 3}],
32 | "react/jsx-no-literals": 2,
33 | "react/no-is-mounted": 0,
34 | "react/self-closing-comp": ["error", {
35 | "component": true,
36 | "html": false
37 | }],
38 | "react/sort-comp": 2
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 |
3 | # Loging files
4 | logs
5 | *.log*
6 | .idea
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like jest
17 | coverage
18 |
19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
20 | .grunt
21 |
22 | # node-waf configuration
23 | .lock-wscript
24 |
25 | # Compiled binary addons (http://nodejs.org/api/addons.html)
26 | build/Release
27 |
28 | # Dependency directory
29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
30 | node_modules
31 |
32 | # yarn files
33 | .yarn-error.log
34 |
35 | #dist folder
36 | /dist
37 | /stage
38 |
39 | # Selenium
40 | /reports
41 |
42 | # IDEA/Webstorm project files
43 | .idea
44 | *.iml
45 |
46 | #VSCode metadata
47 | .vscode
48 |
49 | # Mac files
50 | .DS_Store
51 |
52 | #andy files
53 | circle_example.yml
54 | app/gateways/pouchdb/mockData.js
55 | build-bak
56 | undefinedclock-action-sync
57 | clients_local_db
58 |
59 | # Sublime project files
60 | *.sublime-project
61 | *.sublime-workspace
62 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | after_success:
5 | - npm run test
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 0.0.1
4 | - Initial commit. (Project as it is)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 SHIFTGIG
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Multiday Calendar [![Version Badge][npm-version-svg]][package-url]
2 |
3 | [![dependency status][deps-svg]][deps-url]
4 | [![dev dependency status][dev-deps-svg]][dev-deps-url]
5 | [![License][license-image]][license-url]
6 | [![Github file size][file-size-image]]()
7 | [![Downloads][downloads-image]][downloads-url]
8 | [](https://travis-ci.org/andresmijares/react-calendar-multiday)
9 |
10 |
11 | [![npm badge][npm-badge-png]][package-url]
12 |
13 | A minimalist React Calendar used for scheduling tools.
14 |
15 | If you want to play with it:
16 |
17 | [](https://codesandbox.io/s/m4j347534p)
18 |
19 | ## Install
20 | Using npm or yarn.
21 | ```bash
22 | npm install react-calendar-multiday
23 | ```
24 |
25 | ## Run Example
26 | ```bash
27 | cd app/Example
28 | npm install
29 | npm run start
30 | ```
31 |
32 | ## Import
33 | As a default component:
34 |
35 | ```javascript
36 | import Calendar from 'react-calendar-multiday'
37 | ```
38 |
39 | ## Dependencies
40 | It uses **moment** and **ramda** behind the scenes, we are working to remove the ramda on the next release in order to keep the bundle size as low as we can.
41 |
42 | ## API
43 | |name|type|required|default|description|
44 | |---|---|---|---|---|
45 | |**onChange**|Function|Yes |-|Callback fired once click on a date. Expose the current selections|
46 | |**onAddChannel**|Function|No|-|Callback fired once confirm channel selection. Expose channels and currentChannel|
47 | |**onReset**|Function|No|-|Callback fired once click Reset (Reset needs to be true)|
48 | |**channels**|Dict object|No|-|Store selected dates by channels|
49 | |**selected**|Array of Moment Instances|No| [] |Pass a selection of dates|
50 | |**year**|Moment Object - Year|No| Current |Select the default year|
51 | |**month**|Moment Object - Month|No| Current |Select the default month|
52 | |**currentChannel**|Number|No|-|Key of current channel|
53 | |**reset**|Boolean|No|false|Display a clear selection button|
54 | |**isMultiple**|Boolean|No|false|Define if you need one sigle date selection or multiple|
55 | |**DayComponent**|React Node|No|Default Day Component|Renders each day into the calendar|
56 |
57 |
58 |
59 | ### OnChange
60 | Returns an object with the **current** selection and the **selected** date(s).
61 |
62 | ```javascript
63 | {
64 | current: "2017-10-16T00:00:00-03:00",
65 | selected: ["2017-10-16T00:00:00-03:00"],
66 | channels: {0: ["2017-10-16T00:00:00-03:00"]} // if channels are available
67 | }
68 | ```
69 |
70 | ### isMultiple
71 | The calendar will allow to select one single day or multiples, the only different with be return object on the **onChange** function, it can contains one or more dates.
72 |
73 | ```javascript
74 | {
75 | current: "2017-10-16T00:00:00-03:00",
76 | selected: ["2017-10-16T00:00:00-03:00", "2017-10-27T00:00:00-03:00", "2017-11-05T00:00:00-03:00"],
77 | channels: {0: ["2017-10-16T00:00:00-03:00", "2017-10-27T00:00:00-03:00", "2017-11-05T00:00:00-03:00"]} // if channels are available
78 | }
79 | ```
80 |
81 | ### DayComponent
82 | A component that will render on each day, it receives several props where the most importants **label** and **isSelected**.
83 | * **Label**: String; Represents the day character.
84 | * **isSelected**: Booleam; True if the day is included in the selected array.
85 | * **isCurrentChannelSelected**: Booleam; True if the day is included in the selected array for the current channel.
86 | * **isToday**: Booleam; True if the value match today;s date.
87 | * **isInThePast**: Boolem; True if the value is before than today.
88 |
89 | Some other properties are expose like **selected** which is the selection array, we expose it cause we need that rule to manage inside business cases.
90 |
91 | If you require that past days not be selected, you need to stop the propagation of the click event yourself.
92 |
93 | Common use example:
94 |
95 | ```javascript
96 | const PositionDay = props => {
97 | const onClick = (e) => {
98 | if (props.isInthePast) {
99 | e.stopPropagation()
100 | }
101 | }
102 | return (
103 |
106 | {props.label}
107 |
)
108 | }
109 |
110 | const getStyle = function ({date, isSelected}) {
111 | return `${isSelected ? 'o_selected-day' : ''} ${date.type}-day`
112 | }
113 |
114 | const getInline = ({isToday, isInThePast}) => ({
115 | cursor: isInThePast ? 'not-allowed' : 'inherit',
116 | background: isToday
117 | ? 'rgba(141, 224, 229, 0.5)'
118 | : isInThePast ? '#e4e4e4' : 'inherit',
119 | color: isInThePast ? '#555555' : 'inherit',
120 | })
121 |
122 | ```
123 |
124 | As you can see, we leave the default implementation as open as possible, this way we can support all the use cases we have into our apps.
125 |
126 | ## Styles
127 | We expose a few css clases that you can edit, otherwise, you can use our ugly css default.
128 |
129 | * o_day_picker: the calendar container
130 | * i_day-picker-header: weeks headers
131 | * i_day-picker-body: calendar body
132 | * e_day-picker-buttons: prev and next month
133 | * i_day-picker-row: weeks row
134 | * i_day-picker-reset: reset button
135 | * i_day-picker-add-channel: add channel button
136 | * o_selected-current-channel-day: date selected for current channel
137 |
138 | ## License
139 | MIT
140 |
141 | [package-url]: https://npmjs.org/package/react-calendar-multiday
142 | [npm-version-svg]: http://versionbadg.es/andresmijares/react-calendar-multiday.svg
143 | [npm-badge-png]: https://nodei.co/npm/react-calendar-multiday.png?downloads=true&stars=true
144 | [deps-svg]: https://david-dm.org/andresmijares/react-calendar-multiday.svg
145 | [deps-url]: https://david-dm.org/andresmijares/react-calendar-multiday
146 | [dev-deps-svg]: https://david-dm.org/andresmijares/react-calendar-multiday.svg
147 | [dev-deps-url]: https://david-dm.org/andresmijares/react-calendar-multiday.svg#info=devDependencies
148 | [license-image]: http://img.shields.io/npm/l/react-calendar-multiday.svg
149 | [license-url]: LICENSE
150 | [downloads-image]: http://img.shields.io/npm/dm/react-calendar-multiday.svg
151 | [downloads-url]: http://npm-stat.com/charts.html?package=react-calendar-multiday
152 | [file-size-image]: https://img.shields.io/github/size/andresmijares/react-calendar-multiday/lib/app.min.js.svg
153 |
--------------------------------------------------------------------------------
/app/Calendar/Calendar.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import PropTypes from 'prop-types'
3 | import MonthComponent from './MonthComponent'
4 | import moment from 'moment'
5 | import {extendMoment} from 'moment-range'
6 | import {reject, or, isEmpty, values, equals, cond, T, isNil} from 'ramda'
7 | import {normalize, incMonth, decMonth, setMonthDays, TYPE, getKey, getRealMonthAndYear} from './helpers'
8 |
9 | class Calendar extends Component {
10 | static defaultProps = {
11 | year: moment().get('year'),
12 | month: moment().get('month'),
13 | isMultiple: false,
14 | selected: [],
15 | channels: null,
16 | }
17 |
18 | constructor (props) {
19 | super(props)
20 | this.moment = extendMoment(moment)
21 | const {selected, channels} = this.props
22 | const defaultDate = this.moment(getRealMonthAndYear(this.props.month, this.props.year))
23 |
24 | this.state = {
25 | defaultDate: defaultDate,
26 | selected: normalize(selected, this.moment),
27 | monthDays: setMonthDays(defaultDate, this.moment),
28 | channels,
29 | currentChannel: props.currentChannel || 0,
30 | }
31 |
32 | this.nextMonth = this.nextMonth.bind(this)
33 | this.prevMonth = this.prevMonth.bind(this)
34 | this.onClick = this.onClick.bind(this)
35 | this.retrieveSelected = this.retrieveSelected.bind(this)
36 | this.reset = this.reset.bind(this)
37 | this.addChannel = this.addChannel.bind(this)
38 | this.addOrRemoveDateToChannel = this.addOrRemoveDateToChannel.bind(this)
39 | }
40 |
41 | componentWillReceiveProps (nextProps) {
42 | if (this.props.selected.length !== nextProps.selected.length) {
43 | this.setState({selected: normalize(nextProps.selected, this.moment)})
44 | }
45 | if (this.state.currentChannel !== nextProps.currentChannel) {
46 | this.setState({currentChannel: nextProps.currentChannel})
47 | }
48 | if (!equals(this.props.channels, nextProps.channels)) {
49 | this.setState({channels: nextProps.channels})
50 | }
51 | }
52 |
53 | nextMonth () {
54 | const defaultDate = incMonth(this.state.defaultDate)
55 | this.setState({
56 | defaultDate,
57 | monthDays: setMonthDays(defaultDate, this.moment),
58 | })
59 | }
60 |
61 | prevMonth () {
62 | const defaultDate = decMonth(this.state.defaultDate)
63 | this.setState({
64 | defaultDate,
65 | monthDays: setMonthDays(defaultDate, this.moment),
66 | })
67 | }
68 |
69 | reset () {
70 | const selected = this.state.selected
71 | const empty = Object.keys(selected).length
72 | this.setState({
73 | selected: empty ? {} : normalize(selected, this.moment),
74 | channels: !isNil(this.props.channels) ? {} : null,
75 | currentChannel: 0,
76 | }, () => this.props.onReset ? this.props.onReset() : true
77 | )
78 | }
79 |
80 | onClick (day) {
81 | const formattedDay = day.moment.format()
82 | const calendar = getKey(day.moment)
83 | const {selected, defaultDate, monthDays} = this.state
84 | //const {selected, defaultDate, monthDays} = this.state
85 | const updatedDefaultDate = cond([
86 | [equals(TYPE.NEXT), () => incMonth(defaultDate)],
87 | [equals(TYPE.PREV), () => decMonth(defaultDate)],
88 | [T, () => defaultDate],
89 | ])(day.type)
90 |
91 | this.setState(
92 | {
93 | channels: !isNil(this.props.channels) ? this.addOrRemoveDateToChannel(day) : this.props.channels,
94 | selected: this.props.isMultiple ? {
95 | ...selected,
96 | [calendar]: isEmpty(or(selected[calendar], {})) ? day.moment : {},
97 | }
98 | : [day.moment], // is Single day ,
99 | defaultDate: updatedDefaultDate,
100 | monthDays: cond([
101 | [equals(TYPE.NEXT), () => setMonthDays(updatedDefaultDate, this.moment)],
102 | [equals(TYPE.PREV), () => setMonthDays(updatedDefaultDate, this.moment)],
103 | [T, () => monthDays],
104 | ])(day.type),
105 | }, () => {
106 | /*
107 | Returns information for the listener function
108 | */
109 | this.props.onChange({
110 | channels: this.state.channels,
111 | current: formattedDay,
112 | selected: reject(isEmpty, values(this.state.selected))
113 | .map(d => d.format()),
114 | })
115 | }
116 | )
117 | }
118 |
119 | retrieveSelected () {
120 | const nextMonth = this.moment(this.state.defaultDate).add(1, 'months')
121 | const prevMonth = this.moment(this.state.defaultDate).subtract(1, 'months').subtract(7, 'days')
122 | return reject(isEmpty, values(this.state.selected))
123 | .filter(d => d.isBetween(prevMonth, nextMonth))
124 | }
125 |
126 | addOrRemoveDateToChannel (day) {
127 | const {channels, currentChannel} = this.state
128 | if (!channels[currentChannel]) {
129 | channels[currentChannel] = []
130 | }
131 |
132 | if (!channels[currentChannel].some(d => d.isSame(day.moment, 'day'))) {
133 | channels[currentChannel] = channels[currentChannel].concat([day.moment])
134 | } else {
135 | channels[currentChannel] = channels[currentChannel].filter(d => !d.isSame(day.moment, 'day'))
136 | }
137 | return channels
138 | }
139 |
140 | addChannel () {
141 | const {channels, currentChannel} = this.state
142 | const max = Object.keys(channels).reduce((a, b) => Math.max(a, b), 0)
143 |
144 | if (currentChannel === max && channels[max].length === 0) {
145 | return false
146 | }
147 |
148 | this.setState({
149 | channels,
150 | currentChannel: !isEmpty(channels[max]) ? Number(max) + 1 : max,
151 | }, () => this.props.onAddChannel ? this.props.onAddChannel({
152 | channels: this.state.channels,
153 | currentChannel: this.state.currentChannel,
154 | }) : true
155 | )
156 | }
157 |
158 | render () {
159 | const {defaultDate, monthDays, currentChannel, channels} = this.state
160 | const reset = this.props.reset ? this.reset : null
161 | const addChannel = !isNil(this.props.channels) ? this.addChannel : null
162 | return (
163 |
176 | )
177 | }
178 | }
179 |
180 | Calendar.propTypes = {
181 | channels: PropTypes.object,
182 | DayComponent: PropTypes.node,
183 | onChange: PropTypes.func.isRequired,
184 | onReset: PropTypes.func,
185 | onAddChannel: PropTypes.func,
186 | selected: PropTypes.array,
187 | month: PropTypes.number,
188 | year: PropTypes.number,
189 | currentChannel: PropTypes.number,
190 | reset: PropTypes.bool,
191 | isMultiple: PropTypes.bool,
192 | }
193 |
194 | export default Calendar
195 |
--------------------------------------------------------------------------------
/app/Calendar/DayWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import DefaultDay from './DefaultDay'
4 | import {isNil, flatten} from 'ramda'
5 |
6 | const DayWrapper = (props) => {
7 | const nextProps = {
8 | ...props,
9 | isSelected: isSelected(props),
10 | isCurrentChannelSelected: isCurrentChannelSelected(props),
11 | }
12 |
13 | return (
14 | props.onClick(props.date)}>
17 | {props.children && React.cloneElement(props.children, {...nextProps}) || }
18 |
)
19 | }
20 |
21 | export const isSelected = ({date, selected, channels}) =>
22 | !isNil(channels) ?
23 | flatten(Object.keys(channels).map(key => channels[key])).some(each => each.isSame(date.moment, 'day')) :
24 | selected.some(each => each.isSame(date.moment, 'day'))
25 |
26 | export const isCurrentChannelSelected = ({date, selected, channels, currentChannel}) =>
27 | !isNil(channels) ?
28 | !isNil(channels[currentChannel]) &&
29 | channels[currentChannel].some(each => each.isSame(date.moment, 'day')) :
30 | selected.some(each => each.isSame(date.moment, 'day'))
31 |
32 |
33 | DayWrapper.propTypes = {
34 | date: PropTypes.object,
35 | children: PropTypes.node,
36 | selected: PropTypes.array,
37 | onClick: PropTypes.func,
38 | channels: PropTypes.object,
39 | currentChannel: PropTypes.number,
40 | }
41 |
42 | export default DayWrapper
43 |
--------------------------------------------------------------------------------
/app/Calendar/DefaultDay.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 |
5 | const getInline = (today, before) => ({
6 | cursor: before ? 'not-allowed' : 'inherit',
7 | background: today
8 | ? 'rgba(141, 224, 229, 0.5)'
9 | : before ? 'rgba(155, 155, 155, .2)' : 'inherit',
10 | })
11 |
12 | const DefaultDayComponent = props => {
13 | const { label, date, isToday, isInThePast, isCurrentChannelSelected, isSelected } = props
14 | const disableDate = date.moment.isBefore(moment(), 'day')
15 | const onClick = (e) => {
16 | if (disableDate || (!isCurrentChannelSelected && isSelected)) {
17 | e.stopPropagation()
18 | }
19 | }
20 |
21 | return (
22 |
26 | {label}
27 |
)
28 | }
29 |
30 | DefaultDayComponent.propTypes = {
31 | label: PropTypes.number,
32 | date: PropTypes.object,
33 | isToday: PropTypes.bool,
34 | isInThePast: PropTypes.bool,
35 | }
36 |
37 | export const getStyle = function ({date, isSelected, isCurrentChannelSelected}) {
38 | return `${isCurrentChannelSelected
39 | ? 'o_selected-current-channel-day' : isSelected ? 'o_selected-day' : ''} ${date.type}-day`
40 | }
41 |
42 | export default DefaultDayComponent
43 |
--------------------------------------------------------------------------------
/app/Calendar/MonthComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import {splitEvery, isNil, isEmpty} from 'ramda'
4 | import DayWrapper from './DayWrapper'
5 | import moment from 'moment'
6 |
7 | const MonthComponent = props => {
8 | const { days, dayNames, selected, nextMonth, prevMonth, defaultDate, onClick,
9 | reset, DayComponent, addChannel, channels, currentChannel } = props
10 | const weeks = splitEvery(7, days)
11 | return (
12 |
13 |
14 |
17 |
18 | {defaultDate.format('MMMM YYYY')}
19 |
20 |
23 |
24 |
25 | {dayNames.map(n =>
26 |
{n}
27 | )} {/* we can pass this as props as well */}
28 |
29 |
30 | {weeks.map((w, index) =>
31 |
32 | {w.map((d, i) =>
33 |
43 | {DayComponent}
44 |
45 | )}
46 |
47 | )}
48 |
49 |
50 | { reset &&
51 |
54 | }
55 | { addChannel &&
56 |
60 | }
61 |
62 |
63 | )
64 | }
65 |
66 | MonthComponent.propTypes = {
67 | days: PropTypes.array,
68 | dayNames: PropTypes.array,
69 | selected: PropTypes.array,
70 | onClick: PropTypes.func,
71 | nextMonth: PropTypes.func,
72 | prevMonth: PropTypes.func,
73 | reset: PropTypes.func,
74 | defaultDate: PropTypes.object,
75 | DayComponent: PropTypes.node,
76 | type: PropTypes.string,
77 | channels: PropTypes.object,
78 | currentChannel: PropTypes.number,
79 | }
80 |
81 | export default MonthComponent
82 |
83 |
--------------------------------------------------------------------------------
/app/Calendar/PositionsDay.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 |
5 | const PositionDay = props => {
6 | const { label, selected, date } = props
7 | const disableDate = date.moment.isBefore(moment(), 'day')
8 | const dayIsSelected = isSelected(date.moment, selected)
9 | const onClick = (e) => {
10 | if (disableDate) {
11 | e.stopPropagation()
12 | }
13 | }
14 | const disabledStyles = {
15 | cursor: 'not-allowed',
16 | background: '#e4e4e4',
17 | color: '#555555',
18 | }
19 |
20 | const disabledAndSelected = {
21 | cursor: 'not-allowed',
22 | background: '#4179a3',
23 | }
24 | return (
25 |
32 | {label}
33 |
)
34 | }
35 |
36 | PositionDay.propTypes = {
37 | label: PropTypes.number,
38 | date: PropTypes.object,
39 | selected: PropTypes.array,
40 | }
41 |
42 | export const isSelected = (momentDate, selected) => selected.some(each => each.isSame(momentDate, 'day'))
43 |
44 | export const getStyle = function (day, selected) {
45 | return `${isSelected(day.moment, selected) ? 'o_selected-day' : ''} ${day.type}-day`
46 | }
47 |
48 | export default PositionDay
49 |
--------------------------------------------------------------------------------
/app/Calendar/__tests__/DayPicker.spec.js:
--------------------------------------------------------------------------------
1 | // import React from 'react'
2 | // import Enzyme, {mount} from 'enzyme'
3 | // import Adapter from 'enzyme-adapter-react-16'
4 | // import Calendar from '../Calendar'
5 | // import DayWrapper from '../DayWrapper'
6 | // import toJson from 'enzyme-to-json'
7 | // import {equals, range, inc} from 'ramda'
8 | // import jsdom from 'jsdom'
9 |
10 | // Enzyme.configure({ adapter: new Adapter() })
11 | // var exposedProperties = ['window', 'navigator', 'document']
12 |
13 | // global.document = jsdom.jsdom('')
14 | // global.window = document.defaultView
15 | // Object.keys(document.defaultView).forEach((property) => {
16 | // if (typeof global[property] === 'undefined') {
17 | // exposedProperties.push(property)
18 | // global[property] = document.defaultView[property]
19 | // }
20 | // })
21 |
22 | // global.navigator = {
23 | // userAgent: 'node.js',
24 | // }
25 |
26 | // // const dayPicker = mount()
27 |
28 |
--------------------------------------------------------------------------------
/app/Calendar/helpers.js:
--------------------------------------------------------------------------------
1 | import {head, last, subtract, take, takeLast, cond, lte, T} from 'ramda'
2 |
3 | export const TYPE = {
4 | MONTH: 'month',
5 | NEXT: 'next-month',
6 | PREV: 'prev-month',
7 | }
8 |
9 | export const KEY_FORMAT = 'YYYY-MM-DD'
10 |
11 | export const setMonthDays = (month, moment) => {
12 | const nextMonthD = Array.from(getMonthRange(incMonth(moment(month)), moment).by('day'))
13 | const previousMonthD = Array.from(getMonthRange(decMonth(moment(month)), moment).by('day'))
14 | const monthDays = Array.from(getMonthRange(month, moment).by('day')).map(m => ({moment: m, type: TYPE.MONTH}))
15 | const weekIndexes = monthDays.map(d => d.moment.day())
16 | const nextMonthDays = take(subtract(6, last(weekIndexes)), nextMonthD).map(m => ({moment: m, type: TYPE.NEXT}))
17 | const prevMonthDays = takeLast(head(weekIndexes), previousMonthD).map(m => ({moment: m, type: TYPE.PREV}))
18 | return prevMonthDays.concat(monthDays, nextMonthDays)
19 | }
20 |
21 | export const normalize = (selected, momentInstance) => {
22 | const normalizedDays = Object
23 | .keys(selected)
24 | .map(date => selected[date])
25 | .reduce((prev, d) => {
26 | const moment = momentInstance(d)
27 | prev[getKey(moment)] = moment
28 | return prev
29 | }, {})
30 | return normalizedDays
31 | }
32 |
33 | export const getMonthRange = (month, moment) => {
34 | const start = moment(month.startOf('month'))
35 | const end = month.endOf('month')
36 | return moment.range(start, end)
37 | }
38 |
39 | export const getKey = (moment) => moment.format(KEY_FORMAT)
40 |
41 | export const incMonth = (date) => date.add(1, 'months')
42 |
43 | export const decMonth = (date) => date.subtract(1, 'months')
44 |
45 | export const getRealMonthAndYear = (month, year) => [
46 | cond([
47 | [lte(12), () =>year + 1],
48 | [T, () => year],
49 | ])(month),
50 | cond([
51 | [lte(12), () => month - 12],
52 | [T, () => month],
53 | ])(month),
54 | ]
55 |
--------------------------------------------------------------------------------
/app/Calendar/styles.css:
--------------------------------------------------------------------------------
1 | .o_day-picker {
2 | color: #333333;
3 | }
4 |
5 | .o_day-picker .e_day-picker-buttons {
6 | display: flex;
7 | justify-content: center;
8 | align-items: center;
9 | position: relative;
10 | background-color: white;
11 | }
12 |
13 | .o_day-picker .i_day-picker-title {
14 | padding: 10px;
15 | text-align: center;
16 | font-size: 1.1em;
17 | }
18 |
19 | .o_day-picker .e_day-picker-arrow-container {
20 | cursor: pointer;
21 | padding: 10px;
22 | }
23 |
24 | .o_day-picker .i_day-picker-arrow-left {
25 | width: 0;
26 | height: 0;
27 | border-top: 5px solid transparent;
28 | border-bottom: 5px solid transparent;
29 | border-right:5px solid black;
30 | }
31 |
32 | .o_day-picker .i_day-picker-arrow-right {
33 | width: 0;
34 | height: 0;
35 | border-top: 5px solid transparent;
36 | border-bottom: 5px solid transparent;
37 | border-left: 5px solid black;
38 | }
39 |
40 |
41 | .o_day-picker .i_day-picker-header {
42 | display: flex;
43 | background: #cccccc;
44 | color: white;
45 | }
46 |
47 | .o_day-picker .i_day-picker-header div {
48 | padding: 5px;
49 | width: 20%;
50 | height: 20%;
51 | text-align: center;
52 | font-size: 0.8em;
53 | }
54 |
55 | .o_day-picker .i_day-picker-body {
56 | cursor: pointer;
57 | background-color: white;
58 | }
59 |
60 | .o_day-picker .i_day-picker-row {
61 | display: flex;
62 | text-align: center;
63 | border: solid #eaecec;
64 | border-width: 0 0 0 1px;
65 | }
66 |
67 | .o_day-picker .i_day-picker-row > div {
68 | border: solid #cccccc;
69 | border-width: 0 1px 1px 0;
70 | width: 20%;
71 | }
72 |
73 | .o_day-picker .i_day-picker-row [class*="-day"] {
74 | padding: 5px;
75 | text-align: left;
76 | font-size: 0.7em;
77 | min-height: 4em;
78 | transition: .1s background linear;
79 | outline: none;
80 | }
81 |
82 | .o_day-picker .i_day-picker-row [class*="-day"]:hover {
83 | background-color: rgba(184, 233, 134, .4) !important;
84 | }
85 |
86 | .o_day-picker .i_day-picker-row .selected-day {
87 | color: white;
88 | background-color: #70bde2;
89 | }
90 |
91 | .o_day-picker .i_day-picker-row .prev-month-day {
92 | color: #b3b3b3;
93 | }
94 |
95 | .o_day-picker .i_day-picker-row .next-month-day {
96 | color: #b3b3b3;
97 | }
98 |
99 | .i_day-picker-actions button{
100 | cursor: pointer;
101 | border-radius: 3px;
102 | margin-top: 25px;
103 | text-align: center;
104 | padding: .4em 1.5em;
105 | font-size: 1em;
106 | text-transform: uppercase;
107 | margin-right: 20px;
108 | min-width: 150px;
109 | outline: none;
110 | }
111 | .o_day-picker .i_day-picker-reset {
112 | border: 2px solid #8f8f8f;
113 | color: #8f8f8f;
114 | }
115 |
116 | .o_day-picker .i_day-picker-reset:hover {
117 | background: #8f8f8f;
118 | color: #fff;
119 | }
120 |
121 | .o_day-picker .i_day-picker-add-channel {
122 | border: 2px solid #38b0ed;
123 | color: #38b0ed;
124 | }
125 |
126 | .o_day-picker .i_day-picker-add-channel:disabled {
127 | opacity: .2 !important;
128 | }
129 |
130 | .o_day-picker .i_day-picker-add-channel:hover {
131 | background: #38b0ed;
132 | color: #fff;
133 | }
134 |
135 | .o_day-picker .i_day-picker-row .o_selected-day,
136 | .o_day-picker .i_day-picker-row .o_selected-day:hover,
137 | .o_day-picker .i_day-picker-row [class*="-day"][disabled]:hover{
138 | background: rgba(155, 155, 155, .2) !important;
139 | color: #000 !important;
140 | cursor: not-allowed !important;
141 | }
142 |
143 | .o_selected-current-channel-day {
144 | background: #b8e986 !important;
145 | color: #000 !important;
146 | }
147 |
148 |
149 |
--------------------------------------------------------------------------------
/app/Example/README.md:
--------------------------------------------------------------------------------
1 | React Calendar Multiday Component Example
2 | ==============================
3 |
4 | This is a very simple — yet runnable app — showing how to use React Datepicker component.
5 |
6 | ## Running Example
7 |
8 | **In the project directory, run:**
9 | ```
10 | $ npm install
11 | $ npm start
12 | ```
13 | **Open [http://localhost:3000](http://localhost:3000) to view it in the browser.**
14 |
--------------------------------------------------------------------------------
/app/Example/build/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andresmijares/react-calendar-multiday/511b101c1d5a183a20562529929654ffde0d7e55/app/Example/build/favicon.ico
--------------------------------------------------------------------------------
/app/Example/build/index.html:
--------------------------------------------------------------------------------
1 | React App
--------------------------------------------------------------------------------
/app/Example/build/main.3fbecb66.css:
--------------------------------------------------------------------------------
1 | .o_day-picker{color:#333}.o_day-picker .e_day-picker-buttons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;background-color:#fff}.o_day-picker .i_day-picker-title{-ms-flex-preferred-size:100%;flex-basis:100%;padding:10px;text-align:center}.o_day-picker .e_day-picker-arrow-container{cursor:pointer;padding:10px}.o_day-picker .i_day-picker-arrow-left{border-right:5px solid #000}.o_day-picker .i_day-picker-arrow-left,.o_day-picker .i_day-picker-arrow-right{width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent}.o_day-picker .i_day-picker-arrow-right{border-left:5px solid #000}.o_day-picker .i_day-picker-header{display:-webkit-box;display:-ms-flexbox;display:flex;background:#ccc;color:#fff}.o_day-picker .i_day-picker-header div{padding:5px;width:20%;height:20%;text-align:center}.o_day-picker .i_day-picker-body{cursor:pointer;background-color:#fff}.o_day-picker .i_day-picker-row{display:-webkit-box;display:-ms-flexbox;display:flex;text-align:center;border:solid #eaecec;border-width:0 0 0 1px}.o_day-picker .i_day-picker-row>div{border:solid #eaecec;border-width:0 1px 1px 0;width:20%}.o_day-picker .i_day-picker-row [class*=-day]{padding:5px;text-align:left;font-size:.7em;min-height:4em}.o_day-picker .i_day-picker-row .selected-day{color:#fff;background-color:#70bde2}.o_day-picker .i_day-picker-row .next-month-day,.o_day-picker .i_day-picker-row .prev-month-day{color:#b3b3b3}.o_day-picker .i_day-picker-reset{width:25%;cursor:pointer;border:1px solid #d3d3d3;border-radius:3px;margin-top:10px;text-align:center}.o_selected-day{background:#1a728f!important;color:#fff!important}
2 | /*# sourceMappingURL=main.3fbecb66.css.map*/
--------------------------------------------------------------------------------
/app/Example/build/main.3fbecb66.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"main.3fbecb66.css","sourceRoot":""}
--------------------------------------------------------------------------------
/app/Example/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andresmijares/react-calendar-multiday/511b101c1d5a183a20562529929654ffde0d7e55/app/Example/favicon.ico
--------------------------------------------------------------------------------
/app/Example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Multiday Calendar
6 |
7 |
8 |
9 |
10 | React App
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/Example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sample",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "react-scripts": "0.2.0"
7 | },
8 | "dependencies": {
9 | "moment": "*",
10 | "moment-range": "*",
11 | "ramda": "*",
12 | "react": "*",
13 | "react-dom": "*",
14 | "react-calendar-multiday": "*"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build"
19 | },
20 | "eslintConfig": {
21 | "extends": "./node_modules/react-scripts/config/eslint.js"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/Example/src/index.js:
--------------------------------------------------------------------------------
1 | // import 'babel-polyfill' /* Support for IE11 */
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import Calendar from 'react-calendar-multiday'
5 | import moment from 'moment'
6 | import {omit} from 'ramda'
7 |
8 | const container = {
9 | width: '375px',
10 | float: 'left',
11 | marginRight: '50px',
12 | marginBottom: '50px',
13 | fontFamily: 'system-ui',
14 | }
15 |
16 | const buttonStyle = {
17 | border: 'none',
18 | fontSize: '.75em',
19 | outline: 'none',
20 | marginLeft: '10px',
21 | cursor: 'pointer',
22 | }
23 |
24 | const selectedDays = [
25 | moment().add(3, 'days'),
26 | moment().add(7, 'days'),
27 | moment().add(8, 'days'),
28 | moment().add(10, 'days'),
29 | moment().add(60, 'days'),
30 | ]
31 |
32 | const reactToChange = (ob) => {
33 | console.warn(ob)
34 | }
35 |
36 | class App extends React.Component {
37 | constructor (props) {
38 | super(props)
39 | this.state = {
40 | currentChannel: 0,
41 | channels: {},
42 | }
43 | this.onReset = this.onReset.bind(this)
44 | this.addChannels = this.addChannels.bind(this)
45 | this.deleteChannel = this.deleteChannel.bind(this)
46 | this.setChannel = this.setChannel.bind(this)
47 | }
48 |
49 | addChannels ({channels, currentChannel}) {
50 | this.setState({
51 | channels: channels,
52 | currentChannel: currentChannel,
53 | })
54 | }
55 |
56 | setChannel (index) {
57 | this.setState({currentChannel: index})
58 | }
59 |
60 | deleteChannel (index) {
61 | this.setState({
62 | channels: omit([String(index)], this.state.channels)
63 | })
64 | }
65 |
66 | onReset () {
67 | this.setState({
68 | channels: {},
69 | currentChannel: 0
70 | })
71 | }
72 |
73 | render () {
74 | return (
75 |
76 |
77 |
{`Calendar with channels`}
78 |
83 |
84 |
85 |
{`Calendar with choisable channels`}
86 |
87 | {Object.keys(this.state.channels).map((key, index) => {
88 | const channel = this.state.channels[key]
89 | return
90 |
91 | {`🗓 ${channel.map(day => day.format('MM/DD/YY')).join(' - ')}`}
92 |
93 |
95 |
97 |
98 | })}
99 |
100 |
108 |
109 |
110 |
{`Single Day Calendar`}
111 |
114 |
115 |
116 |
{`Single Day Calendar Pre-selected`}
117 |
120 |
121 |
122 |
123 |
{`Two Months from Now`}
124 |
128 |
129 |
130 |
{`Multiple Day Calendar`}
131 |
134 |
135 |
136 |
137 |
{`Multiple With Pre-selected days`}
138 |
143 |
144 |
145 | )
146 | }
147 | }
148 |
149 | const render = () => {
150 | ReactDOM.render(
151 | ,
152 | document.getElementById('root')
153 | )
154 | }
155 |
156 | /* prevent FOUC https://stackoverflow.com/a/43902734 */
157 | setTimeout(render, 0)
158 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | import Calendar from './Calendar/Calendar'
2 | import './Calendar/styles.css'
3 |
4 | export default Calendar
5 |
--------------------------------------------------------------------------------
/lib/app.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("ramda"),require("moment"),require("moment-range")):"function"==typeof define&&define.amd?define("app",["react","ramda","moment","moment-range"],e):"object"==typeof exports?exports.app=e(require("react"),require("ramda"),require("moment"),require("moment-range")):t.app=e(t.React,t.ramda,t.moment,t["moment-range"])}(this,function(t,e,n,r){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=57)}([function(t,e){var n=t.exports={version:"2.5.1"};"number"==typeof __e&&(__e=n)},function(t,e,n){var r=n(28)("wks"),o=n(17),i=n(3).Symbol,a="function"==typeof i;(t.exports=function(t){return r[t]||(r[t]=a&&i[t]||(a?i:o)("Symbol."+t))}).store=r},function(t,e,n){var r=n(3),o=n(0),i=n(22),a=n(7),u=function(t,e,n){var c,s,f,l=t&u.F,d=t&u.G,p=t&u.S,y=t&u.P,h=t&u.B,v=t&u.W,m=d?o:o[e]||(o[e]={}),b=m.prototype,g=d?r:p?r[e]:(r[e]||{}).prototype;d&&(n=e);for(c in n)(s=!l&&g&&void 0!==g[c])&&c in m||(f=s?g[c]:n[c],m[c]=d&&"function"!=typeof g[c]?n[c]:h&&s?i(f,r):v&&g[c]==f?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(f):y&&"function"==typeof f?i(Function.call,f):f,y&&((m.virtual||(m.virtual={}))[c]=f,t&u.R&&b&&!b[c]&&a(b,c,f)))};u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(8),o=n(42),i=n(23),a=Object.defineProperty;e.f=n(5)?Object.defineProperty:function(t,e,n){if(r(t),e=i(e,!0),r(n),o)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){t.exports=!n(9)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(4),o=n(12);t.exports=n(5)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(11);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(46),o=n(25);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(45),o=n(29);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e,n){var r=n(25);t.exports=function(t){return Object(r(t))}},function(t,e){t.exports={}},function(t,e){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(t){if(f===setTimeout)return setTimeout(t,0);if((f===n||!f)&&setTimeout)return f=setTimeout,setTimeout(t,0);try{return f(t,0)}catch(e){try{return f.call(null,t,0)}catch(e){return f.call(this,t,0)}}}function i(t){if(l===clearTimeout)return clearTimeout(t);if((l===r||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(t);try{return l(t)}catch(e){try{return l.call(null,t)}catch(e){return l.call(this,t)}}}function a(){h&&p&&(h=!1,p.length?y=p.concat(y):v=-1,y.length&&u())}function u(){if(!h){var t=o(a);h=!0;for(var e=y.length;e;){for(p=y,y=[];++v1)for(var n=1;n0?r:n)(t)}},function(t,e,n){var r=n(28)("keys"),o=n(17);t.exports=function(t){return r[t]||(r[t]=o(t))}},function(t,e,n){var r=n(3),o=r["__core-js_shared__"]||(r["__core-js_shared__"]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e){e.f=Object.getOwnPropertySymbols},function(t,e,n){t.exports={default:n(69),__esModule:!0}},function(t,e){t.exports=!0},function(t,e,n){var r=n(8),o=n(81),i=n(29),a=n(27)("IE_PROTO"),u=function(){},c=function(){var t,e=n(43)("iframe"),r=i.length;for(e.style.display="none",n(82).appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write("