@primer/components 0.0.0-20219254849 → 0.0.0-202192633419

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-20219254849
3
+ ## 0.0.0-202192633419
4
4
 
5
5
  ### Patch Changes
6
6
 
@@ -21,6 +21,7 @@ const DatePicker = ({
21
21
  anchorVariant,
22
22
  anchorRef: externalAnchorRef,
23
23
  confirmation,
24
+ dateFormat,
24
25
  focusTrapSettings,
25
26
  focusZoneSettings,
26
27
  maxDate,
@@ -32,17 +33,20 @@ const DatePicker = ({
32
33
  renderAnchor,
33
34
  selection,
34
35
  value,
35
- view
36
+ view,
37
+ weekStartsOn
36
38
  }) => {
37
39
  const buttonRef = (0, _react.useRef)(null);
38
40
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
39
41
  const datePickerConfiguration = {
40
42
  anchorVariant,
41
43
  confirmation,
44
+ dateFormat,
42
45
  maxDate,
43
46
  minDate,
44
47
  selection,
45
- view
48
+ view,
49
+ weekStartsOn
46
50
  };
47
51
 
48
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,10 @@ var _useDatePicker = _interopRequireDefault(require("./useDatePicker"));
23
23
 
24
24
  var _TextInput = _interopRequireDefault(require("../TextInput"));
25
25
 
26
+ var _Box = _interopRequireDefault(require("../Box"));
27
+
28
+ var _dateFns = require("date-fns");
29
+
26
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); }
27
31
 
28
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; }
@@ -33,17 +37,22 @@ const DatePickerAnchorButton = (0, _styledComponents.default)(_Button.default).w
33
37
  displayName: "DatePickerAnchor__DatePickerAnchorButton",
34
38
  componentId: "sc-8gpb9d-0"
35
39
  })(["align-items:center;display:flex;flex-direction:row;justify-content:space-between;max-width:350px;overflow:hidden;& ", "{margin-left:", ";}"], _Text.default, (0, _constants.get)('space.2'));
40
+ const INVALID_DATE = 'Invalid Date';
36
41
 
37
42
  const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
38
43
  onAction
