@equinor/eds-core-react 0.36.0 → 0.37.0
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/dist/eds-core-react.cjs +1192 -53
- package/dist/esm/components/Autocomplete/Autocomplete.js +1 -1
- package/dist/esm/components/Breadcrumbs/Breadcrumbs.js +1 -1
- package/dist/esm/components/Button/Button.js +1 -1
- package/dist/esm/components/Button/tokens/contained_icon.js +2 -2
- package/dist/esm/components/Button/tokens/icon.js +1 -1
- package/dist/esm/components/Datepicker/DatePicker.js +156 -0
- package/dist/esm/components/Datepicker/DateRangePicker.js +151 -0
- package/dist/esm/components/Datepicker/calendars/Calendar.js +83 -0
- package/dist/esm/components/Datepicker/calendars/CalendarCell.js +144 -0
- package/dist/esm/components/Datepicker/calendars/CalendarGrid.js +65 -0
- package/dist/esm/components/Datepicker/calendars/CalendarHeader.js +90 -0
- package/dist/esm/components/Datepicker/calendars/CalendarWrapper.js +8 -0
- package/dist/esm/components/Datepicker/calendars/RangeCalendar.js +72 -0
- package/dist/esm/components/Datepicker/calendars/YearGrid.js +90 -0
- package/dist/esm/components/Datepicker/fields/DateField.js +40 -0
- package/dist/esm/components/Datepicker/fields/DateFieldSegments.js +43 -0
- package/dist/esm/components/Datepicker/fields/DateRangeField.js +43 -0
- package/dist/esm/components/Datepicker/fields/DateSegment.js +47 -0
- package/dist/esm/components/Datepicker/fields/FieldWrapper.js +136 -0
- package/dist/esm/components/Datepicker/fields/Toggle.js +61 -0
- package/dist/esm/components/Datepicker/utils/context.js +21 -0
- package/dist/esm/components/Datepicker/utils/get-calendar-date.js +8 -0
- package/dist/esm/components/Datepicker/utils/useConvertedValidationFunctions.js +20 -0
- package/dist/esm/components/Label/Label.js +1 -1
- package/dist/esm/components/Menu/MenuItem.js +1 -1
- package/dist/esm/components/TextField/TextField.js +2 -1
- package/dist/esm/components/Textarea/Textarea.js +9 -3
- package/dist/esm/index.js +2 -0
- package/dist/esm/node_modules/.pnpm/ramda@0.29.1/node_modules/ramda/es/mergeDeepRight.js +1 -2
- package/dist/esm/node_modules/.pnpm/ramda@0.29.1/node_modules/ramda/es/mergeDeepWithKey.js +1 -2
- package/dist/esm/node_modules/.pnpm/ramda@0.29.1/node_modules/ramda/es/mergeWith.js +1 -2
- package/dist/esm/node_modules/.pnpm/ramda@0.29.1/node_modules/ramda/es/mergeWithKey.js +1 -2
- package/dist/esm/node_modules/.pnpm/ramda@0.29.1/node_modules/ramda/es/pickBy.js +1 -2
- package/dist/types/components/Autocomplete/Autocomplete.d.ts +2 -2
- package/dist/types/components/Chip/Icon.d.ts +3 -1
- package/dist/types/components/Datepicker/DatePicker.d.ts +23 -0
- package/dist/types/components/Datepicker/DatePicker.spec.d.ts +1 -0
- package/dist/types/components/Datepicker/DateRangePicker.d.ts +39 -0
- package/dist/types/components/Datepicker/DateRangePicker.spec.d.ts +1 -0
- package/dist/types/components/Datepicker/calendars/Calendar.d.ts +17 -0
- package/dist/types/components/Datepicker/calendars/CalendarCell.d.ts +9 -0
- package/dist/types/components/Datepicker/calendars/CalendarGrid.d.ts +10 -0
- package/dist/types/components/Datepicker/calendars/CalendarHeader.d.ts +12 -0
- package/dist/types/components/Datepicker/calendars/CalendarWrapper.d.ts +273 -0
- package/dist/types/components/Datepicker/calendars/RangeCalendar.d.ts +11 -0
- package/dist/types/components/Datepicker/calendars/YearGrid.d.ts +4 -0
- package/dist/types/components/Datepicker/fields/DateField.d.ts +20 -0
- package/dist/types/components/Datepicker/fields/DateFieldSegments.d.ts +6 -0
- package/dist/types/components/Datepicker/fields/DateRangeField.d.ts +12 -0
- package/dist/types/components/Datepicker/fields/DateSegment.d.ts +8 -0
- package/dist/types/components/Datepicker/fields/FieldWrapper.d.ts +31 -0
- package/dist/types/components/Datepicker/fields/Toggle.d.ts +15 -0
- package/dist/types/components/Datepicker/index.d.ts +2 -0
- package/dist/types/components/Datepicker/props.d.ts +105 -0
- package/dist/types/components/Datepicker/utils/context.d.ts +9 -0
- package/dist/types/components/Datepicker/utils/get-calendar-date.d.ts +1 -0
- package/dist/types/components/Datepicker/utils/timezone.d.ts +1 -0
- package/dist/types/components/Datepicker/utils/types.d.ts +6 -0
- package/dist/types/components/Datepicker/utils/useConvertedValidationFunctions.d.ts +8 -0
- package/dist/types/components/Label/Label.d.ts +2 -2
- package/dist/types/components/Select/NativeSelect.d.ts +3 -3
- package/dist/types/components/Switch/Switch.styles.d.ts +6 -7
- package/dist/types/components/Tabs/TabList.d.ts +2 -13
- package/dist/types/components/TextField/TextField.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/package.json +22 -19
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { Button } from '../../Button/index.js';
|
|
3
|
+
import { Icon } from '../../Icon/index.js';
|
|
4
|
+
import { chevron_left, chevron_up, chevron_down, chevron_right } from '@equinor/eds-icons';
|
|
5
|
+
import { CalendarDate } from '@internationalized/date';
|
|
6
|
+
import { tokens } from '@equinor/eds-tokens';
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
const HeaderWrapper = styled.div.withConfig({
|
|
10
|
+
displayName: "CalendarHeader__HeaderWrapper",
|
|
11
|
+
componentId: "sc-kuy15-0"
|
|
12
|
+
})(["display:flex;justify-content:space-between;align-items:center;text-transform:capitalize;width:100%;"]);
|
|
13
|
+
function TodayPicker({
|
|
14
|
+
onClick,
|
|
15
|
+
disabled
|
|
16
|
+
}) {
|
|
17
|
+
const today = new Date();
|
|
18
|
+
return /*#__PURE__*/jsx(Button, {
|
|
19
|
+
disabled: disabled,
|
|
20
|
+
onClick: () => onClick(new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate())),
|
|
21
|
+
variant: 'ghost',
|
|
22
|
+
style: {
|
|
23
|
+
marginLeft: 16
|
|
24
|
+
},
|
|
25
|
+
children: "Today"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const HeaderActions = styled.div.withConfig({
|
|
29
|
+
displayName: "CalendarHeader__HeaderActions",
|
|
30
|
+
componentId: "sc-kuy15-1"
|
|
31
|
+
})(["display:flex;align-items:center;width:100%;"]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The default header for the calendar components if no custom header is provided
|
|
35
|
+
*/
|
|
36
|
+
function CalendarHeader({
|
|
37
|
+
state,
|
|
38
|
+
title,
|
|
39
|
+
previousMonthDisabled,
|
|
40
|
+
nextMonthDisabled,
|
|
41
|
+
showYearPicker,
|
|
42
|
+
setShowYearPicker
|
|
43
|
+
}) {
|
|
44
|
+
return /*#__PURE__*/jsx(HeaderWrapper, {
|
|
45
|
+
children: /*#__PURE__*/jsxs(HeaderActions, {
|
|
46
|
+
children: [/*#__PURE__*/jsx(Button, {
|
|
47
|
+
variant: 'ghost_icon',
|
|
48
|
+
"aria-label": 'Previous month',
|
|
49
|
+
disabled: previousMonthDisabled || showYearPicker,
|
|
50
|
+
onClick: () => state.focusPreviousPage(),
|
|
51
|
+
children: /*#__PURE__*/jsx(Icon, {
|
|
52
|
+
data: chevron_left
|
|
53
|
+
})
|
|
54
|
+
}), /*#__PURE__*/jsx("span", {
|
|
55
|
+
style: {
|
|
56
|
+
flex: '1 1 auto'
|
|
57
|
+
}
|
|
58
|
+
}), /*#__PURE__*/jsxs(Button, {
|
|
59
|
+
onClick: () => setShowYearPicker(!showYearPicker),
|
|
60
|
+
"data-testid": 'heading',
|
|
61
|
+
"aria-live": 'polite',
|
|
62
|
+
variant: 'ghost',
|
|
63
|
+
style: {
|
|
64
|
+
fontSize: tokens.typography.heading.h5.fontSize,
|
|
65
|
+
textTransform: 'capitalize'
|
|
66
|
+
},
|
|
67
|
+
children: [title, /*#__PURE__*/jsx(Icon, {
|
|
68
|
+
data: showYearPicker ? chevron_up : chevron_down
|
|
69
|
+
})]
|
|
70
|
+
}), /*#__PURE__*/jsx(TodayPicker, {
|
|
71
|
+
disabled: showYearPicker,
|
|
72
|
+
onClick: v => state.setFocusedDate(v)
|
|
73
|
+
}), /*#__PURE__*/jsx("span", {
|
|
74
|
+
style: {
|
|
75
|
+
flex: '1 1 auto'
|
|
76
|
+
}
|
|
77
|
+
}), /*#__PURE__*/jsx(Button, {
|
|
78
|
+
variant: 'ghost_icon',
|
|
79
|
+
onClick: () => state.focusNextPage(),
|
|
80
|
+
disabled: nextMonthDisabled || showYearPicker,
|
|
81
|
+
"aria-label": 'Next month',
|
|
82
|
+
children: /*#__PURE__*/jsx(Icon, {
|
|
83
|
+
data: chevron_right
|
|
84
|
+
})
|
|
85
|
+
})]
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { CalendarHeader };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
const CalendarWrapper = styled.div.withConfig({
|
|
4
|
+
displayName: "CalendarWrapper",
|
|
5
|
+
componentId: "sc-14hqwiu-0"
|
|
6
|
+
})(["display:grid;grid-gap:16px;max-height:80vh;width:max-content;max-width:560px;overflow:auto;"]);
|
|
7
|
+
|
|
8
|
+
export { CalendarWrapper };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useLocale, useRangeCalendar } from 'react-aria';
|
|
2
|
+
import { useRangeCalendarState } from '@react-stately/calendar';
|
|
3
|
+
import { createCalendar } from '@internationalized/date';
|
|
4
|
+
import { CalendarGrid } from './CalendarGrid.js';
|
|
5
|
+
import { forwardRef, useState } from 'react';
|
|
6
|
+
import { CalendarHeader } from './CalendarHeader.js';
|
|
7
|
+
import { Popover } from '../../Popover/index.js';
|
|
8
|
+
import { CalendarWrapper } from './CalendarWrapper.js';
|
|
9
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
const RangeCalendar = /*#__PURE__*/forwardRef(({
|
|
12
|
+
Header,
|
|
13
|
+
Footer,
|
|
14
|
+
...props
|
|
15
|
+
}, ref) => {
|
|
16
|
+
const [showYearPicker, setShowYearPicker] = useState(false);
|
|
17
|
+
const {
|
|
18
|
+
locale
|
|
19
|
+
} = useLocale();
|
|
20
|
+
const state = useRangeCalendarState({
|
|
21
|
+
...props,
|
|
22
|
+
locale,
|
|
23
|
+
createCalendar
|
|
24
|
+
});
|
|
25
|
+
const {
|
|
26
|
+
calendarProps,
|
|
27
|
+
title
|
|
28
|
+
} = useRangeCalendar(props, state, ref);
|
|
29
|
+
return /*#__PURE__*/jsxs(CalendarWrapper, {
|
|
30
|
+
...calendarProps,
|
|
31
|
+
ref: ref,
|
|
32
|
+
children: [/*#__PURE__*/jsx(Popover.Header, {
|
|
33
|
+
children: Header ? /*#__PURE__*/jsx(Header, {
|
|
34
|
+
month: state.focusedDate.month,
|
|
35
|
+
state: state,
|
|
36
|
+
setMonth: month => state.setFocusedDate(state.focusedDate.set({
|
|
37
|
+
month
|
|
38
|
+
})),
|
|
39
|
+
setYear: year => state.setFocusedDate(state.focusedDate.set({
|
|
40
|
+
year
|
|
41
|
+
})),
|
|
42
|
+
year: state.focusedDate.year
|
|
43
|
+
}) : /*#__PURE__*/jsx(CalendarHeader, {
|
|
44
|
+
state: state,
|
|
45
|
+
title: title,
|
|
46
|
+
setShowYearPicker: setShowYearPicker,
|
|
47
|
+
showYearPicker: showYearPicker
|
|
48
|
+
})
|
|
49
|
+
}), /*#__PURE__*/jsx(Popover.Content, {
|
|
50
|
+
children: /*#__PURE__*/jsx(CalendarGrid, {
|
|
51
|
+
state: state,
|
|
52
|
+
setShowYearPicker: setShowYearPicker,
|
|
53
|
+
showYearPicker: showYearPicker
|
|
54
|
+
})
|
|
55
|
+
}), Footer && /*#__PURE__*/jsx(Popover.Actions, {
|
|
56
|
+
children: /*#__PURE__*/jsx(Footer, {
|
|
57
|
+
month: state.focusedDate.month,
|
|
58
|
+
state: state,
|
|
59
|
+
setMonth: month => state.setFocusedDate(state.focusedDate.set({
|
|
60
|
+
month
|
|
61
|
+
})),
|
|
62
|
+
setYear: year => state.setFocusedDate(state.focusedDate.set({
|
|
63
|
+
year
|
|
64
|
+
})),
|
|
65
|
+
year: state.focusedDate.year
|
|
66
|
+
})
|
|
67
|
+
})]
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
RangeCalendar.displayName = 'RangeCalendar';
|
|
71
|
+
|
|
72
|
+
export { RangeCalendar };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { tokens } from '@equinor/eds-tokens';
|
|
3
|
+
import { FocusScope, useFocusManager } from 'react-aria';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// Disable no-autofocus - it's not the native autofocus attribute, but react-aria's autoFocus prop
|
|
7
|
+
/* eslint-disable jsx-a11y/no-autofocus */
|
|
8
|
+
const Grid = styled.div.withConfig({
|
|
9
|
+
displayName: "YearGrid__Grid",
|
|
10
|
+
componentId: "sc-1l9jho8-0"
|
|
11
|
+
})(["display:grid;grid-template-columns:repeat(6,1fr);grid-gap:8px;margin:8px;"]);
|
|
12
|
+
const GridColumn = styled.button.withConfig({
|
|
13
|
+
displayName: "YearGrid__GridColumn",
|
|
14
|
+
componentId: "sc-1l9jho8-1"
|
|
15
|
+
})(["background-color:transparent;outline:none;border:none;display:flex;justify-content:center;cursor:pointer;padding:8px;font-size:", ";font-family:", ";font-weight:", ";line-height:", ";color:", ";border-radius:999px;", ";&:hover{background-color:#f0f0f0;}&:focus{outline:2px dashed ", ";}"], tokens.typography.navigation.button.fontSize, tokens.typography.navigation.button.fontFamily, tokens.typography.navigation.button.fontWeight, tokens.typography.navigation.button.lineHeight, tokens.colors.text.static_icons__default.rgba, ({
|
|
16
|
+
$active
|
|
17
|
+
}) => $active ? `background-color: ${tokens.colors.interactive.primary__selected_highlight.rgba}` : '', tokens.colors.interactive.primary__resting.rgba);
|
|
18
|
+
const GridFocusManager = ({
|
|
19
|
+
year: selectedYear,
|
|
20
|
+
setFocusedYear
|
|
21
|
+
}) => {
|
|
22
|
+
const focusManager = useFocusManager();
|
|
23
|
+
const onKeyDown = e => {
|
|
24
|
+
const target = e.currentTarget;
|
|
25
|
+
const parent = target.parentElement;
|
|
26
|
+
switch (e.key) {
|
|
27
|
+
case 'ArrowRight':
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
focusManager.focusNext({
|
|
30
|
+
wrap: true
|
|
31
|
+
});
|
|
32
|
+
break;
|
|
33
|
+
case 'ArrowLeft':
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
focusManager.focusPrevious({
|
|
36
|
+
wrap: true
|
|
37
|
+
});
|
|
38
|
+
break;
|
|
39
|
+
case 'ArrowDown':
|
|
40
|
+
{
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
const selfIndex = Array.from(parent.children).indexOf(target);
|
|
43
|
+
const focusElement = Array.from(parent.children).at(selfIndex + 5);
|
|
44
|
+
focusManager.focusNext({
|
|
45
|
+
from: focusElement
|
|
46
|
+
});
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case 'ArrowUp':
|
|
50
|
+
{
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const selfIndex = Array.from(parent.children).indexOf(target);
|
|
53
|
+
const focusElement = Array.from(parent.children).at(selfIndex - 5);
|
|
54
|
+
focusManager.focusPrevious({
|
|
55
|
+
from: focusElement
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const years = Array.from({
|
|
62
|
+
length: 36
|
|
63
|
+
}, (_, i) => i + (selectedYear - 30 / 2));
|
|
64
|
+
return years.map(year => /*#__PURE__*/jsx(GridColumn, {
|
|
65
|
+
$active: selectedYear === year,
|
|
66
|
+
onKeyDown: onKeyDown,
|
|
67
|
+
onClick: () => setFocusedYear(year),
|
|
68
|
+
"aria-label": `Set year to ${year}`,
|
|
69
|
+
tabIndex: 0,
|
|
70
|
+
children: year
|
|
71
|
+
}, year));
|
|
72
|
+
};
|
|
73
|
+
const YearGrid = ({
|
|
74
|
+
setFocusedYear,
|
|
75
|
+
year: selectedYear
|
|
76
|
+
}) => {
|
|
77
|
+
return /*#__PURE__*/jsx(Grid, {
|
|
78
|
+
children: /*#__PURE__*/jsx(FocusScope, {
|
|
79
|
+
contain: true,
|
|
80
|
+
restoreFocus: true,
|
|
81
|
+
autoFocus: true,
|
|
82
|
+
children: /*#__PURE__*/jsx(GridFocusManager, {
|
|
83
|
+
year: selectedYear,
|
|
84
|
+
setFocusedYear: setFocusedYear
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { YearGrid };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useDateFieldState } from '@react-stately/datepicker';
|
|
2
|
+
import { forwardRef, useRef } from 'react';
|
|
3
|
+
import { InputFieldWrapper } from './FieldWrapper.js';
|
|
4
|
+
import { DateFieldSegments } from './DateFieldSegments.js';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Datefield is the input field used in {@link DatePicker} to type in a single date.
|
|
9
|
+
* Encapsulates styling / functionality for typing a date
|
|
10
|
+
*/
|
|
11
|
+
const DateField = /*#__PURE__*/forwardRef(function ({
|
|
12
|
+
fieldProps,
|
|
13
|
+
groupProps,
|
|
14
|
+
variant,
|
|
15
|
+
dateCreateProps,
|
|
16
|
+
...props
|
|
17
|
+
}, ref) {
|
|
18
|
+
const state = useDateFieldState(dateCreateProps);
|
|
19
|
+
const inputRef = useRef(null);
|
|
20
|
+
return /*#__PURE__*/jsxs(InputFieldWrapper, {
|
|
21
|
+
...groupProps,
|
|
22
|
+
readonly: fieldProps.isReadOnly,
|
|
23
|
+
disabled: state.isDisabled,
|
|
24
|
+
color: state.isInvalid ? 'warning' : variant,
|
|
25
|
+
ref: ref,
|
|
26
|
+
className: `field ${state.isInvalid ? 'invalid' : 'valid'}`,
|
|
27
|
+
children: [/*#__PURE__*/jsx(DateFieldSegments, {
|
|
28
|
+
...state,
|
|
29
|
+
...fieldProps,
|
|
30
|
+
ref: inputRef
|
|
31
|
+
}), /*#__PURE__*/jsx("span", {
|
|
32
|
+
style: {
|
|
33
|
+
flex: '1 1 auto'
|
|
34
|
+
}
|
|
35
|
+
}), props.rightAdornments]
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
DateField.displayName = 'DateField';
|
|
39
|
+
|
|
40
|
+
export { DateField };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useDateFieldState } from '@react-stately/datepicker';
|
|
2
|
+
import { useLocale, useDateField } from 'react-aria';
|
|
3
|
+
import { createCalendar } from '@internationalized/date';
|
|
4
|
+
import { DateSegment } from './DateSegment.js';
|
|
5
|
+
import { forwardRef } from 'react';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
// In some cases we need to use the index as key
|
|
9
|
+
/* eslint-disable react/no-array-index-key */
|
|
10
|
+
/**
|
|
11
|
+
* A field that wraps segments for inputting a date / date-time
|
|
12
|
+
*/
|
|
13
|
+
const DateFieldSegments = /*#__PURE__*/forwardRef((props, ref) => {
|
|
14
|
+
const {
|
|
15
|
+
locale
|
|
16
|
+
} = useLocale();
|
|
17
|
+
const state = useDateFieldState({
|
|
18
|
+
...props,
|
|
19
|
+
locale,
|
|
20
|
+
createCalendar
|
|
21
|
+
});
|
|
22
|
+
const {
|
|
23
|
+
fieldProps
|
|
24
|
+
} = useDateField({
|
|
25
|
+
...props,
|
|
26
|
+
// Type-casting as react-aria expects string | ReactNode, but we only pass strings
|
|
27
|
+
'aria-label': props.label ?? 'Date input field'
|
|
28
|
+
}, state, ref);
|
|
29
|
+
return /*#__PURE__*/jsx("div", {
|
|
30
|
+
...fieldProps,
|
|
31
|
+
style: {
|
|
32
|
+
display: 'flex'
|
|
33
|
+
},
|
|
34
|
+
ref: ref,
|
|
35
|
+
children: state.segments.map((segment, i) => /*#__PURE__*/jsx(DateSegment, {
|
|
36
|
+
segment: segment,
|
|
37
|
+
state: state
|
|
38
|
+
}, i))
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
DateFieldSegments.displayName = 'SingleDateField';
|
|
42
|
+
|
|
43
|
+
export { DateFieldSegments };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { forwardRef, useRef } from 'react';
|
|
2
|
+
import { InputFieldWrapper } from './FieldWrapper.js';
|
|
3
|
+
import { DateFieldSegments } from './DateFieldSegments.js';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { Typography } from '../../Typography/Typography.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DateRangeField is the input field used in {@link DateRangePicker} to type in a date range.
|
|
9
|
+
*/
|
|
10
|
+
const DateRangeField = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
11
|
+
const fromRef = useRef(null);
|
|
12
|
+
const toRef = useRef(null);
|
|
13
|
+
return /*#__PURE__*/jsxs(InputFieldWrapper, {
|
|
14
|
+
ref: ref,
|
|
15
|
+
readonly: props.startFieldProps.isReadOnly,
|
|
16
|
+
className: "field",
|
|
17
|
+
disabled: props.disabled,
|
|
18
|
+
color: props.variant,
|
|
19
|
+
...props.groupProps,
|
|
20
|
+
children: [/*#__PURE__*/jsx(DateFieldSegments, {
|
|
21
|
+
...props.startFieldProps,
|
|
22
|
+
ref: fromRef
|
|
23
|
+
}), /*#__PURE__*/jsx(Typography, {
|
|
24
|
+
as: 'span',
|
|
25
|
+
variant: 'text',
|
|
26
|
+
group: 'input',
|
|
27
|
+
style: {
|
|
28
|
+
padding: '0 4px'
|
|
29
|
+
},
|
|
30
|
+
children: "\u2014"
|
|
31
|
+
}), /*#__PURE__*/jsx(DateFieldSegments, {
|
|
32
|
+
...props.endFieldProps,
|
|
33
|
+
ref: toRef
|
|
34
|
+
}), /*#__PURE__*/jsx("span", {
|
|
35
|
+
style: {
|
|
36
|
+
flex: '1 1 auto'
|
|
37
|
+
}
|
|
38
|
+
}), props.rightAdornments]
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
DateRangeField.displayName = 'DateRangeField';
|
|
42
|
+
|
|
43
|
+
export { DateRangeField };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import { useDateSegment } from 'react-aria';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { tokens } from '@equinor/eds-tokens';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
const Segment = styled.div.withConfig({
|
|
8
|
+
displayName: "DateSegment__Segment",
|
|
9
|
+
componentId: "sc-19uidpx-0"
|
|
10
|
+
})(["color:", ";font-family:", ";&:focus-visible{outline:2px solid ", ";background-color:", ";}", ""], tokens.typography.input.text.color, tokens.typography.input.text.fontFamily, tokens.colors.interactive.primary__resting.rgba, tokens.colors.ui.background__medium.rgba, ({
|
|
11
|
+
$disabled
|
|
12
|
+
}) => $disabled && `color: ${tokens.colors.interactive.disabled__text.rgba};`);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* DateSegment is used to represent a single segment of a date in the DateField (i.e. day, month, year)
|
|
16
|
+
*/
|
|
17
|
+
function DateSegment({
|
|
18
|
+
segment,
|
|
19
|
+
state
|
|
20
|
+
}) {
|
|
21
|
+
const ref = useRef(null);
|
|
22
|
+
const {
|
|
23
|
+
segmentProps
|
|
24
|
+
} = useDateSegment(segment, state, ref);
|
|
25
|
+
return /*#__PURE__*/jsx(Segment, {
|
|
26
|
+
...segmentProps,
|
|
27
|
+
$invalid: state.isInvalid,
|
|
28
|
+
$disabled: state.isDisabled,
|
|
29
|
+
$placeholder: segment.isPlaceholder,
|
|
30
|
+
style: {
|
|
31
|
+
padding: segment.type === 'literal' ? '0 2px' : '0'
|
|
32
|
+
},
|
|
33
|
+
onKeyDown: e => {
|
|
34
|
+
if (e.code === 'Enter' || e.code === 'Space') {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
} else if (segmentProps.onKeyDown) {
|
|
38
|
+
segmentProps.onKeyDown(e);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
ref: ref,
|
|
42
|
+
className: `segment ${segment.isPlaceholder ? 'placeholder' : ''}`,
|
|
43
|
+
children: segment.isPlaceholder || segment.type === 'literal' ? segment.text : segment.text.padStart(segment.type === 'year' ? 4 : 2, '0')
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { DateSegment };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { tokens } from '@equinor/eds-tokens';
|
|
3
|
+
import { forwardRef, useEffect } from 'react';
|
|
4
|
+
import { Popover } from '../../Popover/index.js';
|
|
5
|
+
import { filterDOMProps } from '@react-aria/utils';
|
|
6
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { InputWrapper } from '../../InputWrapper/InputWrapper.js';
|
|
8
|
+
import { useEds } from '../../EdsProvider/eds.context.js';
|
|
9
|
+
|
|
10
|
+
const getVariant = variant => {
|
|
11
|
+
if (variant === 'error') {
|
|
12
|
+
return tokens.colors.interactive.danger__resting.rgba;
|
|
13
|
+
}
|
|
14
|
+
if (variant === 'success') {
|
|
15
|
+
return tokens.colors.interactive.success__resting.rgba;
|
|
16
|
+
}
|
|
17
|
+
if (variant === 'warning') {
|
|
18
|
+
return tokens.colors.interactive.warning__resting.rgba;
|
|
19
|
+
}
|
|
20
|
+
return tokens.colors.interactive.primary__resting.rgba;
|
|
21
|
+
};
|
|
22
|
+
const getVariantText = variant => {
|
|
23
|
+
if (variant === 'error') {
|
|
24
|
+
return tokens.colors.interactive.danger__text.rgba;
|
|
25
|
+
}
|
|
26
|
+
if (variant === 'success') {
|
|
27
|
+
return tokens.colors.interactive.success__text.rgba;
|
|
28
|
+
}
|
|
29
|
+
if (variant === 'warning') {
|
|
30
|
+
return tokens.colors.interactive.warning__text.rgba;
|
|
31
|
+
}
|
|
32
|
+
return tokens.typography.input.text.color;
|
|
33
|
+
};
|
|
34
|
+
const StyledInputFieldWrapper = styled.div.withConfig({
|
|
35
|
+
displayName: "FieldWrapper__StyledInputFieldWrapper",
|
|
36
|
+
componentId: "sc-1h0kqs9-0"
|
|
37
|
+
})(["display:flex;align-items:center;background-color:", ";height:", ";padding:0 8px;", " ", " color:", ";cursor:default;"], tokens.colors.ui.background__light.rgba, ({
|
|
38
|
+
$density
|
|
39
|
+
}) => $density === 'compact' ? '24px' : '36px', ({
|
|
40
|
+
$variant,
|
|
41
|
+
$disabled,
|
|
42
|
+
$readonly
|
|
43
|
+
}) => {
|
|
44
|
+
if (!$variant && !$readonly) {
|
|
45
|
+
return `&:focus-within:not(.invalid) {
|
|
46
|
+
outline: 2px solid
|
|
47
|
+
${tokens.colors.interactive.primary__resting.rgba};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
${!$disabled && `&:not(:focus-within) {
|
|
51
|
+
box-shadow: inset 0 -1px 0 0 ${tokens.colors.text.static_icons__tertiary.rgba};`}
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
return `outline: 2px solid ${getVariant($variant)};`;
|
|
56
|
+
}, ({
|
|
57
|
+
$readonly
|
|
58
|
+
}) => {
|
|
59
|
+
return $readonly && `background-color: ${tokens.colors.ui.background__default.rgba};
|
|
60
|
+
outline: none;`;
|
|
61
|
+
}, p => getVariantText(p.$variant));
|
|
62
|
+
/**
|
|
63
|
+
* Applies styles around the date input fields (density, color etc.)
|
|
64
|
+
*/
|
|
65
|
+
const InputFieldWrapper = /*#__PURE__*/forwardRef(({
|
|
66
|
+
children,
|
|
67
|
+
color,
|
|
68
|
+
disabled,
|
|
69
|
+
readonly,
|
|
70
|
+
...props
|
|
71
|
+
}, ref) => {
|
|
72
|
+
const {
|
|
73
|
+
density
|
|
74
|
+
} = useEds();
|
|
75
|
+
// As the props returned are designed for react-aria, some of them are not valid DOM props (i.e. onPress).
|
|
76
|
+
// The filterDOMProps-method strips out the invalid props, but it also removes event listeners due to casing
|
|
77
|
+
const filteredProps = filterDOMProps(props);
|
|
78
|
+
// filterDOMProps also strips event handlers
|
|
79
|
+
const eventHandlers = Object.keys(props).filter(k => k.startsWith('on')).reduce((a, b) => ({
|
|
80
|
+
...a,
|
|
81
|
+
[b]: props[b]
|
|
82
|
+
}), {});
|
|
83
|
+
return /*#__PURE__*/jsx(StyledInputFieldWrapper, {
|
|
84
|
+
ref: ref,
|
|
85
|
+
$density: density,
|
|
86
|
+
$variant: color,
|
|
87
|
+
$disabled: disabled ?? false,
|
|
88
|
+
$readonly: readonly ?? false,
|
|
89
|
+
...filteredProps,
|
|
90
|
+
...eventHandlers,
|
|
91
|
+
children: children
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
const FieldWrapper = /*#__PURE__*/forwardRef(({
|
|
95
|
+
children,
|
|
96
|
+
pickerRef,
|
|
97
|
+
calendar,
|
|
98
|
+
isOpen,
|
|
99
|
+
setIsOpen,
|
|
100
|
+
label,
|
|
101
|
+
readonly,
|
|
102
|
+
...props
|
|
103
|
+
}, ref) => {
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (isOpen === false) {
|
|
106
|
+
// Focus the first segment in the input field
|
|
107
|
+
const segment = ref.current?.querySelector('.segment');
|
|
108
|
+
segment?.focus();
|
|
109
|
+
}
|
|
110
|
+
}, [ref, isOpen, pickerRef]);
|
|
111
|
+
return /*#__PURE__*/jsxs(Fragment, {
|
|
112
|
+
children: [/*#__PURE__*/jsx(InputWrapper, {
|
|
113
|
+
readOnly: readonly,
|
|
114
|
+
label: label,
|
|
115
|
+
onKeyDownCapture: event => {
|
|
116
|
+
const isIconTarget = event.target instanceof SVGSVGElement;
|
|
117
|
+
if (!isIconTarget && (event.code === 'Space' || event.code === 'Enter')) {
|
|
118
|
+
setIsOpen(true);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
...props,
|
|
122
|
+
children: children
|
|
123
|
+
}), /*#__PURE__*/jsx(Popover, {
|
|
124
|
+
open: isOpen ?? false,
|
|
125
|
+
onClose: () => setIsOpen(false),
|
|
126
|
+
anchorEl: ref.current,
|
|
127
|
+
placement: 'bottom-start',
|
|
128
|
+
withinPortal: true,
|
|
129
|
+
children: calendar
|
|
130
|
+
})]
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
InputFieldWrapper.displayName = 'InputFieldWrapper';
|
|
134
|
+
FieldWrapper.displayName = 'FieldWrapper';
|
|
135
|
+
|
|
136
|
+
export { FieldWrapper, InputFieldWrapper };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Icon } from '../../Icon/index.js';
|
|
2
|
+
import { close } from '@equinor/eds-icons';
|
|
3
|
+
import { Button } from '../../Button/Button.js';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { filterDOMProps } from '@react-aria/utils';
|
|
6
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
const StyledButton = styled(Button).withConfig({
|
|
9
|
+
displayName: "Toggle__StyledButton",
|
|
10
|
+
componentId: "sc-1bwqcj7-0"
|
|
11
|
+
})(["height:24px;width:24px;"]);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Toggle component encapsulates the reset and open calendar buttons
|
|
15
|
+
*/
|
|
16
|
+
const Toggle = ({
|
|
17
|
+
reset,
|
|
18
|
+
setOpen,
|
|
19
|
+
open,
|
|
20
|
+
icon,
|
|
21
|
+
disabled,
|
|
22
|
+
buttonProps,
|
|
23
|
+
valueString,
|
|
24
|
+
readonly
|
|
25
|
+
}) => {
|
|
26
|
+
return readonly || disabled ? null : /*#__PURE__*/jsxs(Fragment, {
|
|
27
|
+
children: [/*#__PURE__*/jsx(StyledButton, {
|
|
28
|
+
disabled: disabled,
|
|
29
|
+
variant: 'ghost_icon',
|
|
30
|
+
"aria-label": 'Reset',
|
|
31
|
+
onClick: () => {
|
|
32
|
+
reset();
|
|
33
|
+
},
|
|
34
|
+
onKeyDown: e => {
|
|
35
|
+
if (e.code === 'Enter' || e.code === 'Space') {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
e.stopPropagation();
|
|
38
|
+
reset();
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
children: /*#__PURE__*/jsx(Icon, {
|
|
42
|
+
data: close
|
|
43
|
+
})
|
|
44
|
+
}), /*#__PURE__*/jsx(StyledButton, {
|
|
45
|
+
...filterDOMProps(buttonProps),
|
|
46
|
+
disabled: disabled,
|
|
47
|
+
"aria-label": valueString ? `Change date, ${valueString}` : `Change date`,
|
|
48
|
+
variant: 'ghost_icon',
|
|
49
|
+
onClick: e => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
setOpen(!open);
|
|
53
|
+
},
|
|
54
|
+
children: /*#__PURE__*/jsx(Icon, {
|
|
55
|
+
data: icon
|
|
56
|
+
})
|
|
57
|
+
})]
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { Toggle };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
const intl = new Intl.DateTimeFormat();
|
|
5
|
+
const defaultTimezone = intl.resolvedOptions().timeZone;
|
|
6
|
+
const DatePickerContext = /*#__PURE__*/createContext({
|
|
7
|
+
timezone: defaultTimezone
|
|
8
|
+
});
|
|
9
|
+
const DatePickerProvider = ({
|
|
10
|
+
timezone,
|
|
11
|
+
children
|
|
12
|
+
}) => {
|
|
13
|
+
return /*#__PURE__*/jsx(DatePickerContext.Provider, {
|
|
14
|
+
value: {
|
|
15
|
+
timezone: timezone ?? defaultTimezone
|
|
16
|
+
},
|
|
17
|
+
children: children
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { DatePickerProvider, defaultTimezone };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { toCalendarDateTime, fromDate, toCalendarDate } from '@internationalized/date';
|
|
2
|
+
|
|
3
|
+
const getCalendarDate = (value, timezone, showTimeInput = false) => {
|
|
4
|
+
if (!value) return null;
|
|
5
|
+
return showTimeInput ? toCalendarDateTime(fromDate(value, timezone)) : toCalendarDate(fromDate(value, timezone));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { getCalendarDate };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { toCalendarDate, fromDate } from '@internationalized/date';
|
|
2
|
+
import { defaultTimezone } from './context.js';
|
|
3
|
+
|
|
4
|
+
const useConvertedValidationFunctions = (minValue, maxValue, isDateUnavailable, timezone) => {
|
|
5
|
+
const tz = timezone ?? defaultTimezone;
|
|
6
|
+
const _minValue = minValue ? toCalendarDate(fromDate(minValue, tz)) : undefined;
|
|
7
|
+
const _maxValue = maxValue ? toCalendarDate(fromDate(maxValue, tz)) : undefined;
|
|
8
|
+
const _minTimeValue = minValue ? fromDate(minValue, tz) : undefined;
|
|
9
|
+
const _maxTimeValue = maxValue ? fromDate(maxValue, tz) : undefined;
|
|
10
|
+
const _isDateUnavailable = value => isDateUnavailable ? isDateUnavailable(value.toDate(tz)) : false;
|
|
11
|
+
return {
|
|
12
|
+
_minValue,
|
|
13
|
+
_maxValue,
|
|
14
|
+
_isDateUnavailable,
|
|
15
|
+
_minTimeValue,
|
|
16
|
+
_maxTimeValue
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { useConvertedValidationFunctions };
|