1
- import { Component , Element , h , JSX , Listen , State } from '@stencil/core' ;
1
+ import {
2
+ Component ,
3
+ Element ,
4
+ forceUpdate ,
5
+ h ,
6
+ JSX ,
7
+ Listen ,
8
+ Method ,
9
+ Prop ,
10
+ State
11
+ } from '@stencil/core' ;
2
12
import { IWeekElement , GuxCalendarDayOfWeek } from './gux-calendar.types' ;
3
13
import {
4
14
getWeekdays ,
@@ -27,6 +37,17 @@ export class GuxCalendar {
27
37
@Element ( )
28
38
root : HTMLElement ;
29
39
40
+ // The start day of the week based on user's locale
41
+ // Some locales will have the start day of the week be different than others
42
+ @Prop ( )
43
+ startDayOfWeek : GuxCalendarDayOfWeek ;
44
+
45
+ @Method ( )
46
+ async guxForceUpdate ( ) : Promise < void > {
47
+ this . readSlottedInputState ( ) ;
48
+ forceUpdate ( this . root ) ;
49
+ }
50
+
30
51
/**
31
52
* The date that receives focus when selecting a specific day. This also
32
53
* determines what month is currently displayed. The corresponding element
@@ -56,14 +77,21 @@ export class GuxCalendar {
56
77
// Total number of dates that will display for each month in the calendar
57
78
private MONTH_DATE_COUNT : number = 42 ;
58
79
59
- // Keeps track of the start day of the week based on user's locale
60
- // Some locales will have the start day of the week be different than others
61
- private startDayOfWeek : GuxCalendarDayOfWeek ;
62
-
63
80
private get slottedInput ( ) : HTMLInputElement {
64
81
return this . root . querySelector ( 'input[type="date"]' ) ;
65
82
}
66
83
84
+ private readSlottedInputState ( ) : void {
85
+ // Set min value from the "min" input prop
86
+ if ( this . slottedInput . min ) {
87
+ this . minValue = Temporal . PlainDate . from ( this . slottedInput . min ) ;
88
+ }
89
+ // Set max value from the "max" input prop
90
+ if ( this . slottedInput . max ) {
91
+ this . maxValue = Temporal . PlainDate . from ( this . slottedInput . max ) ;
92
+ }
93
+ }
94
+
67
95
async componentWillLoad ( ) : Promise < void > {
68
96
if ( ! this . slottedInput ) {
69
97
logError (
@@ -75,13 +103,6 @@ export class GuxCalendar {
75
103
this . locale = getDesiredLocale ( this . root ) ;
76
104
77
105
// Set start day of week
78
- const startDayOfWeek = this . slottedInput . getAttribute ( 'start-day-of-week' ) ;
79
- if ( startDayOfWeek ?. length ) {
80
- this . startDayOfWeek = parseInt (
81
- startDayOfWeek ,
82
- 10
83
- ) as GuxCalendarDayOfWeek ;
84
- }
85
106
this . startDayOfWeek = this . startDayOfWeek || getFirstDayOfWeek ( this . locale ) ;
86
107
87
108
this . i18n = await buildI18nForComponent ( this . root , translationResources ) ;
@@ -91,71 +112,29 @@ export class GuxCalendar {
91
112
this . focusDate = selectedDate ;
92
113
}
93
114
94
- // Set min value from the "min" input prop
95
- if ( this . slottedInput . min ) {
96
- this . minValue = Temporal . PlainDate . from ( this . slottedInput . min ) ;
97
- }
98
- // Set max value from the "max" input prop
99
- if ( this . slottedInput . max ) {
100
- this . maxValue = Temporal . PlainDate . from ( this . slottedInput . max ) ;
101
- }
102
- }
103
-
104
- async componentDidRender ( ) {
105
- // Because the selection state may be set on slotted attributes, we have
106
- // to update it after rendering.
107
- this . renderDateSelection ( ) ;
115
+ this . readSlottedInputState ( ) ;
108
116
}
109
117
110
118
/**
111
119
* Finds the `gux-day` element corresponding to the provided
112
- * date string, whether rendered by this component or slotted .
120
+ * date string.
113
121
* @param dateStr The date to find in ISO format
114
122
*/
115
123
private getGuxDayForDate ( dateStr : string ) : HTMLElement | null {
116
- return (
117
- this . root . shadowRoot
118
- // Find the slot for the date
119
- . querySelector < HTMLSlotElement > ( `slot[name="${ dateStr } "]` )
120
- // Get the first of the rendered items for that slot
121
- ?. assignedNodes ( { flatten : true } ) [ 0 ] as HTMLElement
122
- ) ;
124
+ return this . root . shadowRoot . querySelector ( `.day-${ dateStr } ` ) ;
123
125
}
124
126
125
127
private selectDate ( date : Temporal . PlainDate ) : void {
126
128
if ( this . isInvalidDate ( date ) ) {
127
129
return ;
128
130
}
129
- const oldDateStr = this . slottedInput . value ;
130
131
const newDateStr = date . toString ( ) ;
131
132
this . focusDate = date ;
132
133
this . slottedInput . value = newDateStr ;
133
134
134
- afterNextRenderTimeout ( ( ) => {
135
- this . renderDateSelection ( oldDateStr ) ;
136
- } ) ;
137
-
138
135
simulateNativeEvent ( this . root , 'input' ) ;
139
136
}
140
137
141
- /**
142
- * Clears the `gux-selected` attribute on the old element and add it
143
- * to the new element. This enables styling of the selected state in
144
- * the gux-day element in a way that can be used by slotted elements.
145
- */
146
- private renderDateSelection ( oldDateStr ?: string ) : void {
147
- const selectedDate = this . getSelectedDate ( ) ;
148
- if ( oldDateStr ) {
149
- const oldSelectedElement = this . getGuxDayForDate ( oldDateStr ) ;
150
- oldSelectedElement ?. removeAttribute ( 'gux-selected' ) ;
151
- }
152
-
153
- if ( selectedDate ) {
154
- const newSelectedElement = this . getGuxDayForDate ( selectedDate . toString ( ) ) ;
155
- newSelectedElement ?. setAttribute ( 'gux-selected' , '' ) ;
156
- }
157
- }
158
-
159
138
/**
160
139
* Shifts the focused date by the provided interval
161
140
* @param interval A Temporal-compatible interval definition
@@ -347,20 +326,16 @@ export class GuxCalendar {
347
326
{ week . dates . map ( day => {
348
327
const isoDateStr = day . date . toString ( ) ;
349
328
return (
350
- < gux-focus-proxy
329
+ < gux-day-beta
330
+ day = { isoDateStr }
351
331
aria-current = { day . selected ? 'true' : 'false' }
352
332
aria-disabled = { day . disabled ? 'true' : 'false' }
353
333
tabindex = { day . selected || day . focused ? '0' : '-1' }
354
- >
355
- < slot key = { isoDateStr } name = { isoDateStr } >
356
- < gux-day-beta
357
- day = { isoDateStr }
358
- class = { {
359
- 'gux-muted' : ! day . inCurrentMonth || day . disabled
360
- } }
361
- > </ gux-day-beta >
362
- </ slot >
363
- </ gux-focus-proxy >
334
+ class = { `
335
+ ${ ! day . inCurrentMonth || day . disabled ? 'gux-muted' : '' }
336
+ day-${ isoDateStr }
337
+ ` }
338
+ > </ gux-day-beta >
364
339
) as JSX . Element ;
365
340
} ) }
366
341
</ div >
@@ -375,7 +350,11 @@ export class GuxCalendar {
375
350
render ( ) : JSX . Element {
376
351
return (
377
352
< div class = "gux-calendar-beta" >
378
- < slot />
353
+ < slot
354
+ onSlotchange = { ( ) => {
355
+ this . readSlottedInputState ( ) ;
356
+ } }
357
+ />
379
358
{ this . renderHeader ( ) }
380
359
{ this . renderContent ( ) }
381
360
</ div >
0 commit comments