39
44
  }, ref) => {
40
45
  const {
41
46
  configuration: {
42
- anchorVariant
47
+ anchorVariant,
48
+ iconPlacement,
49
+ selection
43
50
  },
44
51
  disabled,
45
- formattedDate
52
+ formattedDate,
53
+ onDateInput
46
54
  } = (0, _useDatePicker.default)();
55
+ const [inputValue, setInputValue] = (0, _react.useState)(formattedDate);
47
56
  const keyPressHandler = (0, _react.useCallback)(event => {
48
57
  if (disabled) {
49
58
  return;
@@ -60,15 +69,98 @@ const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
60
69
 
61
70
  onAction === null || onAction === void 0 ? void 0 : onAction(event);
62
71
  }, [disabled, onAction]);
72
+ const onInputChangeHandler = (0, _react.useCallback)(e => {
73
+ const value = e.currentTarget.value;
74
+ if (!value) return;
75
+
76
+ if (selection === 'range') {
77
+ var _values$, _values$2, _values$3, _values$4, _values$5, _values$6;
78
+
79
+ const values = value.split(' - ');
80
+ const dates = (0, _dateFns.isBefore)(new Date((_values$ = values[0]) === null || _values$ === void 0 ? void 0 : _values$.trim()), new Date((_values$2 = values[1]) === null || _values$2 === void 0 ? void 0 : _values$2.trim())) ? {
81
+ from: new Date((_values$3 = values[0]) === null || _values$3 === void 0 ? void 0 : _values$3.trim()),
82
+ to: new Date((_values$4 = values[1]) === null || _values$4 === void 0 ? void 0 : _values$4.trim())
83
+ } : {
84
+ from: new Date((_values$5 = values[1]) === null || _values$5 === void 0 ? void 0 : _values$5.trim()),
85
+ to: new Date((_values$6 = values[0]) === null || _values$6 === void 0 ? void 0 : _values$6.trim())
86
+ };
87
+ setInputValue(value);
88
+
89
+ if (dates.from.toString() !== INVALID_DATE && dates.to.toString() !== INVALID_DATE) {
90
+ onDateInput(dates);
91
+ }
92
+ } else if (selection === 'multi') {
93
+ const values = value.split(',');
94
+ const dates = [];
95
+
96
+ for (const date of values) {
97
+ dates.push(new Date(date.trim()));
98
+ }
99
+
100
+ setInputValue(value);
101
+
102
+ if (dates.every(d => d.toString() !== INVALID_DATE)) {
103
+ onDateInput(dates);
104
+ }
105
+ } else {
106
+ const date = new Date(value);
107
+ setInputValue(value);
108
+ if (date.toString() !== INVALID_DATE) onDateInput(date);
109
+ }
110
+ }, [onDateInput, selection]);
111
+
112
+ const onBlurHandler = () => {
113
+ setInputValue(formattedDate);
114
+ };
63
115
 
64
116
  if (anchorVariant === 'input') {
65
- return /*#__PURE__*/_react.default.createElement(_TextInput.default, {
66
- value: formattedDate
67
- });
117
+ const calendarButton = side => /*#__PURE__*/_react.default.createElement(_Button.ButtonInvisible, {
118
+ onClick: clickHandler,
119
+ sx: {
120
+ width: '32px',
121
+ px: '6px',
122
+ position: 'absolute',
123
+ [side]: '1px',
124
+ top: '1px'
125
+ }
126
+ }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
127
+ icon: _octiconsReact.CalendarIcon
128
+ }));
129
+
130
+ const inputSx = () => {
131
+ if (iconPlacement === 'start') {
132
+ return {
133
+ pl: 5,
134
+ pr: 2
135
+ };
136
+ } else if (iconPlacement === 'end') {
137
+ return {
138
+ pl: 2,
139
+ pr: 5
140
+ };
141
+ } else {
142
+ return {};
143
+ }
144
+ };
145
+
146
+ return /*#__PURE__*/_react.default.createElement(_Box.default, {
147
+ ref: ref,
148
+ sx: {
149
+ position: 'relative',
150
+ display: 'flex',
151
+ flex: 1
152
+ }
153
+ }, iconPlacement === 'start' && calendarButton('left'), /*#__PURE__*/_react.default.createElement(_TextInput.default, {
154
+ value: inputValue,
155
+ onChange: onInputChangeHandler,
156
+ sx: inputSx(),
157
+ onBlur: onBlurHandler
158
+ }), iconPlacement === 'end' && calendarButton('right'));
68
159
  }
69
160
 
70
- return /*#__PURE__*/_react.default.createElement(DatePickerAnchorButton, {
71
- ref: ref,
161
+ return /*#__PURE__*/_react.default.createElement(_Box.default, {
162
+ ref: ref
163
+ }, /*#__PURE__*/_react.default.createElement(DatePickerAnchorButton, {
72
164
  onClick: clickHandler,
73
165
  onKeyPress: keyPressHandler
74
166
  }, /*#__PURE__*/_react.default.createElement(_StyledOcticon.default, {
@@ -82,7 +174,7 @@ const DatePickerAnchor = /*#__PURE__*/_react.default.forwardRef(({
82
174
  overflow: 'hidden',
83
175
  textOverflow: 'ellipsis'
84
176
  }
85
- }, formattedDate));
177
+ }, formattedDate)));
86
178
  });
87
179
 
88
180
  exports.DatePickerAnchor = DatePickerAnchor;
@@ -19,12 +19,23 @@ 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); }
25
27
 
26
28
  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; }
27
29
 
