@@ -35,6 +35,14 @@ interface RozieMoreLinkSlotCtx {
3535 arg : unknown ;
3636}
3737
38+ interface RozieAllDayContentSlotCtx {
39+ arg : unknown ;
40+ }
41+
42+ interface RozieSlotLaneContentSlotCtx {
43+ arg : unknown ;
44+ }
45+
3846@customElement ( 'rozie-full-calendar' )
3947export default class FullCalendar extends SignalWatcher ( LitElement ) {
4048 static styles = css `
@@ -88,6 +96,12 @@ private _portalContainers = new Set<HTMLElement>();
8896 @state ( ) private _hasSlotMoreLink = false ;
8997 @queryAssignedElements ( { slot : 'moreLink' , flatten : true } ) private _slotMoreLinkElements ! : Element [ ] ;
9098 @property ( { attribute : false } ) moreLink ?: ( scope : { arg : unknown } ) => unknown ;
99+ @state ( ) private _hasSlotAllDayContent = false ;
100+ @queryAssignedElements ( { slot : 'allDayContent' , flatten : true } ) private _slotAllDayContentElements ! : Element [ ] ;
101+ @property ( { attribute : false } ) allDayContent ?: ( scope : { arg : unknown } ) => unknown ;
102+ @state ( ) private _hasSlotSlotLaneContent = false ;
103+ @queryAssignedElements ( { slot : 'slotLaneContent' , flatten : true } ) private _slotSlotLaneContentElements ! : Element [ ] ;
104+ @property ( { attribute : false } ) slotLaneContent ?: ( scope : { arg : unknown } ) => unknown ;
91105
92106 private _disconnectCleanups : Array < ( ) => void > = [ ] ;
93107 // Re-parenting guard: set true once the deferred teardown has actually
@@ -171,6 +185,28 @@ private _portalContainers = new Set<HTMLElement>();
171185 update ( ) ;
172186 }
173187 }
188+
189+ {
190+ const slotEl = this . shadowRoot ?. querySelector ( 'slot[name="allDayContent"]' ) ;
191+ if ( slotEl !== null && slotEl !== undefined ) {
192+ const update = ( ) => { this . _hasSlotAllDayContent = this . _slotAllDayContentElements . length > 0 ; } ;
193+ slotEl . addEventListener ( 'slotchange' , update ) ;
194+ // CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
195+ this . _disconnectCleanups . push ( ( ) => slotEl . removeEventListener ( 'slotchange' , update ) ) ;
196+ update ( ) ;
197+ }
198+ }
199+
200+ {
201+ const slotEl = this . shadowRoot ?. querySelector ( 'slot[name="slotLaneContent"]' ) ;
202+ if ( slotEl !== null && slotEl !== undefined ) {
203+ const update = ( ) => { this . _hasSlotSlotLaneContent = this . _slotSlotLaneContentElements . length > 0 ; } ;
204+ slotEl . addEventListener ( 'slotchange' , update ) ;
205+ // CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
206+ this . _disconnectCleanups . push ( ( ) => slotEl . removeEventListener ( 'slotchange' , update ) ) ;
207+ update ( ) ;
208+ }
209+ }
174210 }
175211
176212 connectedCallback ( ) : void {
@@ -182,6 +218,8 @@ private _portalContainers = new Set<HTMLElement>();
182218 this . _hasSlotWeekNumber = Array . from ( this . children ) . some ( ( el ) => el . getAttribute ( 'slot' ) === 'weekNumber' ) ;
183219 this . _hasSlotNowIndicatorContent = Array . from ( this . children ) . some ( ( el ) => el . getAttribute ( 'slot' ) === 'nowIndicatorContent' ) ;
184220 this . _hasSlotMoreLink = Array . from ( this . children ) . some ( ( el ) => el . getAttribute ( 'slot' ) === 'moreLink' ) ;
221+ this . _hasSlotAllDayContent = Array . from ( this . children ) . some ( ( el ) => el . getAttribute ( 'slot' ) === 'allDayContent' ) ;
222+ this . _hasSlotSlotLaneContent = Array . from ( this . children ) . some ( ( el ) => el . getAttribute ( 'slot' ) === 'slotLaneContent' ) ;
185223 super . connectedCallback ( ) ;
186224 if ( this . hasUpdated && this . _rozieTornDown ) { this . _rozieTornDown = false ; this . _armListeners ( ) ; }
187225 }
@@ -274,6 +312,30 @@ private _portalContainers = new Set<HTMLElement>();
274312 this . _portalContainers . delete ( container ) ;
275313 } ;
276314 } ,
315+ allDayContent : ( container : HTMLElement , scope : { arg : unknown } ) : ( ( ) => void ) => {
316+ const tpl = this . allDayContent ;
317+ if ( typeof tpl !== 'function' ) return ( ) => { } ;
318+ // Spike 004: portal-scope attribute injection.
319+ container . setAttribute ( 'data-rozie-portal-allDayContent' , '5589629a' ) ;
320+ render ( tpl ( scope ) , container ) ;
321+ this . _portalContainers . add ( container ) ;
322+ return ( ) => {
323+ render ( nothing , container ) ;
324+ this . _portalContainers . delete ( container ) ;
325+ } ;
326+ } ,
327+ slotLaneContent : ( container : HTMLElement , scope : { arg : unknown } ) : ( ( ) => void ) => {
328+ const tpl = this . slotLaneContent ;
329+ if ( typeof tpl !== 'function' ) return ( ) => { } ;
330+ // Spike 004: portal-scope attribute injection.
331+ container . setAttribute ( 'data-rozie-portal-slotLaneContent' , '5589629a' ) ;
332+ render ( tpl ( scope ) , container ) ;
333+ this . _portalContainers . add ( container ) ;
334+ return ( ) => {
335+ render ( nothing , container ) ;
336+ this . _portalContainers . delete ( container ) ;
337+ } ;
338+ } ,
277339 } ;
278340
279341 this . _disconnectCleanups . push ( ( ( ) => this . instance ?. destroy ( ) ) ) ;
@@ -488,20 +550,24 @@ private _portalContainers = new Set<HTMLElement>();
488550 } ;
489551 } ;
490552 }
491- // The 6 remaining *Content portal-slots — wired identically to `event`, one
553+ // The 8 remaining *Content portal-slots — wired identically to `event`, one
492554 // per FullCalendar per-cell content hook that fires with the bundled plugins
493555 // (core + daygrid + timegrid + interaction). Each guarded by its own slot so
494- // unfilled slots keep FullCalendar's default rendering.
556+ // unfilled slots keep FullCalendar's default rendering. (9 portal-slots total
557+ // counting `event` above; allDayContent + slotLaneContent are the two timeGrid
558+ // axis/lane hooks added last.)
495559 //
496560 // NOTE the `nowIndicatorContent` slot is named for its FullCalendar engine
497561 // hook (`nowIndicatorContent`) so it does NOT clash with the boolean
498562 // `nowIndicator` PROP — a slot name that equals a declared prop name is now a
499563 // hard compile error (ROZ127 SLOT_PROP_NAME_COLLISION), because Svelte 5
500564 // unifies snippets and props into one `$props` namespace.
501- // The 6 remaining *Content portal-slots — wired identically to `event`, one
565+ // The 8 remaining *Content portal-slots — wired identically to `event`, one
502566 // per FullCalendar per-cell content hook that fires with the bundled plugins
503567 // (core + daygrid + timegrid + interaction). Each guarded by its own slot so
504- // unfilled slots keep FullCalendar's default rendering.
568+ // unfilled slots keep FullCalendar's default rendering. (9 portal-slots total
569+ // counting `event` above; allDayContent + slotLaneContent are the two timeGrid
570+ // axis/lane hooks added last.)
505571 //
506572 // NOTE the `nowIndicatorContent` slot is named for its FullCalendar engine
507573 // hook (`nowIndicatorContent`) so it does NOT clash with the boolean
@@ -580,14 +646,36 @@ private _portalContainers = new Set<HTMLElement>();
580646 } ;
581647 } ;
582648 }
583- // Excluded *Content slots (documented, not gaps): noEventsContent (list-view
584- // only — @fullcalendar/list is not a bundled peer), slotLaneContent (background
585- // lane, no demand), allDayContent (trivial label). Consumers needing those use
586- // the :options passthrough + getApi().
587- // Excluded *Content slots (documented, not gaps): noEventsContent (list-view
588- // only — @fullcalendar/list is not a bundled peer), slotLaneContent (background
589- // lane, no demand), allDayContent (trivial label). Consumers needing those use
590- // the :options passthrough + getApi().
649+ if ( this . allDayContent !== undefined ) {
650+ opts . allDayContent = ( arg : any ) => {
651+ const node = document . createElement ( 'div' ) ;
652+ const dispose = portals . allDayContent ( node , {
653+ arg
654+ } ) ;
655+ return {
656+ domNodes : [ node ] ,
657+ dispose
658+ } ;
659+ } ;
660+ }
661+ if ( this . slotLaneContent !== undefined ) {
662+ opts . slotLaneContent = ( arg : any ) => {
663+ const node = document . createElement ( 'div' ) ;
664+ const dispose = portals . slotLaneContent ( node , {
665+ arg
666+ } ) ;
667+ return {
668+ domNodes : [ node ] ,
669+ dispose
670+ } ;
671+ } ;
672+ }
673+ // The one still-excluded *Content slot (documented, not a gap): noEventsContent
674+ // — list-view only, and @fullcalendar/list is not a bundled engine peer. A
675+ // consumer needing it uses the :options passthrough + getApi().
676+ // The one still-excluded *Content slot (documented, not a gap): noEventsContent
677+ // — list-view only, and @fullcalendar/list is not a bundled engine peer. A
678+ // consumer needing it uses the :options passthrough + getApi().
591679
592680 this . instance = new Calendar ( this . _ref__rozieRoot , opts ) ;
593681 this . instance . render ( ) ;
@@ -643,6 +731,8 @@ private _portalContainers = new Set<HTMLElement>();
643731< slot name ="weekNumber "> </ slot >
644732< slot name ="nowIndicatorContent "> </ slot >
645733< slot name ="moreLink "> </ slot >
734+ < slot name ="allDayContent "> </ slot >
735+ < slot name ="slotLaneContent "> </ slot >
646736` ;
647737 }
648738
0 commit comments