Skip to content

Commit 1dacfaa

Browse files
committed
feat(date-time-field): new tedi-ready component #554
1 parent 37c0e23 commit 1dacfaa

9 files changed

Lines changed: 611 additions & 198 deletions

File tree

src/tedi/components/form/date-calendar/date-calendar.module.scss

Lines changed: 112 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,77 @@
1414
}
1515

1616
.tedi-date-calendar {
17-
table {
17+
&__layout {
18+
display: flex;
19+
flex-direction: column;
20+
width: fit-content;
21+
22+
@extend %tedi-date-calendar-surface;
23+
}
24+
25+
&__main {
26+
width: auto;
27+
}
28+
29+
&__side {
30+
width: auto;
31+
padding-left: var(--card-padding-md-default);
32+
margin: var(--card-padding-md-default);
33+
margin-left: 0;
34+
border-left: 1px solid var(--general-border-primary);
35+
}
36+
37+
&__timepicker {
1838
width: 100%;
19-
border-spacing: 0;
20-
border-collapse: separate;
39+
padding-top: var(--spacing-16);
40+
margin-top: var(--spacing-16);
41+
border-top: 1px solid var(--color-border);
42+
43+
.tedi-time-picker {
44+
width: 100%;
45+
46+
&__wheel {
47+
max-height: 280px;
48+
}
49+
}
2150
}
2251

23-
&__container {
24-
position: relative;
52+
@media (width >= 768px) {
53+
&__layout {
54+
flex-direction: row;
55+
56+
&.tedi-date-calendar__layout--with-timepicker {
57+
min-width: 560px;
58+
}
59+
}
60+
61+
&__timepicker {
62+
min-width: 200px;
63+
padding-top: 0;
64+
padding-left: var(--spacing-16);
65+
margin-top: 0;
66+
margin-left: var(--spacing-16);
67+
border-top: none;
68+
border-left: 1px solid var(--color-border);
69+
70+
.tedi-time-picker__wheel {
71+
min-height: 280px;
72+
}
73+
}
74+
}
75+
76+
&--with-timepicker {
77+
.tedi-date-calendar__layout {
78+
@media (width >= 768px) {
79+
min-width: 560px;
80+
}
81+
}
82+
}
83+
84+
table {
2585
width: 100%;
86+
border-spacing: 0;
87+
border-collapse: separate;
2688
}
2789

2890
&__months-container {
@@ -166,59 +228,59 @@
166228
background: transparent;
167229
}
168230
}
169-
}
170231

171-
.tedi-date-calendar__multivalue div:last-child {
172-
align-items: start;
173-
}
232+
&__multivalue div:last-child {
233+
align-items: start;
234+
}
174235

175-
.tedi-date-calendar__picker-grid {
176-
display: flex;
177-
gap: var(--layout-grid-gutters-08);
178-
align-items: center;
179-
padding: var(--card-padding-md-default);
180-
}
236+
&__picker-grid {
237+
display: flex;
238+
gap: var(--layout-grid-gutters-08);
239+
align-items: center;
240+
padding: var(--card-padding-md-default);
241+
}
181242

182-
.tedi-date-calendar__picker-grid-container {
183-
@extend %tedi-date-calendar-surface;
243+
&__picker-grid-container {
244+
@extend %tedi-date-calendar-surface;
184245

185-
max-width: 315px;
186-
}
246+
max-width: 315px;
247+
}
187248

188-
.tedi-date-calendar__picker-grid-header {
189-
display: flex;
190-
gap: var(--layout-grid-gutters-08);
191-
align-items: center;
192-
justify-content: space-between;
193-
padding: var(--card-padding-md-default);
194-
padding-bottom: 0;
195-
}
249+
&__picker-grid-header {
250+
display: flex;
251+
gap: var(--layout-grid-gutters-08);
252+
align-items: center;
253+
justify-content: space-between;
254+
padding: var(--card-padding-md-default);
255+
padding-bottom: 0;
256+
}
196257

197-
.tedi-date-calendar__grid-button {
198-
display: inline-flex;
199-
align-items: center;
200-
justify-content: center;
201-
width: 100%;
202-
min-height: 2.5rem;
203-
padding: calc(var(--button-md-padding-y) - 1px) var(--button-md-padding-x);
204-
font-size: var(--button-text-size-default);
205-
color: var(--form-checkbox-radio-card-primary-default-text);
206-
text-align: center;
207-
border: 1px solid var(--form-checkbox-radio-card-secondary-default-border);
208-
border-radius: var(--form-checkbox-radio-card-radius);
209-
210-
&:hover {
211-
color: var(--form-checkbox-radio-card-secondary-hover-text);
212-
cursor: pointer;
213-
border: 1px solid var(--form-checkbox-radio-card-secondary-hover-border);
214-
}
215-
216-
&--selected {
217-
color: var(--form-checkbox-radio-card-secondary-selected-text);
218-
border: var(--general-selected-border-width) solid var(--form-checkbox-radio-card-secondary-selected-border);
258+
&__grid-button {
259+
display: inline-flex;
260+
align-items: center;
261+
justify-content: center;
262+
width: 100%;
263+
min-height: 2.5rem;
264+
padding: calc(var(--button-md-padding-y) - 1px) var(--button-md-padding-x);
265+
font-size: var(--button-text-size-default);
266+
color: var(--form-checkbox-radio-card-primary-default-text);
267+
text-align: center;
268+
border: 1px solid var(--form-checkbox-radio-card-secondary-default-border);
269+
border-radius: var(--form-checkbox-radio-card-radius);
219270

220271
&:hover {
221-
border-width: 2px;
272+
color: var(--form-checkbox-radio-card-secondary-hover-text);
273+
cursor: pointer;
274+
border: 1px solid var(--form-checkbox-radio-card-secondary-hover-border);
275+
}
276+
277+
&--selected {
278+
color: var(--form-checkbox-radio-card-secondary-selected-text);
279+
border: var(--general-selected-border-width) solid var(--form-checkbox-radio-card-secondary-selected-border);
280+
281+
&:hover {
282+
border-width: 2px;
283+
}
222284
}
223285
}
224286
}

src/tedi/components/form/date-calendar/date-calendar.tsx

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DateRange, DayPicker, DayPickerProps, Locale, Matcher, OnSelectHandler
44
import { UnknownType } from 'src/tedi/types/commonTypes';
55

66
import { CalendarView, DateFieldMode } from '../date-field/date-field';
7+
import { TimePicker } from '../time-picker/time-picker';
78
import { CalendarHeader } from './components/date-calendar-header/date-calendar-header';
89
import { MonthGrid } from './components/date-calendar-month-grid/date-calendar-month-grid';
910
import { YearGrid } from './components/date-calendar-year-grid/date-calendar-year-grid';
@@ -84,6 +85,27 @@ export interface DateCalendarProps extends Omit<DayPickerProps, 'mode' | 'select
8485
* Optional additional CSS class for the calendar container.
8586
*/
8687
className?: string;
88+
/**
89+
* Show a time picker below the calendar for selecting hours and minutes.
90+
* @default false
91+
*/
92+
showTimePicker?: boolean;
93+
/**
94+
* Optional initial time value as "HH:mm" string.
95+
*/
96+
time?: string;
97+
/**
98+
* Callback fired when time changes.
99+
*/
100+
onTimeChange?: (time: string) => void;
101+
/**
102+
* Slots for custom content in different areas of the calendar.
103+
*/
104+
slots?: {
105+
side?: React.ReactNode;
106+
footer?: React.ReactNode;
107+
header?: React.ReactNode;
108+
};
87109
}
88110

89111
export const DateCalendar = ({
@@ -104,10 +126,20 @@ export const DateCalendar = ({
104126
handleSelect,
105127
applyValue,
106128
className,
129+
showTimePicker = false,
130+
time,
131+
onTimeChange,
132+
slots,
107133
...dayPickerProps
108134
}: DateCalendarProps) => {
109135
return (
110-
<div className={styles['tedi-date-calendar']}>
136+
<div
137+
className={classNames(
138+
styles['tedi-date-calendar'],
139+
showTimePicker && styles['tedi-date-calendar--with-timepicker'],
140+
className
141+
)}
142+
>
111143
{(view === 'years' || calendarView === 'years') && (
112144
<YearGrid
113145
currentMonth={currentMonth}
@@ -139,56 +171,68 @@ export const DateCalendar = ({
139171
)}
140172

141173
{view === 'days' && (
142-
<DayPicker
143-
{...dayPickerProps}
144-
mode={mode}
145-
selected={value as UnknownType}
146-
locale={locale}
147-
month={currentMonth}
148-
onMonthChange={setCurrentMonth}
149-
showOutsideDays={showOutsideDays}
150-
disabled={disabledMatchers?.length ? disabledMatchers : undefined}
151-
required={required}
152-
components={{
153-
MonthCaption: (props) => (
154-
<CalendarHeader
155-
{...props}
156-
monthYearSelectGrid={monthYearSelectGrid}
157-
onOpenMonthGrid={() => setView('months')}
158-
onOpenYearGrid={() => setView('years')}
159-
/>
160-
),
161-
Nav: () => <></>,
162-
}}
163-
footer={footer}
164-
classNames={{
165-
root: classNames(styles['tedi-date-calendar'], className),
166-
month_caption: styles['tedi-date-calendar__caption'],
167-
head: styles['tedi-date-calendar__head'],
168-
row: styles['tedi-date-calendar__row'],
169-
day: styles['tedi-date-calendar__day'],
170-
selected: styles['tedi-date-calendar__day--selected'],
171-
weekday: styles['tedi-date-calendar__weekday'],
172-
outside: styles['tedi-date-calendar__outside-days'],
173-
range_start: styles['tedi-date-calendar__range-start'],
174-
range_middle: styles['tedi-date-calendar__range-middle'],
175-
range_end: styles['tedi-date-calendar__range-end'],
176-
today: styles['tedi-date-calendar__today'],
177-
disabled: styles['tedi-date-calendar__disabled'],
178-
month: styles['tedi-date-calendar__month'],
179-
months: styles['tedi-date-calendar__months-container'],
180-
footer: styles['tedi-date-calendar__footer'],
181-
week_number: styles['tedi-date-calendar__week-number'],
182-
}}
183-
modifiers={{
184-
available:
185-
availableDays instanceof Function
186-
? availableDays
187-
: (d) => availableDays?.some((day) => day.toDateString() === d.toDateString()) ?? false,
188-
}}
189-
modifiersClassNames={{ available: styles['tedi-date-calendar__available-day'] }}
190-
onSelect={handleSelect}
191-
/>
174+
<div className={styles['tedi-date-calendar__layout']}>
175+
<div className={styles['tedi-date-calendar__main']}>
176+
<DayPicker
177+
{...dayPickerProps}
178+
mode={mode}
179+
selected={value as UnknownType}
180+
locale={locale}
181+
month={currentMonth}
182+
onMonthChange={setCurrentMonth}
183+
showOutsideDays={showOutsideDays}
184+
disabled={disabledMatchers?.length ? disabledMatchers : undefined}
185+
required={required}
186+
components={{
187+
MonthCaption: (props) => (
188+
<CalendarHeader
189+
{...props}
190+
monthYearSelectGrid={monthYearSelectGrid}
191+
onOpenMonthGrid={() => setView('months')}
192+
onOpenYearGrid={() => setView('years')}
193+
/>
194+
),
195+
Nav: () => <></>,
196+
}}
197+
footer={slots?.footer || footer}
198+
classNames={{
199+
root: styles['tedi-date-calendar'],
200+
month_caption: styles['tedi-date-calendar__caption'],
201+
head: styles['tedi-date-calendar__head'],
202+
row: styles['tedi-date-calendar__row'],
203+
day: styles['tedi-date-calendar__day'],
204+
selected: styles['tedi-date-calendar__day--selected'],
205+
weekday: styles['tedi-date-calendar__weekday'],
206+
outside: styles['tedi-date-calendar__outside-days'],
207+
range_start: styles['tedi-date-calendar__range-start'],
208+
range_middle: styles['tedi-date-calendar__range-middle'],
209+
range_end: styles['tedi-date-calendar__range-end'],
210+
today: styles['tedi-date-calendar__today'],
211+
disabled: styles['tedi-date-calendar__disabled'],
212+
month: styles['tedi-date-calendar__month'],
213+
months: styles['tedi-date-calendar__months-container'],
214+
footer: styles['tedi-date-calendar__footer'],
215+
week_number: styles['tedi-date-calendar__week-number'],
216+
}}
217+
modifiers={{
218+
available:
219+
availableDays instanceof Function
220+
? availableDays
221+
: (d) => availableDays?.some((day) => day.toDateString() === d.toDateString()) ?? false,
222+
}}
223+
modifiersClassNames={{ available: styles['tedi-date-calendar__available-day'] }}
224+
onSelect={handleSelect}
225+
/>
226+
</div>
227+
228+
{slots?.side && <div className={styles['tedi-date-calendar__side']}>{slots.side}</div>}
229+
230+
{showTimePicker && (
231+
<div className={styles['tedi-date-calendar__timepicker']}>
232+
<TimePicker value={time} onChange={onTimeChange} stepMinutes={1} />
233+
</div>
234+
)}
235+
</div>
192236
)}
193237
</div>
194238
);

src/tedi/components/form/date-field/date-field.module.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
z-index: var(--z-index-dropdown);
44
min-width: 315px;
55
font-family: var(--family-default);
6-
background: var(--card-background-primary);
7-
border: 1px solid var(--card-border-primary);
8-
border-radius: var(--card-radius-rounded);
9-
box-shadow: 0 1px 5px 0 var(--tedi-alpha-20);
106
}
117

128
.tedi-date-field__textfield {

0 commit comments

Comments
 (0)