@instructure/ui-calendar 9.0.2-snapshot-20 → 9.0.2-snapshot-25
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 +5 -2
- package/es/Calendar/index.js +56 -3
- package/es/Calendar/props.js +2 -1
- package/es/Calendar/styles.js +9 -2
- package/es/Calendar/theme.js +2 -0
- package/lib/Calendar/index.js +56 -3
- package/lib/Calendar/props.js +2 -1
- package/lib/Calendar/styles.js +9 -2
- package/lib/Calendar/theme.js +2 -0
- package/package.json +20 -19
- package/src/Calendar/README.md +37 -0
- package/src/Calendar/index.tsx +96 -12
- package/src/Calendar/props.ts +21 -2
- package/src/Calendar/styles.ts +15 -3
- package/src/Calendar/theme.ts +2 -0
- package/tsconfig.build.json +3 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Calendar/index.d.ts +15 -0
- package/types/Calendar/index.d.ts.map +1 -1
- package/types/Calendar/props.d.ts +18 -1
- package/types/Calendar/props.d.ts.map +1 -1
- package/types/Calendar/styles.d.ts +2 -2
- package/types/Calendar/styles.d.ts.map +1 -1
- package/types/Calendar/theme.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [9.0.2-snapshot-
|
|
6
|
+
## [9.0.2-snapshot-25](https://github.com/instructure/instructure-ui/compare/v9.0.1...v9.0.2-snapshot-25) (2024-06-14)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **shared-types,ui-calendar,ui-select:** add yearpicker functionality to calendar ([2c0c6e0](https://github.com/instructure/instructure-ui/commit/2c0c6e081d96fc821c1296df5c4f1fba9a8c162c))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
package/es/Calendar/index.js
CHANGED
|
@@ -40,6 +40,7 @@ import { Day } from './Day';
|
|
|
40
40
|
import { propTypes, allowedProps } from './props';
|
|
41
41
|
import { IconButton } from '@instructure/ui-buttons';
|
|
42
42
|
import { IconArrowOpenStartSolid, IconArrowOpenEndSolid } from '@instructure/ui-icons';
|
|
43
|
+
import { SimpleSelect } from '@instructure/ui-simple-select';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
---
|
|
@@ -98,6 +99,9 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
|
|
|
98
99
|
const visibleMonth = this.state.visibleMonth;
|
|
99
100
|
const newDate = visibleMonth.clone();
|
|
100
101
|
if (direction === 'prev') {
|
|
102
|
+
if (!this.hasPrevMonth) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
101
105
|
if (onRequestRenderPrevMonth) {
|
|
102
106
|
onRequestRenderPrevMonth(e, newDate.subtract({
|
|
103
107
|
months: 1
|
|
@@ -108,6 +112,9 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
|
|
|
108
112
|
months: 1
|
|
109
113
|
});
|
|
110
114
|
} else {
|
|
115
|
+
if (!this.hasNextMonth) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
111
118
|
if (onRequestRenderNextMonth) {
|
|
112
119
|
onRequestRenderNextMonth(e, newDate.add({
|
|
113
120
|
months: 1
|
|
@@ -122,6 +129,19 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
|
|
|
122
129
|
visibleMonth: newDate
|
|
123
130
|
});
|
|
124
131
|
};
|
|
132
|
+
this.handleYearChange = (e, year) => {
|
|
133
|
+
const withYearPicker = this.props.withYearPicker;
|
|
134
|
+
const visibleMonth = this.state.visibleMonth;
|
|
135
|
+
const newDate = visibleMonth.clone();
|
|
136
|
+
if (withYearPicker !== null && withYearPicker !== void 0 && withYearPicker.onRequestYearChange) {
|
|
137
|
+
withYearPicker.onRequestYearChange(e, year);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
newDate.year(year);
|
|
141
|
+
this.setState({
|
|
142
|
+
visibleMonth: newDate
|
|
143
|
+
});
|
|
144
|
+
};
|
|
125
145
|
// date is returned es a ISO string, like 2021-09-14T22:00:00.000Z
|
|
126
146
|
this.handleDayClick = (event, {
|
|
127
147
|
date
|
|
@@ -152,10 +172,21 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
|
|
|
152
172
|
get role() {
|
|
153
173
|
return this.props.role === 'listbox' ? this.props.role : void 0;
|
|
154
174
|
}
|
|
175
|
+
get hasPrevMonth() {
|
|
176
|
+
return !this.props.withYearPicker || this.props.withYearPicker && Number(this.state.visibleMonth.clone().subtract({
|
|
177
|
+
months: 1
|
|
178
|
+
}).format('YYYY')) >= this.props.withYearPicker.startYear;
|
|
179
|
+
}
|
|
180
|
+
get hasNextMonth() {
|
|
181
|
+
return !this.props.withYearPicker || this.props.withYearPicker && Number(this.state.visibleMonth.clone().add({
|
|
182
|
+
months: 1
|
|
183
|
+
}).format('YYYY')) <= this.props.withYearPicker.endYear;
|
|
184
|
+
}
|
|
155
185
|
renderHeader() {
|
|
156
186
|
const _this$props5 = this.props,
|
|
157
187
|
renderNavigationLabel = _this$props5.renderNavigationLabel,
|
|
158
|
-
styles = _this$props5.styles
|
|
188
|
+
styles = _this$props5.styles,
|
|
189
|
+
withYearPicker = _this$props5.withYearPicker;
|
|
159
190
|
const visibleMonth = this.state.visibleMonth;
|
|
160
191
|
const _this$renderMonthNavi = this.renderMonthNavigationButtons(),
|
|
161
192
|
prevButton = _this$renderMonthNavi.prevButton,
|
|
@@ -164,9 +195,31 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
|
|
|
164
195
|
onClick: createChainedFunction(button.props.onClick, onClick)
|
|
165
196
|
});
|
|
166
197
|
const style = [styles === null || styles === void 0 ? void 0 : styles.navigation, ...(prevButton || nextButton ? [styles === null || styles === void 0 ? void 0 : styles.navigationWithButtons] : [])];
|
|
167
|
-
|
|
198
|
+
const yearList = [];
|
|
199
|
+
if (withYearPicker) {
|
|
200
|
+
const startYear = withYearPicker.startYear,
|
|
201
|
+
endYear = withYearPicker.endYear;
|
|
202
|
+
for (let year = endYear; year >= startYear; year--) {
|
|
203
|
+
yearList.push(year);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return jsx("div", null, jsx("div", {
|
|
168
207
|
css: style
|
|
169
|
-
}, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? callRenderProp(renderNavigationLabel) : jsx("span", null, jsx("div", null, visibleMonth.format('MMMM')), jsx("div", null, visibleMonth.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')))
|
|
208
|
+
}, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? callRenderProp(renderNavigationLabel) : jsx("span", null, jsx("div", null, visibleMonth.format('MMMM')), !withYearPicker ? jsx("div", null, visibleMonth.format('YYYY')) : null), nextButton && cloneButton(nextButton, this.handleMonthChange('next'))), withYearPicker ? jsx("div", {
|
|
209
|
+
css: styles === null || styles === void 0 ? void 0 : styles.yearPicker
|
|
210
|
+
}, jsx(SimpleSelect, {
|
|
211
|
+
width: "90px",
|
|
212
|
+
renderLabel: "",
|
|
213
|
+
assistiveText: withYearPicker.screenReaderLabel,
|
|
214
|
+
value: Number(visibleMonth.format('YYYY')),
|
|
215
|
+
onChange: (e, {
|
|
216
|
+
value
|
|
217
|
+
}) => this.handleYearChange(e, Number(value))
|
|
218
|
+
}, yearList.map(year => jsx(SimpleSelect.Option, {
|
|
219
|
+
key: year,
|
|
220
|
+
id: `opt-${year}`,
|
|
221
|
+
value: year
|
|
222
|
+
}, `${year}`)))) : null);
|
|
170
223
|
}
|
|
171
224
|
renderBody() {
|
|
172
225
|
return jsx("table", {
|
package/es/Calendar/props.js
CHANGED
|
@@ -41,7 +41,8 @@ const propTypes = {
|
|
|
41
41
|
role: PropTypes.oneOf(['table', 'listbox']),
|
|
42
42
|
selectedDate: PropTypes.string,
|
|
43
43
|
timezone: PropTypes.string,
|
|
44
|
-
visibleMonth: PropTypes.string
|
|
44
|
+
visibleMonth: PropTypes.string,
|
|
45
|
+
withYearPicker: PropTypes.object
|
|
45
46
|
};
|
|
46
47
|
const allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone', 'visibleMonth'];
|
|
47
48
|
export { propTypes, allowedProps };
|
package/es/Calendar/styles.js
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
* @param {Object} componentTheme The theme variable object.
|
|
31
31
|
* @return {Object} The final style object, which will be used in the component
|
|
32
32
|
*/
|
|
33
|
-
const generateStyle = componentTheme => {
|
|
33
|
+
const generateStyle = (componentTheme, props) => {
|
|
34
|
+
const withYearPicker = props.withYearPicker;
|
|
34
35
|
return {
|
|
35
36
|
navigation: {
|
|
36
37
|
label: 'calendar__navigation',
|
|
@@ -40,7 +41,7 @@ const generateStyle = componentTheme => {
|
|
|
40
41
|
fontFamily: componentTheme.fontFamily,
|
|
41
42
|
fontSize: componentTheme.fontSize,
|
|
42
43
|
fontWeight: componentTheme.fontWeight,
|
|
43
|
-
marginBottom: componentTheme.navMargin
|
|
44
|
+
marginBottom: withYearPicker ? componentTheme.navWithYearMargin : componentTheme.navMargin
|
|
44
45
|
},
|
|
45
46
|
navigationWithButtons: {
|
|
46
47
|
label: 'calendar__navigation--withButtons',
|
|
@@ -52,6 +53,12 @@ const generateStyle = componentTheme => {
|
|
|
52
53
|
label: 'calendar__weekdayHeader',
|
|
53
54
|
textAlign: 'center',
|
|
54
55
|
maxWidth: componentTheme.maxHeaderWidth
|
|
56
|
+
},
|
|
57
|
+
yearPicker: {
|
|
58
|
+
display: 'flex',
|
|
59
|
+
justifyContent: 'center',
|
|
60
|
+
alignItems: 'center',
|
|
61
|
+
marginBottom: componentTheme.yearPickerMargin
|
|
55
62
|
}
|
|
56
63
|
};
|
|
57
64
|
};
|
package/es/Calendar/theme.js
CHANGED
|
@@ -44,6 +44,8 @@ const generateComponentTheme = theme => {
|
|
|
44
44
|
color: colors === null || colors === void 0 ? void 0 : colors.textDarkest,
|
|
45
45
|
background: colors === null || colors === void 0 ? void 0 : colors.backgroundLightest,
|
|
46
46
|
navMargin: spacing === null || spacing === void 0 ? void 0 : spacing.small,
|
|
47
|
+
navWithYearMargin: spacing === null || spacing === void 0 ? void 0 : spacing.xSmall,
|
|
48
|
+
yearPickerMargin: spacing === null || spacing === void 0 ? void 0 : spacing.mediumSmall,
|
|
47
49
|
maxHeaderWidth: spacing === null || spacing === void 0 ? void 0 : spacing.medium
|
|
48
50
|
};
|
|
49
51
|
return {
|
package/lib/Calendar/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var _props = require("./props");
|
|
|
27
27
|
var _IconButton3 = require("@instructure/ui-buttons/lib/IconButton");
|
|
28
28
|
var _IconArrowOpenStartSolid = require("@instructure/ui-icons/lib/IconArrowOpenStartSolid.js");
|
|
29
29
|
var _IconArrowOpenEndSolid = require("@instructure/ui-icons/lib/IconArrowOpenEndSolid.js");
|
|
30
|
+
var _SimpleSelect = require("@instructure/ui-simple-select/lib/SimpleSelect");
|
|
30
31
|
var _dec, _dec2, _class, _Calendar, _IconButton, _IconButton2;
|
|
31
32
|
/*
|
|
32
33
|
* The MIT License (MIT)
|
|
@@ -109,6 +110,9 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
|
|
|
109
110
|
const visibleMonth = this.state.visibleMonth;
|
|
110
111
|
const newDate = visibleMonth.clone();
|
|
111
112
|
if (direction === 'prev') {
|
|
113
|
+
if (!this.hasPrevMonth) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
112
116
|
if (onRequestRenderPrevMonth) {
|
|
113
117
|
onRequestRenderPrevMonth(e, newDate.subtract({
|
|
114
118
|
months: 1
|
|
@@ -119,6 +123,9 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
|
|
|
119
123
|
months: 1
|
|
120
124
|
});
|
|
121
125
|
} else {
|
|
126
|
+
if (!this.hasNextMonth) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
122
129
|
if (onRequestRenderNextMonth) {
|
|
123
130
|
onRequestRenderNextMonth(e, newDate.add({
|
|
124
131
|
months: 1
|
|
@@ -133,6 +140,19 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
|
|
|
133
140
|
visibleMonth: newDate
|
|
134
141
|
});
|
|
135
142
|
};
|
|
143
|
+
this.handleYearChange = (e, year) => {
|
|
144
|
+
const withYearPicker = this.props.withYearPicker;
|
|
145
|
+
const visibleMonth = this.state.visibleMonth;
|
|
146
|
+
const newDate = visibleMonth.clone();
|
|
147
|
+
if (withYearPicker !== null && withYearPicker !== void 0 && withYearPicker.onRequestYearChange) {
|
|
148
|
+
withYearPicker.onRequestYearChange(e, year);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
newDate.year(year);
|
|
152
|
+
this.setState({
|
|
153
|
+
visibleMonth: newDate
|
|
154
|
+
});
|
|
155
|
+
};
|
|
136
156
|
// date is returned es a ISO string, like 2021-09-14T22:00:00.000Z
|
|
137
157
|
this.handleDayClick = (event, {
|
|
138
158
|
date
|
|
@@ -163,10 +183,21 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
|
|
|
163
183
|
get role() {
|
|
164
184
|
return this.props.role === 'listbox' ? this.props.role : void 0;
|
|
165
185
|
}
|
|
186
|
+
get hasPrevMonth() {
|
|
187
|
+
return !this.props.withYearPicker || this.props.withYearPicker && Number(this.state.visibleMonth.clone().subtract({
|
|
188
|
+
months: 1
|
|
189
|
+
}).format('YYYY')) >= this.props.withYearPicker.startYear;
|
|
190
|
+
}
|
|
191
|
+
get hasNextMonth() {
|
|
192
|
+
return !this.props.withYearPicker || this.props.withYearPicker && Number(this.state.visibleMonth.clone().add({
|
|
193
|
+
months: 1
|
|
194
|
+
}).format('YYYY')) <= this.props.withYearPicker.endYear;
|
|
195
|
+
}
|
|
166
196
|
renderHeader() {
|
|
167
197
|
const _this$props5 = this.props,
|
|
168
198
|
renderNavigationLabel = _this$props5.renderNavigationLabel,
|
|
169
|
-
styles = _this$props5.styles
|
|
199
|
+
styles = _this$props5.styles,
|
|
200
|
+
withYearPicker = _this$props5.withYearPicker;
|
|
170
201
|
const visibleMonth = this.state.visibleMonth;
|
|
171
202
|
const _this$renderMonthNavi = this.renderMonthNavigationButtons(),
|
|
172
203
|
prevButton = _this$renderMonthNavi.prevButton,
|
|
@@ -175,9 +206,31 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
|
|
|
175
206
|
onClick: (0, _createChainedFunction.createChainedFunction)(button.props.onClick, onClick)
|
|
176
207
|
});
|
|
177
208
|
const style = [styles === null || styles === void 0 ? void 0 : styles.navigation, ...(prevButton || nextButton ? [styles === null || styles === void 0 ? void 0 : styles.navigationWithButtons] : [])];
|
|
178
|
-
|
|
209
|
+
const yearList = [];
|
|
210
|
+
if (withYearPicker) {
|
|
211
|
+
const startYear = withYearPicker.startYear,
|
|
212
|
+
endYear = withYearPicker.endYear;
|
|
213
|
+
for (let year = endYear; year >= startYear; year--) {
|
|
214
|
+
yearList.push(year);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return (0, _emotion.jsx)("div", null, (0, _emotion.jsx)("div", {
|
|
179
218
|
css: style
|
|
180
|
-
}, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? (0, _callRenderProp.callRenderProp)(renderNavigationLabel) : (0, _emotion.jsx)("span", null, (0, _emotion.jsx)("div", null, visibleMonth.format('MMMM')), (0, _emotion.jsx)("div", null, visibleMonth.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')))
|
|
219
|
+
}, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? (0, _callRenderProp.callRenderProp)(renderNavigationLabel) : (0, _emotion.jsx)("span", null, (0, _emotion.jsx)("div", null, visibleMonth.format('MMMM')), !withYearPicker ? (0, _emotion.jsx)("div", null, visibleMonth.format('YYYY')) : null), nextButton && cloneButton(nextButton, this.handleMonthChange('next'))), withYearPicker ? (0, _emotion.jsx)("div", {
|
|
220
|
+
css: styles === null || styles === void 0 ? void 0 : styles.yearPicker
|
|
221
|
+
}, (0, _emotion.jsx)(_SimpleSelect.SimpleSelect, {
|
|
222
|
+
width: "90px",
|
|
223
|
+
renderLabel: "",
|
|
224
|
+
assistiveText: withYearPicker.screenReaderLabel,
|
|
225
|
+
value: Number(visibleMonth.format('YYYY')),
|
|
226
|
+
onChange: (e, {
|
|
227
|
+
value
|
|
228
|
+
}) => this.handleYearChange(e, Number(value))
|
|
229
|
+
}, yearList.map(year => (0, _emotion.jsx)(_SimpleSelect.SimpleSelect.Option, {
|
|
230
|
+
key: year,
|
|
231
|
+
id: `opt-${year}`,
|
|
232
|
+
value: year
|
|
233
|
+
}, `${year}`)))) : null);
|
|
181
234
|
}
|
|
182
235
|
renderBody() {
|
|
183
236
|
return (0, _emotion.jsx)("table", {
|
package/lib/Calendar/props.js
CHANGED
|
@@ -48,6 +48,7 @@ const propTypes = exports.propTypes = {
|
|
|
48
48
|
role: _propTypes.default.oneOf(['table', 'listbox']),
|
|
49
49
|
selectedDate: _propTypes.default.string,
|
|
50
50
|
timezone: _propTypes.default.string,
|
|
51
|
-
visibleMonth: _propTypes.default.string
|
|
51
|
+
visibleMonth: _propTypes.default.string,
|
|
52
|
+
withYearPicker: _propTypes.default.object
|
|
52
53
|
};
|
|
53
54
|
const allowedProps = exports.allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone', 'visibleMonth'];
|
package/lib/Calendar/styles.js
CHANGED
|
@@ -36,7 +36,8 @@ exports.default = void 0;
|
|
|
36
36
|
* @param {Object} componentTheme The theme variable object.
|
|
37
37
|
* @return {Object} The final style object, which will be used in the component
|
|
38
38
|
*/
|
|
39
|
-
const generateStyle = componentTheme => {
|
|
39
|
+
const generateStyle = (componentTheme, props) => {
|
|
40
|
+
const withYearPicker = props.withYearPicker;
|
|
40
41
|
return {
|
|
41
42
|
navigation: {
|
|
42
43
|
label: 'calendar__navigation',
|
|
@@ -46,7 +47,7 @@ const generateStyle = componentTheme => {
|
|
|
46
47
|
fontFamily: componentTheme.fontFamily,
|
|
47
48
|
fontSize: componentTheme.fontSize,
|
|
48
49
|
fontWeight: componentTheme.fontWeight,
|
|
49
|
-
marginBottom: componentTheme.navMargin
|
|
50
|
+
marginBottom: withYearPicker ? componentTheme.navWithYearMargin : componentTheme.navMargin
|
|
50
51
|
},
|
|
51
52
|
navigationWithButtons: {
|
|
52
53
|
label: 'calendar__navigation--withButtons',
|
|
@@ -58,6 +59,12 @@ const generateStyle = componentTheme => {
|
|
|
58
59
|
label: 'calendar__weekdayHeader',
|
|
59
60
|
textAlign: 'center',
|
|
60
61
|
maxWidth: componentTheme.maxHeaderWidth
|
|
62
|
+
},
|
|
63
|
+
yearPicker: {
|
|
64
|
+
display: 'flex',
|
|
65
|
+
justifyContent: 'center',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
marginBottom: componentTheme.yearPickerMargin
|
|
61
68
|
}
|
|
62
69
|
};
|
|
63
70
|
};
|
package/lib/Calendar/theme.js
CHANGED
|
@@ -50,6 +50,8 @@ const generateComponentTheme = theme => {
|
|
|
50
50
|
color: colors === null || colors === void 0 ? void 0 : colors.textDarkest,
|
|
51
51
|
background: colors === null || colors === void 0 ? void 0 : colors.backgroundLightest,
|
|
52
52
|
navMargin: spacing === null || spacing === void 0 ? void 0 : spacing.small,
|
|
53
|
+
navWithYearMargin: spacing === null || spacing === void 0 ? void 0 : spacing.xSmall,
|
|
54
|
+
yearPickerMargin: spacing === null || spacing === void 0 ? void 0 : spacing.mediumSmall,
|
|
53
55
|
maxHeaderWidth: spacing === null || spacing === void 0 ? void 0 : spacing.medium
|
|
54
56
|
};
|
|
55
57
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-calendar",
|
|
3
|
-
"version": "9.0.2-snapshot-
|
|
3
|
+
"version": "9.0.2-snapshot-25",
|
|
4
4
|
"description": "A calendar component.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -23,29 +23,30 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@instructure/ui-babel-preset": "9.0.2-snapshot-
|
|
27
|
-
"@instructure/ui-color-utils": "9.0.2-snapshot-
|
|
28
|
-
"@instructure/ui-test-locator": "9.0.2-snapshot-
|
|
29
|
-
"@instructure/ui-test-utils": "9.0.2-snapshot-
|
|
30
|
-
"@instructure/ui-themes": "9.0.2-snapshot-
|
|
26
|
+
"@instructure/ui-babel-preset": "9.0.2-snapshot-25",
|
|
27
|
+
"@instructure/ui-color-utils": "9.0.2-snapshot-25",
|
|
28
|
+
"@instructure/ui-test-locator": "9.0.2-snapshot-25",
|
|
29
|
+
"@instructure/ui-test-utils": "9.0.2-snapshot-25",
|
|
30
|
+
"@instructure/ui-themes": "9.0.2-snapshot-25",
|
|
31
31
|
"@testing-library/jest-dom": "^6.4.5",
|
|
32
32
|
"@testing-library/react": "^15.0.7"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@babel/runtime": "^7.24.5",
|
|
36
|
-
"@instructure/console": "9.0.2-snapshot-
|
|
37
|
-
"@instructure/emotion": "9.0.2-snapshot-
|
|
38
|
-
"@instructure/shared-types": "9.0.2-snapshot-
|
|
39
|
-
"@instructure/ui-a11y-content": "9.0.2-snapshot-
|
|
40
|
-
"@instructure/ui-buttons": "9.0.2-snapshot-
|
|
41
|
-
"@instructure/ui-i18n": "9.0.2-snapshot-
|
|
42
|
-
"@instructure/ui-icons": "9.0.2-snapshot-
|
|
43
|
-
"@instructure/ui-prop-types": "9.0.2-snapshot-
|
|
44
|
-
"@instructure/ui-react-utils": "9.0.2-snapshot-
|
|
45
|
-
"@instructure/ui-
|
|
46
|
-
"@instructure/ui-
|
|
47
|
-
"@instructure/ui-
|
|
48
|
-
"@instructure/
|
|
36
|
+
"@instructure/console": "9.0.2-snapshot-25",
|
|
37
|
+
"@instructure/emotion": "9.0.2-snapshot-25",
|
|
38
|
+
"@instructure/shared-types": "9.0.2-snapshot-25",
|
|
39
|
+
"@instructure/ui-a11y-content": "9.0.2-snapshot-25",
|
|
40
|
+
"@instructure/ui-buttons": "9.0.2-snapshot-25",
|
|
41
|
+
"@instructure/ui-i18n": "9.0.2-snapshot-25",
|
|
42
|
+
"@instructure/ui-icons": "9.0.2-snapshot-25",
|
|
43
|
+
"@instructure/ui-prop-types": "9.0.2-snapshot-25",
|
|
44
|
+
"@instructure/ui-react-utils": "9.0.2-snapshot-25",
|
|
45
|
+
"@instructure/ui-simple-select": "9.0.2-snapshot-25",
|
|
46
|
+
"@instructure/ui-testable": "9.0.2-snapshot-25",
|
|
47
|
+
"@instructure/ui-utils": "9.0.2-snapshot-25",
|
|
48
|
+
"@instructure/ui-view": "9.0.2-snapshot-25",
|
|
49
|
+
"@instructure/uid": "9.0.2-snapshot-25",
|
|
49
50
|
"prop-types": "^15.8.1"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
package/src/Calendar/README.md
CHANGED
|
@@ -48,6 +48,43 @@ class Example extends React.Component {
|
|
|
48
48
|
render(<Example />)
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### With year picker
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
---
|
|
55
|
+
type: example
|
|
56
|
+
---
|
|
57
|
+
class Example extends React.Component {
|
|
58
|
+
state = {
|
|
59
|
+
selectedDate: "",
|
|
60
|
+
visibleMonth:"2024-02"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setVisibleMonth = () => {
|
|
64
|
+
this.setState({visibleMonth: "2028-09"})
|
|
65
|
+
}
|
|
66
|
+
render = () =>
|
|
67
|
+
<Calendar
|
|
68
|
+
visibleMonth={this.state.visibleMonth}
|
|
69
|
+
currentDate="2024-02-29"
|
|
70
|
+
disabledDates={['2023-12-22', '2023-12-12', '2023-12-11']}
|
|
71
|
+
selectedDate={this.state.selectedDate}
|
|
72
|
+
onRequestRenderNextMonth={(_e, requestedMonth) => this.setState({visibleMonth: requestedMonth})}
|
|
73
|
+
onRequestRenderPrevMonth={(_e, requestedMonth) => this.setState({visibleMonth: requestedMonth})}
|
|
74
|
+
onDateSelected={(date)=>{
|
|
75
|
+
this.setState({selectedDate: date})
|
|
76
|
+
}}
|
|
77
|
+
withYearPicker={{
|
|
78
|
+
screenReaderLabel: "Year picker",
|
|
79
|
+
startYear:1999,
|
|
80
|
+
endYear:2024,
|
|
81
|
+
maxHeight: "200px"
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
}
|
|
85
|
+
render(<Example />)
|
|
86
|
+
```
|
|
87
|
+
|
|
51
88
|
### Composing a Calendar in your Application
|
|
52
89
|
|
|
53
90
|
By design, the `Calendar` component does not dictate which date libraries or
|
package/src/Calendar/index.tsx
CHANGED
|
@@ -58,6 +58,8 @@ import {
|
|
|
58
58
|
IconArrowOpenEndSolid
|
|
59
59
|
} from '@instructure/ui-icons'
|
|
60
60
|
|
|
61
|
+
import { SimpleSelect } from '@instructure/ui-simple-select'
|
|
62
|
+
|
|
61
63
|
/**
|
|
62
64
|
---
|
|
63
65
|
category: components
|
|
@@ -142,6 +144,26 @@ class Calendar extends Component<CalendarProps, CalendarState> {
|
|
|
142
144
|
return this.props.role === 'listbox' ? this.props.role : undefined
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
get hasPrevMonth() {
|
|
148
|
+
return (
|
|
149
|
+
!this.props.withYearPicker ||
|
|
150
|
+
(this.props.withYearPicker &&
|
|
151
|
+
Number(
|
|
152
|
+
this.state.visibleMonth.clone().subtract({ months: 1 }).format('YYYY')
|
|
153
|
+
) >= this.props.withYearPicker.startYear)
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get hasNextMonth() {
|
|
158
|
+
return (
|
|
159
|
+
!this.props.withYearPicker ||
|
|
160
|
+
(this.props.withYearPicker &&
|
|
161
|
+
Number(
|
|
162
|
+
this.state.visibleMonth.clone().add({ months: 1 }).format('YYYY')
|
|
163
|
+
) <= this.props.withYearPicker.endYear)
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
145
167
|
renderMonthNavigationButtons = () => {
|
|
146
168
|
const { renderNextMonthButton, renderPrevMonthButton } = this.props
|
|
147
169
|
|
|
@@ -176,6 +198,9 @@ class Calendar extends Component<CalendarProps, CalendarState> {
|
|
|
176
198
|
const { visibleMonth } = this.state
|
|
177
199
|
const newDate = visibleMonth.clone()
|
|
178
200
|
if (direction === 'prev') {
|
|
201
|
+
if (!this.hasPrevMonth) {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
179
204
|
if (onRequestRenderPrevMonth) {
|
|
180
205
|
onRequestRenderPrevMonth(
|
|
181
206
|
e,
|
|
@@ -185,6 +210,9 @@ class Calendar extends Component<CalendarProps, CalendarState> {
|
|
|
185
210
|
}
|
|
186
211
|
newDate.subtract({ months: 1 })
|
|
187
212
|
} else {
|
|
213
|
+
if (!this.hasNextMonth) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
188
216
|
if (onRequestRenderNextMonth) {
|
|
189
217
|
onRequestRenderNextMonth(
|
|
190
218
|
e,
|
|
@@ -197,8 +225,23 @@ class Calendar extends Component<CalendarProps, CalendarState> {
|
|
|
197
225
|
this.setState({ visibleMonth: newDate })
|
|
198
226
|
}
|
|
199
227
|
|
|
228
|
+
handleYearChange = (
|
|
229
|
+
e: React.SyntheticEvent<Element, Event>,
|
|
230
|
+
year: number
|
|
231
|
+
) => {
|
|
232
|
+
const { withYearPicker } = this.props
|
|
233
|
+
const { visibleMonth } = this.state
|
|
234
|
+
const newDate = visibleMonth.clone()
|
|
235
|
+
if (withYearPicker?.onRequestYearChange) {
|
|
236
|
+
withYearPicker.onRequestYearChange(e, year)
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
newDate.year(year)
|
|
240
|
+
this.setState({ visibleMonth: newDate })
|
|
241
|
+
}
|
|
242
|
+
|
|
200
243
|
renderHeader() {
|
|
201
|
-
const { renderNavigationLabel, styles } = this.props
|
|
244
|
+
const { renderNavigationLabel, styles, withYearPicker } = this.props
|
|
202
245
|
const { visibleMonth } = this.state
|
|
203
246
|
const { prevButton, nextButton } = this.renderMonthNavigationButtons()
|
|
204
247
|
|
|
@@ -218,18 +261,59 @@ class Calendar extends Component<CalendarProps, CalendarState> {
|
|
|
218
261
|
...(prevButton || nextButton ? [styles?.navigationWithButtons] : [])
|
|
219
262
|
]
|
|
220
263
|
|
|
264
|
+
const yearList: number[] = []
|
|
265
|
+
|
|
266
|
+
if (withYearPicker) {
|
|
267
|
+
const { startYear, endYear } = withYearPicker
|
|
268
|
+
for (let year = endYear; year >= startYear!; year--) {
|
|
269
|
+
yearList.push(year)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
221
273
|
return (
|
|
222
|
-
<div
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
274
|
+
<div>
|
|
275
|
+
<div css={style}>
|
|
276
|
+
{prevButton &&
|
|
277
|
+
cloneButton(prevButton, this.handleMonthChange('prev'))}
|
|
278
|
+
{renderNavigationLabel ? (
|
|
279
|
+
callRenderProp(renderNavigationLabel)
|
|
280
|
+
) : (
|
|
281
|
+
<span>
|
|
282
|
+
<div>{visibleMonth.format('MMMM')}</div>
|
|
283
|
+
{!withYearPicker ? (
|
|
284
|
+
<div>{visibleMonth.format('YYYY')}</div>
|
|
285
|
+
) : null}
|
|
286
|
+
</span>
|
|
287
|
+
)}
|
|
288
|
+
{nextButton &&
|
|
289
|
+
cloneButton(nextButton, this.handleMonthChange('next'))}
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
{withYearPicker ? (
|
|
293
|
+
<div css={styles?.yearPicker}>
|
|
294
|
+
<SimpleSelect
|
|
295
|
+
width="90px"
|
|
296
|
+
renderLabel=""
|
|
297
|
+
assistiveText={withYearPicker.screenReaderLabel}
|
|
298
|
+
value={Number(visibleMonth.format('YYYY'))}
|
|
299
|
+
onChange={(
|
|
300
|
+
e: React.SyntheticEvent<Element, Event>,
|
|
301
|
+
{
|
|
302
|
+
value
|
|
303
|
+
}: {
|
|
304
|
+
value?: string | number | undefined
|
|
305
|
+
id?: string | undefined
|
|
306
|
+
}
|
|
307
|
+
) => this.handleYearChange(e, Number(value))}
|
|
308
|
+
>
|
|
309
|
+
{yearList.map((year) => (
|
|
310
|
+
<SimpleSelect.Option key={year} id={`opt-${year}`} value={year}>
|
|
311
|
+
{`${year}`}
|
|
312
|
+
</SimpleSelect.Option>
|
|
313
|
+
))}
|
|
314
|
+
</SimpleSelect>
|
|
315
|
+
</div>
|
|
316
|
+
) : null}
|
|
233
317
|
</div>
|
|
234
318
|
)
|
|
235
319
|
}
|
package/src/Calendar/props.ts
CHANGED
|
@@ -152,6 +152,24 @@ type CalendarOwnProps = {
|
|
|
152
152
|
* Visible month for the rendered calendar. Formatted as an ISO date string.
|
|
153
153
|
*/
|
|
154
154
|
visibleMonth?: string
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* If set, years can be picked from a dropdown.
|
|
158
|
+
* It accepts an object.
|
|
159
|
+
* screenReaderLabel: string // e.g.: i18n("pick a year")
|
|
160
|
+
*
|
|
161
|
+
* onRequestYearChange?:(e: React.MouseEvent,requestedYear: number): void // if set, on year change, only this will be called and no internal change will take place
|
|
162
|
+
*
|
|
163
|
+
* startYear: number // e.g.: 2001, sets the start year of the selectable list
|
|
164
|
+
*
|
|
165
|
+
* endYear: number // e.g.: 2030, sets the end year of the selectable list
|
|
166
|
+
*/
|
|
167
|
+
withYearPicker?: {
|
|
168
|
+
screenReaderLabel: string
|
|
169
|
+
onRequestYearChange?: (e: any, requestedYear: number) => void
|
|
170
|
+
startYear: number
|
|
171
|
+
endYear: number
|
|
172
|
+
}
|
|
155
173
|
}
|
|
156
174
|
|
|
157
175
|
type PropKeys = keyof CalendarOwnProps
|
|
@@ -163,7 +181,7 @@ type CalendarProps = CalendarOwnProps &
|
|
|
163
181
|
OtherHTMLAttributes<CalendarOwnProps>
|
|
164
182
|
|
|
165
183
|
type CalendarStyle = ComponentStyle<
|
|
166
|
-
'navigation' | 'navigationWithButtons' | 'weekdayHeader'
|
|
184
|
+
'navigation' | 'navigationWithButtons' | 'weekdayHeader' | 'yearPicker'
|
|
167
185
|
>
|
|
168
186
|
|
|
169
187
|
const propTypes: PropValidators<PropKeys> = {
|
|
@@ -184,7 +202,8 @@ const propTypes: PropValidators<PropKeys> = {
|
|
|
184
202
|
role: PropTypes.oneOf(['table', 'listbox']),
|
|
185
203
|
selectedDate: PropTypes.string,
|
|
186
204
|
timezone: PropTypes.string,
|
|
187
|
-
visibleMonth: PropTypes.string
|
|
205
|
+
visibleMonth: PropTypes.string,
|
|
206
|
+
withYearPicker: PropTypes.object
|
|
188
207
|
}
|
|
189
208
|
|
|
190
209
|
const allowedProps: AllowedPropKeys = [
|
package/src/Calendar/styles.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import type { CalendarTheme } from '@instructure/shared-types'
|
|
26
|
-
import type { CalendarStyle } from './props'
|
|
26
|
+
import type { CalendarStyle, CalendarProps } from './props'
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* ---
|
|
@@ -33,7 +33,11 @@ import type { CalendarStyle } from './props'
|
|
|
33
33
|
* @param {Object} componentTheme The theme variable object.
|
|
34
34
|
* @return {Object} The final style object, which will be used in the component
|
|
35
35
|
*/
|
|
36
|
-
const generateStyle = (
|
|
36
|
+
const generateStyle = (
|
|
37
|
+
componentTheme: CalendarTheme,
|
|
38
|
+
props: CalendarProps
|
|
39
|
+
): CalendarStyle => {
|
|
40
|
+
const { withYearPicker } = props
|
|
37
41
|
return {
|
|
38
42
|
navigation: {
|
|
39
43
|
label: 'calendar__navigation',
|
|
@@ -43,7 +47,9 @@ const generateStyle = (componentTheme: CalendarTheme): CalendarStyle => {
|
|
|
43
47
|
fontFamily: componentTheme.fontFamily,
|
|
44
48
|
fontSize: componentTheme.fontSize,
|
|
45
49
|
fontWeight: componentTheme.fontWeight,
|
|
46
|
-
marginBottom:
|
|
50
|
+
marginBottom: withYearPicker
|
|
51
|
+
? componentTheme.navWithYearMargin
|
|
52
|
+
: componentTheme.navMargin
|
|
47
53
|
},
|
|
48
54
|
navigationWithButtons: {
|
|
49
55
|
label: 'calendar__navigation--withButtons',
|
|
@@ -55,6 +61,12 @@ const generateStyle = (componentTheme: CalendarTheme): CalendarStyle => {
|
|
|
55
61
|
label: 'calendar__weekdayHeader',
|
|
56
62
|
textAlign: 'center',
|
|
57
63
|
maxWidth: componentTheme.maxHeaderWidth
|
|
64
|
+
},
|
|
65
|
+
yearPicker: {
|
|
66
|
+
display: 'flex',
|
|
67
|
+
justifyContent: 'center',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
marginBottom: componentTheme.yearPickerMargin
|
|
58
70
|
}
|
|
59
71
|
}
|
|
60
72
|
}
|