30
+ const weekdayEnum = {
31
+ Sunday: 0,
32
+ Monday: 1,
33
+ Tuesday: 2,
34
+ Wednesday: 3,
35
+ Thursday: 4,
36
+ Friday: 5,
37
+ Saturday: 6
38
+ };
28
39
  const MonthComponent = (0, _styledComponents.default)(_Box.default).withConfig({
29
40
  displayName: "Month__MonthComponent",
30
41
  componentId: "l6j7o0-0"
@@ -42,17 +53,25 @@ const Month = ({
42
53
  month,
43
54
  year
44
55
  }) => {
56
+ const {
57
+ configuration
58
+ } = (0, _useDatePicker.default)();
45
59
  const [selectedDay, setSelectedDay] = (0, _react.useState)(null);
46
60
  const getTitle = (0, _react.useMemo)(() => `${(0, _dateFns.format)(new Date(year, month), 'MMMM yyyy')}`, [month, year]);
47
61
  const weekdayHeaders = (0, _react.useMemo)(() => {
62
+ var _configuration$weekSt;
63
+
48
64
  const now = new Date(year, month);
65
+ const weekOptions = {
66
+ weekStartsOn: weekdayEnum[(_configuration$weekSt = configuration.weekStartsOn) !== null && _configuration$weekSt !== void 0 ? _configuration$weekSt : 'Sunday']
67
+ };
49
68
  return (0, _dateFns.eachDayOfInterval)({
50
- start: (0, _dateFns.startOfWeek)(now),
51
- end: (0, _dateFns.endOfWeek)(now)
69
+ start: (0, _dateFns.startOfWeek)(now, weekOptions),
70
+ end: (0, _dateFns.endOfWeek)(now, weekOptions)
52
71
  }).map(d => /*#__PURE__*/_react.default.createElement(WeekdayHeader, {
53
72
  key: `weekday-${d}-header`
54
73
  }, (0, _dateFns.format)(d, 'EEEEEE')));
55
- }, [month, year]);
74
+ }, [configuration.weekStartsOn, month, year]);
56
75
 
57
76
  const dayAction = date => {
58
77
  setSelectedDay(date);
@@ -61,8 +80,9 @@ const Month = ({
61
80
  const dayComponents = (0, _react.useMemo)(() => {
62
81
  const components = [];
63
82
  const firstDay = new Date(year, month, 1);
83
+ const preBlanks = configuration.weekStartsOn === 'Sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
64
84
 
65
- for (let i = 0; i < firstDay.getDay(); i++) {
85
+ for (let i = 0; i < preBlanks; i++) {
66
86
  components.push( /*#__PURE__*/_react.default.createElement(_Day.BlankDay, {
67
87
  key: `month-pre-blank-${i}`
68
88
  }));
@@ -79,15 +99,16 @@ const Month = ({
79
99
  }
80
100
 
81
101
  const lastDay = (0, _dateFns.lastDayOfMonth)(firstDay);
102
+ const postBlanks = configuration.weekStartsOn === 'Sunday' ? lastDay.getDay() : (lastDay.getDay() + 6) % 7;
82
103
 
83
- for (let i = 6; i > lastDay.getDay(); i--) {
104
+ for (let i = 6; i > postBlanks; i--) {
84
105
  components.push( /*#__PURE__*/_react.default.createElement(_Day.BlankDay, {
85
106
  key: `month-post-blank-${i}`
86
107
  }));
87
108
  }
88
109
 
89
110
  return components;
90
- }, [month, selectedDay, year]);
111
+ }, [configuration.weekStartsOn, month, selectedDay, year]);
91
112
  return /*#__PURE__*/_react.default.createElement(MonthComponent, {
92
113
  role: "grid"
93
114
  }, /*#__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;
@@ -35,9 +37,10 @@ export interface DatePickerContext {
35
37
  selectionActive?: boolean;
36
38
  formattedDate: string;
37
39
  nextMonth: () => void;
38
- onSelection: (date: Date) => void;
40
+ onDateInput: (updatedSelection: Selection) => void;
39
41
  onDayFocus: (date: Date) => void;
40
42
  onDayBlur: (date: Date) => void;
43
+ onSelection: (date: Date) => void;
41
44
  previousMonth: () => void;
42
45
  revertValue: () => void;
43
46
  saveValue: (selection?: Selection) => void;
@@ -61,9 +64,10 @@ declare const useDatePicker: (date?: Date | undefined) => {
61
64
  selectionActive?: boolean | undefined;
62
65
  formattedDate: string;
63
66
  nextMonth: () => void;
64
- onSelection: (date: Date) => void;
67
+ onDateInput: (updatedSelection: Selection) => void;
65
68
  onDayFocus: (date: Date) => void;
66
69
  onDayBlur: (date: Date) => void;
70
+ onSelection: (date: Date) => void;
67
71
  previousMonth: () => void;
68
72
  revertValue: () => void;
69
73
  saveValue: (selection?: Selection | undefined) => void;
@@ -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 = ({
@@ -201,7 +203,7 @@ const DatePickerProvider = ({
201
203
 
202
204
  let template = 'MMM d';
203
205
 
204
- if (configuration.dateFormat) {
206
+ if (configuration.anchorVariant !== 'input' && configuration.dateFormat) {
205
207
  switch (configuration.dateFormat) {
206
208
  case 'short':
207
209
  template = 'MMM d';
@@ -215,6 +217,8 @@ const DatePickerProvider = ({
215
217
  template = configuration.dateFormat;
216
218
  break;
217
219
  }
220
+ } else {
221
+ template = 'MM/dd/yyyy';
218
222
  }
219
223
 
220
224
  switch (configuration.selection) {
@@ -272,11 +276,15 @@ const DatePickerProvider = ({
272
276
  return 'Invalid Configuration';
273
277
  }
274
278
  }
275
- }, [configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
279
+ }, [configuration.anchorVariant, configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
276
280
  const saveValue = (0, _react.useCallback)(updatedSelection => {
277
281
  setPreviousSelection(updatedSelection !== null && updatedSelection !== void 0 ? updatedSelection : selection);
278
282
  closePicker === null || closePicker === void 0 ? void 0 : closePicker();
279
283
  }, [closePicker, selection]);
284
+ const inputHandler = (0, _react.useCallback)(updatedSelection => {
285
+ // validate date falls within range
286
+ setSelection(updatedSelection);
287
+ }, []);
280
288
  const selectionHandler = (0, _react.useCallback)(date => {
281
289
  if (configuration.selection === 'multi') {
282
290
  const selections = [...selection];
@@ -352,6 +360,7 @@ const DatePickerProvider = ({
352
360
  goToMonth,
353
361
  hoverRange,
354
362
  nextMonth,
363
+ onDateInput: inputHandler,
355
364
  onDayBlur: blurHnadler,
356
365
  onDayFocus: focusHnadler,
357
366
  onSelection: selectionHandler,
@@ -361,7 +370,7 @@ const DatePickerProvider = ({
361
370
  selectionActive: false,
362
371
  selection
363
372
  };
364
- }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
373
+ }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, inputHandler, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
365
374
  return /*#__PURE__*/_react.default.createElement(DatePickerContext.Provider, {
366
375
  value: datePickerCtx
367
376
  }, children);
@@ -0,0 +1,2 @@
1
+ declare function useDebounce<T>(value: T, delay: number): T;
2
+ export default useDebounce;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = require("react");
9
+
10
+ function useDebounce(value, delay) {
11
+ const [debouncedValue, setDebouncedValue] = (0, _react.useState)(value);
12
+ (0, _react.useEffect)(() => {
13
+ const handler = setTimeout(() => {
14
+ setDebouncedValue(value);
15
+ }, delay);
16
+ return () => {
17
+ clearTimeout(handler);
18
+ };
19
+ }, [value, delay]);
20
+ return debouncedValue;
21
+ }
22
+
23
+ var _default = useDebounce;
24
+ exports.default = _default;
@@ -6,6 +6,7 @@ export const DatePicker = ({
6
6
  anchorVariant,
7
7
  anchorRef: externalAnchorRef,
8
8
  confirmation,
9
+ dateFormat,
9
10
  focusTrapSettings,
10
11
  focusZoneSettings,
11
12
  maxDate,
@@ -17,17 +18,20 @@ export const DatePicker = ({
17
18
  renderAnchor,
18
19
  selection,
19
20
  value,
20
- view
21
+ view,
22
+ weekStartsOn
21
23
  }) => {
22
24
  const buttonRef = useRef(null);
23
25
  const [isOpen, setIsOpen] = useState(false);
24
26
  const datePickerConfiguration = {
25
27
  anchorVariant,
26
28
  confirmation,
29
+ dateFormat,
27
30
  maxDate,
28
31
  minDate,
29
32
  selection,
30
- view
33
+ view,
34
+ weekStartsOn
31
35
  };
32
36
 
33
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,26 +1,33 @@
1
1
  import { CalendarIcon } from '@primer/octicons-react';
2
2
  import styled from 'styled-components';
3
- import React, { useCallback } from 'react';
4
- import Button from '../Button';
3
+ import React, { useCallback, useState } from 'react';
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';
11
+ import { isBefore } from 'date-fns';
10
12
  const DatePickerAnchorButton = styled(Button).withConfig({
11
13
  displayName: "DatePickerAnchor__DatePickerAnchorButton",
12
14
  componentId: "sc-8gpb9d-0"
13
15
  })(["align-items:center;display:flex;flex-direction:row;justify-content:space-between;max-width:350px;overflow:hidden;& ", "{margin-left:", ";}"], Text, get('space.2'));
16
+ const INVALID_DATE = 'Invalid Date';
14
17
  export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
15
18
  onAction
16
19
  }, ref) => {
17
20
  const {
18
21
  configuration: {
19
- anchorVariant
22
+ anchorVariant,
23
+ iconPlacement,
24
+ selection
20
25
  },
21
26
  disabled,
22
- formattedDate
27
+ formattedDate,
28
+ onDateInput
23
29
  } = useDatePicker();
30
+ const [inputValue, setInputValue] = useState(formattedDate);
24
31
  const keyPressHandler = useCallback(event => {
25
32
  if (disabled) {
26
33
  return;
@@ -37,15 +44,98 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
37
44
 
38
45
  onAction === null || onAction === void 0 ? void 0 : onAction(event);
39
46
  }, [disabled, onAction]);
47
+ const onInputChangeHandler = useCallback(e => {
48
+ const value = e.currentTarget.value;
49
+ if (!value) return;
50
+
51
+ if (selection === 'range') {
52
+ var _values$, _values$2, _values$3, _values$4, _values$5, _values$6;
53
+
54
+ const values = value.split(' - ');
55
+ const dates = isBefore(new Date((_values$ = values[0]) === null || _values$ === void 0 ? void 0 : _values$.trim()), new Date((_values$2 = values[1]) === null || _values$2 === void 0 ? void 0 : _values$2.trim())) ? {
56
+ from: new Date((_values$3 = values[0]) === null || _values$3 === void 0 ? void 0 : _values$3.trim()),
57
+ to: new Date((_values$4 = values[1]) === null || _values$4 === void 0 ? void 0 : _values$4.trim())
58
+ } : {
59
+ from: new Date((_values$5 = values[1]) === null || _values$5 === void 0 ? void 0 : _values$5.trim()),
60
+ to: new Date((_values$6 = values[0]) === null || _values$6 === void 0 ? void 0 : _values$6.trim())
61
+ };
62
+ setInputValue(value);
63
+
64
+ if (dates.from.toString() !== INVALID_DATE && dates.to.toString() !== INVALID_DATE) {
65
+ onDateInput(dates);
66
+ }
67
+ } else if (selection === 'multi') {
68
+ const values = value.split(',');
69
+ const dates = [];
70
+
71
+ for (const date of values) {
72
+ dates.push(new Date(date.trim()));
73
+ }
74
+
75
+ setInputValue(value);
76
+
77
+ if (dates.every(d => d.toString() !== INVALID_DATE)) {
78
+ onDateInput(dates);
79
+ }
80
+ } else {
81
+ const date = new Date(value);
82
+ setInputValue(value);
83
+ if (date.toString() !== INVALID_DATE) onDateInput(date);
84
+ }
85
+ }, [onDateInput, selection]);
86
+
87
+ const onBlurHandler = () => {
88
+ setInputValue(formattedDate);
89
+ };
40
90
 
41
91
  if (anchorVariant === 'input') {
42
- return /*#__PURE__*/React.createElement(TextInput, {
43
- value: formattedDate
44
- });
92
+ const calendarButton = side => /*#__PURE__*/React.createElement(ButtonInvisible, {
93
+ onClick: clickHandler,
94
+ sx: {
95
+ width: '32px',
96
+ px: '6px',
97
+ position: 'absolute',
98
+ [side]: '1px',
99
+ top: '1px'
100
+ }
101
+ }, /*#__PURE__*/React.createElement(StyledOcticon, {
102
+ icon: CalendarIcon
103
+ }));
104
+
105
+ const inputSx = () => {
106
+ if (iconPlacement === 'start') {
107
+ return {
108
+ pl: 5,
109
+ pr: 2
110
+ };
111
+ } else if (iconPlacement === 'end') {
112
+ return {
113
+ pl: 2,
114
+ pr: 5
115
+ };
116
+ } else {
117
+ return {};
118
+ }
119
+ };
120
+
121
+ return /*#__PURE__*/React.createElement(Box, {
122
+ ref: ref,
123
+ sx: {
124
+ position: 'relative',
125
+ display: 'flex',
126
+ flex: 1
127
+ }
128
+ }, iconPlacement === 'start' && calendarButton('left'), /*#__PURE__*/React.createElement(TextInput, {
129
+ value: inputValue,
130
+ onChange: onInputChangeHandler,
131
+ sx: inputSx(),
132
+ onBlur: onBlurHandler
133
+ }), iconPlacement === 'end' && calendarButton('right'));
45
134
  }
46
135
 
47
- return /*#__PURE__*/React.createElement(DatePickerAnchorButton, {
48
- ref: ref,
136
+ return /*#__PURE__*/React.createElement(Box, {
137
+ ref: ref
138
+ }, /*#__PURE__*/React.createElement(DatePickerAnchorButton, {
49
139
  onClick: clickHandler,
50
140
  onKeyPress: keyPressHandler
51
141
  }, /*#__PURE__*/React.createElement(StyledOcticon, {
@@ -59,5 +149,5 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
59
149
  overflow: 'hidden',
60
150
  textOverflow: 'ellipsis'
61
151
  }
62
- }, formattedDate));
152
+ }, formattedDate)));
63
153
  });
@@ -5,6 +5,16 @@ 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';
9
+ const weekdayEnum = {
10
+ Sunday: 0,
11
+ Monday: 1,
12
+ Tuesday: 2,
13
+ Wednesday: 3,
14
+ Thursday: 4,
15
+ Friday: 5,
16
+ Saturday: 6
17
+ };
8
18
  const MonthComponent = styled(Box).withConfig({
9
19
  displayName: "Month__MonthComponent",
10
20
  componentId: "l6j7o0-0"
@@ -21,17 +31,25 @@ export const Month = ({
21
31
  month,
22
32
  year
23
33
  }) => {
34
+ const {
35
+ configuration
36
+ } = useDatePicker();
24
37
  const [selectedDay, setSelectedDay] = useState(null);
25
38
  const getTitle = useMemo(() => `${format(new Date(year, month), 'MMMM yyyy')}`, [month, year]);
26
39
  const weekdayHeaders = useMemo(() => {
40
+ var _configuration$weekSt;
41
+
27
42
  const now = new Date(year, month);
43
+ const weekOptions = {
44
+ weekStartsOn: weekdayEnum[(_configuration$weekSt = configuration.weekStartsOn) !== null && _configuration$weekSt !== void 0 ? _configuration$weekSt : 'Sunday']
45
+ };
28
46
  return eachDayOfInterval({
29
- start: startOfWeek(now),
30
- end: endOfWeek(now)
47
+ start: startOfWeek(now, weekOptions),
48
+ end: endOfWeek(now, weekOptions)
31
49
  }).map(d => /*#__PURE__*/React.createElement(WeekdayHeader, {
32
50
  key: `weekday-${d}-header`
33
51
  }, format(d, 'EEEEEE')));
34
- }, [month, year]);
52
+ }, [configuration.weekStartsOn, month, year]);
35
53
 
36
54
  const dayAction = date => {
37
55
  setSelectedDay(date);
@@ -40,8 +58,9 @@ export const Month = ({
40
58
  const dayComponents = useMemo(() => {
41
59
  const components = [];
42
60
  const firstDay = new Date(year, month, 1);
61
+ const preBlanks = configuration.weekStartsOn === 'Sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
43
62
 
44
- for (let i = 0; i < firstDay.getDay(); i++) {
63
+ for (let i = 0; i < preBlanks; i++) {
45
64
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
46
65
  key: `month-pre-blank-${i}`
47
66
  }));
@@ -58,15 +77,16 @@ export const Month = ({
58
77
  }
59
78
 
60
79
  const lastDay = lastDayOfMonth(firstDay);
80
+ const postBlanks = configuration.weekStartsOn === 'Sunday' ? lastDay.getDay() : (lastDay.getDay() + 6) % 7;
61
81
 
62
- for (let i = 6; i > lastDay.getDay(); i--) {
82
+ for (let i = 6; i > postBlanks; i--) {
63
83
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
64
84
  key: `month-post-blank-${i}`
65
85
  }));
66
86
  }
67
87
 
68
88
  return components;
69
- }, [month, selectedDay, year]);
89
+ }, [configuration.weekStartsOn, month, selectedDay, year]);
70
90
  return /*#__PURE__*/React.createElement(MonthComponent, {
71
91
  role: "grid"
72
92
  }, /*#__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;
@@ -35,9 +37,10 @@ export interface DatePickerContext {
35
37
  selectionActive?: boolean;
36
38
  formattedDate: string;
37
39
  nextMonth: () => void;
38
- onSelection: (date: Date) => void;
40
+ onDateInput: (updatedSelection: Selection) => void;
39
41
  onDayFocus: (date: Date) => void;
40
42
  onDayBlur: (date: Date) => void;
43
+ onSelection: (date: Date) => void;
41
44
  previousMonth: () => void;
42
45
  revertValue: () => void;
43
46
  saveValue: (selection?: Selection) => void;
@@ -61,9 +64,10 @@ declare const useDatePicker: (date?: Date | undefined) => {
61
64
  selectionActive?: boolean | undefined;
62
65
  formattedDate: string;
63
66
  nextMonth: () => void;
64
- onSelection: (date: Date) => void;
67
+ onDateInput: (updatedSelection: Selection) => void;
65
68
  onDayFocus: (date: Date) => void;
66
69
  onDayBlur: (date: Date) => void;
70
+ onSelection: (date: Date) => void;
67
71
  previousMonth: () => void;
68
72
  revertValue: () => void;
69
73
  saveValue: (selection?: Selection | undefined) => void;
@@ -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 = {},
@@ -175,7 +177,7 @@ export const DatePickerProvider = ({
175
177
 
176
178
  let template = 'MMM d';
177
179
 
178
- if (configuration.dateFormat) {
180
+ if (configuration.anchorVariant !== 'input' && configuration.dateFormat) {
179
181
  switch (configuration.dateFormat) {
180
182
  case 'short':
181
183
  template = 'MMM d';
@@ -189,6 +191,8 @@ export const DatePickerProvider = ({
189
191
  template = configuration.dateFormat;
190
192
  break;
191
193
  }
194
+ } else {
195
+ template = 'MM/dd/yyyy';
192
196
  }
193
197
 
194
198
  switch (configuration.selection) {
@@ -246,11 +250,15 @@ export const DatePickerProvider = ({
246
250
  return 'Invalid Configuration';
247
251
  }
248
252
  }
249
- }, [configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
253
+ }, [configuration.anchorVariant, configuration.dateFormat, configuration.placeholder, configuration.selection, selection]);
250
254
  const saveValue = useCallback(updatedSelection => {
251
255
  setPreviousSelection(updatedSelection !== null && updatedSelection !== void 0 ? updatedSelection : selection);
252
256
  closePicker === null || closePicker === void 0 ? void 0 : closePicker();
253
257
  }, [closePicker, selection]);
258
+ const inputHandler = useCallback(updatedSelection => {
259
+ // validate date falls within range
260
+ setSelection(updatedSelection);
261
+ }, []);
254
262
  const selectionHandler = useCallback(date => {
255
263
  if (configuration.selection === 'multi') {
256
264
  const selections = [...selection];
@@ -326,6 +334,7 @@ export const DatePickerProvider = ({
326
334
  goToMonth,
327
335
  hoverRange,
328
336
  nextMonth,
337
+ onDateInput: inputHandler,
329
338
  onDayBlur: blurHnadler,
330
339
  onDayFocus: focusHnadler,
331
340
  onSelection: selectionHandler,
@@ -335,7 +344,7 @@ export const DatePickerProvider = ({
335
344
  selectionActive: false,
336
345
  selection
337
346
  };
338
- }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
347
+ }, [blurHnadler, configuration, currentViewingDate, focusHnadler, getFormattedDate, goToMonth, hoverRange, inputHandler, nextMonth, previousMonth, revertValue, saveValue, selection, selectionHandler]);
339
348
  return /*#__PURE__*/React.createElement(DatePickerContext.Provider, {
340
349
  value: datePickerCtx
341
350
  }, children);
@@ -0,0 +1,2 @@
1
+ declare function useDebounce<T>(value: T, delay: number): T;
2
+ export default useDebounce;
@@ -0,0 +1,16 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ function useDebounce(value, delay) {
4
+ const [debouncedValue, setDebouncedValue] = useState(value);
5
+ useEffect(() => {
6
+ const handler = setTimeout(() => {
7
+ setDebouncedValue(value);
8
+ }, delay);
9
+ return () => {
10
+ clearTimeout(handler);
11
+ };
12
+ }, [value, delay]);
13
+ return debouncedValue;
14
+ }
15
+
16
+ export default useDebounce;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/components",
3
- "version": "0.0.0-20219254849",
3
+ "version": "0.0.0-202192633419",
4
4
  "description": "Primer react components",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",