@primer/components 0.0.0-202192422618 → 0.0.0-202192522854

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @primer/components
2
2
 
3
- ## 0.0.0-202192422618
3
+ ## 0.0.0-202192522854
4
4
 
5
5
  ### Patch Changes
6
6
 
@@ -21,8 +21,11 @@ const DatePicker = ({
21
21
  anchorVariant,
22
22
  anchorRef: externalAnchorRef,
23
23
  confirmation,
24
+ dateFormat,
24
25
  focusTrapSettings,
25
26
  focusZoneSettings,
27
+ maxDate,
28
+ minDate,
26
29
  onOpen: onOpenExternal,
27
30
  onClose: onCloseExternal,
28
31
  open,
@@ -30,15 +33,20 @@ const DatePicker = ({
30
33
  renderAnchor,
31
34
  selection,
32
35
  value,
33
- view
36
+ view,
37
+ weekStartsOn
34
38
  }) => {
35
39
  const buttonRef = (0, _react.useRef)(null);
36
40
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
37
41
  const datePickerConfiguration = {
38
42
  anchorVariant,
39
43
  confirmation,
44
+ dateFormat,
45
+ maxDate,
46
+ minDate,
40
47
  selection,
41
- view
48
+ view,
49
+ weekStartsOn
42
50
  };
43
51
 
44
52
  const onOpen = gesture => {
@@ -2,4 +2,4 @@ import React from 'react';
2
2
  export interface DatePickerAnchorProps {
3
3
  onAction?: (event?: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
4
4
  }
5
- export declare const DatePickerAnchor: React.ForwardRefExoticComponent<DatePickerAnchorProps & React.RefAttributes<HTMLButtonElement>>;
5
+ export declare const DatePickerAnchor: React.ForwardRefExoticComponent<DatePickerAnchorProps & React.RefAttributes<HTMLDivElement>>;
@@ -11,7 +11,7 @@ var _styledComponents = _interopRequireDefault(require("styled-components"));
11
11
 
12
12
  var _react = _interopRequireWildcard(require("react"));
13
13
 
14
- var _Button = _interopRequireDefault(require("../Button"));
14
+ var _Button = _interopRequireWildcard(require("../Button"));
15
15
 
16
16
  var _Text = _interopRequireDefault(require("../Text"));
17
17
 
@@ -23,6 +23,8 @@ var _useDatePicker = _interopRequireDefault(require("./useDatePicker"));
23
23
 
24
24
  var _TextInput = _interopRequireDefault(require("../TextInput"));
25
25
 
26
+ var _Box = _interopRequireDefault(require("../Box"));
27
+
26
28
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
27
29
 
28
30
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -39,7 +41,8 @@ const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
39
41
  }, ref) => {
40
42
  const {
41
43
  configuration: {
42
- anchorVariant
44
+ anchorVariant,
45
+ iconPlacement
43
46
  },
44
47
  disabled,
45
48
  formattedDate
@@ -60,15 +63,61 @@ const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
60
63
 
61
64
  onAction === null || onAction === void 0 ? void 0 : onAction(event);
62
65
  }, [disabled, onAction]);
66
+ const onKeyPressHandler = (0, _react.useCallback)(e => {
67
+ console.log(e.currentTarget.value);
68
+ }, []);
69
+ const onInputChangeHandler = (0, _react.useCallback)(e => {
70
+ console.log(e.currentTarget.value);
71
+ }, []);
63
72
 
64
73
  if (anchorVariant === 'input') {
65
- return /*#__PURE__*/_react.default.createElement(_TextInput.default, {
66
- value: formattedDate
67
- });
74
+ const calendarButton = side => /*#__PURE__*/_react.default.createElement(_Button.ButtonInvisible, {
75
+ onClick: clickHandler,
76
+ sx: {
77
+ width: '32px',
78
+ px: '6px',
79
+ position: 'absolute',
80
+ [side]: '1px',
81
+ top: '1px'
82
+ }
83
+ }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
84
+ icon: _octiconsReact.CalendarIcon
85
+ }));
86
+
87
+ const inputSx = () => {
88
+ if (iconPlacement === 'start') {
89
+ return {
90
+ pl: 5,
91
+ pr: 2
92
+ };
93
+ } else if (iconPlacement === 'end') {
94
+ return {
95
+ pl: 2,
96
+ pr: 5
97
+ };
98
+ } else {
99
+ return {};
100
+ }
101
+ };
102
+
103
+ return /*#__PURE__*/_react.default.createElement(_Box.default, {
104
+ ref: ref,
105
+ sx: {
106
+ position: 'relative',
107
+ display: 'flex',
108
+ flex: 1
109
+ }
110
+ }, iconPlacement === 'start' && calendarButton('left'), /*#__PURE__*/_react.default.createElement(_TextInput.default, {
111
+ defaultValue: formattedDate,
112
+ onKeyPress: onKeyPressHandler,
113
+ onChange: onInputChangeHandler,
114
+ sx: inputSx()
115
+ }), iconPlacement === 'end' && calendarButton('right'));
68
116
  }
69
117
 
70
- return /*#__PURE__*/_react.default.createElement(DatePickerAnchorButton, {
71
- ref: ref,
118
+ return /*#__PURE__*/_react.default.createElement(_Box.default, {
119
+ ref: ref
120
+ }, /*#__PURE__*/_react.default.createElement(DatePickerAnchorButton, {
72
121
  onClick: clickHandler,
73
122
  onKeyPress: keyPressHandler
74
123
  }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
@@ -82,7 +131,7 @@ const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
82
131
  overflow: 'hidden',
83
132
  textOverflow: 'ellipsis'
84
133
  }
85
- }, formattedDate));
134
+ }, formattedDate)));
86
135
  });
