From 28417751cd5d2b58ad67391b5cf8323789fce802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aper=C3=A7u?= Date: Sun, 17 Jan 2016 16:50:10 +0100 Subject: [PATCH] feat(calendar): add ability to change firstDayOfWeek and use localized abbreviations When it's available --- .../DatePicker/ExampleInternational.jsx | 1 + .../pages/components/DatePicker/Page.jsx | 2 +- src/date-picker/calendar-month.jsx | 3 +- src/date-picker/calendar.jsx | 19 ++++--- src/date-picker/date-picker-dialog.jsx | 3 ++ src/date-picker/date-picker.jsx | 10 ++++ src/utils/date-time.js | 52 ++++++++++++------- 7 files changed, 62 insertions(+), 28 deletions(-) diff --git a/docs/src/app/components/pages/components/DatePicker/ExampleInternational.jsx b/docs/src/app/components/pages/components/DatePicker/ExampleInternational.jsx index 65dbaf5bead8a3..7d3a638c2fb1b6 100644 --- a/docs/src/app/components/pages/components/DatePicker/ExampleInternational.jsx +++ b/docs/src/app/components/pages/components/DatePicker/ExampleInternational.jsx @@ -8,6 +8,7 @@ const DatePickerExampleInternational = () => ( // Intl is supported by most modern browsers, see http://caniuse.com/#search=intl // for browsers that don't support it use this polyfill https://github.com/andyearnshaw/Intl.js wordings={{ok: 'OK', cancel: 'Annuler'}} + firstDayOfWeek={1} locale="fr" /> ); diff --git a/docs/src/app/components/pages/components/DatePicker/Page.jsx b/docs/src/app/components/pages/components/DatePicker/Page.jsx index f6cb9c11cdd0da..0b26cf0e748e6e 100644 --- a/docs/src/app/components/pages/components/DatePicker/Page.jsx +++ b/docs/src/app/components/pages/components/DatePicker/Page.jsx @@ -23,7 +23,7 @@ const descriptions = { ranged: 'This example allows you to set a date range, and to toggle `AutoOk`, and `disableYearSelection`.', controlled: 'Implements a controlled input, where the value is handled by state in the parent (example) component.', localised: 'Demonstrates a localised `DatePicker`, in this case in French. ' + - 'Note that the buttons must be localised using the `wordings` property.', + 'Note that the buttons must be localised using the `wordings` property and we set the `firstDayOfWeek` to Monday.', }; if (!window.Intl) { diff --git a/src/date-picker/calendar-month.jsx b/src/date-picker/calendar-month.jsx index 9d8d38eab8d4ca..a2b9d75c70e7d4 100644 --- a/src/date-picker/calendar-month.jsx +++ b/src/date-picker/calendar-month.jsx @@ -8,6 +8,7 @@ const CalendarMonth = React.createClass({ propTypes: { autoOk: React.PropTypes.bool, displayDate: React.PropTypes.object.isRequired, + firstDayOfWeek: React.PropTypes.number, maxDate: React.PropTypes.object, minDate: React.PropTypes.object, onDayTouchTap: React.PropTypes.func, @@ -20,7 +21,7 @@ const CalendarMonth = React.createClass({ }, _getWeekElements() { - let weekArray = DateTime.getWeekArray(this.props.displayDate); + let weekArray = DateTime.getWeekArray(this.props.displayDate, this.props.firstDayOfWeek); return weekArray.map((week, i) => { return ( diff --git a/src/date-picker/calendar.jsx b/src/date-picker/calendar.jsx index 6518136dccc1b2..abe68955fb5a00 100644 --- a/src/date-picker/calendar.jsx +++ b/src/date-picker/calendar.jsx @@ -13,11 +13,14 @@ import ClearFix from '../clearfix'; import ThemeManager from '../styles/theme-manager'; import DefaultRawTheme from '../styles/raw-themes/light-raw-theme'; +const daysArray = [...Array(7)]; + const Calendar = React.createClass({ propTypes: { DateTimeFormat: React.PropTypes.func.isRequired, disableYearSelection: React.PropTypes.bool, + firstDayOfWeek: React.PropTypes.number, initialDate: React.PropTypes.object, locale: React.PropTypes.string.isRequired, maxDate: React.PropTypes.object, @@ -243,7 +246,7 @@ const Calendar = React.createClass({ render() { let yearCount = DateTime.yearDiff(this.props.maxDate, this.props.minDate) + 1; - let weekCount = DateTime.getWeekArray(this.state.displayDate).length; + let weekCount = DateTime.getWeekArray(this.state.displayDate, this.props.firstDayOfWeek).length; let toolbarInteractions = this._getToolbarInteractions(); let isLandscape = this.props.mode === 'landscape'; let styles = { @@ -295,6 +298,7 @@ const Calendar = React.createClass({ const { DateTimeFormat, locale, + firstDayOfWeek, } = this.props; return ( @@ -325,13 +329,11 @@ const Calendar = React.createClass({ elementType="ul" style={styles.weekTitle} > -
  • S
  • -
  • M
  • -
  • T
  • -
  • W
  • -
  • T
  • -
  • F
  • -
  • S
  • + {daysArray.map((e, i) => ( +
  • + {DateTime.localizedWeekday(DateTimeFormat, locale, i, firstDayOfWeek)} +
  • + ))} diff --git a/src/date-picker/date-picker-dialog.jsx b/src/date-picker/date-picker-dialog.jsx index 85ffc9a5850b04..764aae7c282e77 100644 --- a/src/date-picker/date-picker-dialog.jsx +++ b/src/date-picker/date-picker-dialog.jsx @@ -18,6 +18,7 @@ const DatePickerDialog = React.createClass({ autoOk: React.PropTypes.bool, container: React.PropTypes.oneOf(['dialog', 'inline']), disableYearSelection: React.PropTypes.bool, + firstDayOfWeek: React.PropTypes.number, initialDate: React.PropTypes.object, locale: React.PropTypes.string, maxDate: React.PropTypes.object, @@ -151,6 +152,7 @@ const DatePickerDialog = React.createClass({ onAccept, style, container, + firstDayOfWeek, ...other, } = this.props; @@ -216,6 +218,7 @@ const DatePickerDialog = React.createClass({ > ); diff --git a/src/utils/date-time.js b/src/utils/date-time.js index 566183bb348399..a4a6856b25391f 100644 --- a/src/utils/date-time.js +++ b/src/utils/date-time.js @@ -1,5 +1,6 @@ import warning from 'warning'; +const dayAbbreviation = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; const dayList = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const monthList = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; @@ -20,11 +21,11 @@ function DateTimeFormat(locale, options) { output = dayList[date.getDay()] + ', '; output += monthList[date.getMonth()] + ' '; output += date.getDate(); - } else if (options.month === 'long' - && options.year === 'numeric') { - + } else if (options.month === 'long' && options.year === 'numeric') { output = monthLongList[date.getMonth()]; output += ' ' + date.getFullYear(); + } else if (options.weekday === 'narrow') { + output = dayAbbreviation[date.getDay()]; } else { warning(false, 'Wrong usage of DateTimeFormat'); } @@ -77,35 +78,50 @@ export default { return new Date(d.getFullYear(), d.getMonth(), 1); }, - getWeekArray(d) { + getFirstDayOfWeek() { + const now = new Date(); + return new Date(now.setDate(now.getDate() - now.getDay())); + }, + + getWeekArray(d, firstDayOfWeek) { let dayArray = []; let daysInMonth = this.getDaysInMonth(d); - let daysInWeek; - let emptyDays; - let firstDayOfWeek; - let week; let weekArray = []; + let week = []; for (let i = 1; i <= daysInMonth; i++) { dayArray.push(new Date(d.getFullYear(), d.getMonth(), i)); } - while (dayArray.length) { - firstDayOfWeek = dayArray[0].getDay(); - daysInWeek = 7 - firstDayOfWeek; - emptyDays = 7 - daysInWeek; - week = dayArray.splice(0, daysInWeek); - - for (let i = 0; i < emptyDays; i++) { - week.unshift(null); + const addWeek = week => { + const emptyDays = 7 - week.length; + for (let i = 0; i < emptyDays; ++i) { + week[weekArray.length ? 'push' : 'unshift'](null); } - weekArray.push(week); - } + }; + + dayArray.forEach(day => { + if (week.length > 0 && day.getDay() === firstDayOfWeek) { + addWeek(week); + week = []; + } + week.push(day); + if (dayArray.indexOf(day) === dayArray.length - 1) { + addWeek(week); + } + }); return weekArray; }, + localizedWeekday(DateTimeFormat, locale, day, firstDayOfWeek) { + const weekdayFormatter = new DateTimeFormat(locale, {weekday: 'narrow'}); + const firstDayDate = this.getFirstDayOfWeek(); + + return weekdayFormatter.format(this.addDays(firstDayDate, day + firstDayOfWeek)); + }, + format(date) { const m = date.getMonth() + 1; const d = date.getDate();