@@ -12,6 +12,7 @@ import { watch } from '../../internal/watch';
1212import cx from 'classix' ;
1313import SolidElement from '../../internal/solid-element' ;
1414import type { SolidFormControl } from '../../internal/solid-element' ;
15+ import type SdPopup from '../popup/popup' ;
1516
1617/**
1718 * @summary Used to enter or select a date or a range of dates using a calendar view.
@@ -274,6 +275,8 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
274275 /** The text value shown in the input, synchronized with selection. */
275276 @state ( ) private inputValue = '' ;
276277
278+ @query ( 'sd-popup' ) popup : SdPopup ;
279+
277280 @query ( '#invalid-message' ) invalidMessage : HTMLDivElement ;
278281
279282 @query ( '#input' ) input : HTMLInputElement ;
@@ -326,6 +329,13 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
326329 this . formControlController . updateValidity ( ) ;
327330 }
328331
332+ @watch ( 'open' , { waitUntilFirstUpdate : true } )
333+ handleOpenChange ( ) {
334+ if ( this . popup ) {
335+ this . popup . active = this . open && ! this . disabled && ! this . visuallyDisabled ;
336+ }
337+ }
338+
329339 disconnectedCallback ( ) {
330340 super . disconnectedCallback ( ) ;
331341 this . removeEventListener ( 'focusin' , this . onFocusIn ) ;
@@ -823,7 +833,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
823833 } ;
824834
825835 show ( ) {
826- if ( this . open || this . disabled || this . visuallyDisabled ) {
836+ if ( this . open || this . disabled || this . visuallyDisabled || this . readonly ) {
827837 this . open = false ;
828838 return ;
829839 }
@@ -851,7 +861,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
851861 } ;
852862
853863 private handleMouseDown ( event : MouseEvent ) {
854- if ( this . visuallyDisabled || this . disabled ) {
864+ if ( this . visuallyDisabled || this . disabled || this . readonly ) {
855865 event . preventDefault ( ) ;
856866 event . stopPropagation ( ) ;
857867 return ;
@@ -869,14 +879,14 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
869879 private handleFocus ( ) {
870880 this . hasFocus = true ;
871881
872- if ( ! this . open && ! this . disabled && ! this . visuallyDisabled ) {
882+ if ( ! this . open && ! this . disabled && ! this . visuallyDisabled && ! this . readonly ) {
873883 this . show ( ) ;
874884 }
875885 this . emit ( 'sd-focus' ) ;
876886 }
877887
878888 private handleInput = ( ev : Event ) => {
879- if ( this . disabled || this . visuallyDisabled ) {
889+ if ( this . disabled || this . visuallyDisabled || this . readonly ) {
880890 ev . preventDefault ?.( ) ;
881891 ev . stopPropagation ?.( ) ;
882892 return ;
@@ -1127,8 +1137,12 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
11271137 this . handleBlur ( ) ;
11281138 } ;
11291139
1130- private handleCurrentPlacement = ( ev : CustomEvent < { placement : 'top' | 'bottom' } > ) => {
1131- this . currentPlacement = ev . detail . placement ;
1140+ private handleCurrentPlacement = ( e : CustomEvent < 'top' | 'bottom' > ) => {
1141+ const incomingPlacement = e . detail ;
1142+
1143+ if ( incomingPlacement ) {
1144+ this . currentPlacement = incomingPlacement ;
1145+ }
11321146 } ;
11331147
11341148 private setMonth ( offset : number ) {
@@ -1216,6 +1230,9 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
12161230 direction : - 1 | 1 ,
12171231 isLastHeaderControl : boolean
12181232 ) => {
1233+ if ( this . disabled || this . visuallyDisabled || this . readonly ) {
1234+ return ;
1235+ }
12191236 // Only the last header control sends focus into the grid on Tab
12201237 if ( ev . key === 'Tab' && ! ev . shiftKey ) {
12211238 if ( isLastHeaderControl ) {
@@ -1236,6 +1253,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
12361253 } ;
12371254
12381255 private selectSingleDate ( d : Date ) {
1256+ if ( this . readonly ) return ;
12391257 if ( this . isDisabled ( d ) ) return ;
12401258
12411259 const localMidnight = DateUtils . startOfDayLocal ( d ) ;
@@ -1263,6 +1281,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
12631281 }
12641282
12651283 private selectRangeDate ( d : Date ) {
1284+ if ( this . readonly ) return ;
12661285 const day = DateUtils . startOfDayLocal ( d ) ;
12671286 const rs = this . rangeStart ? DateUtils . parseLocalISO ( this . rangeStart ) : null ;
12681287 const re = this . rangeEnd ? DateUtils . parseLocalISO ( this . rangeEnd ) : null ;
@@ -1402,12 +1421,13 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
14021421 }
14031422
14041423 private selectDate ( d : Date ) {
1424+ if ( this . readonly ) return ;
14051425 if ( this . range ) this . selectRangeDate ( d ) ;
14061426 else this . selectSingleDate ( d ) ;
14071427 }
14081428
14091429 private onKeyDown = ( ev : KeyboardEvent ) => {
1410- if ( this . disabled || this . visuallyDisabled ) {
1430+ if ( this . disabled || this . visuallyDisabled || this . readonly ) {
14111431 ev . preventDefault ( ) ;
14121432 ev . stopPropagation ( ) ;
14131433 return ;
@@ -1487,7 +1507,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
14871507 } ;
14881508
14891509 private handleInputKeyDown = ( ev : KeyboardEvent ) => {
1490- if ( ( this . disabled || this . visuallyDisabled ) && ev . key !== 'Tab' ) {
1510+ if ( ( this . disabled || this . visuallyDisabled || this . readonly ) && ev . key !== 'Tab' ) {
14911511 ev . preventDefault ( ) ;
14921512 ev . stopPropagation ( ) ;
14931513 return ;
@@ -1513,7 +1533,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
15131533 return ;
15141534 }
15151535
1516- if ( ev . key === 'Enter' && ! this . open && ! this . disabled && ! this . visuallyDisabled ) {
1536+ if ( ev . key === 'Enter' && ! this . open && ! this . disabled && ! this . visuallyDisabled && ! this . readonly ) {
15171537 ev . preventDefault ( ) ;
15181538 this . show ( ) ;
15191539 }
@@ -1585,7 +1605,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
15851605
15861606 /** Mouse enter on a day: updates preview end when selecting a range. */
15871607 private onDayMouseEnter ( day : Date ) {
1588- if ( ! this . range ) return ;
1608+ if ( ! this . range || this . readonly ) return ;
15891609 const rs = this . rangeStart ? DateUtils . parseLocalISO ( this . rangeStart ) : null ;
15901610 const re = this . rangeEnd ? DateUtils . parseLocalISO ( this . rangeEnd ) : null ;
15911611 if ( rs && ! re ) {
@@ -1622,9 +1642,14 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
16221642 return html `
16231643 <div
16241644 part= "datepicker"
1625- class = "w-[284px] z-50 absolute top-full bg-white border-2 border-t-0 border-primary py-3 px-4 ${ this . open
1626- ? 'block rounded-bl-default rounded-br-default'
1627- : 'hidden' } ${ this . alignment === 'left' ? 'left-0' : 'right-0' } "
1645+ class = ${ cx (
1646+ 'w-[284px] z-50 bg-white py-3 px-4' ,
1647+ this . open ? 'block' : 'hidden' ,
1648+ this . currentPlacement ?. startsWith ( 'bottom' )
1649+ ? 'border-r-2 border-b-2 border-l-2 rounded-br-default rounded-bl-default'
1650+ : 'border-r-2 border-t-2 border-l-2 rounded-tr-default rounded-tl-default' ,
1651+ 'border-primary'
1652+ ) }
16281653 >
16291654 <div class= "flex flex-row items-center w-full justify-between mb-3" part = "header" >
16301655 <div class= "flex items-center" >
@@ -1814,8 +1839,10 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
18141839 aria- colindex= ${ colIndex + 1 }
18151840 aria- labelledby= ${ 'col-' + ( colIndex + 1 ) }
18161841 .tabIndex = ${ tabIndex }
1817- ?dis abled= ${ disabled || this . disabled }
1818- aria- dis abled= ${ disabled || this . visuallyDisabled || this . disabled ? 'true' : 'false' }
1842+ ?dis abled= ${ disabled || this . disabled || this . readonly }
1843+ aria- dis abled= ${ disabled || this . visuallyDisabled || this . disabled || this . readonly
1844+ ? 'true'
1845+ : 'false' }
18191846 aria- selected= ${ isSelectedSingle || inSelectedRange || isRangeStart || isRangeEnd
18201847 ? 'true'
18211848 : 'false' }
@@ -1936,7 +1963,11 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
19361963 'absolute top-0 w-full h-full pointer-events-none border rounded-default z-10 transition-[border] duration-medium ease-in-out' ,
19371964 borderColor ,
19381965 this . open && this . alignment === 'left' ? 'rounded-bl-none' : '' ,
1939- this . open && this . alignment === 'right' ? 'rounded-br-none' : ''
1966+ this . open && this . alignment === 'right' ? 'rounded-br-none' : '' ,
1967+ this . open &&
1968+ ( this . currentPlacement ?. startsWith ( 'bottom' )
1969+ ? 'rounded-bl-none rounded-br-none'
1970+ : 'rounded-tl-none rounded-tr-none' )
19401971 ) }
19411972 >
19421973 ${ hasLabel && this . floatingLabel
@@ -1973,8 +2004,13 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
19732004 </ div>
19742005 <sd- popup
19752006 @sd-current-placement = ${ this . handleCurrentPlacement }
1976- class= ${ cx ( 'inline-flex relative w-full' ) }
1977- sync= "width"
2007+ class= ${ cx (
2008+ 'inline-flex relative w-full' ,
2009+ this . currentPlacement ?. startsWith ( 'bottom' ) ? 'origin-top' : 'origin-bottom'
2010+ ) }
2011+ placement= ${ this . alignment === 'left' ? `${ this . placement } -start` : `${ this . placement } -end` }
2012+ flip
2013+ shift
19782014 auto - size= "vertical"
19792015 auto-size-padding = "10"
19802016 expor tparts= "popup: popup__content, "
@@ -2047,9 +2083,8 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
20472083 name=" calendar"
20482084 @click=${ this . show }
20492085 ></sd-icon>
2050-
2051- ${ this . renderCalendar ( ) }
20522086 </div>
2087+ ${ this . renderCalendar ( ) }
20532088 </sd-popup>
20542089 </div>
20552090 <slot
0 commit comments