87
136
 
88
137
  exports.DatePickerAnchor = DatePickerAnchor;
@@ -7,7 +7,7 @@ exports.DatePickerPanel = void 0;
7
7
 
8
8
  var _dateFns = require("date-fns");
9
9
 
10
- var _react = _interopRequireDefault(require("react"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _Box = _interopRequireDefault(require("../Box"));
13
13
 
@@ -25,12 +25,12 @@ var _StyledOcticon = _interopRequireDefault(require("../StyledOcticon"));
25
25
 
26
26
  var _Button = _interopRequireWildcard(require("../Button"));
27
27
 
28
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
+
28
30
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
29
31
 
30
32
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
31
33
 
32
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
-
34
34
  const DatePickerPanelContainer = (0, _styledComponents.default)(_Box.default).withConfig({
35
35
  displayName: "DatePickerPanel__DatePickerPanelContainer",
36
36
  componentId: "sc-19upxpo-0"
@@ -52,23 +52,58 @@ const DatePickerPanel = () => {
52
52
  const {
53
53
  configuration,
54
54
  saveValue,
55
- revertValue
55
+ revertValue,
56
+ currentViewingDate,
57
+ goToMonth,
58
+ nextMonth,
59
+ previousMonth
56
60
  } = (0, _useDatePicker.default)();
61
+ const previousDisabled = (0, _react.useMemo)(() => {
62
+ const {
63
+ minDate
64
+ } = configuration;
65
+ if (!minDate) return false;
66
+ const previous = (0, _dateFns.subMonths)(currentViewingDate, 1);
67
+
68
+ if (minDate.getFullYear() >= previous.getFullYear() && minDate.getMonth() > previous.getMonth()) {
69
+ return true;
70
+ }
71
+
72
+ return false;
73
+ }, [configuration, currentViewingDate]);
74
+ const nextDisabled = (0, _react.useMemo)(() => {
75
+ const {
76
+ maxDate,
77
+ view
78
+ } = configuration;
79
+ if (!maxDate) return false;
80
+ const next = (0, _dateFns.addMonths)(currentViewingDate, view === '2-month' ? 2 : 1);
81
+
82
+ if (maxDate.getFullYear() <= next.getFullYear() && maxDate.getMonth() < next.getMonth()) {
83
+ return true;
84
+ }
85
+
86
+ return false;
87
+ }, [configuration, currentViewingDate]);
57
88
  return /*#__PURE__*/_react.default.createElement(DatePickerPanelContainer, null, /*#__PURE__*/_react.default.createElement(DatePickerPanelMonths, null, /*#__PURE__*/_react.default.createElement(ArrowButton, {
58
89
  variant: "small",
59
- side: "left"
90
+ side: "left",
91
+ onClick: previousMonth,
92
+ disabled: previousDisabled
60
93
  }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
61
94
  icon: _octiconsReact.ChevronLeftIcon,
62
95
  color: "fg.muted"
63
96
  })), /*#__PURE__*/_react.default.createElement(_Month.Month, {
64
- month: new Date().getMonth(),
65
- year: new Date().getFullYear()
97
+ month: currentViewingDate.getMonth(),
98
+ year: currentViewingDate.getFullYear()
66
99
  }), configuration.view === '2-month' && /*#__PURE__*/_react.default.createElement(_Month.Month, {
67
- month: (0, _dateFns.addMonths)(new Date(), 1).getMonth(),
68
- year: (0, _dateFns.addMonths)(new Date(), 1).getFullYear()
100
+ month: (0, _dateFns.addMonths)(currentViewingDate, 1).getMonth(),
101
+ year: (0, _dateFns.addMonths)(currentViewingDate, 1).getFullYear()
69
102
  }), /*#__PURE__*/_react.default.createElement(ArrowButton, {
70
103
  variant: "small",
71
- side: "right"
104
+ side: "right",
105
+ onClick: nextMonth,
106
+ disabled: nextDisabled
72
107
  }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
73
108
  icon: _octiconsReact.ChevronRightIcon,
74
109
  color: "fg.muted"
@@ -79,7 +114,8 @@ const DatePickerPanel = () => {
79
114
  },
80
115
  onClick: () => revertValue()
81
116
  }, "Reset"), /*#__PURE__*/_react.default.createElement(_Button.default, {
82
- variant: "small"
117
+ variant: "small",
118
+ onClick: () => goToMonth(new Date())
83
119
  }, "Today")), configuration.confirmation && /*#__PURE__*/_react.default.createElement(_Button.ButtonPrimary, {
84
120
  variant: "small",
85
121
  onClick: () => saveValue()
@@ -120,7 +120,7 @@ const DayComponent = (0, _styledComponents.default)(DayBaseComponent).attrs(prop
120
120
  })).withConfig({
121
121
  displayName: "Day__DayComponent",
122
122
  componentId: "sc-1japneh-1"
123
- })(["background-color:", ";border-radius:", ";transition:0.2s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.2s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, _Text.default, props => props.textColor, (0, _constants.get)('fonts.mono'), (0, _constants.get)('fontSizes.0'), props => props.backgroundHover, _Text.default, props => props.textColorHover, props => props.backgroundPressed, (0, _constants.get)('shadows.shadow.medium'), _Text.default, props => props.textColorPressed);
123
+ })(["background-color:", ";border-radius:", ";transition:0.1s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.1s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, _Text.default, props => props.textColor, (0, _constants.get)('fonts.mono'), (0, _constants.get)('fontSizes.0'), props => props.backgroundHover, _Text.default, props => props.textColorHover, props => props.backgroundPressed, (0, _constants.get)('shadows.shadow.medium'), _Text.default, props => props.textColorPressed);
124
124
 
125
125
  const Day = ({
126
126
  date,
@@ -19,6 +19,8 @@ var _constants = require("../constants");
19
19
 
20
20
  var _Day = require("./Day");
21
21
 
22
+ var _useDatePicker = _interopRequireDefault(require("./useDatePicker"));
23
+
22
24
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
25
 
24
26
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -42,17 +44,23 @@ const Month = ({
42
44
  month,
43
45
  year
44
46
  }) => {
47
+ const {
48
+ configuration
49
+ } = (0, _useDatePicker.default)();
45
50
  const [selectedDay, setSelectedDay] = (0, _react.useState)(null);
46
51
  const getTitle = (0, _react.useMemo)(() => `${(0, _dateFns.format)(new Date(year, month), 'MMMM yyyy')}`, [month, year]);
47
52
  const weekdayHeaders = (0, _react.useMemo)(() => {
48
53
  const now = new Date(year, month);
54
+ const weekOptions = {
55
+ weekStartsOn: configuration.weekStartsOn === 'Sunday' ? 0 : 1
56
+ };
49
57
  return (0, _dateFns.eachDayOfInterval)({
50
- start: (0, _dateFns.startOfWeek)(now),
51
- end: (0, _dateFns.endOfWeek)(now)
58
+ start: (0, _dateFns.startOfWeek)(now, weekOptions),
59
+ end: (0, _dateFns.endOfWeek)(now, weekOptions)
52
60
  }).map(d => /*#__PURE__*/_react.default.createElement(WeekdayHeader, {
53
61
  key: `weekday-${d}-header`
54
62
  }, (0, _dateFns.format)(d, 'EEEEEE')));
55
- }, [month, year]);
63
+ }, [configuration.weekStartsOn, month, year]);
56
64
 
57
65
  const dayAction = date => {
58
66
  setSelectedDay(date);
@@ -61,8 +69,9 @@ const Month = ({
61
69
  const dayComponents = (0, _react.useMemo)(() => {
62
70
  const components = [];
63
71
  const firstDay = new Date(year, month, 1);
72
+ const preBlanks = configuration.weekStartsOn === 'Sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
64
73
 
65
- for (let i = 0; i < firstDay.getDay(); i++) {
74
+ for (let i = 0; i < preBlanks; i++) {
66
75
  components.push( /*#__PURE__*/_react.default.createElement(_Day.BlankDay, {
67
76
  key: `month-pre-blank-${i}`
68
77
  }));
@@ -79,15 +88,16 @@ const Month = ({
79
88
  }
80
89
 
81
90
  const lastDay = (0, _dateFns.lastDayOfMonth)(firstDay);
91
+ const postBlanks = configuration.weekStartsOn === 'Sunday' ? lastDay.getDay() : (lastDay.getDay() + 6) % 7;
82
92
 
83
- for (let i = 6; i > lastDay.getDay(); i--) {
93
+ for (let i = 6; i > postBlanks; i--) {
84
94
  components.push( /*#__PURE__*/_react.default.createElement(_Day.BlankDay, {
85
95
  key: `month-post-blank-${i}`
86
96
  }));
87
97
  }
88
98
 
89
99
  return components;
90
- }, [month, selectedDay, year]);
100
+ }, [configuration.weekStartsOn, month, selectedDay, year]);
91
101
  return /*#__PURE__*/_react.default.createElement(MonthComponent, {
92
102
  role: "grid"
93
103
  }, /*#__PURE__*/_react.default.createElement(MonthTitle, null, getTitle), weekdayHeaders, dayComponents);
@@ -9,12 +9,14 @@ export interface DatePickerConfiguration {
9
9
  contiguousSelection?: boolean;
10
10
  dateFormat?: DateFormat;
11
11
  dimWeekends?: boolean;
12
+ iconPlacement?: 'start' | 'end' | 'none';
12
13
  minDate?: Date;
13
14
  maxDate?: Date;
14
15
  placeholder?: string;
15
16
  rangeIncrement?: number;
16
17
  selection?: SelectionVariant;
17
18
  view?: '1-month' | '2-month';
19
+ weekStartsOn?: 'Sunday' | 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday';
18
20
  }
19
21
  export declare type RangeSelection = {
20
22
  from: Date;
@@ -27,14 +29,18 @@ export declare type StringRangeSelection = {
27
29
  export interface DatePickerContext {
28
30
  disabled?: boolean;
29
31
  configuration: DatePickerConfiguration;
32
+ currentViewingDate: Date;
33
+ goToMonth: (date: Date) => void;
30
34
  hoverRange?: RangeSelection | null;
31
35
  selection?: Selection;
32
36
  softSelection?: Partial<RangeSelection> | null;
33
37
  selectionActive?: boolean;
34
38
  formattedDate: string;
39
+ nextMonth: () => void;
35
40
  onSelection: (date: Date) => void;
36
41
  onDayFocus: (date: Date) => void;
37
42
  onDayBlur: (date: Date) => void;
43
+ previousMonth: () => void;
38
44
  revertValue: () => void;
39
45
  saveValue: (selection?: Selection) => void;
40
46
  }
@@ -49,14 +55,18 @@ declare const useDatePicker: (date?: Date | undefined) => {
49
55
  disabled: boolean;
50
56
  selected: DaySelection;
51
57
  configuration: DatePickerConfiguration;
58
+ currentViewingDate: Date;
59
+ goToMonth: (date: Date) => void;
52
60
  hoverRange?: RangeSelection | null | undefined;
53
61
  selection?: Selection | undefined;
54
62
  softSelection?: Partial<RangeSelection> | null | undefined;
55
63
  selectionActive?: boolean | undefined;
56
64
  formattedDate: string;
65
+ nextMonth: () => void;
57
66
  onSelection: (date: Date) => void;
58
67
  onDayFocus: (date: Date) => void;
59
68
  onDayBlur: (date: Date) => void;
69
+ previousMonth: () => void;
60
70
  revertValue: () => void;
61
71
  saveValue: (selection?: Selection | undefined) => void;
62
72
  };
@@ -164,9 +164,11 @@ const defaultConfiguration = {
164
164
  confirmation: false,
165
165
  contiguousSelection: false,
166
166
  dimWeekends: false,
167
+ iconPlacement: 'start',
167
168
  placeholder: 'Select a Date...',
168
169
  selection: 'single',
169
- view: '2-month'
170
+ view: '2-month',
171
+ weekStartsOn: 'Sunday'
170
172
  };
171
173
 
172
174
  const DatePickerProvider = ({
@@ -179,11 +181,21 @@ const DatePickerProvider = ({
179
181
  const [previousSelection, setPreviousSelection] = (0, _react.useState)(parseSelection(value, configuration.selection));
180
182
  const [selection, setSelection] = (0, _react.useState)(parseSelection(value, configuration.selection));
181
183
  const [hoverRange, setHoverRange] = (0, _react.useState)(null);
184
+ const [currentViewingDate, setCurrentViewingDate] = (0, _react.useState)(new Date());
182
185
  (0, _react.useEffect)(() => {
183
186
  setConfiguration((0, _deepmerge.default)(defaultConfiguration, externalConfig));
184
187
  setSelection(parseSelection(selection, configuration.selection)); // Don't want this to run every time selection gets updated
185
188
  // eslint-disable-next-line react-hooks/exhaustive-deps
186
189
  }, [configuration.selection, externalConfig]);
190
+ const goToMonth = (0, _react.useCallback)(date => {
191
+ setCurrentViewingDate(new Date(new Date(date).toDateString()));
192
+ }, []);
193
+ const nextMonth = (0, _react.useCallback)(() => {
194
+ setCurrentViewingDate((0, _dateFns.addMonths)(currentViewingDate, 1));
195
+ }, [currentViewingDate]);
196
+ const previousMonth = (0, _react.useCallback)(() => {
197
+ setCurrentViewingDate((0, _dateFns.subMonths)(currentViewingDate, 1));
198
+ }, [currentViewingDate]);
187
199
  const getFormattedDate = (0, _react.useMemo)(() => {
188
200
  if (!selection) {
189
201
  return configuration.placeholder;
@@ -191,7 +203,7 @@ const DatePickerProvider = ({
191
203
 
192
204
  let template = 'MMM d';
193
205
 
194
- if (configuration.dateFormat) {
206
+ if (configuration.anchorVariant !== 'input' && configuration.dateFormat) {
195
207
  switch (configuration.dateFormat) {
196
208
  case 'short':
197
209
  template = 'MMM d';
@@ -205,6 +217,8 @@ const DatePickerProvider = ({
205
217
  template = configuration.dateFormat;
206
218
  break;
207
219
  }
220
+ } else {
221
+ template = 'MM/dd/yyyy';
208
222
  }
209
223
 
210
224
  switch (configuration.selection) {
@@ -262,7 +276,7 @@ const DatePickerProvider = ({
262
276
  return 'Invalid Configuration';
263
277
  }
264
278
  }
265
- }, [configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
279
+ }, [configuration.anchorVariant, configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
266
280
  const saveValue = (0, _react.useCallback)(updatedSelection => {
267
281
  setPreviousSelection(updatedSelection !== null && updatedSelection !== void 0 ? updatedSelection : selection);
268
282
  closePicker === null || closePicker === void 0 ? void 0 : closePicker();
@@ -336,18 +350,22 @@ const DatePickerProvider = ({
336
350
  const datePickerCtx = (0, _react.useMemo)(() => {
337
351
  return {
338
352
  configuration,
353
+ currentViewingDate,
339
354
  disabled: false,
340
355
  formattedDate: getFormattedDate,
356
+ goToMonth,
341
357
  hoverRange,
358
+ nextMonth,
342
359
  onDayBlur: blurHnadler,
343
360
  onDayFocus: focusHnadler,
344
361
  onSelection: selectionHandler,
362
+ previousMonth,
345
363
  revertValue,
346
364
  saveValue,
347
365
  selectionActive: false,
348
366
  selection
349
367
  };
350
- }, [blurHnadler, configuration, focusHnadler, getFormattedDate, hoverRange, revertValue, saveValue, selection, selectionHandler]);
368
+ }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
351
369
  return /*#__PURE__*/_react.default.createElement(DatePickerContext.Provider, {
352
370
  value: datePickerCtx
353
371
  }, children);
@@ -6,8 +6,11 @@ export const DatePicker = ({
6
6
  anchorVariant,
7
7
  anchorRef: externalAnchorRef,
8
8
  confirmation,
9
+ dateFormat,
9
10
  focusTrapSettings,
10
11
  focusZoneSettings,
12
+ maxDate,
13
+ minDate,
11
14
  onOpen: onOpenExternal,
12
15
  onClose: onCloseExternal,
13
16
  open,
@@ -15,15 +18,20 @@ export const DatePicker = ({
15
18
  renderAnchor,
16
19
  selection,
17
20
  value,
18
- view
21
+ view,
22
+ weekStartsOn
19
23
  }) => {
20
24
  const buttonRef = useRef(null);
21
25
  const [isOpen, setIsOpen] = useState(false);
22
26
  const datePickerConfiguration = {
23
27
  anchorVariant,
24
28
  confirmation,
29
+ dateFormat,
30
+ maxDate,
31
+ minDate,
25
32
  selection,
26
- view
33
+ view,
34
+ weekStartsOn
27
35
  };
28
36
 
29
37
  const onOpen = gesture => {
@@ -2,4 +2,4 @@ import React from 'react';
2
2
  export interface DatePickerAnchorProps {
3
3
  onAction?: (event?: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
4
4
  }
5
- export declare const DatePickerAnchor: React.ForwardRefExoticComponent<DatePickerAnchorProps & React.RefAttributes<HTMLButtonElement>>;
5
+ export declare const DatePickerAnchor: React.ForwardRefExoticComponent<DatePickerAnchorProps & React.RefAttributes<HTMLDivElement>>;
@@ -1,12 +1,13 @@
1
1
  import { CalendarIcon } from '@primer/octicons-react';
2
2
  import styled from 'styled-components';
3
3
  import React, { useCallback } from 'react';
4
- import Button from '../Button';
4
+ import Button, { ButtonInvisible } from '../Button';
5
5
  import Text from '../Text';
6
6
  import { get } from '../constants';
7
7
  import StyledOcticon from '../StyledOcticon';
8
8
  import useDatePicker from './useDatePicker';
9
9
  import TextInput from '../TextInput';
10
+ import Box from '../Box';
10
11
  const DatePickerAnchorButton = styled(Button).withConfig({
11
12
  displayName: "DatePickerAnchor__DatePickerAnchorButton",
12
13
  componentId: "sc-8gpb9d-0"
@@ -16,7 +17,8 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
16
17
  }, ref) => {
17
18
  const {
18
19
  configuration: {
19
- anchorVariant
20
+ anchorVariant,
21
+ iconPlacement
20
22
  },
21
23
  disabled,
22
24
  formattedDate
@@ -37,15 +39,61 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
37
39
 
38
40
  onAction === null || onAction === void 0 ? void 0 : onAction(event);
39
41
  }, [disabled, onAction]);
42
+ const onKeyPressHandler = useCallback(e => {
43
+ console.log(e.currentTarget.value);
44
+ }, []);
45
+ const onInputChangeHandler = useCallback(e => {
46
+ console.log(e.currentTarget.value);
47
+ }, []);
40
48
 
41
49
  if (anchorVariant === 'input') {
42
- return /*#__PURE__*/React.createElement(TextInput, {
43
- value: formattedDate
44
- });
50
+ const calendarButton = side => /*#__PURE__*/React.createElement(ButtonInvisible, {
51
+ onClick: clickHandler,
52
+ sx: {
53
+ width: '32px',
54
+ px: '6px',
55
+ position: 'absolute',
56
+ [side]: '1px',
57
+ top: '1px'
58
+ }
59
+ }, /*#__PURE__*/React.createElement(StyledOcticon, {
60
+ icon: CalendarIcon
61
+ }));
62
+
63
+ const inputSx = () => {
64
+ if (iconPlacement === 'start') {
65
+ return {
66
+ pl: 5,
67
+ pr: 2
68
+ };
69
+ } else if (iconPlacement === 'end') {
70
+ return {
71
+ pl: 2,
72
+ pr: 5
73
+ };
74
+ } else {
75
+ return {};
76
+ }
77
+ };
78
+
79
+ return /*#__PURE__*/React.createElement(Box, {
80
+ ref: ref,
81
+ sx: {
82
+ position: 'relative',
83
+ display: 'flex',
84
+ flex: 1
85
+ }
86
+ }, iconPlacement === 'start' && calendarButton('left'), /*#__PURE__*/React.createElement(TextInput, {
87
+ defaultValue: formattedDate,
88
+ onKeyPress: onKeyPressHandler,
89
+ onChange: onInputChangeHandler,
90
+ sx: inputSx()
91
+ }), iconPlacement === 'end' && calendarButton('right'));
45
92
  }
46
93
 
47
- return /*#__PURE__*/React.createElement(DatePickerAnchorButton, {
48
- ref: ref,
94
+ return /*#__PURE__*/React.createElement(Box, {
95
+ ref: ref
96
+ }, /*#__PURE__*/React.createElement(DatePickerAnchorButton, {
49
97
  onClick: clickHandler,
50
98
  onKeyPress: keyPressHandler
51
99
  }, /*#__PURE__*/React.createElement(StyledOcticon, {
@@ -59,5 +107,5 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
59
107
  overflow: 'hidden',
60
108
  textOverflow: 'ellipsis'
61
109
  }
62
- }, formattedDate));
110
+ }, formattedDate)));
63
111
  });
@@ -1,5 +1,5 @@
1
- import { addMonths } from 'date-fns';
2
- import React from 'react';
1
+ import { addMonths, subMonths } from 'date-fns';
2
+ import React, { useMemo } from 'react';
3
3
  import Box from '../Box';
4
4
  import { Month } from './Month';
5
5
  import styled from 'styled-components';
@@ -28,23 +28,58 @@ export const DatePickerPanel = () => {
28
28
  const {
29
29
  configuration,
30
30
  saveValue,
31
- revertValue
31
+ revertValue,
32
+ currentViewingDate,
33
+ goToMonth,
34
+ nextMonth,
35
+ previousMonth
32
36
  } = useDatePicker();
37
+ const previousDisabled = useMemo(() => {
38
+ const {
39
+ minDate
40
+ } = configuration;
41
+ if (!minDate) return false;
42
+ const previous = subMonths(currentViewingDate, 1);
43
+
44
+ if (minDate.getFullYear() >= previous.getFullYear() && minDate.getMonth() > previous.getMonth()) {
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ }, [configuration, currentViewingDate]);
50
+ const nextDisabled = useMemo(() => {
51
+ const {
52
+ maxDate,
53
+ view
54
+ } = configuration;
55
+ if (!maxDate) return false;
56
+ const next = addMonths(currentViewingDate, view === '2-month' ? 2 : 1);
57
+
58
+ if (maxDate.getFullYear() <= next.getFullYear() && maxDate.getMonth() < next.getMonth()) {
59
+ return true;
60
+ }
61
+
62
+ return false;
63
+ }, [configuration, currentViewingDate]);
33
64
  return /*#__PURE__*/React.createElement(DatePickerPanelContainer, null, /*#__PURE__*/React.createElement(DatePickerPanelMonths, null, /*#__PURE__*/React.createElement(ArrowButton, {
34
65
  variant: "small",
35
- side: "left"
66
+ side: "left",
67
+ onClick: previousMonth,
68
+ disabled: previousDisabled
36
69
  }, /*#__PURE__*/React.createElement(StyledOcticon, {
37
70
  icon: ChevronLeftIcon,
38
71
  color: "fg.muted"
39
72
  })), /*#__PURE__*/React.createElement(Month, {
40
- month: new Date().getMonth(),
41
- year: new Date().getFullYear()
73
+ month: currentViewingDate.getMonth(),
74
+ year: currentViewingDate.getFullYear()
42
75
  }), configuration.view === '2-month' && /*#__PURE__*/React.createElement(Month, {
43
- month: addMonths(new Date(), 1).getMonth(),
44
- year: addMonths(new Date(), 1).getFullYear()
76
+ month: addMonths(currentViewingDate, 1).getMonth(),
77
+ year: addMonths(currentViewingDate, 1).getFullYear()
45
78
  }), /*#__PURE__*/React.createElement(ArrowButton, {
46
79
  variant: "small",
47
- side: "right"
80
+ side: "right",
81
+ onClick: nextMonth,
82
+ disabled: nextDisabled
48
83
  }, /*#__PURE__*/React.createElement(StyledOcticon, {
49
84
  icon: ChevronRightIcon,
50
85
  color: "fg.muted"
@@ -55,7 +90,8 @@ export const DatePickerPanel = () => {
55
90
  },
56
91
  onClick: () => revertValue()
57
92
  }, "Reset"), /*#__PURE__*/React.createElement(Button, {
58
- variant: "small"
93
+ variant: "small",
94
+ onClick: () => goToMonth(new Date())
59
95
  }, "Today")), configuration.confirmation && /*#__PURE__*/React.createElement(ButtonPrimary, {
60
96
  variant: "small",
61
97
  onClick: () => saveValue()
@@ -101,7 +101,7 @@ const DayComponent = styled(DayBaseComponent).attrs(props => ({
101
101
  })).withConfig({
102
102
  displayName: "Day__DayComponent",
103
103
  componentId: "sc-1japneh-1"
104
- })(["background-color:", ";border-radius:", ";transition:0.2s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.2s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, Text, props => props.textColor, get('fonts.mono'), get('fontSizes.0'), props => props.backgroundHover, Text, props => props.textColorHover, props => props.backgroundPressed, get('shadows.shadow.medium'), Text, props => props.textColorPressed);
104
+ })(["background-color:", ";border-radius:", ";transition:0.1s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.1s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, Text, props => props.textColor, get('fonts.mono'), get('fontSizes.0'), props => props.backgroundHover, Text, props => props.textColorHover, props => props.backgroundPressed, get('shadows.shadow.medium'), Text, props => props.textColorPressed);
105
105
  export const Day = ({
106
106
  date,
107
107
  onAction
@@ -5,6 +5,7 @@ import Box from '../Box';
5
5
  import Text from '../Text';
6
6
  import { get } from '../constants';
7
7
  import { BlankDay, Day } from './Day';
8
+ import useDatePicker from './useDatePicker';
8
9
  const MonthComponent = styled(Box).withConfig({
9
10
  displayName: "Month__MonthComponent",
10
11
  componentId: "l6j7o0-0"
@@ -21,17 +22,23 @@ export const Month = ({
21
22
  month,
22
23
  year
23
24
  }) => {
25
+ const {
26
+ configuration
27
+ } = useDatePicker();
24
28
  const [selectedDay, setSelectedDay] = useState(null);
25
29
  const getTitle = useMemo(() => `${format(new Date(year, month), 'MMMM yyyy')}`, [month, year]);
26
30
  const weekdayHeaders = useMemo(() => {
27
31
  const now = new Date(year, month);
32
+ const weekOptions = {
33
+ weekStartsOn: configuration.weekStartsOn === 'Sunday' ? 0 : 1
34
+ };
28
35
  return eachDayOfInterval({
29
- start: startOfWeek(now),
30
- end: endOfWeek(now)
36
+ start: startOfWeek(now, weekOptions),
37
+ end: endOfWeek(now, weekOptions)
31
38
  }).map(d => /*#__PURE__*/React.createElement(WeekdayHeader, {
32
39
  key: `weekday-${d}-header`
33
40
  }, format(d, 'EEEEEE')));
34
- }, [month, year]);
41
+ }, [configuration.weekStartsOn, month, year]);
35
42
 
36
43
  const dayAction = date => {
37
44
  setSelectedDay(date);
@@ -40,8 +47,9 @@ export const Month = ({
40
47
  const dayComponents = useMemo(() => {
41
48
  const components = [];
42
49
  const firstDay = new Date(year, month, 1);
50
+ const preBlanks = configuration.weekStartsOn === 'Sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
43
51
 
44
- for (let i = 0; i < firstDay.getDay(); i++) {
52
+ for (let i = 0; i < preBlanks; i++) {
45
53
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
46
54
  key: `month-pre-blank-${i}`
47
55
  }));
@@ -58,15 +66,16 @@ export const Month = ({
58
66
  }
59
67
 
60
68
  const lastDay = lastDayOfMonth(firstDay);
69
+ const postBlanks = configuration.weekStartsOn === 'Sunday' ? lastDay.getDay() : (lastDay.getDay() + 6) % 7;
61
70
 
62
- for (let i = 6; i > lastDay.getDay(); i--) {
71
+ for (let i = 6; i > postBlanks; i--) {
63
72
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
64
73
  key: `month-post-blank-${i}`
65
74
  }));
66
75
  }
67
76
 
68
77
  return components;
69
- }, [month, selectedDay, year]);
78
+ }, [configuration.weekStartsOn, month, selectedDay, year]);
70
79
  return /*#__PURE__*/React.createElement(MonthComponent, {
71
80
  role: "grid"
72
81
  }, /*#__PURE__*/React.createElement(MonthTitle, null, getTitle), weekdayHeaders, dayComponents);
@@ -9,12 +9,14 @@ export interface DatePickerConfiguration {
9
9
  contiguousSelection?: boolean;
10
10
  dateFormat?: DateFormat;
11
11
  dimWeekends?: boolean;
12
+ iconPlacement?: 'start' | 'end' | 'none';
12
13
  minDate?: Date;
13
14
  maxDate?: Date;
14
15
  placeholder?: string;
15
16
  rangeIncrement?: number;
16
17
  selection?: SelectionVariant;
17
18
  view?: '1-month' | '2-month';
19
+ weekStartsOn?: 'Sunday' | 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday';
18
20
  }
19
21
  export declare type RangeSelection = {
20
22
  from: Date;
@@ -27,14 +29,18 @@ export declare type StringRangeSelection = {
27
29
  export interface DatePickerContext {
28
30
  disabled?: boolean;
29
31
  configuration: DatePickerConfiguration;
32
+ currentViewingDate: Date;
33
+ goToMonth: (date: Date) => void;
30
34
  hoverRange?: RangeSelection | null;
31
35
  selection?: Selection;
32
36
  softSelection?: Partial<RangeSelection> | null;
33
37
  selectionActive?: boolean;
34
38
  formattedDate: string;
39
+ nextMonth: () => void;
35
40
  onSelection: (date: Date) => void;
36
41
  onDayFocus: (date: Date) => void;
37
42
  onDayBlur: (date: Date) => void;
43
+ previousMonth: () => void;
38
44
  revertValue: () => void;
39
45
  saveValue: (selection?: Selection) => void;
40
46
  }
@@ -49,14 +55,18 @@ declare const useDatePicker: (date?: Date | undefined) => {
49
55
  disabled: boolean;
50
56
  selected: DaySelection;
51
57
  configuration: DatePickerConfiguration;
58
+ currentViewingDate: Date;
59
+ goToMonth: (date: Date) => void;
52
60
  hoverRange?: RangeSelection | null | undefined;
53
61
  selection?: Selection | undefined;
54
62
  softSelection?: Partial<RangeSelection> | null | undefined;
55
63
  selectionActive?: boolean | undefined;
56
64
  formattedDate: string;
65
+ nextMonth: () => void;
57
66
  onSelection: (date: Date) => void;
58
67
  onDayFocus: (date: Date) => void;
59
68
  onDayBlur: (date: Date) => void;
69
+ previousMonth: () => void;
60
70
  revertValue: () => void;
61
71
  saveValue: (selection?: Selection | undefined) => void;
62
72
  };
@@ -1,4 +1,4 @@
1
- import { format, isEqual, isAfter, isBefore } from 'date-fns';
1
+ import { format, isEqual, isAfter, isBefore, addMonths, subMonths } from 'date-fns';
2
2
  import deepmerge from 'deepmerge';
3
3
  import React, { createContext, useCallback, useContext, useMemo, useEffect, useState } from 'react';
4
4
  const DatePickerContext = /*#__PURE__*/createContext(null);
@@ -139,9 +139,11 @@ const defaultConfiguration = {
139
139
  confirmation: false,
140
140
  contiguousSelection: false,
141
141
  dimWeekends: false,
142
+ iconPlacement: 'start',
142
143
  placeholder: 'Select a Date...',
143
144
  selection: 'single',
144
- view: '2-month'
145
+ view: '2-month',
146
+ weekStartsOn: 'Sunday'
145
147
  };
146
148
  export const DatePickerProvider = ({
147
149
  configuration: externalConfig = {},
@@ -153,11 +155,21 @@ export const DatePickerProvider = ({
153
155
  const [previousSelection, setPreviousSelection] = useState(parseSelection(value, configuration.selection));
154
156
  const [selection, setSelection] = useState(parseSelection(value, configuration.selection));
155
157
  const [hoverRange, setHoverRange] = useState(null);
158
+ const [currentViewingDate, setCurrentViewingDate] = useState(new Date());
156
159
  useEffect(() => {
157
160
  setConfiguration(deepmerge(defaultConfiguration, externalConfig));
158
161
  setSelection(parseSelection(selection, configuration.selection)); // Don't want this to run every time selection gets updated
159
162
  // eslint-disable-next-line react-hooks/exhaustive-deps
160
163
  }, [configuration.selection, externalConfig]);
164
+ const goToMonth = useCallback(date => {
165
+ setCurrentViewingDate(new Date(new Date(date).toDateString()));
166
+ }, []);
167
+ const nextMonth = useCallback(() => {
168
+ setCurrentViewingDate(addMonths(currentViewingDate, 1));
169
+ }, [currentViewingDate]);
170
+ const previousMonth = useCallback(() => {
171
+ setCurrentViewingDate(subMonths(currentViewingDate, 1));
172
+ }, [currentViewingDate]);
161
173
  const getFormattedDate = useMemo(() => {
162
174
  if (!selection) {
163
175
  return configuration.placeholder;
@@ -165,7 +177,7 @@ export const DatePickerProvider = ({
165
177
 
166
178
  let template = 'MMM d';
167
179
 
168
- if (configuration.dateFormat) {
180
+ if (configuration.anchorVariant !== 'input' && configuration.dateFormat) {
169
181
  switch (configuration.dateFormat) {
170
182
  case 'short':
171
183
  template = 'MMM d';
@@ -179,6 +191,8 @@ export const DatePickerProvider = ({
179
191
  template = configuration.dateFormat;
180
192
  break;
181
193
  }
194
+ } else {
195
+ template = 'MM/dd/yyyy';
182
196
  }
183
197
 
184
198
  switch (configuration.selection) {
@@ -236,7 +250,7 @@ export const DatePickerProvider = ({
236
250
  return 'Invalid Configuration';
237
251
  }
238
252
  }
239
- }, [configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
253
+ }, [configuration.anchorVariant, configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
240
254
  const saveValue = useCallback(updatedSelection => {
241
255
  setPreviousSelection(updatedSelection !== null && updatedSelection !== void 0 ? updatedSelection : selection);
242
256
  closePicker === null || closePicker === void 0 ? void 0 : closePicker();
@@ -310,18 +324,22 @@ export const DatePickerProvider = ({
310
324
  const datePickerCtx = useMemo(() => {
311
325
  return {
312
326
  configuration,
327
+ currentViewingDate,
313
328
  disabled: false,
314
329
  formattedDate: getFormattedDate,
330
+ goToMonth,
315
331
  hoverRange,
332
+ nextMonth,
316
333
  onDayBlur: blurHnadler,
317
334
  onDayFocus: focusHnadler,
318
335
  onSelection: selectionHandler,
336
+ previousMonth,
319
337
  revertValue,
320
338
  saveValue,
321
339
  selectionActive: false,
322
340
  selection
323
341
  };
324
- }, [blurHnadler, configuration, focusHnadler, getFormattedDate, hoverRange, revertValue, saveValue, selection, selectionHandler]);
342
+ }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
325
343
  return /*#__PURE__*/React.createElement(DatePickerContext.Provider, {
326
344
  value: datePickerCtx
327
345
  }, children);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/components",
3
- "version": "0.0.0-202192422618",
3
+ "version": "0.0.0-202192522854",
4
4
  "description": "Primer react components",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",