PageSourceSearch

https://scolago.com/Content/JavaScripts/fullcalendar-scheduler-7.0.2/all/global.js

js scolago.com collected 2026-10-01 15:44:37 UTC 327,351 bytes, 6,048 lines download raw bytes

1/*!
2FullCalendar Scheduler (Vanilla JS) v7.0.2
3Docs & License: https://fullcalendar.io/docs/premium
4(c) 2026 Adam Shaw
5*/
6(function (protectedApi, dayGridPlugin, jsxRuntime, publicApi, classNames, compat, timeGridPlugin) {
7    'use strict';
8
9    function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
11    var dayGridPlugin__default = /*#__PURE__*/_interopDefaultLegacy(dayGridPlugin);
12    var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
13    var timeGridPlugin__default = /*#__PURE__*/_interopDefaultLegacy(timeGridPlugin);
14
15    function identity(raw) {
16        return raw;
17    }
18
19    const PRIVATE_ID_PREFIX = '_fc:';
20    const RESOURCE_REFINERS = {
21        id: String,
22        parentId: String,
23        children: identity,
24        title: String,
25        businessHours: identity,
26        extendedProps: identity,
27        // event-ui
28        eventEditable: Boolean,
29        eventStartEditable: Boolean,
30        eventDurationEditable: Boolean,
31        eventConstraint: identity,
32        eventOverlap: Boolean, // can NOT be a func, different from OptionsInput
33        eventAllow: identity,
34        eventClass: protectedApi.refineClassName,
35        eventColor: String,
36        eventContrastColor: String,
37    };
38    /*
39    needs a full store so that it can populate children too
40    */
41    function parseResource(raw, parentId = '', store, context) {
42        let { refined, extra } = protectedApi.refineProps(raw, RESOURCE_REFINERS);
43        let resource = {
44            id: refined.id || (PRIVATE_ID_PREFIX + protectedApi.guid()),
45            parentId: refined.parentId || parentId,
46            title: refined.title || '',
47            businessHours: refined.businessHours ? protectedApi.parseBusinessHours(refined.businessHours, context) : null,
48            ui: protectedApi.createEventUi({
49                editable: refined.eventEditable,
50                startEditable: refined.eventStartEditable,
51                durationEditable: refined.eventDurationEditable,
52                constraint: refined.eventConstraint,
53                overlap: refined.eventOverlap,
54                allow: refined.eventAllow,
55                className: refined.eventClass,
56                color: refined.eventColor,
57                contrastColor: refined.eventContrastColor,
58            }, context),
59            extendedProps: {
60                ...extra,
61                ...refined.extendedProps,
62            },
63        };
64        // help out ResourceApi from having user modify props
65        Object.freeze(resource.ui.className); // might be simple string, but freeze still works
66        Object.freeze(resource.extendedProps);
67        if (!store[resource.id]) { // no duplicate resource ID?
68            store[resource.id] = resource;
69            if (refined.children) {
70                for (let childInput of refined.children) {
71                    parseResource(childInput, resource.id, store, context);
72                }
73            }
74        }
75        return resource;
76    }
77    /*
78    TODO: use this in more places
79    */
80    function getPublicId(id) {
81        if (id.indexOf(PRIVATE_ID_PREFIX) === 0) {
82            return '';
83        }
84        return id;
85    }
86
87    class ResourceApi {
88        constructor(_context, _resource) {
89            this._context = _context;
90            this._resource = _resource;
91        }
92        setProp(name, value) {
93            let oldResource = this._resource;
94            this._context.dispatch({
95                type: 'SET_RESOURCE_PROP',
96                resourceId: oldResource.id,
97                propName: name,
98                propValue: value,
99            });
100            this.sync(oldResource);
101        }
102        setExtendedProp(name, value) {
103            let oldResource = this._resource;
104            this._context.dispatch({
105                type: 'SET_RESOURCE_EXTENDED_PROP',
106                resourceId: oldResource.id,
107                propName: name,
108                propValue: value,
109            });
vendor: 10,365 bytes, lines 110-363
110            this.sync(oldResource);
111        }
112        sync(oldResource) {
113            let context = this._context;
114            let resourceId = oldResource.id;
115            // TODO: what if dispatch didn't complete synchronously?
116            this._resource = context.getCurrentData().resourceStore[resourceId];
117            context.emitter.trigger('resourceChange', {
118                oldResource: new ResourceApi(context, oldResource),
119                resource: this,
120                revert() {
121                    context.dispatch({
122                        type: 'ADD_RESOURCE', // function as a merge. TODO: rename
123                        resourceHash: {
124                            [resourceId]: oldResource,
125                        },
126                    });
127                },
128            });
129        }
130        remove() {
131            let context = this._context;
132            let internalResource = this._resource;
133            let resourceId = internalResource.id;
134            context.dispatch({
135                type: 'REMOVE_RESOURCE',
136                resourceId,
137            });
138            context.emitter.trigger('resourceRemove', {
139                resource: this,
140                revert() {
141                    context.dispatch({
142                        type: 'ADD_RESOURCE', // function as a merge. TODO: rename
143                        resourceHash: {
144                            [resourceId]: internalResource,
145                        },
146                    });
147                },
148            });
149        }
150        getParent() {
151            let context = this._context;
152            let parentId = this._resource.parentId;
153            if (parentId) {
154                return new ResourceApi(context, context.getCurrentData().resourceStore[parentId]);
155            }
156            return null;
157        }
158        getChildren() {
159            let thisResourceId = this._resource.id;
160            let context = this._context;
161            let { resourceStore } = context.getCurrentData();
162            let childApis = [];
163            for (let resourceId in resourceStore) {
164                if (resourceStore[resourceId].parentId === thisResourceId) {
165                    childApis.push(new ResourceApi(context, resourceStore[resourceId]));
166                }
167            }
168            return childApis;
169        }
170        /*
171        this is really inefficient!
172        TODO: make EventApi::resourceIds a hash or keep an index in the Calendar's state
173        */
174        getEvents() {
175            let thisResourceId = this._resource.id;
176            let context = this._context;
177            let { defs, instances } = context.getCurrentData().eventStore;
178            let eventApis = [];
179            for (let instanceId in instances) {
180                let instance = instances[instanceId];
181                let def = defs[instance.defId];
182                if (def.resourceIds.indexOf(thisResourceId) !== -1) { // inefficient!!!
183                    eventApis.push(new protectedApi.EventImpl(context, def, instance));
184                }
185            }
186            return eventApis;
187        }
188        get id() { return getPublicId(this._resource.id); }
189        get title() { return this._resource.title; }
190        get eventConstraint() { return this._resource.ui.constraints[0] || null; } // TODO: better type
191        get eventOverlap() { return this._resource.ui.overlap; }
192        get eventAllow() { return this._resource.ui.allows[0] || null; } // TODO: better type
193        get eventColor() { return this._resource.ui.color; }
194        get eventContrastColor() { return this._resource.ui.contrastColor; }
195        // NOTE: user can't modify these because Object.freeze was called in event-def parsing
196        get eventClass() { return this._resource.ui.className; }
197        get extendedProps() { return this._resource.extendedProps; }
198        toPlainObject(settings = {}) {
199            let internal = this._resource;
200            let { ui } = internal;
201            let publicId = this.id;
202            let res = {};
203            if (publicId) {
204                res.id = publicId;
205            }
206            if (internal.title) {
207                res.title = internal.title;
208            }
209            if (ui.contrastColor) {
210                res.eventContrastColor = ui.contrastColor;
211            }
212            if (ui.contrastColor) {
213                res.eventContrastColor = ui.contrastColor;
214            }
215            if (ui.className) {
216                res.eventClass = ui.className;
217            }
218            if (Object.keys(internal.extendedProps).length) {
219                if (settings.collapseExtendedProps) {
220                    Object.assign(res, internal.extendedProps);
221                }
222                else {
223                    res.extendedProps = internal.extendedProps;
224                }
225            }
226            return res;
227        }
228        toJSON() {
229            return this.toPlainObject();
230        }
231    }
232    function buildResourceApis(resourceStore, context) {
233        let resourceApis = [];
234        for (let resourceId in resourceStore) {
235            resourceApis.push(new ResourceApi(context, resourceStore[resourceId]));
236        }
237        return resourceApis;
238    }
239
240    protectedApi.EventImpl.prototype.getResources = function () {
241        let { calendarApi } = this._context;
242        return this._def.resourceIds.map((resourceId) => calendarApi.getResourceById(resourceId));
243    };
244    protectedApi.EventImpl.prototype.setResources = function (resources) {
245        let resourceIds = [];
246        // massage resources -> resourceIds
247        for (let resource of resources) {
248            let resourceId = null;
249            if (typeof resource === 'string') {
250                resourceId = resource;
251            }
252            else if (typeof resource === 'number') {
253                resourceId = String(resource);
254            }
255            else if (resource instanceof ResourceApi) {
256                resourceId = resource.id; // guaranteed to always have an ID. hmmm
257            }
258            else {
259                protectedApi.warn(`Invalid resource value \`${resource}\`. Expected a resource ID or ResourceApi object.`);
260            }
261            if (resourceId) {
262                resourceIds.push(resourceId);
263            }
264        }
265        this.mutate({
266            standardProps: {
267                resourceIds,
268            },
269        });
270    };
271
272    protectedApi.CalendarApiImpl.prototype.addResource = function (input, scrollTo = true) {
273        let currentState = this.getCurrentData();
274        let resourceHash;
275        let resource;
276        if (input instanceof ResourceApi) {
277            resource = input._resource;
278            resourceHash = { [resource.id]: resource };
279        }
280        else {
281            resourceHash = {};
282            resource = parseResource(input, '', resourceHash, currentState);
283        }
284        this.dispatch({
285            type: 'ADD_RESOURCE',
286            resourceHash,
287        });
288        if (scrollTo) {
289            // TODO: wait til dispatch completes somehow
290            this.trigger('_resourceScrollRequest', resource.id);
291        }
292        let resourceApi = new ResourceApi(currentState, resource);
293        currentState.emitter.trigger('resourceAdd', {
294            resource: resourceApi,
295            revert: () => {
296                this.dispatch({
297                    type: 'REMOVE_RESOURCE',
298                    resourceId: resource.id,
299                });
300            },
301        });
302        return resourceApi;
303    };
304    protectedApi.CalendarApiImpl.prototype.getResourceById = function (id) {
305        id = String(id);
306        let currentState = this.getCurrentData(); // eslint-disable-line react/no-this-in-sfc
307        if (currentState.resourceStore) { // guard against calendar with no resource functionality
308            let rawResource = currentState.resourceStore[id];
309            if (rawResource) {
310                return new ResourceApi(currentState, rawResource);
311            }
312        }
313        return null;
314    };
315    protectedApi.CalendarApiImpl.prototype.getResources = function () {
316        let currentState = this.getCurrentData();
317        let { resourceStore } = currentState;
318        let resourceApis = [];
319        if (resourceStore) { // guard against calendar with no resource functionality
320            for (let resourceId in resourceStore) {
321                resourceApis.push(new ResourceApi(currentState, resourceStore[resourceId]));
322            }
323        }
324        return resourceApis;
325    };
326    protectedApi.CalendarApiImpl.prototype.getTopLevelResources = function () {
327        let currentState = this.getCurrentData();
328        let { resourceStore } = currentState;
329        let resourceApis = [];
330        if (resourceStore) { // guard against calendar with no resource functionality
331            for (let resourceId in resourceStore) {
332                if (!resourceStore[resourceId].parentId) {
333                    resourceApis.push(new ResourceApi(currentState, resourceStore[resourceId]));
334                }
335            }
336        }
337        return resourceApis;
338    };
339    protectedApi.CalendarApiImpl.prototype.refetchResources = function () {
340        this.dispatch({
341            type: 'REFETCH_RESOURCES',
342        });
343    };
344    function transformDatePoint(dateSpan, context) {
345        return dateSpan.resourceId ?
346            { resource: context.calendarApi.getResourceById(dateSpan.resourceId) } :
347            {};
348    }
349    function transformDateSpan(dateSpan, context) {
350        return dateSpan.resourceId ?
351            { resource: context.calendarApi.getResourceById(dateSpan.resourceId) } :
352            {};
353    }
354
355    const UPGRADE_WINDOW = 365 + 7; // days. 1 week leeway, for tz shift reasons too
356    const INVALID_LICENSE_URL = 'https://fullcalendar.io/docs/schedulerLicenseKey#invalid';
357    const OUTDATED_LICENSE_URL = 'https://fullcalendar.io/docs/schedulerLicenseKey#outdated';
358    const PRESET_LICENSE_KEYS = [
359        'AGPL-My-Frontend-And-Backend-Are-Open-Source',
360        'CC-Attribution-NonCommercial-NoDerivatives',
361    ];
362    function buildLicenseWarning(context) {
363        const { options, pluginHooks } = context;
364        const key = options.schedulerLicenseKey;
365        const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
366        if (!isImmuneUrl(currentUrl)) {
367            const status = processLicenseKey(key, pluginHooks.premiumReleaseDate);
368            let statusText;
369            let statusUrl;
370            if (status === 'outdated') {
371                statusText = 'Your license key is too old to work with this version.';
372                statusUrl = OUTDATED_LICENSE_URL;
373            }
374            else {
375                statusText = 'Your license key is invalid.';
376                statusUrl = INVALID_LICENSE_URL;
377            }
378            if (status !== 'valid') {
379                return (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.popoverClass, classNames__default["default"].popoverZ, classNames__default["default"].abs, classNames__default["default"].noPadding), style: {
380                        bottom: 0,
381                        left: 0, // TODO: use direction-aware start
382                        minWidth: 'auto',
383                    }, children: jsxRuntime.jsxs("div", { style: {
384                            padding: '5px',
385                            fontSize: '12px',
386                            lineHeight: '1em',
387                        }, children: [statusText, ' ', jsxRuntime.jsx("a", { href: statusUrl, target: '_blank', style: {
388                                    textDecoration: 'underline',
389                                }, children: "More info" })] }) }));
390            }
391        }
392        return null;
393    }
394    /*
395    This decryption is not meant to be bulletproof. Just a way to remind about an upgrade.
396    */
397    function processLicenseKey(key, premiumReleaseDate) {
398        if (PRESET_LICENSE_KEYS.indexOf(key) !== -1) {
399            return 'valid';
400        }
401        const parts = (key || '').match(/^(\d+)-fcs-(\d+)$/);
402        if (parts && (parts[1].length === 10)) {
403            const purchaseDate = new Date(parseInt(parts[2], 10) * 1000);
404            const releaseDate = protectedApi.config.mockSchedulerReleaseDate || premiumReleaseDate;
405            if (protectedApi.isValidDate(releaseDate)) { // token won't be replaced in dev mode
406                const minPurchaseDate = protectedApi.addDays(releaseDate, -UPGRADE_WINDOW);
407                if (minPurchaseDate < purchaseDate) {
408                    return 'valid';
409                }
410                return 'outdated';
411            }
412        }
413        return 'invalid';
414    }
415    function isImmuneUrl(url) {
416        return /\w+:\/\/fullcalendar\.io\/|\/examples\/[\w-]+\.html$/.test(url);
417    }
418
419    const OPTION_REFINERS = {
420        // Premium Common
421        // --------------
422        schedulerLicenseKey: String,
423        virtualization: Boolean,
424        // Resource
425        // --------
426        initialResources: protectedApi.identity,
427        resources: protectedApi.identity,
428        eventResourceEditable: Boolean,
429        refetchResourcesOnNavigate: Boolean,
430        resourceOrder: protectedApi.parseFieldSpecs,
431        filterResourcesWithEvents: Boolean,
432        resourceGroupField: String,
433        resourcesInitiallyExpanded: Boolean,
434        datesAboveResources: Boolean,
435        needsResourceData: Boolean, // internal-only
436        // Resource DayGrid
437        // ----------------
438        resourceDayHeaderClass: protectedApi.refineClassNameGenerator,
439        resourceDayHeaderInnerClass: protectedApi.refineClassNameGenerator,
440        resourceDayHeaderContent: protectedApi.identity,
441        resourceDayHeaderDidMount: protectedApi.identity,
442        resourceDayHeaderWillUnmount: protectedApi.identity,
443        resourceDayHeaderAlign: protectedApi.identity,
444        // stickiness for cell-inner-contents laterally. experimental settings
445        _resourceDayHeaderSticky: protectedApi.identity,
446        // Resource Timeline
447        // -----------------
448        printMaxRows: Number,
449        resourceColumnsWidth: protectedApi.identity,
450        resourceColumns: protectedApi.identity,
451        resourceColumnDividerClass: protectedApi.refineClassName,
452        // datagrid super-header & normal column headers
453        resourceColumnHeaderClass: protectedApi.refineClassNameGenerator,
454        resourceColumnHeaderInnerClass: protectedApi.refineClassNameGenerator,
455        resourceColumnResizerClass: protectedApi.refineClassNameGenerator,
456        resourceColumnHeaderContent: protectedApi.identity,
457        resourceColumnHeaderDidMount: protectedApi.identity,
458        resourceColumnHeaderWillUnmount: protectedApi.identity,
459        resourceHeaderRowClass: protectedApi.refineClassName,
460        // For both resources & resource groups
461        resourceRowClass: protectedApi.refineClassName,
462        // datagrid cells, for both resources & resource-GROUP
463        resourceCellClass: protectedApi.refineClassNameGenerator,
464        resourceCellInnerClass: protectedApi.refineClassNameGenerator,
465        resourceCellContent: protectedApi.identity,
466        resourceCellDidMount: protectedApi.identity,
467        resourceCellWillUnmount: protectedApi.identity,
468        // datagrid, for resource-GROUP entire row
469        resourceGroupHeaderClass: protectedApi.refineClassNameGenerator,
470        resourceGroupHeaderInnerClass: protectedApi.refineClassNameGenerator,
471        resourceGroupHeaderContent: protectedApi.identity,
472        resourceGroupHeaderDidMount: protectedApi.identity,
473        resourceGroupHeaderWillUnmount: protectedApi.identity,
474        // timeline lane, for resource-GROUP
475        resourceGroupLaneClass: protectedApi.refineClassNameGenerator,
476        resourceGroupLaneInnerClass: protectedApi.refineClassNameGenerator,
477        resourceGroupLaneContent: protectedApi.identity,
478        resourceGroupLaneDidMount: protectedApi.identity,
479        resourceGroupLaneWillUnmount: protectedApi.identity,
480        // timeline, lane, for resource
481        resourceLaneClass: protectedApi.refineClassNameGenerator,
482        resourceLaneDidMount: protectedApi.identity,
483        resourceLaneWillUnmount: protectedApi.identity,
484        resourceLaneTopClass: protectedApi.refineClassNameGenerator,
485        resourceLaneTopContent: protectedApi.identity,
486        resourceLaneBottomClass: protectedApi.refineClassNameGenerator,
487        resourceLaneBottomContent: protectedApi.identity,
488        resourceIndentClass: protectedApi.refineClassName,
489        resourceExpanderClass: protectedApi.refineClassNameGenerator,
490        resourceExpanderContent: protectedApi.identity,
491        // Timeline
492        // --------
493        timelineTopClass: protectedApi.refineClassName,
494        timelineBottomClass: protectedApi.refineClassName,
495    };
496    const OPTION_DEFAULTS = {
497        // Resource Timeline
498        // -----------------
499        printMaxRows: 1000,
500        // Resource DayGrid
501        // ----------------
502        // resourceDayHeaderAlign: 'start' as const, --- this default was inlined due to plugin ordering problems
503        _resourceDayHeaderSticky: true,
504    };
505    const LISTENER_REFINERS = {
506        // Resource
507        // --------
508        resourcesSet: protectedApi.identity,
509        resourceAdd: protectedApi.identity,
510        resourceChange: protectedApi.identity,
511        resourceRemove: protectedApi.identity,
512        // internal
513        _resourceScrollRequest: protectedApi.identity,
514    };
515
516    var premiumCommonPlugin = {
517        name: 'common',
518        premiumReleaseDate: '2026-07-24',
519        optionRefiners: OPTION_REFINERS,
520        optionDefaults: OPTION_DEFAULTS,
521        listenerRefiners: LISTENER_REFINERS,
522        viewContainerAppends: [buildLicenseWarning],
523    };
524
525    function massageEventDragMutation(eventMutation, hit0, hit1) {
526        let resource0 = hit0.dateSpan.resourceId;
vendor: 5,702 bytes, lines 527-660
527        let resource1 = hit1.dateSpan.resourceId;
528        if (resource0 && resource1 &&
529            resource0 !== resource1) {
530            eventMutation.resourceMutation = {
531                matchResourceId: resource0,
532                setResourceId: resource1,
533            };
534        }
535    }
536    /*
537    TODO: all this would be much easier if we were using a hash!
538    */
539    function applyEventDefMutation(eventDef, mutation, context) {
540        let resourceMutation = mutation.resourceMutation;
541        if (resourceMutation && computeResourceEditable(eventDef, context)) {
542            let index = eventDef.resourceIds.indexOf(resourceMutation.matchResourceId);
543            if (index !== -1) {
544                let resourceIds = eventDef.resourceIds.slice(); // copy
545                resourceIds.splice(index, 1); // remove
546                if (resourceIds.indexOf(resourceMutation.setResourceId) === -1) { // not already in there
547                    resourceIds.push(resourceMutation.setResourceId); // add
548                }
549                eventDef.resourceIds = resourceIds;
550            }
551        }
552    }
553    /*
554    HACK
555    TODO: use EventUi system instead of this
556    */
557    function computeResourceEditable(eventDef, context) {
558        let { resourceEditable } = eventDef;
559        if (resourceEditable == null) {
560            let source = eventDef.sourceId && context.getCurrentData().eventSources[eventDef.sourceId];
561            if (source) {
562                resourceEditable = source.extendedProps.resourceEditable; // used the Source::extendedProps hack
563            }
564            if (resourceEditable == null) {
565                resourceEditable = context.options.eventResourceEditable;
566                if (resourceEditable == null) {
567                    resourceEditable = context.options.editable; // TODO: use defaults system instead
568                }
569            }
570        }
571        return resourceEditable;
572    }
573    function transformEventDrop(mutation, context) {
574        let { resourceMutation } = mutation;
575        if (resourceMutation) {
576            let { calendarApi } = context;
577            return {
578                oldResource: calendarApi.getResourceById(resourceMutation.matchResourceId),
579                newResource: calendarApi.getResourceById(resourceMutation.setResourceId),
580            };
581        }
582        return {
583            oldResource: null,
584            newResource: null,
585        };
586    }
587
588    class ResourceDataAdder {
589        constructor() {
590            this.filterResources = protectedApi.memoize(filterResources);
591        }
592        transform(viewProps, calendarProps) {
593            if (calendarProps.viewSpec.optionDefaults.needsResourceData) {
594                return {
595                    resourceStore: this.filterResources(calendarProps.resourceStore, calendarProps.options.filterResourcesWithEvents, calendarProps.eventStore, calendarProps.dateProfile.activeRange),
596                    resourceEntityExpansions: calendarProps.resourceEntityExpansions,
597                };
598            }
599            return null;
600        }
601    }
602    function filterResources(resourceStore, doFilterResourcesWithEvents, eventStore, activeRange) {
603        if (doFilterResourcesWithEvents) {
604            let instancesInRange = filterEventInstancesInRange(eventStore.instances, activeRange);
605            let hasEvents = computeHasEvents(instancesInRange, eventStore.defs);
606            Object.assign(hasEvents, computeAncestorHasEvents(hasEvents, resourceStore));
607            return protectedApi.filterHash(resourceStore, (resource, resourceId) => hasEvents[resourceId]);
608        }
609        return resourceStore;
610    }
611    function filterEventInstancesInRange(eventInstances, activeRange) {
612        return protectedApi.filterHash(eventInstances, (eventInstance) => protectedApi.rangesIntersect(eventInstance.range, activeRange));
613    }
614    function computeHasEvents(eventInstances, eventDefs) {
615        let hasEvents = {};
616        for (let instanceId in eventInstances) {
617            let instance = eventInstances[instanceId];
618            for (let resourceId of eventDefs[instance.defId].resourceIds) {
619                hasEvents[resourceId] = true;
620            }
621        }
622        return hasEvents;
623    }
624    /*
625    mark resources as having events if any of their ancestors have them
626    NOTE: resourceStore might not have all the resources that hasEvents{} has keyed
627    */
628    function computeAncestorHasEvents(hasEvents, resourceStore) {
629        let res = {};
630        for (let resourceId in hasEvents) {
631            let resource;
632            while ((resource = resourceStore[resourceId])) {
633                resourceId = resource.parentId; // now functioning as the parentId
634                if (resourceId) {
635                    res[resourceId] = true;
636                }
637                else {
638                    break;
639                }
640            }
641        }
642        return res;
643    }
644    /*
645    for making sure events that have editable resources are always draggable in resource views
646    */
647    function transformIsDraggable(val, eventDef, eventUi, context) {
648        if (!val) {
649            let state = context.getCurrentData();
650            let viewSpec = state.viewSpecs[state.currentViewType];
651            if (viewSpec.optionDefaults.needsResourceData) {
652                if (computeResourceEditable(eventDef, context)) {
653                    return true;
654                }
655            }
656        }
657        return val;
658    }
659
660    // for when non-resource view should be given EventUi info (for event coloring/constraints based off of resource data)
661    class ResourceEventConfigAdder {
662        constructor() {
663            this.buildResourceEventUis = protectedApi.memoize(buildResourceEventUis, protectedApi.isPropsEqualShallow);
664            this.injectResourceEventUis = protectedApi.memoize(injectResourceEventUis);
665        }
666        transform(viewProps, calendarProps) {
667            if (!calendarProps.viewSpec.optionDefaults.needsResourceData) {
668                return {
669                    eventUiBases: this.injectResourceEventUis(viewProps.eventUiBases, viewProps.eventStore.defs, this.buildResourceEventUis(calendarProps.resourceStore)),
670                };
671            }
672            return null;
673        }
674    }
675    function buildResourceEventUis(resourceStore) {
676        return protectedApi.mapHash(resourceStore, (resource) => resource.ui);
677    }
678    function injectResourceEventUis(eventUiBases, eventDefs, resourceEventUis) {
679        return protectedApi.mapHash(eventUiBases, (eventUi, defId) => {
680            if (defId) { // not the '' key
681                return injectResourceEventUi(eventUi, eventDefs[defId], resourceEventUis);
682            }
683            return eventUi;
684        });
685    }
686    function injectResourceEventUi(origEventUi, eventDef, resourceEventUis) {
687        let parts = [];
688        // first resource takes precedence, which fights with the ordering of combineEventUis, thus the unshifts
689        for (let resourceId of eventDef.resourceIds) {
690            if (resourceEventUis[resourceId]) {
691                parts.unshift(resourceEventUis[resourceId]);
692            }
693        }
694        parts.unshift(origEventUi);
695        return protectedApi.combineEventUis(parts);
696    }
697
698    const arrayResourceSource = {
699        ignoreRange: true,
700        parseMeta(refined) {
701            if (Array.isArray(refined.resources)) {
702                return refined.resources;
703            }
704            return null;
705        },
706        fetch(arg, successCallback) {
707            successCallback({
708                rawResources: arg.resourceSource.meta,
709            });
710        },
711    };
712
713    const jsonFeedResourceSource = {
714        parseMeta(refined) {
715            if (refined.url) {
716                return {
717                    url: refined.url,
718                    method: (refined.method || 'GET').toUpperCase(),
719                    extraParams: refined.extraParams,
720                };
721            }
722            return null;
723        },
724        fetch(arg, successCallback, errorCallback) {
725            const meta = arg.resourceSource.meta;
726            const requestParams = buildRequestParams(meta, arg.range, arg.context);
727            protectedApi.requestJson(meta.method, meta.url, requestParams).then(([rawResources, response]) => {
728                successCallback({ rawResources, response });
729            }, errorCallback);
730        },
731    };
732    // TODO: somehow consolidate with event json feed
733    function buildRequestParams(meta, range, context) {
734        let { dateEnv, options } = context;
735        let startParam;
736        let endParam;
737        let timeZoneParam;
738        let customRequestParams;
739        let params = {};
740        if (range) {
741            startParam = meta.startParam;
742            if (startParam == null) {
743                startParam = options.startParam;
744            }
745            endParam = meta.endParam;
746            if (endParam == null) {
747                endParam = options.endParam;
748            }
749            timeZoneParam = meta.timeZoneParam;
750            if (timeZoneParam == null) {
751                timeZoneParam = options.timeZoneParam;
752            }
753            params[startParam] = dateEnv.formatIso(range.start);
754            params[endParam] = dateEnv.formatIso(range.end);
755            if (dateEnv.timeZone !== 'local') {
756                params[timeZoneParam] = dateEnv.timeZone;
757            }
758        }
759        // retrieve any outbound GET/POST data from the options
760        if (typeof meta.extraParams === 'function') {
761            // supplied as a function that returns a key/value object
762            customRequestParams = meta.extraParams();
763        }
764        else {
765            // probably supplied as a straight key/value object
766            customRequestParams = meta.extraParams || {};
767        }
768        Object.assign(params, customRequestParams);
769        return params;
770    }
771
772    const funcResourceSource = {
773        parseMeta(refined) {
774            if (typeof refined.resources === 'function') {
775                return refined.resources;
776            }
777            return null;
778        },
779        fetch(info, successCallback, errorCallback) {
780            const dateEnv = info.context.dateEnv;
781            const func = info.resourceSource.meta;
782            const publicData = info.range ? {
783                start: dateEnv.toDate(info.range.start),
784                end: dateEnv.toDate(info.range.end),
785                startStr: dateEnv.formatIso(info.range.start),
786                endStr: dateEnv.formatIso(info.range.end),
787                timeZone: dateEnv.timeZone,
788            } : {};
789            protectedApi.unpromisify(func.bind(null, publicData), (rawResources) => successCallback({ rawResources }), errorCallback);
790        },
791    };
792
793    let defs = [
794        arrayResourceSource,
795        jsonFeedResourceSource,
796        funcResourceSource,
797    ];
798    function getResourceSourceDef(id) {
799        return defs[id];
800    }
801    function getResourceSourceDefs() {
802        return defs;
803    }
804
805    // TODO: make this a plugin-able parser
806    // TODO: success/failure
807    const RESOURCE_SOURCE_REFINERS = {
808        id: String,
809        // for array. TODO: move to resource-array
810        resources: protectedApi.identity,
811        // for json feed. TODO: move to resource-json-feed
812        url: String,
813        method: String,
814        startParam: String,
815        endParam: String,
816        timeZoneParam: String,
817        extraParams: protectedApi.identity,
818    };
819    function parseResourceSource(input) {
820        let inputObj;
821        if (typeof input === 'string') {
822            inputObj = { url: input };
823        }
824        else if (typeof input === 'function' || Array.isArray(input)) {
825            inputObj = { resources: input };
826        }
827        else if (typeof input === 'object' && input) { // non-null object
828            inputObj = input;
829        }
830        if (inputObj) {
831            let { refined, extra } = protectedApi.refineProps(inputObj, RESOURCE_SOURCE_REFINERS);
832            warnUnknownProps(extra);
833            let metaRes = buildResourceSourceMeta(refined);
834            if (metaRes) {
835                return {
836                    _raw: input,
837                    sourceId: protectedApi.guid(),
838                    sourceDefId: metaRes.sourceDefId,
839                    meta: metaRes.meta,
840                    publicId: refined.id || '',
841                    isFetching: false,
842                    latestFetchId: '',
843                    fetchRange: null,
844                };
845            }
846        }
847        return null;
848    }
849    function buildResourceSourceMeta(refined) {
850        let defs = getResourceSourceDefs();
851        for (let i = defs.length - 1; i >= 0; i -= 1) { // later-added plugins take precedence
852            let def = defs[i];
853            let meta = def.parseMeta(refined);
854            if (meta) {
855                return { meta, sourceDefId: i };
856            }
857        }
858        return null;
859    }
860    function warnUnknownProps(props) {
861        for (let propName in props) {
862            protectedApi.warn(`Unknown resource-source property \`${propName}\`.`);
863        }
864    }
865
866    function reduceResourceSource(source, action, context) {
867        let { options, dateProfile } = context;
868        if (!source || !action) {
869            return createSource(options.initialResources || options.resources, dateProfile.activeRange, options.refetchResourcesOnNavigate, context);
870        }
871        switch (action.type) {
872            case 'RESET_RESOURCE_SOURCE':
873                return createSource(action.resourceSourceInput, dateProfile.activeRange, options.refetchResourcesOnNavigate, context);
874            case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
875            case 'NEXT':
876            case 'CHANGE_DATE':
877            case 'CHANGE_VIEW_TYPE':
878                return handleRangeChange(source, dateProfile.activeRange, options.refetchResourcesOnNavigate, context);
879            case 'RECEIVE_RESOURCES':
880            case 'RECEIVE_RESOURCE_ERROR':
881                return receiveResponse(source, action.fetchId, action.fetchRange);
882            case 'REFETCH_RESOURCES':
883                return fetchSource(source, dateProfile.activeRange, context);
884            default:
885                return source;
886        }
887    }
888    function createSource(input, activeRange, refetchResourcesOnNavigate, context) {
889        if (input) {
890            let source = parseResourceSource(input);
891            source = fetchSource(source, refetchResourcesOnNavigate ? activeRange : null, context);
892            return source;
893        }
894        return null;
895    }
896    function handleRangeChange(source, activeRange, refetchResourcesOnNavigate, context) {
897        if (refetchResourcesOnNavigate &&
898            !doesSourceIgnoreRange(source) &&
899            (!source.fetchRange || !protectedApi.rangesEqual(source.fetchRange, activeRange))) {
900            return fetchSource(source, activeRange, context);
901        }
902        return source;
903    }
904    function doesSourceIgnoreRange(source) {
905        return Boolean(getResourceSourceDef(source.sourceDefId).ignoreRange);
906    }
907    function fetchSource(source, fetchRange, context) {
908        let sourceDef = getResourceSourceDef(source.sourceDefId);
909        let fetchId = protectedApi.guid();
910        sourceDef.fetch({
911            resourceSource: source,
912            range: fetchRange,
913            context,
914        }, (res) => {
915            context.dispatch({
916                type: 'RECEIVE_RESOURCES',
917                fetchId,
918                fetchRange,
919                rawResources: res.rawResources,
920            });
921        }, (error) => {
922            context.dispatch({
923                type: 'RECEIVE_RESOURCE_ERROR',
924                fetchId,
925                fetchRange,
926                error,
927            });
928        });
929        return {
930            ...source,
931            isFetching: true,
932            latestFetchId: fetchId,
933        };
934    }
935    function receiveResponse(source, fetchId, fetchRange) {
936        if (fetchId === source.latestFetchId) {
937            return {
938                ...source,
939                isFetching: false,
940                fetchRange,
941            };
942        }
943        return source;
944    }
945
946    function reduceResourceStore(store, action, source, context) {
947        if (!store || !action) {
948            return {};
949        }
950        switch (action.type) {
951            case 'RECEIVE_RESOURCES':
952                return receiveRawResources(store, action.rawResources, action.fetchId, source, context);
953            case 'ADD_RESOURCE':
954                return addResource(store, action.resourceHash);
955            case 'REMOVE_RESOURCE':
956                return removeResource(store, action.resourceId);
957            case 'SET_RESOURCE_PROP':
958                return setResourceProp(store, action.resourceId, action.propName, action.propValue);
959            case 'SET_RESOURCE_EXTENDED_PROP':
960                return setResourceExtendedProp(store, action.resourceId, action.propName, action.propValue);
961            default:
962                return store;
963        }
964    }
965    function receiveRawResources(existingStore, inputs, fetchId, source, context) {
966        if (source.latestFetchId === fetchId) {
967            let nextStore = {};
968            for (let input of inputs) {
969                parseResource(input, '', nextStore, context);
970            }
971            return nextStore;
972        }
973        return existingStore;
974    }
975    function addResource(existingStore, additions) {
976        // TODO: warn about duplicate IDs
977        return { ...existingStore, ...additions };
978    }
979    function removeResource(existingStore, resourceId) {
980        let newStore = { ...existingStore };
981        delete newStore[resourceId];
982        // promote children
983        for (let childResourceId in newStore) { // a child, *maybe* but probably not
984            if (newStore[childResourceId].parentId === resourceId) {
985                newStore[childResourceId] = {
986                    ...newStore[childResourceId],
987                    parentId: '',
988                };
989            }
990        }
991        return newStore;
992    }
993    function setResourceProp(existingStore, resourceId, name, value) {
994        let existingResource = existingStore[resourceId];
995        // TODO: sanitization
996        if (existingResource) {
997            return {
998                ...existingStore,
999                [resourceId]: {
1000                    ...existingResource,
1001                    [name]: value,
1002                },
1003            };
1004        }
1005        return existingStore;
1006    }
1007    function setResourceExtendedProp(existingStore, resourceId, name, value) {
1008        let existingResource = existingStore[resourceId];
1009        if (existingResource) {
1010            return {
1011                ...existingStore,
1012                [resourceId]: {
1013                    ...existingResource,
1014                    extendedProps: {
1015                        ...existingResource.extendedProps,
1016                        [name]: value,
1017                    },
1018                },
1019            };
1020        }
1021        return existingStore;
1022    }
1023
1024    function reduceResourceEntityExpansions(expansions, action) {
1025        if (!expansions || !action) {
1026            return {};
1027        }
1028        switch (action.type) {
1029            case 'SET_RESOURCE_ENTITY_EXPANDED':
1030                return {
1031                    ...expansions,
1032                    [action.id]: action.isExpanded,
1033                };
1034            default:
1035                return expansions;
1036        }
1037    }
1038
1039    function reduceResources(state, action, context) {
1040        let resourceSource = reduceResourceSource(state && state.resourceSource, action, context);
1041        let resourceStore = reduceResourceStore(state && state.resourceStore, action, resourceSource, context);
1042        let resourceEntityExpansions = reduceResourceEntityExpansions(state && state.resourceEntityExpansions, action);
1043        return {
1044            resourceSource,
1045            resourceStore,
1046            resourceEntityExpansions,
1047        };
1048    }
1049
1050    const EVENT_REFINERS = {
1051        resourceId: String,
1052        resourceIds: protectedApi.identity,
1053        resourceEditable: Boolean,
1054    };
1055    function generateEventDefResourceMembers(refined) {
1056        return {
1057            resourceIds: ensureStringArray(refined.resourceIds)
1058                .concat(refined.resourceId ? [refined.resourceId] : []),
1059            resourceEditable: refined.resourceEditable,
1060        };
1061    }
1062    function ensureStringArray(items) {
1063        return (items || []).map((item) => String(item));
1064    }
1065
1066    function transformDateSelectionJoin(hit0, hit1) {
1067        let resourceId0 = hit0.dateSpan.resourceId;
1068        let resourceId1 = hit1.dateSpan.resourceId;
1069        if (resourceId0 && resourceId1) {
1070            return { resourceId: resourceId0 };
1071        }
1072        return null;
1073    }
1074
1075    /*
1076    splits things BASED OFF OF which resources they are associated with.
1077    creates a '' entry which is when something has NO resource.
1078    */
1079    class ResourceSplitter extends protectedApi.Splitter {
1080        getKeyInfo(props) {
1081            return {
1082                '': {}, // needed for non-resource
1083                ...props.resourceStore, // already has `ui` and `businessHours` keys!
1084            };
1085        }
1086        getKeysForDateSpan(dateSpan) {
1087            return [dateSpan.resourceId || ''];
1088        }
1089        getKeysForEventDef(eventDef) {
1090            let resourceIds = eventDef.resourceIds;
1091            if (!resourceIds.length) {
1092                return [''];
1093            }
1094            return resourceIds;
1095        }
1096    }
1097
1098    function isPropsValidWithResources(combinedProps, context) {
1099        let splitter = new ResourceSplitter();
1100        let sets = splitter.splitProps({
1101            ...combinedProps,
1102            resourceStore: context.getCurrentData().resourceStore,
1103        });
1104        for (let resourceId in sets) {
1105            let props = sets[resourceId];
1106            // merge in event data from the non-resource segment
1107            if (resourceId && sets['']) { // current segment is not the non-resource one, and there IS a non-resource one
1108                props = {
1109                    ...props,
1110                    eventStore: protectedApi.mergeEventStores(sets[''].eventStore, props.eventStore),
1111                    eventUiBases: { ...sets[''].eventUiBases, ...props.eventUiBases },
1112                };
1113            }
1114            if (!protectedApi.isPropsValid(props, context, { resourceId }, filterConfig.bind(null, resourceId))) {
1115                return false;
1116            }
1117        }
1118        return true;
1119    }
1120    function filterConfig(resourceId, config) {
1121        return {
1122            ...config,
1123            constraints: filterConstraints(resourceId, config.constraints),
1124        };
1125    }
1126    function filterConstraints(resourceId, constraints) {
1127        return constraints.map((constraint) => {
1128            let defs = constraint.defs;
1129            if (defs) { // we are dealing with an EventStore
1130                // if any of the events define constraints to resources that are NOT this resource,
1131                // then this resource is unconditionally prohibited, which is what a `false` value does.
1132                for (let defId in defs) {
1133                    let resourceIds = defs[defId].resourceIds;
1134                    if (resourceIds.length && resourceIds.indexOf(resourceId) === -1) { // TODO: use a hash?!!! (for other reasons too)
1135                        return false;
1136                    }
1137                }
1138            }
1139            return constraint;
1140        });
1141    }
1142
1143    function transformExternalDef(dateSpan) {
1144        return dateSpan.resourceId ?
1145            { resourceId: dateSpan.resourceId } :
1146            {};
1147    }
1148
1149    const optionChangeHandlers = {
1150        resources: handleResources,
1151    };
1152    function handleResources(newSourceInput, context) {
1153        let oldSourceInput = context.getCurrentData().resourceSource._raw;
1154        if (oldSourceInput !== newSourceInput) {
1155            context.dispatch({
1156                type: 'RESET_RESOURCE_SOURCE',
1157                resourceSourceInput: newSourceInput,
1158            });
1159        }
1160    }
1161
1162    const DEFAULT_RESOURCE_ORDER = protectedApi.parseFieldSpecs('id,title');
1163    function handleResourceStore(resourceStore, calendarData) {
1164        let { emitter } = calendarData;
1165        if (emitter.hasHandlers('resourcesSet')) {
1166            emitter.trigger('resourcesSet', buildResourceApis(resourceStore, calendarData));
1167        }
1168    }
1169
1170    var resourcePlugin = {
1171        name: 'resource',
1172        premiumReleaseDate: '2026-07-24',
1173        deps: [premiumCommonPlugin],
1174        reducers: [reduceResources],
1175        isLoadingFuncs: [
1176            (state) => state.resourceSource && state.resourceSource.isFetching,
1177        ],
1178        eventRefiners: EVENT_REFINERS,
1179        eventDefMemberAdders: [generateEventDefResourceMembers],
1180        isDraggableTransformers: [transformIsDraggable],
1181        eventDragMutationMassagers: [massageEventDragMutation],
1182        eventDefMutationAppliers: [applyEventDefMutation],
1183        dateSelectionTransformers: [transformDateSelectionJoin],
1184        datePointTransforms: [transformDatePoint],
1185        dateSpanTransforms: [transformDateSpan],
1186        viewPropsTransformers: [ResourceDataAdder, ResourceEventC
1186onfigAdder],
1187        isPropsValid: isPropsValidWithResources,
1188        externalDefTransforms: [transformExternalDef],
1189        eventDropTransformers: [transformEventDrop],
1190        optionChangeHandlers,
1191        propSetHandlers: { resourceStore: handleResourceStore },
1192    };
1193
1194    /*
1195    TODO: move away from pooledHeight. simply create child-parent relationships w/ groups+resource
1196    The groups that are row-groups VS col-groups can be determined by colGroupDepth
1197    */
1198    function buildResourceHierarchy(resourceStore, orderSpecs, // why accepted when resourceNodeHash already ordered?
1199    groupSpecs = [], groupRowDepth = 0) {
1200        const resourceNodeHash = buildResourceNodeHash(resourceStore, orderSpecs);
1201        const resNodes = [];
1202        for (let resourceId in resourceNodeHash) {
1203            let resourceNode = resourceNodeHash[resourceId];
1204            if (!resourceNode.entity.parentId) {
1205                insertResourceNode(resourceNode, orderSpecs, groupSpecs, groupRowDepth, resNodes);
1206            }
1207        }
1208        return resNodes;
1209    }
1210    /*
1211    Builds a hash-by-id that contains ALL resources,
1212    but .children[] array is wired up as well
1213    */
1214    function buildResourceNodeHash(resourceStore, orderSpecs) {
1215        let nodeHash = {};
1216        for (let resourceId in resourceStore) {
1217            let resource = resourceStore[resourceId];
1218            nodeHash[resourceId] = {
1219                entity: resource,
1220                resourceFields: buildResourceFields(resource),
1221                children: [],
1222            };
1223        }
1224        for (let resourceId in resourceStore) {
1225            let resource = resourceStore[resourceId];
1226            if (resource.parentId) {
1227                let parentNode = nodeHash[resource.parentId];
1228                if (parentNode) {
1229                    insertResourceNodeInSiblings(nodeHash[resourceId], orderSpecs, parentNode.children);
1230                }
1231            }
1232        }
1233        return nodeHash;
1234    }
1235    function insertResourceNode(resourceNode, orderSpecs, groupSpecs, groupRowDepth, resNodes, groupIdPath = []) {
1236        if (groupSpecs.length) {
1237            const groupSpec = groupSpecs[0];
1238            const nextGroupIdPath = buildGroupIdPath(groupIdPath, groupSpec.field, resourceNode.resourceFields[groupSpec.field]);
1239            let groupNode = ensureGroupNodes(resourceNode, groupSpec, groupRowDepth <= 0, resNodes, nextGroupIdPath);
1240            insertResourceNode(resourceNode, orderSpecs, groupSpecs.slice(1), groupRowDepth - 1, groupNode.children, nextGroupIdPath);
1241        }
1242        else {
1243            insertResourceNodeInSiblings(resourceNode, orderSpecs, resNodes);
1244        }
1245    }
1246    function ensureGroupNodes(resourceNode, groupSpec, pooledHeight, resNodes, groupIdPath) {
1247        let groupValue = resourceNode.resourceFields[groupSpec.field];
1248        let groupNode;
1249        let newGroupIndex;
1250        // find an existing group that matches, or determine the position for a new group
1251        if (groupSpec.order) {
1252            for (newGroupIndex = 0; newGroupIndex < resNodes.length; newGroupIndex += 1) {
1253                let node = resNodes[newGroupIndex];
1254                let group = node.entity;
1255                let cmp = protectedApi.flexibleCompare(groupValue, group.value) * groupSpec.order;
1256                if (cmp === 0) {
1257                    groupNode = node;
1258                    break;
1259                }
1260                else if (cmp < 0) {
1261                    break;
1262                }
1263            }
1264        }
1265        else { // the groups are unordered
1266            for (newGroupIndex = 0; newGroupIndex < resNodes.length; newGroupIndex += 1) {
1267                let node = resNodes[newGroupIndex];
1268                let group = node.entity;
1269                if (group && groupValue === group.value) {
1270                    groupNode = node;
1271                    break;
1272                }
1273            }
1274        }
1275        if (!groupNode) {
1276            groupNode = {
1277                entity: {
1278                    id: buildGroupId(groupIdPath),
1279                    value: groupValue,
1280                    spec: groupSpec,
1281                },
1282                pooledHeight,
1283                children: [],
1284            };
1285            resNodes.splice(newGroupIndex, 0, groupNode);
1286        }
1287        return groupNode;
1288    }
1289    function insertResourceNodeInSiblings(resourceNode, orderSpecs, siblings) {
1290        let i;
1291        for (i = 0; i < siblings.length; i += 1) {
1292            let cmp = protectedApi.compareByFieldSpecs(// TODO: pass in ResourceApi?
1293            siblings[i].resourceFields, resourceNode.resourceFields, orderSpecs);
1294            if (cmp > 0) { // went 1 past. insert at i
1295                break;
1296            }
1297        }
1298        siblings.splice(i, 0, resourceNode);
1299    }
1300    function buildResourceFields(resource) {
1301        let obj = { ...resource.extendedProps, ...resource.ui, ...resource };
1302        delete obj.ui;
1303        delete obj.extendedProps;
1304        return obj;
1305    }
1306    // Hierarchy + Flattening
1307    // -------------------------------------------------------------------------------------------------
1308    function flattenResources(resourceStore, orderSpecs) {
1309        const hierarchy = buildResourceHierarchy(resourceStore, orderSpecs);
1310        const resResources = [];
1311        flattenResourceHierarchy(hierarchy, resResources);
1312        return resResources;
1313    }
1314    function flattenResourceHierarchy(resourceNodes, resResources) {
1315        for (const node of resourceNodes) {
1316            resResources.push(node.entity);
1317            flattenResourceHierarchy(node.children, resResources);
1318        }
1319    }
1320    // Utils
1321    // -------------------------------------------------------------------------------------------------
1322    function createEntityId(entity) {
1323        return isEntityGroup(entity) ? createGroupId(entity) : entity.id;
1324    }
1325    function createGroupId(group) {
1326        return group.id || buildGroupId(buildGroupIdPath([], group.spec.field, group.value));
1327    }
1328    function isEntityGroup(entity) {
1329        return Boolean(entity.spec);
1330    }
1331    function isGroupsEqual(group0, group1) {
1332        return createGroupId(group0) === createGroupId(group1);
1333    }
1334    function buildGroupIdPath(parentPath, field, value) {
1335        return parentPath.concat([[field, value]]);
1336    }
1337    function buildGroupId(path) {
1338        return JSON.stringify(path);
1339    }
1340
1341    class ResourceIndex {
1342        constructor(resources) {
1343            let indicesById = {};
1344            let ids = [];
1345            for (let i = 0; i < resources.length; i += 1) {
1346                let id = resources[i].id;
1347                ids.push(id);
1348                indicesById[id] = i;
1349            }
1350            this.ids = ids;
1351            this.indicesById = indicesById;
1352            this.length = resources.length;
1353        }
1354    }
1355
1356    /*
1357    TODO: move this so @fullcalendar/resource-daygrid
1358    */
1359    class AbstractResourceDayTableModel {
1360        constructor(dayTableModel, resources, rowCount, colCount) {
1361            this.dayTableModel = dayTableModel;
1362            this.resources = resources;
1363            this.rowCount = rowCount;
1364            this.colCount = colCount;
1365            this.resourceIndex = new ResourceIndex(resources);
1366            this.cells = this.buildCells();
1367        }
1368    }
1369
1370    /*
1371    TODO: move this so @fullcalendar/resource-daygrid
1372    */
1373    class ResourcefulDayTableModel extends AbstractResourceDayTableModel {
1374        constructor(dayTableModel, resources, context) {
1375            super(dayTableModel, resources, dayTableModel.rowCount, // rowCount
1376            dayTableModel.colCount * resources.length);
1377            this.context = context;
1378        }
1379        buildCells() {
1380            let { rowCount, dayTableModel, resources } = this;
1381            let rows = [];
1382            let hasMajor = resources.length > 1 && dayTableModel.colCount > 1;
1383            for (let row = 0; row < rowCount; row += 1) {
1384                let rowCells = [];
1385                for (let dateCol = 0; dateCol < dayTableModel.colCount; dateCol += 1) {
1386                    for (let resourceCol = 0; resourceCol < resources.length; resourceCol += 1) {
1387                        let resource = resources[resourceCol];
1388                        let date = dayTableModel.cellRows[row][dateCol].date;
1389                        let renderProps = { resource: new ResourceApi(this.context, resource) };
1390                        let attrs = { 'data-resource-id': resource.id };
1391                        let dateSpanProps = { resourceId: resource.id };
1392                        let realCol = this.computeCol(dateCol, resourceCol);
1393                        rowCells[realCol] = {
1394                            key: resource.id + ':' + date.toISOString(), // TODO: DRY up this logic with header-tier somehow
1395                            date,
1396                            isMajor: hasMajor && this.colIsMajor(realCol),
1397                            renderProps,
1398                            attrs,
1399                            className: '', // kill this property?
1400                            dateSpanProps,
1401                        };
1402                    }
1403                }
1404                rows.push(rowCells);
1405            }
1406            return rows;
1407        }
1408    }
1409
1410    /*
1411    dates over resources
1412    */
1413    class DayResourceTableModel extends ResourcefulDayTableModel {
1414        computeCol(dateI, resourceI) {
1415            return dateI * this.resources.length + resourceI;
1416        }
1417        colIsMajor(col) {
1418            const resourceCnt = this.resources.length;
1419            return resourceCnt > 1 && !(col % resourceCnt);
1420        }
1421        /*
1422        every single day is broken up
1423        */
1424        computeColRanges(dateStartI, dateEndI, resourceI) {
1425            let segs = [];
1426            for (let i = dateStartI; i < dateEndI; i += 1) {
1427                let col = this.computeCol(i, resourceI);
1428                segs.push({
1429                    start: col,
1430                    end: col + 1,
1431                    isStart: i === dateStartI,
1432                    isEnd: i === dateEndI - 1,
1433                });
1434            }
1435            return segs;
1436        }
1437    }
1438
1439    /*
1440    resources over dates
1441    */
1442    class ResourceDayTableModel extends ResourcefulDayTableModel {
1443        computeCol(dateI, resourceI) {
1444            return resourceI * this.dayTableModel.colCount + dateI;
1445        }
1446        colIsMajor(col) {
1447            const dayCnt = this.dayTableModel.colCount;
1448            return dayCnt > 1 && !(col % dayCnt);
1449        }
1450        /*
1451        all date ranges are intact
1452        */
1453        computeColRanges(dateStartI, dateEndI, resourceI) {
1454            return [
1455                {
1456                    start: this.computeCol(dateStartI, resourceI),
1457                    end: this.computeCol(dateEndI, resourceI),
1458                    isStart: true,
1459                    isEnd: true,
1460                },
1461            ];
1462        }
1463    }
1464
1465    /*
1466    TODO: move this so @fullcalendar/resource-daygrid
1467    */
1468    class ResourcelessDayTableModel extends AbstractResourceDayTableModel {
1469        constructor(dayTableModel) {
1470            super(dayTableModel, [], // resources
1471            dayTableModel.rowCount, // rowCount
1472            dayTableModel.colCount);
1473        }
1474        computeCol(dateI, _resourceI) {
1475            return dateI;
1476        }
1477        computeColRanges(dateStartI, dateEndI, _resourceI) {
1478            return [
1479                {
1480                    start: dateStartI,
1481                    end: dateEndI,
1482                    isStart: true,
1483                    isEnd: true,
1484                },
1485            ];
1486        }
1487        buildCells() {
1488            return this.dayTableModel.buildCells();
vendor: 6,171 bytes, lines 1489-1628
1489        }
1490    }
1491
1492    /*
1493    TODO: just use ResourceHash somehow? could then use the generic ResourceSplitter
1494    */
1495    class VResourceSplitter extends protectedApi.Splitter {
1496        getKeyInfo(props) {
1497            let { resourceDayTableModel } = props;
1498            let hash = protectedApi.mapHash(resourceDayTableModel.resourceIndex.indicesById, (i) => resourceDayTableModel.resources[i]); // :(
1499            hash[''] = {};
1500            return hash;
1501        }
1502        getKeysForDateSpan(dateSpan) {
1503            return [dateSpan.resourceId || ''];
1504        }
1505        getKeysForEventDef(eventDef) {
1506            let resourceIds = eventDef.resourceIds;
1507            if (!resourceIds.length) {
1508                return [''];
1509            }
1510            return resourceIds;
1511        }
1512    }
1513
1514    const NO_SEGS = []; // for memoizing
1515    class VResourceJoiner {
1516        constructor() {
1517            this.joinDateSelection = protectedApi.memoize(this.joinSegs);
1518            this.joinBusinessHours = protectedApi.memoize(this.joinSegs);
1519            this.joinFgEvents = protectedApi.memoize(this.joinSegs);
1520            this.joinBgEvents = protectedApi.memoize(this.joinSegs);
1521            this.joinEventDrags = protectedApi.memoize(this.joinInteractions);
1522            this.joinEventResizes = protectedApi.memoize(this.joinInteractions);
1523        }
1524        /*
1525        propSets also has a '' key for things with no resource
1526        */
1527        joinProps(propSets, resourceDayTable) {
1528            let dateSelectionSets = [];
1529            let businessHoursSets = [];
1530            let fgEventSets = [];
1531            let bgEventSets = [];
1532            let eventDrags = [];
1533            let eventResizes = [];
1534            let eventSelection = '';
1535            let keys = resourceDayTable.resourceIndex.ids.concat(['']); // add in the all-resource key
1536            for (let key of keys) {
1537                let props = propSets[key];
1538                dateSelectionSets.push(props.dateSelectionSegs);
1539                businessHoursSets.push(key ? props.businessHourSegs : NO_SEGS); // don't include redundant all-resource businesshours
1540                fgEventSets.push(key ? props.fgEventSegs : NO_SEGS); // don't include fg all-resource segs
1541                bgEventSets.push(props.bgEventSegs);
1542                eventDrags.push(props.eventDrag);
1543                eventResizes.push(props.eventResize);
1544                eventSelection = eventSelection || props.eventSelection;
1545            }
1546            return {
1547                dateSelectionSegs: this.joinDateSelection(resourceDayTable, ...dateSelectionSets),
1548                businessHourSegs: this.joinBusinessHours(resourceDayTable, ...businessHoursSets),
1549                fgEventSegs: this.joinFgEvents(resourceDayTable, ...fgEventSets),
1550                bgEventSegs: this.joinBgEvents(resourceDayTable, ...bgEventSets),
1551                eventDrag: this.joinEventDrags(resourceDayTable, ...eventDrags),
1552                eventResize: this.joinEventResizes(resourceDayTable, ...eventResizes),
1553                eventSelection,
1554            };
1555        }
1556        joinSegs(resourceDayTable, ...segGroups) {
1557            let resourceCnt = resourceDayTable.resources.length;
1558            let transformedSegs = [];
1559            for (let i = 0; i < resourceCnt; i += 1) {
1560                for (let seg of segGroups[i]) {
1561                    transformedSegs.push(...this.transformSeg(seg, resourceDayTable, i));
1562                }
1563                for (let seg of segGroups[resourceCnt]) { // one beyond. the all-resource
1564                    transformedSegs.push(...this.transformSeg(seg, resourceDayTable, i));
1565                }
1566            }
1567            return transformedSegs;
1568        }
1569        /*
1570        for expanding non-resource segs to all resources.
1571        only for public use.
1572        no memoizing.
1573        */
1574        expandSegs(resourceDayTable, segs) {
1575            let resourceCnt = resourceDayTable.resources.length;
1576            let transformedSegs = [];
1577            for (let i = 0; i < resourceCnt; i += 1) {
1578                for (let seg of segs) {
1579                    transformedSegs.push(...this.transformSeg(seg, resourceDayTable, i));
1580                }
1581            }
1582            return transformedSegs;
1583        }
1584        joinInteractions(resourceDayTable, ...interactions) {
1585            let resourceCnt = resourceDayTable.resources.length;
1586            let affectedInstances = {};
1587            let transformedSegs = [];
1588            let anyInteractions = false;
1589            let isEvent = false;
1590            for (let i = 0; i < resourceCnt; i += 1) {
1591                let interaction = interactions[i];
1592                if (interaction) {
1593                    anyInteractions = true;
1594                    for (let seg of interaction.segs) {
1595                        transformedSegs.push(...this.transformSeg(seg, resourceDayTable, i));
1596                    }
1597                    Object.assign(affectedInstances, interaction.affectedInstances);
1598                    isEvent = isEvent || interaction.isEvent;
1599                }
1600                if (interactions[resourceCnt]) { // one beyond. the all-resource
1601                    for (let seg of interactions[resourceCnt].segs) {
1602                        transformedSegs.push(...this.transformSeg(seg, resourceDayTable, i));
1603                    }
1604                }
1605            }
1606            if (anyInteractions) {
1607                return {
1608                    affectedInstances,
1609                    segs: transformedSegs,
1610                    isEvent,
1611                };
1612            }
1613            return null;
1614        }
1615    }
1616
1617    class ResourceDayTableJoiner extends VResourceJoiner {
1618        transformSeg(seg, resourceDayTableModel, resourceI) {
1619            let colRanges = resourceDayTableModel.computeColRanges(seg.start, seg.end, resourceI);
1620            return colRanges.map((colRange) => ({
1621                ...seg,
1622                ...colRange,
1623                isStart: seg.isStart && colRange.isStart,
1624                isEnd: seg.isEnd && colRange.isEnd,
1625            }));
1626        }
1627    }
1628
1629    function buildResourceRowConfigs(resources, datesAboveResources, dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, // TODO: rename to dateHeaderFormat?
1630    context) {
1631        if (!resources.length) {
1632            return [
1633                protectedApi.buildDateRowConfig(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, context)
1634            ];
1635        }
1636        if (dates.length === 1) {
1637            return [
1638                buildResourceRowConfig(resources, dates[0], dateProfile, todayRange, context)
1639            ];
1640        }
1641        if (datesAboveResources) {
1642            const resourceDataConfigsPerDate = dates.map((date) => {
1643                return buildResourceDataConfigs(resources, date, dateProfile, todayRange, context, 
1644                /* colSpan = */ 1, 
1645                /* isMajorMod = */ resources.length);
1646            });
1647            return [
1648                // date row
1649                protectedApi.buildDateRowConfig(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, context, 
1650                /* colSpan = */ resources.length, 
1651                /* isMajorMod = */ 1),
1652                // resource row
1653                {
1654                    isDateRow: false,
1655                    renderConfig: buildResourceRenderConfig(context),
1656                    dataConfigs: [].concat(...resourceDataConfigsPerDate), // flatten
1657                }
1658            ];
1659        }
1660        else { // resources above dates
1661            const dateDataConfigsPerResource = resources.map((resource) => {
1662                const resourceApi = new ResourceApi(context, resource);
1663                const resourceApiId = resourceApi.id;
1664                return protectedApi.buildDateDataConfigs(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, context, 1, // rowSpan
1665                resourceApiId + ':', {
1666                    resource: resourceApi,
1667                }, {
1668                    'data-resource-id': resourceApiId,
1669                }, 
1670                /* className = */ undefined, // TODO: remove
1671                /* isMajorMod = */ dates.length);
1672            });
1673            return [
1674                // resource row
1675                buildResourceRowConfig(resources, undefined, undefined, undefined, context, 
1676                /* colSpan = */ dates.length, 
1677                /* isMajorMod = */ 1),
1678                // date row
1679                {
1680                    isDateRow: true,
1681                    renderConfig: protectedApi.buildDateRenderConfig(dayHeaderFormat, datesRepDistinctDays, context),
1682                    dataConfigs: [].concat(...dateDataConfigsPerResource), // flatten
1683                }
1684            ];
1685        }
1686    }
1687    /*
1688    Single row, just resources (might be under dates, might not)
1689    */
1690    function buildResourceRowConfig(resources, dateMarker, dateProfile, todayRange, context, colSpan, isMajorMod) {
1691        return {
1692            isDateRow: false,
1693            renderConfig: buildResourceRenderConfig(context),
1694            dataConfigs: buildResourceDataConfigs(resources, dateMarker, dateProfile, todayRange, context, colSpan, isMajorMod),
1695        };
1696    }
1697    function buildResourceRenderConfig(context) {
1698        const { options } = context;
1699        return {
1700            generatorName: 'resourceDayHeaderContent',
1701            customGenerator: options.resourceDayHeaderContent,
1702            innerClassNameGenerator: options.resourceDayHeaderInnerClass,
1703            classNameGenerator: options.resourceDayHeaderClass,
1704            didMount: options.resourceDayHeaderDidMount,
1705            willUnmount: options.resourceDayHeaderWillUnmount,
1706            align: options.resourceDayHeaderAlign,
1707            sticky: options._resourceDayHeaderSticky,
1708        };
1709    }
1710    function buildResourceDataConfigs(resources, dateMarker, dateProfile, todayRange, context, colSpan = 1, isMajorMod) {
1711        const dateMeta = dateMarker
1712            ? protectedApi.getDateMeta(dateMarker, context.dateEnv, dateProfile, todayRange)
1713            : {};
1714        return resources.map((resource, i) => {
1715            const resourceApi = new ResourceApi(context, resource);
1716            const resourceApiId = resourceApi.id;
1717            return {
1718                key: (dateMarker ? dateMarker.toUTCString() + ':' : '') + resource.id,
1719                dateMarker,
1720                renderProps: {
1721                    isDisabled: false,
1722                    ...dateMeta,
1723                    resource: resourceApi,
1724                    isMajor: isMajorMod != null && !(i % isMajorMod),
1725                    isNarrow: false, // HACK. gets overridden later
1726                    level: 0, // HACK. gets overridden later
1727                    text: resource.title || resourceApiId || '',
1728                    view: context.viewApi,
1729                },
1730                attrs: {
1731                    'data-resource-id': resourceApiId,
1732                    'data-date': dateMarker ? protectedApi.formatDayString(dateMarker) : undefined,
1733                },
1734                colSpan,
1735                className: '', // TODO: remove this prop
1736            };
1737        });
1738    }
1739
1740    class ResourceDayGridView extends protectedApi.DateComponent {
1741        constructor() {
1742            super(...arguments);
1743            // memo
1744            this.flattenResources = protectedApi.memoize(flattenResources);
1745            this.buildResourceDayTableModel = protectedApi.memoize(buildResourceDayTableModel);
1746            this.createDayHeaderFormatter = protectedApi.memoize(protectedApi.createDayHeaderFormatter);
1747            this.buildResourceRowConfigs = protectedApi.memoize(buildResourceRowConfigs);
1748            this.splitter = new VResourceSplitter();
1749            this.slicers = {};
1750            this.joiner = new ResourceDayTableJoiner();
1751            this.isHitComboAllowed = (hit0, hit1) => {
1752                let allowAcrossResources = this.resourceDayTableModel.dayTableModel.colCount === 1;
1753                return allowAcrossResources || hit0.dateSpan.resourceId === hit1.dateSpan.resourceId;
1754            };
1755        }
1756        render() {
1757            let { props, context } = this;
1758            let { options } = context;
1759            let resourceOrderSpecs = options.resourceOrder || DEFAULT_RESOURCE_ORDER;
1760            let resources = this.flattenResources(props.resourceStore, resourceOrderSpecs);
1761            let resourceDayTableModel = this.resourceDayTableModel = this.buildResourceDayTableModel(props.dateProfile, context.dateProfileGenerator, resources, options.datesAboveResources, context);
1762            let splitProps = this.splitter.splitProps({
1763                resourceDayTableModel,
1764                businessHours: props.businessHours,
1765                dateSelection: props.dateSelection,
1766                eventStore: props.eventStore,
1767                eventUiBases: props.eventUiBases,
1768                eventSelection: props.eventSelection,
1769                eventDrag: props.eventDrag,
1770                eventResize: props.eventResize,
1771            });
1772            this.slicers = protectedApi.mapHash(splitProps, (split, resourceId) => this.slicers[resourceId] || new protectedApi.DayTableSlicer());
1773            let slicedProps = protectedApi.mapHash(this.slicers, (slicer, resourceId) => slicer.sliceProps(splitProps[resourceId], props.dateProfile, options.nextDayThreshold, context, resourceDayTableModel.dayTableModel));
1774            let joinedSlicedProps = this.joiner.joinProps(slicedProps, resourceDayTableModel);
1775            let datesRepDistinctDays = resourceDayTableModel.dayTableModel.rowCount === 1;
1776            let dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, resourceDayTableModel.colCount);
1777            return (jsxRuntime.jsx(protectedApi.NowTimer, { unit: "day", children: (nowDate, todayRange) => {
1778                    const headerTiers = this.buildResourceRowConfigs(resources, options.datesAboveResources, resourceDayTableModel.dayTableModel.headerDates, datesRepDistinctDays, props.dateProfile, todayRange, dayHeaderFormat, context);
1779                    return (jsxRuntime.jsx(protectedApi.DayGridLayout, { labelId: props.labelId, labelStr: props.labelStr, dateProfile: props.dateProfile, todayRange: todayRange, cellRows: resourceDayTableModel.cells, forPrint: props.forPrint, isHitComboAllowed: this.isHitComboAllowed, className: props.className, 
1780                        // header content
1781                        headerTiers: headerTiers, 
1782                        // body content
1783                        fgEventSegs: joinedSlicedProps.fgEventSegs, bgEventSegs: joinedSlicedProps.bgEventSegs, businessHourSegs: joinedSlicedProps.businessHourSegs, dateSelectionSegs: joinedSlicedProps.dateSelectionSegs, eventDrag: joinedSlicedProps.eventDrag, eventResize: joinedSlicedProps.eventResize, eventSelection: joinedSlicedProps.eventSelection }));
1784                } }));
1785        }
1786    }
1787    function buildResourceDayTableModel(dateProfile, dateProfileGenerator, resources, datesAboveResources, context) {
1788        let dayTable = protectedApi.buildDayTableModel(dateProfile, dateProfileGenerator, context.dateEnv);
1789        if (!resources.length) {
1790            return new ResourcelessDayTableModel(dayTable);
1791        }
1792        return datesAboveResources ?
1793            new DayResourceTableModel(dayTable, resources, context) :
1794            new ResourceDayTableModel(dayTable, resources, context);
1795    }
1796
1797    var resourceDayGridPlugin = {
1798        name: 'resource-daygrid',
1799        premiumReleaseDate: '2026-07-24',
1800        deps: [
1801            premiumCommonPlugin,
1802            resourcePlugin,
1803            dayGridPlugin__default["default"],
1804        ],
1805        initialView: 'resourceDayGridDay',
1806        views: {
1807            resourceDayGrid: {
1808                type: 'dayGrid', // will inherit this configuration
1809                component: ResourceDayGridView,
1810                needsResourceData: true,
1811            },
1812            resourceDayGridDay: {
1813                type: 'resourceDayGrid',
1814                duration: { days: 1 },
1815            },
1816            resourceDayGridWeek: {
1817                type: 'resourceDayGrid',
1818                duration: { weeks: 1 },
1819            },
1820            resourceDayGridMonth: {
1821                type: 'resourceDayGrid',
1822                duration: { months: 1 },
1823                fixedWeekCount: true,
1824            },
1825        },
1826    };
1827
1828    /*
1829    TODO: detangle and use from @full-ui/headless-grid
1830    */
1831    class ScrollerSyncer {
1832        constructor(isHorizontal = false) {
1833            this.isHorizontal = isHorizontal;
1834            this.emitter = new protectedApi.Emitter();
1835            this.scrollers = [];
1836            this.destroyFuncs = [];
1837            this.isPaused = false;
1838        }
1839        handleChildren(scrollers) {
1840            if (!protectedApi.isArraysEqual(this.scrollers, scrollers)) {
1841                this.destroy();
1842                for (const scroller of scrollers) {
1843                    if (scroller) { // could be null
1844                        this.destroyFuncs.push(this.bindScroller(scroller));
1845                        this.scrollers.push(scroller);
1846                    }
1847                }
1848            }
1849        }
1850        destroy() {
1851            for (let destroyFunc of this.destroyFuncs) {
1852                destroyFunc();
1853            }
1854            this.destroyFuncs = [];
1855            this.scrollers = [];
1856        }
1857        get x() {
1858            const { scrollers, masterScroller } = this;
1859            return (masterScroller || scrollers[0]).x;
1860        }
1861        get y() {
1862            const { scrollers, masterScroller } = this;
1863            return (masterScroller || scrollers[0]).y;
1864        }
1865        scrollTo(scrollArg) {
1866            this.isPaused = true; // Not sure how this works, given that scroll handlers fire asynchronously
1867            const { scrollers } = this;
1868            for (let scroller of scrollers) {
1869                scroller.scrollTo(scrollArg);
1870            }
1871            this.isPaused = false;
1872        }
1873        addScrollListener(handler) {
1874            this.emitter.on('scroll', handler);
1875        }
1876        removeScrollListener(handler) {
1877            this.emitter.off('scroll', handler);
1878        }
1879        addScrollStartListener(handler) {
1880            this.emitter.on('scrollStart', handler);
1881        }
1882        removeScrollStartListener(handler) {
1883            this.emitter.off('scrollStart', handler);
1884        }
1885        addScrollEndListener(handler) {
1886            this.emitter.on('scrollEnd', handler);
1887        }
1888        removeScrollEndListener(handler) {
1889            this.emitter.off('scrollEnd', handler);
1890        }
1891        bindScroller(scroller) {
1892            let { isHorizontal } = this;
1893            const onScroll = (isDevice) => {
1894                if (!this.isPaused) {
1895                    if (!this.masterScroller) {
1896                        this.emitter.trigger('scrollStart', isDevice);
1897                    }
1898                    if (!this.masterScroller || (this.masterScroller !== scroller && isDevice)) {
1899                        this.assignMaster(scroller);
1900                    }
1901                    if (this.masterScroller === scroller) { // dealing with current
1902                        for (let otherScroller of this.scrollers) {
1903                            if (otherScroller !== scroller) {
1904                                if (isHorizontal) {
1905                                    // TODO: user raw scrollLeft for better performance. No normalization necessary
1906                                    otherScroller.scrollTo({ x: scroller.x });
1907                                }
1908                                else {
1909                                    otherScroller.scrollTo({ y: scroller.y });
1910                                }
1911                            }
1912                        }
1913                        this.emitter.trigger('scroll', isDevice, isHorizontal ? scroller.x : scroller.y);
1914                    }
1915                }
1916            };
1917            const onScrollEnd = (isDevice) => {
1918                if (this.masterScroller === scroller) {
1919                    this.masterScroller = null;
1920                    const { x, y } = this; // new values
1921                    let isMoved = false;
1922                    if (this.isHorizontal) {
1923                        if (x !== this.prevX) {
1924                            this.prevX = x;
1925                            isMoved = true;
1926                        }
1927                    }
1928                    else {
1929                        if (y !== this.prevY) {
1930                            this.prevY = y;
1931                            isMoved = true;
1932                        }
1933                    }
1934                    if (isMoved) {
1935                        this.emitter.trigger('scrollEnd', isDevice);
1936                    }
1937                }
1938            };
1939            scroller.listener.emitter.on('scroll', onScroll);
1940            scroller.listener.emitter.on('scrollEnd', onScrollEnd);
1941            return () => {
1942                scroller.listener.emitter.off('scroll', onScroll);
1943                scroller.listener.emitter.off('scrollEnd', onScrollEnd);
1944            };
1945        }
1946        assignMaster(masterScroller) {
1947            this.masterScroller = masterScroller;
1948            for (let scroller of this.scrollers) {
1949                if (scroller !== masterScroller) {
1950                    scroller.endScroll(); // to prevent residual scrolls from reclaiming master
1951                }
1952            }
1953        }
1954    }
1955
1956    function joinDateTimeFormatParts(parts) {
1957        let s = '';
1958        for (const part of parts) {
1959            s += part.value;
1960        }
1961        return s;
1962    }
1963
1964    const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
1965    const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
1966    const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
1967    protectedApi.config.MAX_TIMELINE_SLOTS = 1000;
1968    // potential nice values for slot-duration and interval-duration
1969    const STOCK_SUB_DURATIONS = [
1970        { years: 1 },
1971        { months: 1 },
1972        { days: 1 },
1973        { hours: 1 },
1974        { minutes: 30 },
1975        { minutes: 15 },
1976        { minutes: 10 },
1977        { minutes: 5 },
1978        { minutes: 1 },
1979        { seconds: 30 },
1980        { seconds: 15 },
1981        { seconds: 10 },
1982        { seconds: 5 },
1983        { seconds: 1 },
1984        { milliseconds: 500 },
1985        { milliseconds: 100 },
1986        { milliseconds: 10 },
1987        { milliseconds: 1 },
1988    ];
1989    function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
1990        let tDateProfile = {
1991            labelInterval: allOptions.slotHeaderInterval,
1992            slotDuration: allOptions.slotDuration,
1993        };
1994        validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
1995        ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
1996        ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
1997        let input = allOptions.slotHeaderFormat;
1998        let rawFormats;
1999        let headerFormatUnits;
2000        if (Array.isArray(input)) {
2001            rawFormats = input;
2002            headerFormatUnits = input.map(inferHeaderFormatUnit);
2003        }
2004        else if (input != null) {
2005            rawFormats = [input];
2006            headerFormatUnits = [inferHeaderFormatUnit(input)];
2007        }
2008        else {
2009            const computed = computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
2010            rawFormats = computed.formats;
2011            headerFormatUnits = computed.units;
2012        }
2013        tDateProfile.headerFormats = rawFormats.map((rawFormat) => protectedApi.createFormatter(rawFormat));
2014        tDateProfile.headerFormatUnits = headerFormatUnits;
2015        tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
2016        let largeUnit = null;
2017        if (!tDateProfile.isTimeScale) {
2018            const slotUnit = protectedApi.greatestDurationDenominator(tDateProfile.slotDuration).unit;
2019            if (/year|month|week/.test(slotUnit)) {
2020                largeUnit = slotUnit;
2021            }
2022        }
2023        tDateProfile.largeUnit = largeUnit;
2024        /*
2025        console.log('label interval =', timelineView.labelInterval.humanize())
2026        console.log('slot duration =', timelineView.slotDuration.humanize())
2027        console.log('header formats =', timelineView.headerFormats)
2028        console.log('isTimeScale', timelineView.isTimeScale)
2029        console.log('largeUnit', timelineView.largeUnit)
2030        */
2031        let rawSnapDuration = allOptions.snapDuration;
2032        let snapDuration;
2033        let snapsPerSlot;
2034        if (rawSnapDuration) {
2035            snapDuration = protectedApi.createDuration(rawSnapDuration);
2036            snapsPerSlot = protectedApi.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
2037            // ^ TODO: warning if not whole?
2038        }
2039        if (snapsPerSlot == null) {
2040            snapDuration = tDateProfile.slotDuration;
2041            snapsPerSlot = 1;
2042        }
2043        tDateProfile.snapDuration = snapDuration;
2044        tDateProfile.snapsPerSlot = snapsPerSlot;
2045        // more...
2046        let timeWindowMs = protectedApi.asRoughMs(dateProfile.slotMaxTime) - protectedApi.asRoughMs(dateProfile.slotMinTime);
2047        // TODO: why not use normalizeRange!?
2048        let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
2049        let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
2050        // apply slotMinTime/slotMaxTime
2051        // TODO: View should be responsible.
2052        if (tDateProfile.isTimeScale) {
2053            normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
2054            normalizedEnd = dateEnv.add(protectedApi.addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
2055        }
2056        tDateProfile.timeWindowMs = timeWindowMs;
2057        tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
2058        let slotDates = [];
2059        let slotDatesMajor = [];
2060        let date = normalizedStart;
2061        let majorUnit = protectedApi.computeMajorUnit(dateProfile, dateEnv);
2062        while (date < normalizedEnd) {
2063            if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
2064                slotDates.push(date);
2065                slotDatesMajor.push(protectedApi.isMajorUnit(date, majorUnit, dateEnv));
2066            }
2067            date = dateEnv.add(date, tDateProfile.slotDuration);
2068        }
2069        tDateProfile.slotDates = slotDates;
2070        tDateProfile.slotDatesMajor = slotDatesMajor;
2071        // more...
2072        let snapIndex = -1;
2073        let snapDiff = 0; // index of the diff :(
2074        const snapDiffToIndex = [];
2075        const snapIndexToDiff = [];
2076        date = normalizedStart;
2077        while (date < normalizedEnd) {
2078            if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
2079                snapIndex += 1;
2080                snapDiffToIndex.push(snapIndex);
2081                snapIndexToDiff.push(snapDiff);
2082            }
2083            else {
2084                snapDiffToIndex.push(snapIndex + 0.5);
2085            }
2086            date = dateEnv.add(date, tDateProfile.snapDuration);
2087            snapDiff += 1;
2088        }
2089        tDateProfile.snapDiffToIndex = snapDiffToIndex;
2090        tDateProfile.snapIndexToDiff = snapIndexToDiff;
2091        tDateProfile.snapCnt = snapIndex + 1; // is always one behind
2092        tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
2093        // more...
2094        tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, majorUnit);
2095        tDateProfile.slotsPerLabel = protectedApi.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
2096        return tDateProfile;
2097    }
2098    /*
2099    snaps to appropriate unit
2100    */
2101    function normalizeDate(date, tDateProfile, dateEnv) {
2102        let normalDate = date;
2103        if (!tDateProfile.isTimeScale) {
2104            normalDate = protectedApi.startOfDay(normalDate);
2105            if (tDateProfile.largeUnit) {
2106                normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
2107            }
2108        }
2109        return normalDate;
2110    }
2111    /*
2112    snaps to appropriate unit
2113    */
2114    function normalizeRange(range, tDateProfile, dateEnv) {
2115        if (!tDateProfile.isTimeScale) {
2116            range = protectedApi.computeVisibleDayRange(range);
2117            if (tDateProfile.largeUnit) {
2118                let dayRange = range; // preserve original result
2119                range = {
2120                    start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
2121                    end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
2122                };
2123                // if date is partially through the interval, or is in the same interval as the start,
2124                // make the exclusive end be the *next* interval
2125                if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
2126                    range = {
2127                        start: range.start,
2128                        end: dateEnv.add(range.end, tDateProfile.slotDuration),
2129                    };
2130                }
2131            }
2132        }
2133        return range;
2134    }
2135    function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
2136        if (dateProfileGenerator.isHiddenDay(date)) {
2137            return false;
2138        }
2139        if (tDateProfile.isTimeScale) {
2140            // determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
2141            let day = protectedApi.startOfDay(date);
2142            let timeMs = date.valueOf() - day.valueOf();
2143            let ms = timeMs - protectedApi.asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
2144            ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
2145            return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
2146        }
2147        return true;
2148    }
2149    function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
2150        const { currentRange } = dateProfile;
2151        // make sure labelInterval doesn't exceed the max number of cells
2152        if (tDateProfile.labelInterval) {
2153            const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
2154            if (labelCnt > protectedApi.config.MAX_TIMELINE_SLOTS) {
2155                protectedApi.warn('Invalid option `slotHeaderInterval`: too many timeline cells.');
2156                tDateProfile.labelInterval = null;
2157            }
2158        }
2159        // make sure slotDuration doesn't exceed the maximum number of cells
2160        if (tDateProfile.slotDuration) {
2161            const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
2162            if (slotCnt > protectedApi.config.MAX_TIMELINE_SLOTS) {
2163                protectedApi.warn('Invalid option `slotDuration`: too many timeline cells.');
2164                tDateProfile.slotDuration = null;
2165            }
2166        }
2167        // make sure labelInterval is a multiple of slotDuration
2168        if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
2169            const slotsPerLabel = protectedApi.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
2170            if (slotsPerLabel === null || slotsPerLabel < 1) {
2171                protectedApi.warn('Invalid option `slotHeaderInterval`: must be a multiple of `slotDuration`.');
2172                tDateProfile.slotDuration = null;
2173            }
2174        }
2175    }
2176    function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
2177        const { currentRange } = dateProfile;
2178        let { labelInterval } = tDateProfile;
2179        if (!labelInterval) {
2180            // compute based off the slot duration
2181            // find the largest label interval with an acceptable slots-per-label
2182            let input;
2183            if (tDateProfile.slotDuration) {
2184                for (input of STOCK_SUB_DURATIONS) {
2185                    const tryLabelInterval = protectedApi.createDuration(input);
2186                    const slotsPerLabel = protectedApi.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
2187                    if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
2188                        labelInterval = tryLabelInterval;
2189                        break;
2190                    }
2191                }
2192                // use the slot duration as a last resort
2193                if (!labelInterval) {
2194                    labelInterval = tDateProfile.slotDuration;
2195                }
2196                // compute based off the view's duration
2197                // find the largest label interval that yields the minimum number of labels
2198            }
2199            else {
2200                for (input of STOCK_SUB_DURATIONS) {
2201                    labelInterval = protectedApi.createDuration(input);
2202                    const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
2203                    if (labelCnt >= MIN_AUTO_LABELS) {
2204                        break;
2205                    }
2206                }
2207            }
2208            tDateProfile.labelInterval = labelInterval;
2209        }
2210        return labelInterval;
2211    }
2212    function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
2213        const { currentRange } = dateProfile;
2214        let { slotDuration } = tDateProfile;
2215        if (!slotDuration) {
2216            const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
2217            // compute based off the label interval
2218            // find the largest slot duration that is different from labelInterval, but still acceptable
2219            for (let input of STOCK_SUB_DURATIONS) {
2220                const trySlotDuration = protectedApi.createDuration(input);
2221                const slotsPerLabel = protectedApi.wholeDivideDurations(labelInterval, trySlotDuration);
2222                if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
2223                    slotDuration = trySlotDuration;
2224                    break;
2225                }
2226            }
2227            // only allow the value if it won't exceed the view's # of slots limit
2228            if (slotDuration) {
2229                const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
2230                if (slotCnt > MAX_AUTO_CELLS) {
2231                    slotDuration = null;
2232                }
2233            }
2234            // use the label interval as a last resort
2235            if (!slotDuration) {
2236                slotDuration = labelInterval;
2237            }
2238            tDateProfile.slotDuration = slotDuration;
2239        }
2240        return slotDuration;
2241    }
2242    function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
2243        let format1;
2244        let format2;
2245        let unit1 = null;
2246        let unit2 = null;
2247        const { labelInterval } = tDateProfile;
2248        const { currentRange } = dateProfile;
2249        let unit = protectedApi.greatestDurationDenominator(labelInterval).unit;
2250        const weekNumbersVisible = allOptions.weekNumbers;
2251        let format0 = (format1 = (format2 = null));
2252        let unit0 = null;
2253        // NOTE: weekNumber computation function wont work
2254        if ((unit === 'week') && !weekNumbersVisible) {
2255            unit = 'day';
2256        }
2257        switch (unit) {
2258            case 'year':
2259                format0 = { year: 'numeric' }; // '2015'
2260                unit0 = 'year';
2261                break;
2262            case 'month':
2263                if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
2264                    format0 = { year: 'numeric' }; // '2015'
2265                    unit0 = 'year';
2266                }
2267                format1 = { month: 'short' }; // 'Jan'
2268                unit1 = 'month';
2269                break;
2270            case 'week':
2271                if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
2272                    format0 = { year: 'numeric' }; // '2015'
2273                    unit0 = 'year';
2274                }
2275                format1 = { week: 'narrow' }; // 'Wk4'
2276                unit1 = 'week';
2277                break;
2278            case 'day':
2279                if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
2280                    format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
2281                    unit0 = 'month';
2282                }
2283                else if (dateEnv.diffWholeMonths(currentRange.start, currentRange.end) > 1) {
2284                    format0 = { month: 'long' }; // 'January'
2285                    unit0 = 'month';
2286                }
2287                if (weekNumbersVisible) {
2288                    format1 = { week: 'short' }; // 'Wk 4'
2289                    unit1 = 'week';
2290                }
2291                format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
2292                unit2 = 'day';
2293                break;
2294            case 'hour':
2295                if (weekNumbersVisible) {
2296                    format0 = { week: 'short' }; // 'Wk 4'
2297                    unit0 = 'week';
2298                }
2299                if (protectedApi.diffWholeDays(currentRange.start, currentRange.end) > 1) {
2300                    format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
2301                    unit1 = 'day';
2302                }
2303                format2 = {
2304                    hour: 'numeric',
2305                    minute: '2-digit',
2306                    omitZeroMinute: true,
2307                    meridiem: 'short',
2308                };
2309                unit2 = null;
2310                break;
2311            case 'minute':
2312                // sufficiently large number of different minute cells?
2313                if ((protectedApi.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
2314                    format0 = {
2315                        hour: 'numeric',
2316                        meridiem: 'short',
2317                    };
2318                    format1 = (params) => (':' + protectedApi.padStart(params.date.minute, 2) // ':30'
2319                    );
2320                }
2321                else {
2322                    format0 = {
2323                        hour: 'numeric',
2324                        minute: 'numeric',
2325                        meridiem: 'short',
2326                    };
2327                }
2328                break;
2329            case 'second':
2330                // sufficiently large number of different second cells?
2331                if ((protectedApi.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
2332                    format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
2333                    format1 = (params) => (':' + protectedApi.padStart(params.date.second, 2) // ':30'
2334                    );
2335                }
2336                else {
2337                    format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
2338                }
2339                break;
2340            case 'millisecond':
2341                format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
2342                format1 = (params) => ('.' + protectedApi.padStart(params.millisecond, 3));
2343                break;
2344        }
2345        return {
2346            formats: [].concat(format0 || [], format1 || [], format2 || []),
2347            units: [].concat(format0 ? [unit0] : [], format1 ? [unit1] : [], format2 ? [unit2] : []),
2348        };
2349    }
2350    function inferHeaderFormatUnit(rawFormat) {
2351        if (typeof rawFormat === 'object' && rawFormat) {
2352            if (rawFormat.fractionalSecondDigits ||
2353                rawFormat.second ||
2354                rawFormat.minute ||
2355                rawFormat.hour ||
2356                rawFormat.timeZoneName) {
2357                return 'time';
2358            }
2359            if (rawFormat.day || rawFormat.weekday) {
2360                return 'day';
2361            }
2362            if (rawFormat.week) {
2363                return 'week';
2364            }
2365            if (rawFormat.month) {
2366                return 'month';
2367            }
2368            if (rawFormat.year) {
2369                return 'year';
2370            }
2371        }
2372        return null;
2373    }
2374    function buildCellRows(tDateProfile, dateEnv, majorUnit) {
2375        let slotDates = tDateProfile.slotDates;
2376        let formats = tDateProfile.headerFormats;
2377        let cellRows = formats.map(() => []); // indexed by row,col
2378        let slotAsDays = protectedApi.asCleanDays(tDateProfile.slotDuration);
2379        let guessedSlotUnit = slotAsDays === 7 ? 'week' :
2380            slotAsDays === 1 ? 'day' :
2381                null;
2382        // specifically for navclicks
2383        let rowUnitsFromFormats = tDateProfile.headerFormatUnits;
2384        // builds cellRows and slotCells
2385        for (let i = 0; i < slotDates.length; i += 1) {
2386            let date = slotDates[i];
2387            for (let row = 0; row < formats.length; row += 1) {
2388                let format = formats[row];
2389                let rowCells = cellRows[row];
2390                let leadingCell = rowCells[rowCells.length - 1];
2391                let isLastRow = row === formats.length - 1;
2392                let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
2393                let isMajor = protectedApi.isMajorUnit(date, majorUnit, dateEnv);
2394                let newCell = null;
2395                let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
2396                if (isSuperRow) {
2397                    let text = joinDateTimeFormatParts(dateEnv.formatToParts(date, format));
2398                    if (!leadingCell || (leadingCell.text !== text)) {
2399                        newCell = buildCellObject(date, isMajor, text, rowUnit);
2400                    }
2401                    else {
2402                        leadingCell.colspan += 1;
2403                    }
2404                }
2405                else if (!leadingCell ||
2406                    protectedApi.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
2407                    let text = joinDateTimeFormatParts(dateEnv.formatToParts(date, format));
2408                    newCell = buildCellObject(date, isMajor, text, rowUnit);
2409                }
2410                else {
2411                    leadingCell.colspan += 1;
2412                }
2413                if (newCell) {
2414                    rowCells.push(newCell);
2415                }
2416            }
2417        }
2418        return cellRows;
2419    }
2420    function buildCellObject(date, isMajor, text, rowUnit) {
2421        return { date, isMajor, text, rowUnit, colspan: 1 }; // colspan mutated later
2422    }
2423
2424    class TimelineSlatCell extends protectedApi.BaseComponent {
2425        constructor() {
2426            super(...arguments);
2427            // memo
2428            this.getDateMeta = protectedApi.memoize(protectedApi.getDateMeta);
2429        }
2430        render() {
2431            let { props, context } = this;
2432            let { dateEnv, options } = context;
2433            let { date, tDateProfile, isMajor } = props;
2434            let dateMeta = this.getDateMeta(props.date, dateEnv, props.dateProfile, props.todayRange, props.nowDate);
2435            let isMinor = tDateProfile.isTimeScale &&
2436                !protectedApi.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval));
2437            let renderProps = {
2438                ...dateMeta,
2439                isMajor,
2440                isMinor,
2441                view: context.viewApi,
2442            };
2443            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].alignStart, // shrinks width of InnerContent
2444                props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone, classNames__default["default"].internalTimelineSlot), attrs: {
2445                    'data-date': dateEnv.formatIso(date, {
2446                        omitTimeZoneOffset: true,
2447                        omitTime: !tDateProfile.isTimeScale,
2448                    }),
2449                    ...(dateMeta.isToday ? { 'aria-current': 'date' } : {}),
2450                }, style: {
2451                    width: props.width,
2452                }, renderProps: renderProps, generatorName: undefined, classNameGenerator: options.slotLaneClass, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }));
2453        }
2454    }
2455
2456    class TimelineSlats extends protectedApi.BaseComponent {
2457        render() {
2458            let { props } = this;
2459            let { tDateProfile, slotWidth, slatStartIndex, slatCount } = props;
2460            let { slotDates, slotDatesMajor } = tDateProfile;
2461            slatStartIndex = props.slatStartIndex || 0;
2462            if (slatStartIndex || slatCount !== undefined) {
2463                slotDates = slotDates.slice(slatStartIndex, slatStartIndex + slatCount);
2464            }
2465            return (jsxRuntime.jsx("div", { "aria-hidden": true, className: publicApi.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].fillY), style: {
2466                    height: props.height,
2467                    width: (props.slotWidth ?? 0) * slotDates.length,
2468                    insetInlineStart: 0,
2469                }, children: slotDates.map((slotDate, i) => {
2470                    let key = slotDate.toISOString();
2471                    return (jsxRuntime.jsx(TimelineSlatCell, { date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isMajor: slotDatesMajor[slatStartIndex + i], borderStart: Boolean(slatStartIndex + i), 
2472                        // dimensions
2473                        width: slotWidth }, key));
2474                }) }));
2475        }
2476    }
2477
2478    class TimelineHeaderCell extends protectedApi.BaseComponent {
2479        constructor() {
2480            super(...arguments);
2481            this.state = {};
2482            // memo
2483            this.getDateMeta = protectedApi.memoize(protectedApi.getDateMeta);
2484            // ref
2485            this.innerWrapperElRef = compat.createRef();
2486        }
2487        render() {
2488            let { props, state, context } = this;
2489            let { dateEnv, options } = context;
2490            let { cell, dateProfile, tDateProfile } = props;
2491            // the cell.rowUnit is f'd
2492            // giving 'month' for a 3-day view
2493            // workaround: to infer day, do NOT time
2494            let dateMeta = this.getDateMeta(cell.date, dateEnv, dateProfile, props.todayRange, props.nowDate);
2495            let hasNavLink = options.navLinks && !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
2496            let isTime = tDateProfile.isTimeScale && !props.rowLevel; // HACK: faulty way of determining this
2497            let renderProps = {
2498                ...dateMeta,
2499                level: props.rowLevel,
2500                isMajor: cell.isMajor,
2501                isMinor: false,
2502                isNarrow: false,
2503                isTime,
2504                hasNavLink,
2505                text: cell.text,
2506                isFirst: props.isFirst,
2507                view: context.viewApi,
2508            };
2509            const { slotHeaderAlign } = options;
2510            const align = this.align =
2511                typeof slotHeaderAlign === 'function'
2512                    ? slotHeaderAlign({ level: props.rowLevel, isTime })
2513                    : slotHeaderAlign;
2514            const isSticky = this.isSticky =
2515                props.rowLevel && options.slotHeaderSticky !== false;
2516            let edgeCoord;
2517            if (isSticky) {
2518                if (align === 'center') {
2519                    if (state.innerWidth != null) {
2520                        edgeCoord = `calc(50% - ${state.innerWidth / 2}px)`;
2521                    }
2522                }
2523                else {
2524                    edgeCoord = (typeof options.slotHeaderSticky === 'number' ||
2525                        typeof options.slotHeaderSticky === 'string') ? options.slotHeaderSticky : 0;
2526                }
2527            }
2528            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, props.isFirst ? classNames__default["default"].borderNone : classNames__default["default"].borderOnlyS, align === 'center' ? classNames__default["default"].alignCenter :
2529                    align === 'end' ? classNames__default["default"].alignEnd :
2530                        classNames__default["default"].alignStart, classNames__default["default"].internalTimelineSlot), attrs: {
2531                    'data-date': dateEnv.formatIso(cell.date, {
2532                        omitTime: !tDateProfile.isTimeScale,
2533                        omitTimeZoneOffset: true,
2534                    }),
2535                    ...(dateMeta.isToday ? { 'aria-current': 'date' } : {}),
2536                }, style: {
2537                    width: props.slotWidth != null
2538                        ? props.slotWidth * cell.colspan
2539                        : undefined,
2540                }, renderProps: renderProps, generatorName: "slotHeaderContent", customGenerator: options.slotHeaderContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotHeaderClass, didMount: options.slotHeaderDidMount, willUnmount: options.slotHeaderWillUnmount, children: (InnerContent) => (jsxRuntime.jsx("div", { ref: this.innerWrapperElRef, className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, isSticky && classNames__default["default"].sticky), style: {
2541                        left: edgeCoord,
2542                        right: edgeCoord,
2543                    }, children: jsxRuntime.jsx(InnerContent, { tag: 'div', attrs: hasNavLink
2544                            // not tabbable because parent is aria-hidden
2545                            ? protectedApi.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
2546                            : {} // don't bother with aria-hidden because parent already hidden
2547                        , className: protectedApi.generateClassName(options.slotHeaderInnerClass, renderProps) }) })) }));
2548        }
2549        componentDidMount() {
2550            this._isUnmounting = false;
2551            const { props } = this;
2552            const innerWrapperEl = this.innerWrapperElRef.current; // TODO: make dynamic with useEffect
2553            this.disconnectSize = protectedApi.watchSize(innerWrapperEl, (width, height) => {
2554                if (this._isUnmounting)
2555                    return;
2556                protectedApi.setRef(props.innerWidthRef, width);
2557                protectedApi.setRef(props.innerHeightRef, height);
2558                if (this.align === 'center' && this.isSticky) {
2559                    this.setState({ innerWidth: width });
2560                }
2561            });
2562        }
2563        componentWillUnmount() {
2564            const { props } = this;
2565            this._isUnmounting = true;
2566            this.disconnectSize();
2567            protectedApi.setRef(props.innerWidthRef, null);
2568            protectedApi.setRef(props.innerHeightRef, null);
2569        }
2570    }
2571    // Utils
2572    // -------------------------------------------------------------------------------------------------
2573    function renderInnerContent(renderProps) {
2574        return renderProps.text;
2575    }
2576
2577    class TimelineHeaderRow extends protectedApi.BaseComponent {
2578        constructor() {
2579            super(...arguments);
2580            this.state = {};
2581            // refs
2582            this.innerWidthRefMap = new protectedApi.RefMap(() => {
2583                protectedApi.afterSize(this.handleInnerWidths);
2584            });
2585            this.innerHeightRefMap = new protectedApi.RefMap(() => {
2586                protectedApi.afterSize(this.handleInnerHeights);
2587            });
2588            this.handleInnerWidths = () => {
2589                if (this._isUnmounting)
2590                    return;
2591                const innerWidthMap = this.innerWidthRefMap.current;
2592                let max = null;
2593                for (const innerWidth of innerWidthMap.values()) {
2594                    max = Math.max(max || 0, innerWidth);
2595                }
2596                // TODO: ensure not equal?
2597                protectedApi.setRef(this.props.innerWidthRef, max);
2598            };
2599            this.handleInnerHeights = () => {
2600                if (this._isUnmounting)
2601                    return;
2602                const innerHeightMap = this.innerHeightRefMap.current;
2603                let max = null;
2604                for (const innerHeight of innerHeightMap.values()) {
2605                    max = Math.max(max || 0, innerHeight);
2606                }
2607                // TODO: ensure not equal?
2608                protectedApi.setRef(this.props.innerHeighRef, max);
2609                this.setState({ innerHeight: max });
2610            };
2611        }
2612        render() {
2613            const { props, innerWidthRefMap, innerHeightRefMap, state, context } = this;
2614            const { options } = context;
2615            let { cells, cellStartIndex, cellCount } = props;
2616            cellStartIndex = cellStartIndex || 0;
2617            if (cellStartIndex || cellCount !== undefined) {
2618                cells = cells.slice(cellStartIndex, cellStartIndex + cellCount);
2619            }
2620            return (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(props.className, options.slotHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, props.rowLevel // not the last row?
2621                    ? classNames__default["default"].borderOnlyB
2622                    : classNames__default["default"].borderNone), style: {
2623                    insetInlineStart: props.insetInlineStart,
2624                    width: props.width,
2625                    // we assign height because we allow cells to have distorted heights for visual effect
2626                    // but we still want to keep the overall extrenal mass
2627                    height: state.innerHeight,
2628                }, children: cells.map((cell, cellI) => {
2629                    // TODO: make this part of the cell obj?
2630                    // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
2631                    // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
2632                    // TODO: use rowUnit to key the Row itself?
2633                    const key = cell.rowUnit + ':' + cell.date.toISOString();
2634                    return (jsxRuntime.jsx(TimelineHeaderCell, { cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isFirst: cellStartIndex + cellI === 0, 
2635                        // refs
2636                        innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key), 
2637                        // dimensions
2638                        slotWidth: props.slotWidth }, key));
2639                }) }));
2640        }
2641        componentDidMount() {
2642            this._isUnmounting = false;
2643        }
2644        componentWillUnmount() {
2645            this._isUnmounting = true;
2646            protectedApi.setRef(this.props.innerWidthRef, null);
2647            protectedApi.setRef(this.props.innerHeighRef, null);
2648        }
2649    }
2650
2651    // Timeline-specific
2652    // -------------------------------------------------------------------------------------------------
2653    /*
2654    TODO: DRY with computeSlatHeight?
2655    */
2656    function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
2657        if (labelInnerWidth == null || viewportWidth == null) {
2658            return [undefined, undefined, false];
2659        }
2660        if (slatMinWidth == null) {
2661            slatMinWidth = Math.ceil((labelInnerWidth + 1) / slatsPerLabel);
2662        }
2663        const slatTryWidth = viewportWidth / slatCnt;
2664        let slotLiquid;
2665        let slatWidth;
2666        if (slatTryWidth >= slatMinWidth) {
2667            slotLiquid = true;
2668            slatWidth = slatTryWidth;
2669        }
2670        else {
2671            slotLiquid = false;
2672            slatWidth = Math.max(slatMinWidth, slatTryWidth);
2673        }
2674        return [slatWidth * slatCnt, slatWidth, slotLiquid];
2675    }
2676    function timeToCoord(// pixels
2677    time, dateEnv, dateProfile, tDateProfile, slowWidth) {
2678        let date = dateEnv.add(dateProfile.activeRange.start, time);
2679        if (!tDateProfile.isTimeScale) {
2680            date = protectedApi.startOfDay(date);
2681        }
2682        return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
2683    }
2684    function dateToCoord(// pixels
2685    date, dateEnv, tDateProfile, slotWidth) {
2686        let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
2687        let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
2688        return slotCoverage * slotWidth;
2689    }
2690    /*
2691    returned value is between 0 and the number of snaps
2692    */
2693    function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
2694        let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
2695        if (snapDiff < 0) {
2696            return 0;
2697        }
2698        if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
2699            return tDateProfile.snapCnt;
2700        }
2701        let snapDiffInt = Math.floor(snapDiff);
2702        let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
2703        if (protectedApi.isInt(snapCoverage)) { // not an in-between value
2704            snapCoverage += snapDiff - snapDiffInt; // add the remainder
2705        }
2706        else {
2707            // a fractional value, meaning the date is not visible
2708            // always round up in this case. works for start AND end dates in a range.
2709            snapCoverage = Math.ceil(snapCoverage);
2710        }
2711        return snapCoverage;
2712    }
2713
2714    class TimelineNowIndicatorLine extends protectedApi.BaseComponent {
2715        render() {
2716            const { props, context } = this;
2717            const clipStart = props.clipStart ?? 0;
2718            const xStyle = props.slotWidth == null
2719                ? {}
2720                : {
2721                    insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth) - clipStart
2722                };
2723            return (jsxRuntime.jsxs("div", { className: classNames__default["default"].fill, style: {
2724                    zIndex: 2, // inlined from $now-indicator-z
2725                    pointerEvents: 'none', // TODO: className
2726                }, children: [jsxRuntime.jsx(protectedApi.NowIndicatorLineContainer, { className: publicApi.joinClassNames(classNames__default["default"].fillY, classNames__default["default"].noMarginY, classNames__default["default"].borderlessY), style: xStyle, date: props.nowDate }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, // better for negative margins
2727                        classNames__default["default"].fillY), style: xStyle, children: jsxRuntime.jsx("div", { 
2728                            // stickiness on NowIndicatorDot misbehaves b/c of negative marginss
2729                            className: classNames__default["default"].stickyT, children: jsxRuntime.jsx(protectedApi.NowIndicatorDot, {}) }) })] }));
2730        }
2731    }
2732
2733    /*
2734    TODO: DRY with other NowIndicator components
2735    */
2736    class TimelineNowIndicatorArrow extends protectedApi.BaseComponent {
2737        render() {
2738            const { props, context } = this;
2739            const xStyle = props.slotWidth == null
2740                ? {}
2741                : {
2742                    insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
2743                };
2744            return (jsxRuntime.jsx("div", { 
2745                // crop any overflow that the arrow/line might cause
2746                // TODO: just do this on the entire canvas within the scroller
2747                className: publicApi.joinClassNames(classNames__default["default"].fill, classNames__default["default"].crop), style: {
2748                    zIndex: 2, // inlined from $now-indicator-z
2749                    pointerEvents: 'none', // TODO: className
2750                }, children: jsxRuntime.jsx(protectedApi.NowIndicatorHeaderContainer, { className: classNames__default["default"].abs, style: xStyle, date: props.nowDate }) }));
2751        }
2752    }
2753
2754    function getTimelineSlotEl(parentEl, index) {
2755        return parentEl.querySelectorAll(`.${classNames__default["default"].internalTimelineSlot}`)[index];
2756    }
2757
2758    /*
2759    TODO: rename this file!
2760    */
2761    // returned value is between 0 and the number of snaps
2762    function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
2763        let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
2764        if (snapDiff < 0) {
2765            return 0;
2766        }
2767        if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
2768            return tDateProfile.snapCnt;
2769        }
2770        let snapDiffInt = Math.floor(snapDiff);
2771        let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
2772        if (protectedApi.isInt(snapCoverage)) { // not an in-between value
2773            snapCoverage += snapDiff - snapDiffInt; // add the remainder
2774        }
2775        else {
2776            // a fractional value, meaning the date is not visible
2777            // always round up in this case. works for start AND end dates in a range.
2778            snapCoverage = Math.ceil(snapCoverage);
2779        }
2780        return snapCoverage;
2781    }
2782
2783    class TimelineLaneSlicer extends protectedApi.Slicer {
2784        sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
2785            let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
2786            let segs = [];
2787            // protect against when the span is entirely in an invali
2787d date region
2788            if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
2789                < computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
2790                // intersect the footprint's range with the grid's range
2791                let slicedRange = protectedApi.intersectRanges(normalRange, tDateProfile.normalizedRange);
2792                if (slicedRange) {
2793                    segs.push({
2794                        startDate: slicedRange.start,
2795                        endDate: slicedRange.end,
2796                        isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
2797                            && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
2798                        isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
2799                            && isValidDate(protectedApi.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
2800                    });
2801                }
2802            }
2803            return segs;
2804        }
2805    }
2806
2807    const DEFAULT_TIME_FORMAT = protectedApi.createFormatter({
2808        hour: 'numeric',
2809        minute: '2-digit',
2810        omitZeroMinute: true,
2811        meridiem: 'narrow',
2812    });
2813    class TimelineEvent extends protectedApi.BaseComponent {
2814        render() {
2815            let { props } = this;
2816            return (jsxRuntime.jsx(protectedApi.StandardEvent, { ...props, display: 'row', defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale }));
2817        }
2818    }
2819
2820    class TimelineLaneMoreLink extends protectedApi.BaseComponent {
2821        render() {
2822            let { props } = this;
2823            let { hiddenSegs, resourceId } = props;
2824            let dateSpanProps = resourceId ? { resourceId } : {};
2825            return (jsxRuntime.jsx(protectedApi.MoreLinkContainer, { display: 'row', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, isNarrow: false, isMicro: false, popoverContent: () => (jsxRuntime.jsx(jsxRuntime.Fragment, { children: hiddenSegs.map((seg) => {
2826                        let { eventRange } = seg;
2827                        let { instanceId } = eventRange.instance;
2828                        let isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
2829                        let isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
2830                        let isInvisible = isDragging || isResizing;
2831                        return (jsxRuntime.jsx("div", { style: { visibility: isInvisible ? 'hidden' : undefined }, children: jsxRuntime.jsx(TimelineEvent, { isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: false, isSelected: instanceId === props.eventSelection, ...protectedApi.getEventRangeMeta(eventRange, props.todayRange, props.nowDate) }) }, instanceId));
2832                    }) })) }));
2833        }
2834    }
2835
2836    function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth, clipStart = 0, // uses it as a new origin!
2837    clipEnd = Infinity) {
2838        const res = {};
2839        for (const seg of segs) {
2840            const horizontals = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth, clipStart, clipEnd);
2841            if (horizontals) {
2842                res[protectedApi.getEventKey(seg)] = horizontals;
2843            }
2844        }
2845        return res;
2846    }
2847    function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth, clipStart = 0, // uses it as a new origin!
2848    clipEnd = Infinity) {
2849        let startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
2850        let endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
2851        startCoord = Math.max(startCoord, clipStart);
2852        endCoord = Math.min(endCoord, clipEnd);
2853        if (startCoord < endCoord) {
2854            let size = endCoord - startCoord;
2855            if (segMinWidth) {
2856                size = Math.max(size, segMinWidth);
2857            }
2858            return { start: startCoord - clipStart, size };
2859        }
2860    }
2861    function computeFgSegPlacements(// mostly horizontals
2862    segs, segHorizontals, // TODO: use Map
2863    segHeights, // keyed by instanceId
2864    hiddenGroupHeights, strictOrder, maxDepth) {
2865        const segRanges = [];
2866        // isn't it true that there will either be ALL hcoords or NONE? can optimize
2867        for (const seg of segs) {
2868            const hcoords = segHorizontals[protectedApi.getEventKey(seg)];
2869            if (hcoords) {
2870                segRanges.push({
2871                    ...seg,
2872                    start: hcoords.start,
2873                    end: hcoords.start + hcoords.size,
2874                });
2875            }
2876        }
2877        const hierarchy = new protectedApi.SegHierarchy(segRanges, (seg) => segHeights.get(protectedApi.getEventKey(seg)), strictOrder, undefined, // maxCoord
2878        maxDepth);
2879        const segTops = new Map();
2880        hierarchy.traverseSegs((seg, segTop) => {
2881            segTops.set(protectedApi.getEventKey(seg), segTop);
2882        });
2883        const { hiddenSegs } = hierarchy;
2884        let totalHeight = 0;
2885        for (const segRange of segRanges) {
2886            const segKey = protectedApi.getEventKey(segRange);
2887            const segHeight = segHeights.get(segKey);
2888            const segTop = segTops.get(segKey);
2889            if (segHeight != null) {
2890                if (segTop != null) {
2891                    totalHeight = Math.max(totalHeight, segTop + segHeight);
2892                }
2893            }
2894        }
2895        const hiddenGroups = protectedApi.groupIntersectingSegs(hiddenSegs);
2896        const hiddenGroupTops = new Map();
2897        // HACK for hiddenGroup findInsertion() call
2898        hierarchy.strictOrder = true;
2899        for (const hiddenGroup of hiddenGroups) {
2900            const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
2901            const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
2902            hiddenGroupTops.set(hiddenGroup.key, top);
2903            totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
2904        }
2905        return [
2906            segTops,
2907            hiddenGroups,
2908            hiddenGroupTops,
2909            totalHeight,
2910        ];
2911    }
2912
2913    /*
2914    TODO: make DRY with other Event Harnesses
2915    */
2916    class TimelineEventHarness extends compat.Component {
2917        constructor() {
2918            super(...arguments);
2919            // ref
2920            this.rootElRef = compat.createRef();
2921        }
2922        render() {
2923            const { props } = this;
2924            return (jsxRuntime.jsx("div", { className: classNames__default["default"].abs, style: props.style, ref: this.rootElRef, children: props.children }));
2925        }
2926        componentDidMount() {
2927            this._isUnmounting = false;
2928            const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
2929            this.disconnectHeight = protectedApi.watchHeight(rootEl, (height) => {
2930                if (this._isUnmounting)
2931                    return;
2932                protectedApi.setRef(this.props.heightRef, height);
2933            });
2934        }
2935        componentWillUnmount() {
2936            this._isUnmounting = true;
2937            this.disconnectHeight();
2938            protectedApi.setRef(this.props.heightRef, null);
2939        }
2940    }
2941
2942    class TimelineFg extends protectedApi.BaseComponent {
2943        constructor() {
2944            super(...arguments);
2945            this.state = {};
2946            // memo
2947            this.sortEventSegs = protectedApi.memoize(protectedApi.sortEventSegs);
2948            // refs
2949            this.segHeightRefMap = new protectedApi.RefMap(() => {
2950                protectedApi.afterSize(this.handleSegHeights);
2951            });
2952            this.moreLinkHeightRefMap = new protectedApi.RefMap(() => {
2953                protectedApi.afterSize(this.handleMoreLinkHeights);
2954            });
2955            this.handleMoreLinkHeights = () => {
2956                if (this._isUnmounting)
2957                    return;
2958                this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
2959            };
2960            this.handleSegHeights = () => {
2961                if (this._isUnmounting)
2962                    return;
2963                this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
2964            };
2965        }
2966        /*
2967        TODO: lots of memoization needed here!
2968        */
2969        render() {
2970            let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
2971            let { options } = context;
2972            let { tDateProfile } = props;
2973            let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
2974                (props.eventResize ? props.eventResize.segs : null) ||
2975                [];
2976            let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
2977            let fgSegHorizontals = computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth, props.clipStart, props.clipEnd);
2978            let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
2979            this.totalHeight = totalHeight;
2980            return (jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(classNames__default["default"].rel, classNames__default["default"].noShrink), style: {
2981                    height: totalHeight,
2982                }, children: [this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, hiddenGroups, hiddenGroupTops, 
2983                    /* isMirror = */ false), this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
2984                        ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth, props.clipStart, props.clipEnd)
2985                        : {}, fgSegTops, 
2986                    /* hiddenGroups = */ [], 
2987                    /* hiddenGroupTops = */ new Map(), 
2988                    /* isMirror = */ true)] }));
2989        }
2990        renderFgSegs(segs, segHorizontals, segTops, hiddenGroups, hiddenGroupTops, isMirror) {
2991            const { props, segHeightRefMap, moreLinkHeightRefMap } = this;
2992            return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [segs.map((seg) => {
2993                        const { eventRange } = seg;
2994                        const { instanceId } = eventRange.instance;
2995                        const segTop = segTops.get(instanceId);
2996                        const segHorizontalMaybe = segHorizontals[instanceId];
2997                        const segHorizontal = segHorizontalMaybe || {};
2998                        const isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
2999                        const isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
3000                        const isInvisible = !isMirror && (isDragging || isResizing || !segHorizontalMaybe || segTop == null);
3001                        const isSelected = instanceId === props.eventSelection;
3002                        return (jsxRuntime.jsx(TimelineEventHarness, { style: {
3003                                visibility: isInvisible ? 'hidden' : undefined,
3004                                zIndex: isSelected ? 1000 : 1, // scope z
3004-indexes within; HACK: relies on hardcoded z-index offset; fragile if stacking context changes
3005                                top: segTop || 0,
3006                                insetInlineStart: segHorizontal.start,
3007                                width: segHorizontal.size,
3008                            }, heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId), children: jsxRuntime.jsx(TimelineEvent, { isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: isMirror, isSelected: isSelected, ...protectedApi.getEventRangeMeta(eventRange, props.todayRange, props.nowDate) }) }, instanceId));
3009                    }), hiddenGroups.map((hiddenGroup) => (jsxRuntime.jsx(TimelineEventHarness, { style: {
3010                            top: hiddenGroupTops.get(hiddenGroup.key) || 0,
3011                            insetInlineStart: hiddenGroup.start,
3012                            width: hiddenGroup.end - hiddenGroup.start,
3013                        }, heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key), children: jsxRuntime.jsx(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, resourceId: props.resourceId }) }, hiddenGroup.key)))] }));
3014        }
3015        componentDidMount() {
3016            this._isUnmounting = false;
3017            this.fireHeight(); // could be ready to fire if zero events and zero height
3018        }
3019        componentDidUpdate() {
3020            this.fireHeight();
3021        }
3022        componentWillUnmount() {
3023            this._isUnmounting = true;
3024            this.firedTotalHeight = undefined;
3025            protectedApi.setRef(this.props.heightRef, null);
3026        }
3027        fireHeight() {
3028            if (this.totalHeight !== this.firedTotalHeight) {
3029                this.firedTotalHeight = this.totalHeight;
3030                protectedApi.setRef(this.props.heightRef, this.totalHeight);
3031            }
3032        }
3033    }
3034
3035    class TimelineBg extends protectedApi.BaseComponent {
3036        render() {
3037            let { props } = this;
3038            let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs || []);
3039            return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [this.renderSegs(props.businessHourSegs || [], 'non-business'), this.renderSegs(props.bgEventSegs || [], 'bg-event'), this.renderSegs(highlightSeg, 'highlight')] }));
3040        }
3041        renderSegs(segs, fillType) {
3042            const { props, context } = this;
3043            const { dateEnv, options } = context;
3044            const { tDateProfile, todayRange, nowDate, slotWidth } = props;
3045            const clipStart = props.clipStart ?? 0;
3046            const clipEnd = props.clipEnd ?? Infinity;
3047            return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: segs.map((seg) => {
3048                    if (slotWidth != null) {
3049                        let { start, size } = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
3050                        let end = start + size;
3051                        start = Math.max(start, clipStart);
3052                        end = Math.min(end, clipEnd);
3053                        if (start < end) {
3054                            return (jsxRuntime.jsx("div", { className: classNames__default["default"].fillY, style: {
3055                                    insetInlineStart: start - clipStart,
3056                                    width: end - start,
3057                                }, children: fillType === 'bg-event' ? (jsxRuntime.jsx(protectedApi.BgEvent, { eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isVertical: false, ...protectedApi.getEventRangeMeta(seg.eventRange, todayRange, nowDate) })) : (protectedApi.renderFill(fillType, options)) }, protectedApi.buildEventRangeKey(seg.eventRange)));
3058                        }
3059                    }
3060                }) }));
3061        }
3062    }
3063
3064    class TimelineView extends protectedApi.DateComponent {
3065        constructor() {
3066            super(...arguments);
3067            this.state = {};
3068            // memoized
3069            this.buildTimelineDateProfile = protectedApi.memoize(buildTimelineDateProfile);
3070            this.computeSlotWidth = protectedApi.memoize(computeSlotWidth);
3071            // refs
3072            this.headerScrollerRef = compat.createRef();
3073            this.bodyScrollerRef = compat.createRef();
3074            this.footerScrollerRef = compat.createRef();
3075            this.headerRowInnerWidthMap = new protectedApi.RefMap(() => {
3076                protectedApi.afterSize(this.handleSlotInnerWidths);
3077            });
3078            this.slicer = new TimelineLaneSlicer();
3079            // Sizing
3080            // -----------------------------------------------------------------------------------------------
3081            this.handleSlotInnerWidths = () => {
3082                if (this._isUnmounting)
3083                    return;
3084                const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
3085                if (headerSlotInnerWidth != null && headerSlotInnerWidth !== this.state.slotInnerWidth) {
3086                    this.setState({ slotInnerWidth: headerSlotInnerWidth });
3087                }
3088            };
3089            this.handleTotalWidth = (totalWidth) => {
3090                if (this._isUnmounting)
3091                    return;
3092                this.setState({
3093                    totalWidth,
3094                });
3095            };
3096            this.handleClientWidth = (clientWidth) => {
3097                if (this._isUnmounting)
3098                    return;
3099                this.setState({
3100                    clientWidth,
3101                });
3102            };
3103            this.handleTimeScrollRequest = (scrollTime) => {
3104                this.scrollTime = scrollTime;
3105                this.scrollX = undefined;
3106                this.applyTimeScroll();
3107            };
3108            this.handleTimeScrollStart = (isDevice) => {
3109                if (isDevice) {
3110                    this.scrollX = undefined;
3111                    this.scrollTime = undefined;
3112                }
3113            };
3114            this.handleTimeScrollEnd = (isDevice) => {
3115                if (isDevice) {
3116                    this.scrollX = this.syncedScroller.x;
3117                    this.scrollTime = undefined;
3118                }
3119            };
3120            // Hit System
3121            // -----------------------------------------------------------------------------------------------
3122            this.handeBodyEl = (el) => {
3123                this.bodyEl = el;
3124                if (el) {
3125                    this.context.registerInteractiveComponent(this, { el });
3126                }
3127                else {
3128                    this.context.unregisterInteractiveComponent(this);
3129                }
3130            };
3131        }
3132        render() {
3133            const { props, state, context } = this;
3134            const { options } = context;
3135            const { borderlessX, borderlessTop, borderlessBottom } = protectedApi.computeViewBorderless(options);
3136            const { totalWidth, clientWidth } = state;
3137            const endScrollbarWidth = (totalWidth != null && clientWidth != null)
3138                ? totalWidth - clientWidth
3139                : undefined;
3140            /* date */
3141            const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
3142            const { cellRows } = tDateProfile;
3143            let { unit: timerUnit, value: timerUnitValue } = protectedApi.greatestDurationDenominator(tDateProfile.slotDuration);
3144            /* table settings */
3145            const verticalScrolling = !props.forPrint && !protectedApi.getIsHeightAuto(options);
3146            const tableHeaderSticky = !props.forPrint && protectedApi.getTableHeaderSticky(options);
3147            const footerScrollbarSticky = !props.forPrint && protectedApi.getFooterScrollbarSticky(options);
3148            /* table positions */
3149            const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
3150            clientWidth);
3151            /* sliced */
3152            let slicedProps = this.slicer.sliceProps(props, props.dateProfile, tDateProfile.isTimeScale ? null : options.nextDayThreshold, context, // wish we didn't have to pass in the rest of the args...
3153            props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
3154            return (jsxRuntime.jsx(protectedApi.NowTimer, { unit: timerUnit, unitValue: timerUnitValue, children: (nowDate, todayRange) => {
3155                    const enableNowIndicator = // TODO: DRY
3156                     !props.forPrint &&
3157                        options.nowIndicator &&
3158                        slotWidth != null &&
3159                        protectedApi.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
3160                    return (jsxRuntime.jsxs(protectedApi.ViewContainer, { viewSpec: context.viewSpec, className: publicApi.joinClassNames(
3161                        // HACK for Safari print-mode, where noScrollbars won't take effect for
3162                        // the below Scrollers if they have liquid flex height
3163                        !props.forPrint && classNames__default["default"].flexCol, props.className, protectedApi.generateClassName(options.tableClass, {
3164                            borderlessX,
3165                            borderlessTop,
3166                            borderlessBottom,
3167                            multiMonthColumns: 0,
3168                        }), classNames__default["default"].isolate), children: [jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableHeaderClass, {
3169                                    isSticky: tableHeaderSticky,
3170                                    borderlessX,
3171                                    borderlessTop,
3172                                    borderlessBottom,
3173                                    multiMonthColumns: 0,
3174                                }), classNames__default["default"].flexCol, tableHeaderSticky && classNames__default["default"].tableHeaderSticky), style: {
3175                                    zIndex: 1,
3176                                }, children: [jsxRuntime.jsxs(protectedApi.Scroller, { horizontal: true, hideScrollbars: true, className: classNames__default["default"].flexRow, ref: this.headerScrollerRef, children: [jsxRuntime.jsxs("div", { 
3177                                                // TODO: DRY
3178                                                className: publicApi.joinClassNames(classNames__default["default"].rel, // origin for now-indicator
3179                                                canvasWidth == null && classNames__default["default"].liquid
3179), style: { width: canvasWidth }, children: [cellRows.map((cells, rowIndex) => {
3180                                                        const rowLevel = cellRows.length - rowIndex - 1;
3181                                                        return (jsxRuntime.jsx(TimelineHeaderRow, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowIndex) }, rowIndex));
3182                                                    }), enableNowIndicator && (jsxRuntime.jsx(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))] }), Boolean(endScrollbarWidth) && (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.fillerClass, { inTableHeader: true }), classNames__default["default"].borderOnlyS), style: { minWidth: endScrollbarWidth } }))] }), jsxRuntime.jsx("div", { className: protectedApi.generateClassName(options.slotHeaderDividerClass, {
3183                                            inTableHeader: true,
3184                                            options: { dayMinWidth: options.dayMinWidth },
3185                                        }) })] }), jsxRuntime.jsx(protectedApi.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: footerScrollbarSticky ||
3186                                    props.forPrint // prevents blank space in print-view on Safari
3187                                , className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableBodyClass, {
3188                                    borderlessX,
3189                                    borderlessTop,
3190                                    borderlessBottom,
3191                                    multiMonthColumns: 0,
3192                                }), classNames__default["default"].flexCol, verticalScrolling && classNames__default["default"].liquid), style: {
3193                                    zIndex: 0,
3194                                }, ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, children: jsxRuntime.jsxs("div", { "aria-label": options.eventsHint, className: publicApi.joinClassNames(classNames__default["default"].rel, // for canvas origin?
3195                                    classNames__default["default"].grow), style: { width: canvasWidth }, ref: this.handeBodyEl, children: [jsxRuntime.jsx(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, 
3196                                            // dimensions
3197                                            slotWidth: slotWidth }), jsxRuntime.jsx(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, 
3198                                            // content
3199                                            bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: props.forPrint ? null : slicedProps.businessHourSegs, dateSelectionSegs: props.forPrint ? null : slicedProps.dateSelectionSegs, eventResizeSegs: props.forPrint ? null : (slicedProps.eventResize ? slicedProps.eventResize.segs : null), 
3200                                            // dimensions
3201                                            slotWidth: slotWidth }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.timelineTopClass) }), jsxRuntime.jsx(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, 
3202                                            // content
3203                                            fgEventSegs: slicedProps.fgEventSegs, eventDrag: props.forPrint ? null : slicedProps.eventDrag, eventResize: props.forPrint ? null : slicedProps.eventResize, eventSelection: slicedProps.eventSelection, 
3204                                            // dimensions
3205                                            slotWidth: slotWidth }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.timelineBottomClass) }), enableNowIndicator && (jsxRuntime.jsx(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: n
3205owDate, slotWidth: slotWidth }))] }) }), Boolean(footerScrollbarSticky) && (jsxRuntime.jsx(protectedApi.FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })), jsxRuntime.jsx(protectedApi.Ruler, { widthRef: this.handleTotalWidth })] }));
3206                } }));
3207        }
3208        // Lifecycle
3209        // -----------------------------------------------------------------------------------------------
3210        componentDidMount() {
3211            this._isUnmounting = false;
3212            this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
3213            this.updateSyncedScroller();
3214            this.resetScroll();
3215            this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
3216            this.syncedScroller.addScrollStartListener(this.handleTimeScrollStart);
3217            this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
3218        }
3219        componentDidUpdate(prevProps, prevState) {
3220            const { props } = this;
3221            const { options } = this.context;
3222            this.updateSyncedScroller();
3223            const dateProfileChange = prevProps.dateProfile !== props.dateProfile;
3224            const slotWidthChange = prevState.clientWidth !== this.state.clientWidth ||
3225                prevState.slotInnerWidth !== this.state.slotInnerWidth;
3226            if (dateProfileChange || slotWidthChange) {
3227                if (dateProfileChange && options.scrollTimeReset) {
3228                    this.resetScroll();
3229                }
3230                else {
3231                    this.applyTimeScroll();
3232                }
3233            }
3234        }
3235        componentWillUnmount() {
3236            this._isUnmounting = true;
3237            this.syncedScroller.destroy();
3238            this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
3239            this.syncedScroller.removeScrollStartListener(this.handleTimeScrollStart);
3240            this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
3241        }
3242        // Scrolling
3243        // -----------------------------------------------------------------------------------------------
3244        updateSyncedScroller() {
3245            this.syncedScroller.handleChildren([
3246                this.headerScrollerRef.current,
3247                this.bodyScrollerRef.current,
3248                this.footerScrollerRef.current
3249            ]);
3250        }
3251        resetScroll() {
3252            this.handleTimeScrollRequest(this.context.options.scrollTime);
3253        }
3254        applyTimeScroll() {
3255            const x = this.computeTimeScroll();
3256            if (x != null) {
3257                this.syncedScroller.scrollTo({ x });
3258            }
3259        }
3260        computeTimeScroll() {
3261            const { props, context, tDateProfile, scrollTime, scrollX } = this;
3262            const slotWidth = this.getSlotWidth();
3263            let x = scrollX;
3264            if (x == null && scrollTime != null && slotWidth != null) {
3265                x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
3266                if (x) {
3267                    x += 1; // overcome border. TODO: DRY this up
3268                }
3269            }
3270            return x;
3271        }
3272        getSlotWidth() {
3273            const { tDateProfile, state, context } = this;
3274            if (tDateProfile) {
3275                return this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, context.options.slotMinWidth, state.slotInnerWidth, state.clientWidth)[1];
3276            }
3277        }
3278        queryHit(isRtl, positionLeft, positionTop, elWidth, elHeight) {
3279            const { props, context, tDateProfile } = this;
3280            const { dateEnv } = context;
3281            const slotWidth = this.getSlotWidth();
3282            if (slotWidth) {
3283                const x = isRtl ? elWidth - positionLeft : positionLeft;
3284                const slatIndex = Math.floor(x / slotWidth);
3285                const slatX = slatIndex * slotWidth;
3286                const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
3287                const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
3288                let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], protectedApi.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
3289                let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
3290                // TODO: generalize this coord stuff to TimeGrid?
3291                let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
3292                let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
3293                let endCoord = startCoord + snapWidth;
3294                let left, right;
3295                if (isRtl) {
3296                    left = elWidth - endCoord;
3297                    right = elWidth - startCoord;
3298                }
3299                else {
3300                    left = startCoord;
3301                    right = endCoord;
3302                }
3303                return {
3304                    dateProfile: props.dateProfile,
3305                    dateSpan: {
3306                        range: { start: startDate, end: endDate },
3307                        allDay: !tDateProfile.isTimeScale,
3308                    },
3309                    rect: {
3310                        left,
3311                        right,
3312                        top: 0,
3313                        bottom: elHeight,
3314                    },
3315                    getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
3316                    layer: 0,
3317                };
3318            }
3319            return null;
3320        }
3321    }
3322
3323    var timelinePlugin = {
3324        name: 'timeline',
3325        premiumReleaseDate: '2026-07-24',
3326        deps: [premiumCommonPlugin],
3327        initialView: 'timelineDay',
3328        views: {
3329            timeline: {
3330                component: TimelineView,
3331                usesMinMaxTime: true,
3332                eventResizableFromStart: true, // how is this consumed for TimelineView tho?
3333            },
3334            timelineDay: {
3335                type: 'timeline',
3336                duration: { days: 1 },
3337            },
3338            timelineWeek: {
3339                type: 'timeline',
3340                duration: { weeks: 1 },
3341            },
3342            timelineMonth: {
3343                type: 'timeline',
3344                duration: { months: 1 },
3345            },
3346            timelineYear: {
3347                type: 'timeline',
3348                duration: { years: 1 },
3349            },
3350        },
3351    };
3352
3353    class ResourceDayTimeColsJoiner extends VResourceJoiner {
3354        transformSeg(seg, resourceDayTable, resourceI) {
3355            return [
3356                {
3357                    ...seg,
3358                    col: resourceDayTable.computeCol(seg.col, resourceI),
3359                },
3360            ];
3361        }
3362    }
3363
3364    class ResourceTimeGridView extends protectedApi.DateComponent {
3365        constructor() {
3366            super(...arguments);
3367            this.state = {};
3368            // memo
3369            this.flattenResources = protectedApi.memoize(flattenResources);
3370            this.buildResourceTimeColsModel = protectedApi.memoize(buildResourceTimeColsModel);
3371            this.buildResourceRowConfigs = protectedApi.memoize(buildResourceRowConfigs);
3372            this.createDayHeaderFormatter = protectedApi.memoize(protectedApi.createDayHeaderFormatter);
3373            // internal
3374            this.allDaySplitter = new protectedApi.AllDaySplitter();
3375            // for all-day-resource props
3376            this.allDayResourceSplitter = new VResourceSplitter();
3377            this.allDayResourceSlicers = {};
3378            this.allDayResourceJoiner = new ResourceDayTableJoiner();
3379            // for timed resource props
3380            this.buildDayRanges = protectedApi.memoize(protectedApi.buildDayRanges);
3381            this.timedResourceSplitter = new VResourceSplitter();
3382            this.timedResourceSlicers = {};
3383            this.timedResourceJoiner = new ResourceDayTimeColsJoiner();
3384            // timed-only column splitting
3385            this.splitFgEventSegs = protectedApi.memoize(protectedApi.organizeSegsByCol);
3386            this.splitBgEventSegs = protectedApi.memoize(protectedApi.organizeSegsByCol);
3387            this.splitBusinessHourSegs = protectedApi.memoize(protectedApi.organizeSegsByCol);
3388            this.splitNowIndicatorSegs = protectedApi.memoize(protectedApi.organizeSegsByCol);
3389            this.splitDateSelectionSegs = protectedApi.memoize(protectedApi.organizeSegsByCol);
3390            this.splitEventDrag = protectedApi.memoize(protectedApi.splitInteractionByCol);
3391            this.splitEventResize = protectedApi.memoize(protectedApi.splitInteractionByCol);
3392            this.isHitComboAllowed = (hit0, hit1) => {
3393                let allowAcrossResources = this.dayRanges.length === 1;
3394                return allowAcrossResources || hit0.dateSpan.resourceId === hit1.dateSpan.resourceId;
3395            };
3396        }
3397        render() {
3398            let { props, context } = this;
3399            let { options, dateEnv } = context;
3400            let { dateProfile } = props;
3401            let resourceOrderSpecs = options.resourceOrder || DEFAULT_RESOURCE_ORDER;
3402            let resources = this.flattenResources(props.resourceStore, resourceOrderSpecs);
3403            let resourceDayTableModel = this.buildResourceTimeColsModel(
3403dateProfile, context.dateProfileGenerator, resources, options.datesAboveResources, context);
3404            // split seg by all-day/timed
3405            let splitProps = this.allDaySplitter.splitProps(props);
3406            // split the all-day segs by resource
3407            let allDayResourceSplitProps = this.allDayResourceSplitter.splitProps({
3408                businessHours: splitProps.allDay.businessHours,
3409                dateSelection: splitProps.allDay.dateSelection,
3410                eventStore: splitProps.allDay.eventStore,
3411                eventUiBases: splitProps.allDay.eventUiBases,
3412                eventSelection: splitProps.allDay.eventSelection,
3413                eventDrag: splitProps.allDay.eventDrag,
3414                eventResize: splitProps.allDay.eventResize,
3415                resourceDayTableModel,
3416            });
3417            this.allDayResourceSlicers = protectedApi.mapHash(allDayResourceSplitProps, (split, resourceId) => this.allDayResourceSlicers[resourceId] || new protectedApi.DayTableSlicer());
3418            let allDayResourceSlicedProps = protectedApi.mapHash(this.allDayResourceSlicers, (slicer, resourceId) => slicer.sliceProps(allDayResourceSplitProps[resourceId], dateProfile, options.nextDayThreshold, context, resourceDayTableModel.dayTableModel));
3419            let allDayResourceJoinedProps = this.allDayResourceJoiner.joinProps(allDayResourceSlicedProps, resourceDayTableModel);
3420            // split the timed segs by resource
3421            let dayRanges = this.dayRanges = this.buildDayRanges(resourceDayTableModel.dayTableModel, dateProfile, dateEnv);
3422            let timedResourceSplitProps = this.timedResourceSplitter.splitProps({
3423                businessHours: splitProps.timed.businessHours,
3424                dateSelection: splitProps.timed.dateSelection,
3425                eventStore: splitProps.timed.eventStore,
3426                eventUiBases: splitProps.timed.eventUiBases,
3427                eventSelection: splitProps.timed.eventSelection, // result not used
3428                eventDrag: splitProps.timed.eventDrag,
3429                eventResize: splitProps.timed.eventResize,
3430                resourceDayTableModel,
3431            });
3432            this.timedResourceSlicers = protectedApi.mapHash(timedResourceSplitProps, (split, resourceId) => this.timedResourceSlicers[resourceId] || new protectedApi.DayTimeColsSlicer());
3433            let timedResourceSlicedProps = protectedApi.mapHash(this.timedResourceSlicers, (slicer, resourceId) => slicer.sliceProps(timedResourceSplitProps[resourceId], dateProfile, null, context, dayRanges));
3434            let timedResourceJoinedProps = this.timedResourceJoiner.joinProps(timedResourceSlicedProps, resourceDayTableModel);
3435            let datesRepDistinctDays = resourceDayTableModel.dayTableModel.rowCount === 1;
3436            let dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, resourceDayTableModel.colCount);
3437            return (jsxRuntime.jsx(protectedApi.NowTimer, { unit: options.nowIndicator ? 'minute' : 'day' /* hacky */, children: (nowDate, todayRange) => {
3438                    // timed-only column splitting
3439                    let colCount = resourceDayTableModel.colCount;
3440                    let fgEventSegsByCol = this.splitFgEventSegs(timedResourceJoinedProps.fgEventSegs, colCount);
3441                    let bgEventSegsByCol = this.splitBgEventSegs(timedResourceJoinedProps.bgEventSegs, colCount);
3442                    let businessHourSegsByCol = this.splitBusinessHourSegs(timedResourceJoinedProps.businessHourSegs, colCount);
3443                    let nowIndicatorSegsByCol = this.splitNowIndicatorSegs((() => {
3444                        // was buildNowIndicatorSegs
3445                        let nonResourceSegs = !props.forPrint && options.nowIndicator
3446                            ? this.timedResourceSlicers[''].sliceNowDate(nowDate, this.props.dateProfile, this.context.options.nextDayThreshold, this.context, this.dayRanges)
3447                            : []; // TODO: breaks memoization?
3448                        const expandedSegs = this.timedResourceJoiner.expandSegs(resourceDayTableModel, nonResourceSegs);
3449                        const dayCnt = resourceDayTableModel.dayTableModel.colCount;
3450                        const resourceCnt = resourceDayTableModel.resources.length;
3451                        return expandedSegs.map((seg) => ({
3452                            ...seg,
3453                            showDot: !options.datesAboveResources && dayCnt > 1
3454                                ? true
3455                                : seg.col % resourceCnt === 0,
3456                        }));
3457                    })(), colCount);
3458                    let dateSelectionSegsByCol = this.splitDateSelectionSegs(timedResourceJoinedProps.dateSelectionSegs, colCount);
3459                    let eventDragByCol = this.splitEventDrag(timedResourceJoinedProps.eventDrag, colCount);
3460                    let eventResizeByCol = this.splitEventResize(timedResourceJoinedProps.eventResize, colCount);
3461                    const headerTiers = this.buildResourceRowConfigs(resources, options.datesAboveResources, resourceDayTableModel.dayTableModel.headerDates, datesRepDistinctDays, props.dateProfile, todayRange, dayHeaderFormat, context);
3462                    return (jsxRuntime.jsx(protectedApi.TimeGridLayout, { labelId: props.labelId, labelStr: props.labelStr, dateProfile: dateProfile, nowDate: nowDate, todayRange: todayRange, cells: resourceDayTableModel.cells[0], forPrint: props.forPrint, isHitComboAllowed: this.isHitComboAllowed, className: props.className, 
3463                        // header content
3464                        headerTiers: headerTiers, 
3465                        // all-day content
3466                        fgEventSegs: allDayResourceJoinedProps.fgEventSegs, bgEventSegs: allDayResourceJoinedProps.bgEventSegs, businessHourSegs: allDayResourceJoinedProps.businessHourSegs, dateSelectionSegs: allDayResourceJoinedProps.dateSelectionSegs, eventDrag: allDayResourceJoinedProps.eventDrag, eventResize: allDayResourceJoinedProps.eventResize, 
3467                        // timed content
3468                        fgEventSegsByCol: fgEventSegsByCol, bgEventSegsByCol: bgEventSegsByCol, businessHourSegsByCol: businessHourSegsByCol, nowIndicatorSegsByCol: nowIndicatorSegsByCol, dateSelectionSegsByCol: dateSelectionSegsByCol, eventDragByCol: eventDragByCol, eventResizeByCol: eventResizeByCol, 
3469                        // universal content
3470                        eventSelection: allDayResourceJoinedProps.eventSelection || splitProps.timed.eventSelection }));
3471                } }));
3472        }
3473    }
3474    /*
3475    TODO: kill this and DayResourceTableModel/ResourceDayTableModel
3476    */
3477    function buildResourceTimeColsModel(dateProfile, dateProfileGenerator, resources, datesAboveResources, context) {
3478        let dayTable = protectedApi.buildTimeColsModel(dateProfile, dateProfileGenerator, context.dateEnv);
3479        if (!resources.length) {
3480            return new ResourcelessDayTableModel(dayTable);
3481        }
3482        return datesAboveResources ?
3483            new DayResourceTableModel(dayTable, resources, context) :
3484            new ResourceDayTableModel(dayTable, resources, context);
3485    }
3486
3487    var resourceTimeGridPlugin = {
3488        name: 'resource-timegrid',
3489        premiumReleaseDate: '2026-07-24',
3490        deps: [
3491            premiumCommonPlugin,
3492            timeGridPlugin__default["default"],
3493            resourcePlugin,
3494            resourceDayGridPlugin,
3495        ],
3496        initialView: 'resourceTimeGridDay',
3497        views: {
3498            resourceTimeGrid: {
3499                type: 'timeGrid', // will inherit this configuration
3500                component: ResourceTimeGridView,
3501                needsResourceData: true,
3502            },
3503            resourceTimeGridDay: {
3504                type: 'resourceTimeGrid',
3505                duration: { days: 1 },
3506            },
3507            resourceTimeGridWeek: {
3508                type: 'resourceTimeGrid',
3509                duration: { weeks: 1 },
3510            },
3511        },
3512    };
3513
3514    function fracToCssDim(frac) {
3515        return frac * 100 + '%';
3516    }
3517
3518    function parseDimConfig(input, minDim = 0) {
3519        if (input != null) {
3520            if (typeof input === 'string') {
3521                let m = input.match(/^(.*)(%|px)$/);
3522                if (m) {
3523                    const num = parseFloat(m[1]);
3524                    if (!isNaN(num)) {
3525                        if (m[2] === '%') {
3526                            return { pixels: 0, frac: num / 100, min: minDim };
3527                        }
3528                        else {
3529                            return { pixels: num, frac: 0, min: minDim };
3530                        }
3531                    }
3532                }
3533            }
3534            else if (typeof input === 'number' && !isNaN(input)) {
3535                return { pixels: input, frac: 0, min: minDim };
3536            }
3537        }
3538    }
3539    function parseSiblingDimConfig(input, grow, // TODO: use (and sanitize)
3540    minDim) {
3541        const partialDimConfig = parseDimConfig(input, minDim);
3542        return partialDimConfig
3543            ? { ...partialDimConfig, grow: grow || 0 }
3544            : { pixels: 0, frac: 0, grow: grow || 1, min: minDim };
3545    }
3546    /*
3547    Ensure at least one column can grow
3548    Mutates in-place
3549    */
3550    function ensureDimConfigsGrow(dimConfigs) {
3551        for (const dimConfig of dimConfigs) {
3552            if (dimConfig.grow) {
3553                return;
3554            }
3555        }
3556        // make all expand equally
3557        for (const dimConfig of dimConfigs) {
3558            dimConfig.grow = 1;
3559        }
3560    }
3561    function pixelizeDimConfigs(dimConfigs, clientDim) {
3562        const pixelDims = [];
3563        let preGrowTotal = 0;
3564        let growDenom = 0;
3565        for (const dimConfig of dimConfigs) {
3566            const constrainedPixels = Math.max(dimConfig.pixels + (dimConfig.frac * clientDim), dimConfig.min);
3567            pixelDims.push(constrainedPixels);
3568            preGrowTotal += constrainedPixels;
3569            growDenom += dimConfig.grow;
3570        }
3571        if (preGrowTotal < clientDim) {
3572            const remainder = clientDim - preGrowTotal;
3573            for (let i = 0; i < dimConfigs.length; i++) {
3574                pixelDims[i] += remainder * (dimConfigs[i].grow / growDenom);
3575            }
3576        }
3577        return [pixelDims, preGrowTotal];
3578    }
3579    function flexifyDimConfigs(dimConfigs, pixelDims) {
3580        const flexDims = [];
3581        const flexGrows = [];
3582        let pixelTotal = 0;
3583        let fracTotal = 0;
3584        for (let i = 0; i < dimConfigs.length; i++) {
3585            const dimConfig = dimConfigs[i];
3586            const constrainedPixels = Math.max(dimConfig.pixels, dimConfig.min);
3587            flexDims.push(constrainedPixels);
3588            flexGrows.push(pixelDims[i] - constrainedPixels); // a pixel value, but used as a proportion
3589            pixelTotal += dimConfig.pixels + (dimConfig.grow ? dimConfig.min : 0);
3590            fracTotal += dimConfig.frac; // not possible to enforce min for percentage here
3591        }
3592        const minCanvasDim = serializeDimConfig({
3593            pixels: pixelTotal,
3594            frac: fracTotal,
3595        });
3596        return [flexDims, flexGrows, minCanvasDim];
3597    }
3598    function serializeDimConfig({ pixels, frac }) {
3599        if (!frac) {
3600            return pixels;
3601        }
3602        if (!pixels) {
3603            return fracToCssDim(frac);
3604        }
3605        return `calc(${fracToCssDim(frac)} + ${pixels}px)`;
3606    }
3607    function resizeSiblingDimConfig(dimConfigs, pixelDims, clientDim, resizeIndex, resizeDim) {
3608        const newDimConfigs = [];
3609        for (let i = 0; i < resizeIndex; i++) {
3610            newDimConfigs.push(resizeDimConfig(dimConfigs[i], pixelDims[i], clientDim));
3611        }
3612        newDimConfigs.push(resizeDimConfig(dimConfigs[resizeIndex], resizeDim, clientDim));
3613        const len = dimConfigs.length;
3614        let anyGrow = false;
3615        for (let i = resizeIndex + 1; i < len; i++) {
3616            const dimConfig = dimConfigs[i];
3617            newDimConfigs.push(dimConfig);
3618            if (dimConfig.grow) {
3619                anyGrow = true;
3620            }
3621        }
3622        if (!anyGrow) {
3623            for (let i = resizeIndex + 1; i < len; i++) {
3624                newDimConfigs[i] = Object.assign({}, newDimConfigs[i], { grow: 1 });
3625            }
3626        }
3627        return newDimConfigs;
3628    }
3629    function resizeDimConfig(dimConfig, newPixels, clientDim) {
3630        const { min } = dimConfig;
3631        newPixels = Math.max(min, newPixels);
3632        if (dimConfig.pixels) {
3633            return { pixels: newPixels, frac: 0, grow: 0, min };
3634        }
3635        return { pixels: 0, frac: newPixels / clientDim, grow: 0, min };
3636    }
3637
3638    const ROW_BORDER_WIDTH = 1;
3639    // Resource/Group Verticals
3640    // -------------------------------------------------------------------------------------------------
3641    function computeHeights(siblingNodes, getEntityKey, getEntityHeight, minHeight) {
3642        const heightMap = new Map();
3643        let [totalHeight, expandableCount] = computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap);
3644        if (minHeight != null && minHeight > totalHeight) {
3645            expandHeights(siblingNodes, getEntityKey, heightMap, (minHeight - totalHeight) / expandableCount);
3646            totalHeight = minHeight;
3647        }
3648        return [heightMap, totalHeight];
3649    }
3650    function computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap) {
3651        let totalHeight = 0;
3652        let expandableCount = 0;
3653        for (const siblingNode of siblingNodes) {
3654            const entityKey = getEntityKey(siblingNode.entity);
3655            let ownHeight = getEntityHeight(entityKey) || 0;
3656            const [childrenHeight, childrenExpandableCount] = computeTightHeights(siblingNode.children, getEntityKey, getEntityHeight, heightMap);
3657            if (siblingNode.pooledHeight) { // 'own' is side-by-side with children
3658                if (ownHeight > childrenHeight) {
3659                    // children are smaller. grow them
3660                    expandHeights(siblingNode.children, getEntityKey, heightMap, (ownHeight - childrenHeight) / childrenExpandableCount);
3661                }
3662                else {
3663                    // own-element is smaller. grow to height of children
3664                    ownHeight = childrenHeight;
3665                }
3666                totalHeight += ownHeight;
3667            }
3668            else { // 'own' is above children
3669                totalHeight += ownHeight + ROW_BORDER_WIDTH + childrenHeight;
3670            }
3671            heightMap.set(entityKey, ownHeight);
3672            // expandable?
3673            // vertically stacked, and not a group
3674            if (siblingNode.pooledHeight === undefined) {
3675                expandableCount++;
3676            }
3677            expandableCount += childrenExpandableCount;
3678        }
3679        return [
3680            totalHeight + ROW_BORDER_WIDTH * (siblingNodes.length - 1),
3681            expandableCount,
3682        ];
3683    }
3684    function expandHeights(siblingNodes, getEntityKey, heightMap, expansion) {
3685        for (const siblingNode of siblingNodes) {
3686            // expandable?
3687            // vertically stacked, and not a group
3688            if (siblingNode.pooledHeight === undefined) {
3689                const entityKey = getEntityKey(siblingNode.entity);
3690                heightMap.set(entityKey, heightMap.get(entityKey) + expansion);
3691            }
3692            expandHeights(siblingNode.children, getEntityKey, heightMap, expansion);
3693        }
3694    }
3695    function computeTopsFromHeights(siblingNodes, getEntityKey, heightMap) {
3696        const topMap = new Map();
3697        computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, 0);
3698        return topMap;
3699    }
3700    function computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, top) {
3701        for (let i = 0; i < siblingNodes.length; i++) {
3702            const siblingNode = siblingNodes[i];
3703            const entityKey = getEntityKey(siblingNode.entity);
3704            topMap.set(entityKey, top);
3705            if (!siblingNode.pooledHeight) {
3706                top += heightMap.get(entityKey) + ROW_BORDER_WIDTH;
3707            }
3708            top = computeTopsStartingAt(siblingNode.children, getEntityKey, heightMap, topMap, top);
3709        }
3710        return top;
3711    }
3712    function findEntityByCoord(siblingNodes, entityTops, // keyed by createEntityId
3713    entityHeights, // keyed by createEntityId
3714    coord, // assumed >= 0
3715    createEntityId) {
3716        for (const siblingNode of siblingNodes) {
3717            const entityKey = createEntityId(siblingNode.entity);
3718            const siblingOwnTop = entityTops.get(entityKey);
3719            const siblingOwnHeight = entityHeights.get(entityKey);
3720            // intersection of the sibling's own element?
3721            if (siblingOwnTop + siblingOwnHeight > coord) {
3722                if (siblingNode.pooledHeight) {
3723                    // need more specific result
3724                    return findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
3725                }
3726                else {
3727                    return [siblingNode.entity, siblingOwnTop, siblingOwnHeight];
3728                }
3729            }
3730            else if (!siblingNode.pooledHeight) {
3731                // search siblings below
3732                const childrenRes = findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
3733                if (childrenRes) {
3734                    return childrenRes;
3735                }
3736            }
3737        }
3738    }
3739
3740    /*
3741    A "layout" wraps the resource/group entity with presentation information (indent, isExpanded, etc)
3742    Creates a layout hierarchy (`layouts`) and flattened projections for each row type and all rows.
3743    Filters away not-expanded nodes.
3744
3745    TODO: even when a row is collapsed, consistent global rowIndex
3746    */
3747    function buildResourceLayouts(hierarchy, hasNesting, expansions, expansionDefault) {
3748        const flatResourceRowLayouts = [];
3749        const flatGroupRowLayouts = [];
3750        const flatGroupColLayouts = [];
3751        const flatRowLayouts = [];
3752        function processNodes(nodes, startingIndex, startingVisibleIndex, depth, rowDepth, indent, isVisible) {
3753            const layouts = [];
3754            let totalCnt = 0; // visible or not
3755            let visibleCnt = 0;
3756            // TODO: more DRY within
3757            for (const node of nodes) {
3758                // ResourceRow
3759                if (node.resourceFields) {
3760                    const isExpanded = expansions[node.entity.id] ?? expansionDefault;
3761                    const rowIndex = startingIndex + totalCnt++;
3762                    const visibleIndex = startingVisibleIndex + visibleCnt++;
3763                    const resourceLayout = {
3764                        rowIndex,
3765                        visibleIndex,
3766                        entity: node.entity,
3767                        resourceFields: node.resourceFields,
3768                        isExpanded,
3769                        hasChildren: Boolean(node.children.length),
3770                        depth,
3771                        rowDepth,
3772                        indent,
3773                        children: [],
3774                    };
3775                    if (isVisible) {
3776                        flatRowLayouts.push(resourceLayout);
3777                        flatResourceRowLayouts.push(resourceLayout);
3778                        layouts.push(resourceLayout);
3779                    }
3780                    const [localChildren, subtreeTotalCnt, subtreeVisibleCnt] = processNodes(node.children, startingIndex + totalCnt, startingVisibleIndex + visibleCnt, depth + 1, rowDepth + 1, indent + 1, isVisible && isExpanded);
3781                    if (isVisible && isExpanded) {
3782                        resourceLayout.children = localChildren;
3783                        visibleCnt += subtreeVisibleCnt;
3784                    }
3785                    totalCnt += subtreeTotalCnt;
3786                    // GroupCell
3787                }
3788                else if (node.pooledHeight) {
3789                    const rowIndex = startingIndex + totalCnt; // DON'T advance
3790                    const visibleIndex = startingVisibleIndex + visibleCnt; // DON'T advance
3791                    const groupCellLayout = {
3792                        rowIndex,
3793                        visibleIndex,
3794                        rowSpan: 0, // populated very soon...
3795                        entity: node.entity,
3796                        pooledHeight: true,
3797                        children: [], // populates very soon...
3798                    };
3799                    if (isVisible) {
3800                        (flatGroupColLayouts[depth] || (flatGroupColLayouts[depth] = []))
3801                            .push(groupCellLayout);
3802                        layouts.push(groupCellLayout);
3803                    }
3804                    const [localChildren, subtreeTotalCnt, subtreeVisibleCnt] = processNodes(node.children, startingIndex + totalCnt, startingVisibleIndex + visibleCnt, depth + 1, rowDepth, // do NOT advance
3805                    indent, isVisible);
3806                    if (isVisible) {
3807                        groupCellLayout.children = localChildren;
3808                        groupCellLayout.rowSpan = subtreeTotalCnt;
3809                        visibleCnt += subtreeVisibleCnt;
3810                    }
3811                    totalCnt += subtreeTotalCnt;
3812                    // GroupRow
3813                }
3814                else {
3815                    const isExpanded = expansions[createGroupId(node.entity)] ?? expansionDefault;
3816                    const rowIndex = startingIndex + totalCnt++;
3817                    const visibleIndex = startingVisibleIndex + visibleCnt++;
3818                    const groupRowLayout = {
3819                        rowIndex,
3820                        visibleIndex,
3821                        entity: node.entity,
3822                        pooledHeight: false,
3823                        isExpanded,
3824                        hasChildren: Boolean(node.children.length),
3825                        depth,
3826                        rowDepth,
3827                        indent,
3828                        children: [], // populates very soon...
3829                    };
3830                    if (isVisible) {
3831                        flatRowLayouts.push(groupRowLayout);
3832                        flatGroupRowLayouts.push(groupRowLayout);
3833                        layouts.push(groupRowLayout);
3834                    }
3835                    const [localChildren, subtreeTotalCnt, subtreeVisibleCnt] 
3835= processNodes(node.children, startingIndex + totalCnt, startingVisibleIndex + visibleCnt, depth + 1, rowDepth + 1, indent + 1, isVisible && isExpanded);
3836                    if (isVisible && isExpanded) {
3837                        groupRowLayout.children = localChildren;
3838                        visibleCnt += subtreeVisibleCnt;
3839                    }
3840                    totalCnt += subtreeTotalCnt;
3841                }
3842            }
3843            return [layouts, totalCnt, visibleCnt];
3844        }
3845        const [layouts, totalCnt] = processNodes(hierarchy, 0, 0, 0, 0, hasNesting ? 1 : 0, true);
3846        return {
3847            layouts,
3848            flatRowLayouts,
3849            flatResourceRowLayouts,
3850            flatGroupRowLayouts,
3851            flatGroupColLayouts,
3852            totalCnt,
3853        };
3854    }
3855
3856    function buildAriaCellId(prefix, rowIndex, colIndex) {
3857        return `${prefix}-${rowIndex}-${colIndex}`;
3858    }
3859    // Derives the cell's DOM id (referenced by proxy-row aria-owns) and aria-colindex from the same
3860    // (prefix, rowIndex, colIndex) source, so the id and column attributes can never disagree.
3861    function buildAriaCellAttrs(props) {
3862        return {
3863            id: (props.cellIdPrefix && props.cellRowIndex != null && props.cellColIndex != null)
3864                ? buildAriaCellId(props.cellIdPrefix, props.cellRowIndex, props.cellColIndex)
3865                : undefined,
3866            'aria-colindex': props.cellColIndex != null ? 1 + props.cellColIndex : undefined,
3867        };
3868    }
3869    // Renders visually hidden proxy rows that own cells split across the spreadsheet and timeline panes.
3870    // The grid declares aria-colcount and every owned cell declares aria-colindex. Inference from the
3871    // owned-cell order alone would misnumber rows covered by a vertically spanning group-column cell:
3872    // only the first covered row owns the spanning cell, so later covered rows' first owned cell would
3873    // be inferred as column 1 unless the AT applies aria-rowspan across proxy rows (unreliable).
3874    function AriaProxyRows(props) {
3875        const { cellIdPrefix, bodyRows, resourceColCnt, superHeaderRowSpan, hasNesting, } = props;
3876        const totalHeaderRowSpan = 1 + superHeaderRowSpan;
3877        const resourceHeaderCellIds = Array.from({ length: resourceColCnt }, (_value, colIndex) => {
3878            return buildAriaCellId(cellIdPrefix, 1 + superHeaderRowSpan, colIndex);
3879        });
3880        const timelineHeaderCellId = buildAriaCellId(cellIdPrefix, 1, resourceColCnt);
3881        return (jsxRuntime.jsxs("div", { role: 'presentation', style: { position: 'absolute', width: 0, height: 0, overflow: 'hidden' }, children: [jsxRuntime.jsx("div", { role: 'row', "aria-rowindex": 1, "aria-owns": superHeaderRowSpan
3882                        ? `${buildAriaCellId(cellIdPrefix, 1, 0)} ${timelineHeaderCellId}`
3883                        : [...resourceHeaderCellIds, timelineHeaderCellId].join(' ') }), Boolean(superHeaderRowSpan) && (jsxRuntime.jsx("div", { role: 'row', "aria-rowindex": 2, "aria-owns": resourceHeaderCellIds.join(' ') })), bodyRows.map((ariaRow) => {
3884                    const rowLayout = ariaRow.layout;
3885                    const rowIndex = 1 + totalHeaderRowSpan + rowLayout.rowIndex;
3886                    return (jsxRuntime.jsx("div", { role: 'row', "aria-rowindex": rowIndex, "aria-level": hasNesting ? 1 + rowLayout.rowDepth : undefined, "aria-expanded": isEntityGroup(rowLayout.entity) || rowLayout.hasChildren
3887                            ? rowLayout.isExpanded
3888                            : undefined, "aria-owns": Array.from(ariaRow.colIndices)
3889                            .sort((colIndex0, colIndex1) => colIndex0 - colIndex1)
3890                            .map((colIndex) => buildAriaCellId(cellIdPrefix, rowIndex, colIndex))
3891                            .join(' ') }, rowIndex));
3892                })] }));
3893    }
3894    // Builds proxy body-row ownership from the independently virtualized cell sources.
3895    // Only mounted cells are included, and cells from different panes accumulate on the same logical row.
3896    function buildAriaBodyRows(flatRowLayouts, groupColPositions, rowPositions, groupColCnt, resourceColCnt) {
3897        const ariaBodyRowMap = new Map();
3898        // Merge a mounted cell into its logical owner row while avoiding duplicate column references.
3899        const addCell = (rowLayout, colIndex) => {
3900            let ariaRow = ariaBodyRowMap.get(rowLayout.visibleIndex);
3901            if (!ariaRow) {
3902                ariaRow = {
3903                    layout: rowLayout,
3904                    colIndices: new Set(),
3905                };
3906                ariaBodyRowMap.set(rowLayout.visibleIndex, ariaRow);
3907            }
3908            ariaRow.colIndices.add(colIndex);
3909        };
3910        // A vertically spanning group-column cell belongs to the first logical row it covers,
3911        // mirroring HTML rowspan, where the spanned cell is a child of the first covered <tr>.
3912        // Because the cell and the rows are virtualized independently, the owner row might be outside
3913        // rowPositions while the spanning cell itself intersects the viewport (the cell covers later,
3914        // mounted rows). We still emit a proxy row for that unmounted owner — it will own only the
3915        // group cell, lacking the resource's naming rowheader and lane — because the alternative is
3916        // worse: a mounted cell owned by no row surfaces as a stray direct child of the grid,
3917        // breaking the table structure for assistive technology.
3918        for (let colIndex = 0; colIndex < groupColPositions.length; colIndex += 1) {
3919            for (const groupCellPosition of groupColPositions[colIndex]) {
3920                addCell(flatRowLayouts[groupCellPosition.item.visibleIndex], colIndex);
3921            }
3922        }
3923        // Add cells rendered once per mounted logical row. Group rows have one spreadsheet cell spanning
3924        // the resource columns; resource rows have one cell per non-group spreadsheet column.
3925        for (const rowPosition of rowPositions) {
3926            const rowLayout = rowPosition.item;
3927            if (isEntityGroup(rowLayout.entity)) {
3928                addCell(rowLayout, 0);
3929            }
3930            else {
3931                for (let colIndex = groupColCnt; colIndex < resourceColCnt; colIndex += 1) {
3932                    addCell(rowLayout, colIndex);
3933                }
3934            }
3935            // The timeline lane is the final column for both group and resource rows.
3936            addCell(rowLayout, resourceColCnt);
3937        }
3938        // Collection begins with column-group cells, so map insertion order is not necessarily visual order.
3939        // Proxy-row DOM order must match the visual vertical order.
3940        return Array.from(ariaBodyRowMap.values()).sort((row0, row1) => {
3941            return row0.layout.visibleIndex - row1.layout.visibleIndex;
3942        });
3943    }
3944
3945    /*
3946    parallels the ResourceGroupHeaderSubrow
3947    */
3948    class GroupLane extends protectedApi.BaseComponent {
3949        constructor() {
3950            super(...arguments);
3951            // ref
3952            this.innerElRef = compat.createRef();
3953        }
3954        render() {
3955            let { props, context } = this;
3956            let { group } = props;
3957            let groupSpec = group.spec; // type HACK
3958            let renderProps = {
3959                fieldValue: group.value,
3960                view: context.viewApi,
3961            };
3962            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
3963                    ...buildAriaCellAttrs(props),
3964                    role: 'gridcell',
3965                }, className: publicApi.joinClassNames(classNames__default["default"].liquid, // expand to whole row
3966                classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].contentBox, props.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone), style: {
3967                    height: props.height,
3968                }, renderProps: renderProps, generatorName: "resourceGroupLaneContent", customGenerator: groupSpec.laneContent, classNameGenerator: groupSpec.laneClass, didMount: groupSpec.laneDidMount, willUnmount: groupSpec.laneWillUnmount, children: (InnerContainer) => (jsxRuntime.jsx(InnerContainer, { tag: "div", elRef: this.innerElRef, className: publicApi.joinClassNames(protectedApi.generateClassName(groupSpec.laneInnerClass, renderProps), classNames__default["default"].noShrink, classNames__default["default"].noMargin) })) }));
3969        }
3970        componentDidMount() {
3971            this._isUnmounting = false;
3972            const innerEl = this.innerElRef.current;
3973            this.disconnectInnerHeight = protectedApi.watchHeight(innerEl, (height) => {
3974                if (this._isUnmounting)
3975                    return;
3976                protectedApi.setRef(this.props.innerHeightRef, height);
3977            });
3978        }
3979        componentWillUnmount() {
3980            this._isUnmounting = true;
3981            this.disconnectInnerHeight();
3982            protectedApi.setRef(this.props.innerHeightRef, null);
3983        }
3984    }
3985
3986    class ResourceLane extends protectedApi.BaseComponent {
3987        constructor() {
3988            super(...arguments);
3989            // memo
3990            this.refineRenderProps = protectedApi.memoizeObjArg(refineRenderProps$1);
3991            this.slicer = new TimelineLaneSlicer();
3992            this.handleEventsHeight = (eventsHeight) => {
3993                if (this._isUnmounting)
3994                    return;
3995                this.eventsHeight = eventsHeight;
3996                protectedApi.afterSize(this.handleHeight);
3997            };
3998            this.handleTopEl = (topEl) => {
3999                if (this.disconnectTopHeight) {
4000                    this.disconnectTopHeight();
4001                    this.disconnectTopHeight = undefined;
4002                }
4003                if (topEl) {
4004                    this.disconnectTopHeight = protectedApi.watchHeight(topEl, (topHeight) => {
4005                        if (this._isUnmounting)
4006                            return;
4007                        this.topHeight = topHeight;
4008                        protectedApi.afterSize(this.handleHeight);
4009                    });
4010                }
4011            };
4012            this.handleBottomEl = (footerEl) => {
4013                if (this.disconnectBottomHeight) {
4014                    this.disconnectBottomHeight();
4015                    this.disconnectBottomHeight = undefined;
4016                }
4017                if (footerEl) {
4018                    this.disconnectBottomHeight = protectedApi.watchHeight(footerEl, (bottomHeight) => {
4019                        if (this._isUnmounting)
4020                            return;
4021                        this.bottomHeight = bottomHeight;
4022                        protectedApi.afterSize(this.handleHeight);
4023                    });
4024                }
4025            };
4026            this.handleHeight = () => {
4027                if (this._isUnmounting)
4028                    return;
4029                const { topHeight, bottomHeight, eventsHeight } = this;
4030                if (topHeight != null && bottomHeight != null && eventsHeight != null) {
4031                    protectedApi.setRef(this.props.heightRef, topHeight + bottomHeight + eventsHeight);
4032                }
4033            };
4034        }
4035        render() {
4036            let { props, context } = this;
4037            let { resource }
4037 = props;
4038            let { options } = context;
4039            let renderProps = this.refineRenderProps({
4040                resource,
4041                context,
4042                eventOverlap: Boolean(options.eventOverlap),
4043            });
4044            /* sliced */
4045            let slicedProps = this.slicer.sliceProps(props, props.dateProfile, props.tDateProfile.isTimeScale ? null : options.nextDayThreshold, context, // wish we didn't have to pass in the rest of the args...
4046            props.dateProfile, context.dateProfileGenerator, props.tDateProfile, context.dateEnv);
4047            return (jsxRuntime.jsx("div", { role: props.role, className: publicApi.joinClassNames(props.className, // probably contains fillX
4048                classNames__default["default"].flexRow), style: {
4049                    top: props.top,
4050                    width: props.width,
4051                    insetInlineStart: props.insetInlineStart ?? props.clipStart,
4052                }, children: jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
4053                        ...buildAriaCellAttrs(props),
4054                        role: 'gridcell',
4055                        'data-resource-id': resource.id,
4056                    }, className: publicApi.joinClassNames(classNames__default["default"].liquid, classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].contentBox, props.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone, classNames__default["default"].rel), style: {
4057                        height: props.height,
4058                    }, renderProps: renderProps, generatorName: undefined, classNameGenerator: options.resourceLaneClass, didMount: options.resourceLaneDidMount, willUnmount: options.resourceLaneWillUnmount, children: () => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(TimelineBg, { tDateProfile: props.tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, 
4059                                // content
4060                                bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null, 
4061                                // dimensions
4062                                slotWidth: props.slotWidth, 
4063                                // virtualization
4064                                clipStart: props.clipStart, clipEnd: props.clipEnd }), jsxRuntime.jsx(protectedApi.ContentContainer, { tag: 'div', elRef: this.handleTopEl, className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noShrink), renderProps: renderProps, generatorName: 'resourceLaneTopContent', customGenerator: options.resourceLaneTopContent, classNameGenerator: options.resourceLaneTopClass }), jsxRuntime.jsx(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, 
4065                                // content
4066                                fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection, resourceId: resource.id, 
4067                                // dimensions
4068                                slotWidth: props.slotWidth, 
4069                                // virtualization
4070                                clipStart: props.clipStart, clipEnd: props.clipEnd, 
4071                                // ref
4072                                heightRef: this.handleEventsHeight }), jsxRuntime.jsx(protectedApi.ContentContainer, { tag: 'div', elRef: this.handleBottomEl, className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noShrink), renderProps: renderProps, generatorName: 'resourceLaneBottomContent', customGenerator: options.resourceLaneBottomContent, classNameGenerator: options.resourceLaneBottomClass })] })) }) }));
4073        }
4074        componentDidMount() {
4075            this._isUnmounting = false;
4076        }
4077        componentWillUnmount() {
4078            this._isUnmounting = true;
4079        }
4080    }
4081    function refineRenderProps$1(input) {
4082        return {
4083            resource: new ResourceApi(input.context, input.resource),
4084            options: { eventOverlap: input.eventOverlap },
4085        };
4086    }
4087
4088    const MIN_RESOURCE_AREA_WIDTH = 30; // definitely bigger than scrollbars
4089    class ResizableTwoCol extends protectedApi.BaseComponent {
4090        constructor() {
4091            super(...arguments);
4092            this.state = {};
4093            // memo
4094            this.parseWidthConfig = protectedApi.memoize(parseDimConfig);
4095            // ref
4096            this.rootEl = null;
4097            this.startElRef = compat.createRef();
4098            this.resizerElRef = compat.createRef();
4099            this.handleRootEl = (el) => {
4100                this.rootEl = el;
4101                if (this.props.elRef) {
4102                    protectedApi.setRef(this.props.elRef, el);
4103                }
4104            };
4105        }
4106        render() {
4107            const { props, state, context } = this;
4108            const { options } = context;
4109            const initialWidthConfig = this.parseWidthConfig(props.initialStartWidth, MIN_RESOURCE_AREA_WIDTH);
4110            const widthConfig = this.widthConfig = state.widthOverride || initialWidthConfig;
4111            return (jsxRuntime.jsxs("div", { ref: this.handleRootEl, className: publicApi.joinClassNames(props.className, classNames__default["default"].flexRow), children: [jsxRuntime.jsx("div", { className: props.startClassName, style: {
4112                            width: serializeDimConfig(widthConfig),
4113                        }, ref: this.startElRef, children: props.startContent }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.resourceColumnDividerClass, classNames__default["default"].rel), 
4114                        // TODO: make a className somehow?...
4115                        // TODO: what if not resizable?
4116                        style: { cursor: 'col-resize' }, ref: this.resizerElRef, children: jsxRuntime.jsx("div", { className: classNames__default["default"].hitXSkinny, style: { zIndex: 1 } }) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(props.endClassName, classNames__default["default"].liquid
4116), children: props.endContent })] }));
4117        }
4118        componentDidMount() {
4119            const ElementDraggingImpl = this.context.pluginHooks.elementDraggingImpl;
4120            if (ElementDraggingImpl) {
4121                const resizerEl = this.resizerElRef.current;
4122                const dragging = this.resizerDragging = new ElementDraggingImpl(resizerEl);
4123                dragging.emitter.on('dragstart', () => {
4124                    const isRtl = protectedApi.computeElIsRtl(resizerEl);
4125                    const viewWidth = this.rootEl.getBoundingClientRect().width;
4126                    const origWidth = this.startElRef.current.getBoundingClientRect().width;
4127                    const origWidthConfig = this.widthConfig;
4128                    let newWidthConfig;
4129                    dragging.emitter.on('dragmove', (pev) => {
4130                        const newWidth = Math.min(origWidth + pev.deltaX * (isRtl ? -1 : 1), viewWidth);
4131                        newWidthConfig = resizeDimConfig(origWidthConfig, newWidth, viewWidth);
4132                        this.setState({
4133                            widthOverride: newWidthConfig,
4134                        });
4135                    });
4136                    dragging.emitter.on('dragend', () => {
4137                        if (newWidthConfig) {
4138                            protectedApi.setRef(this.props.resizedWidthRef, serializeDimConfig(newWidthConfig));
4139                        }
4140                    });
4141                    dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
4142                });
4143            }
4144        }
4145        componentWillUnmount() {
4146            if (this.resizerDragging) {
4147                this.resizerDragging.destroy();
4148            }
4149        }
4150    }
4151
4152    /*
4153    Group cell that spans vertically, consuming multiple rowspan
4154    */
4155    class ResourceGroupSubrow extends protectedApi.BaseComponent {
4156        constructor() {
4157            super(...arguments);
4158            // ref
4159            this.innerElRef = compat.createRef();
4160        }
4161        render() {
4162            let { props, context } = this;
4163            let { options } = context;
4164            let { colSpec } = props;
4165            let renderProps = {
4166                fieldValue: props.fieldValue,
4167                view: context.viewApi,
4168            };
4169            // a grouped cell. no data that is specific to this specific resource
4170            // `colSpec` is for the group. a GroupSpec :(
4171            return (jsxRuntime.jsx("div", { role: props.role, className: publicApi.joinClassNames(options.resourceRowClass, props.className, classNames__default["default"].flexRow, props.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone), style: {
4172                    top: props.top,
4173                    height: props.height,
4174                    minWidth: 0,
4175                    width: props.width,
4176                    flexGrow: props.grow,
4177                }, children: jsxRuntime.jsx(protectedApi.ContentContainer // the "cell"
4178                , { tag: "div", attrs: {
4179                        ...buildAriaCellAttrs(props),
4180                        role: 'rowheader',
4181                        'aria-rowspan': props.rowSpan,
4182                    }, className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].alignStart, classNames__default["default"].liquid, // liquid-height
4183                    props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone), renderProps: renderProps, generatorName: "resourceCellContent", customGenerator: colSpec.cellContent, defaultGenerator: renderGroupInner, classNameGenerator: colSpec.cellClass, didMount: colSpec.cellDidMount, willUnmount: colSpec.cellWillUnmount, children: (InnerContent) => (jsxRuntime.jsx("div", { ref: this.innerElRef, className: publicApi.joinClassNames(classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, classNames__default["default"].flexRow, // mimics what ResourceCell does
4184                        classNames__default["default"].stickyT), children: jsxRuntime.jsx(InnerContent // the "inner"
4185                        , { tag: "div", className: protectedApi.generateClassName(colSpec.cellInnerClass, renderProps) }) })) }) }));
4186        }
4187        componentDidMount() {
4188            this._isUnmounting = false;
4189            const innerEl = this.innerElRef.current;
4190            this.disconnectInnerHeight = protectedApi.watchHeight(innerEl, (height) => {
4191                if (this._isUnmounting)
4192                    return;
4193                protectedApi.setRef(this.props.innerHeightRef, height);
4194            });
4195        }
4196        componentWillUnmount() {
4197            this._isUnmounting = true;
4198            this.disconnectInnerHeight();
4199            protectedApi.setRef(this.props.innerHeightRef, null);
4200        }
4201    }
4202    function renderGroupInner(renderProps) {
4203        return renderProps.fieldValue || jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0" });
4204    }
4205
4206    class ResourceIndent extends protectedApi.BaseComponent {
4207        render() {
4208            const { props, context } = this;
4209            return (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(context.options.resourceIndentClass, classNames__default["default"].noShrink, classNames__default["default"].flexCol, classNames__default["default"].alignEnd), style: {
4210                    ...props.style,
4211                    width: props.indentWidth != null
4212                        ? props.indentWidth * props.level
4213                        : 0
4214                }, children: props.children }));
4215        }
4216    }
4217
4218    class ResourceExpander extends protectedApi.BaseComponent {
4219        render() {
4220            const { props } = this;
4221            const { options } = this.context;
4222            const classNameGenerator = options.resourceExpanderClass;
4223            const contentGenerator = options.resourceExpanderContent;
4224            const renderProps = {
4225                isExpanded: props.isExpanded,
4226            };
4227            return (jsxRuntime.jsx("span", { "aria-hidden": true, className: publicApi.joinClassNames(protectedApi.generateClassName(classNameGenerator, renderProps), classNames__default["default"].flexRow, classNames__default["default"].cursorPointer, classNames__default["default"].selectNone, // prevent text-selecting cell underneath
4228                classNames__default["default"].noMargin, props.className), onClick: props.onExpanderClick, ref: props.elRef, children: contentGenerator && (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: 'span', style: { display: 'contents' }, attrs: { 'aria-hidden': true }, renderProps: renderProps, generatorName: undefined, customGenerator: contentGenerator })) }));
4229        }
4230    }
4231
4232    /*
4233    Group cell that spans horizontally, consuming multiple colspans
4234    */
4235    class ResourceGroupHeaderSubrow extends protectedApi.BaseComponent {
4236        constructor() {
4237            super(...arguments);
4238            // ref
4239            this.innerElRef = compat.createRef();
4240            this.onExpanderClick = () => {
4241                let { props } = this;
4242                this.context.dispatch({
4243                    type: 'SET_RESOURCE_ENTITY_EXPANDED',
4244                    id: createGroupId(props.group),
4245                    isExpanded: !props.isExpanded,
4246                });
4247            };
4248        }
4249        render() {
4250            let { props, context } = this;
4251            let renderProps = {
4252                fieldValue: props.group.value,
4253                view: context.viewApi,
4254            };
4255            let spec = props.group.spec; // type HACK
4256            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
4257                    ...buildAriaCellAttrs(props),
4258                    role: 'rowheader',
4259                    'aria-colspan': props.colSpan,
4260                }, className: publicApi.joinClassNames(classNames__default["default"].liquid, // expand to whole row
4261                classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].alignStart, // h-align
4262                classNames__default["default"].crop, classNames__default["default"].contentBox, props.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone), style: {
4263                    height: props.height,
4264                }, renderProps: renderProps, generatorName: "resourceGroupHeaderContent", customGenerator: spec.labelContent, defaultGenerator: renderCellInner, classNameGenerator: spec.labelClass, didMount: spec.labelDidMount, willUnmount: spec.labelWillUnmount, children: (InnerContent) => (jsxRuntime.jsxs("div", { ref: this.innerElRef, className: publicApi.joinClassNames(classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, classNames__default["default"].flexRow), style: {
4265                        isolation: 'isolate', // TODO: className
4266                    }, children: [jsxRuntime.jsx(ResourceIndent, { level: 1, indentWidth: props.indentWidth, style: { zIndex: 2 }, children: jsxRuntime.jsx(ResourceExpander, { isExpanded: props.isExpanded, onExpanderClick: this.onExpanderClick }) }), jsxRuntime.jsx(InnerContent, { tag: 'div', className: protectedApi.generateClassName(spec.labelInnerClass, renderProps), style: { zIndex: 1 } })] })) }));
4267        }
4268        componentDidMount() {
4269            this._isUnmounting = false;
4270            const innerEl = this.innerElRef.current;
4271            this.disconnectInnerHeight = protectedApi.watchHeight(innerEl, (height) => {
4272                if (this._isUnmounting)
4273                    return;
4274                protectedApi.setRef(this.props.innerHeightRef, height);
4275            });
4276        }
4277        componentWillUnmount() {
4278            this._isUnmounting = true;
4279            this.disconnectInnerHeight();
4280            protectedApi.setRef(this.props.innerHeightRef, null);
4281        }
4282    }
4283    ResourceGroupHeaderSubrow.addPropsEquality({
4284        group: isGroupsEqual,
4285    });
4286    function renderCellInner(renderProps) {
4287        return renderProps.fieldValue || jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0" });
4288    }
4289
4290    class ResourceCell extends protectedApi.BaseComponent {
4291        constructor() {
4292            super(...arguments);
4293            this.innerElRef = compat.createRef();
4294            this.refineRenderProps = protectedApi.memoizeObjArg(refineRenderProps);
4295            this.onExpanderClick = (ev) => {
4296                let { props } = this;
4297                if (props.hasChildren) {
4298                    this.context.dispatch({
4299                        type: 'SET_RESOURCE_ENTITY_EXPANDED',
4300                        id: props.resource.id,
4301                        isExpanded: !props.isExpanded,
4302                    });
4303                }
4304            };
4305        }
4306        render() {
4307            let { props, context } = this;
4308            let { colSpec } = props;
4309            let renderProps = this.refineRenderProps({
4310                resource: props.resource,
4311                field: props.field,
4312                fieldValue: props.fieldValue,
4313                context,
4314            });
4315            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
4316                    ...buildAriaCellAttrs(props),
4317                    role: colSpec.isMain ? 'rowheader' : 'gridcell',
4318                    'data-resource-id': props.resource.id,
4319                }, className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].alignStart, props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone, classNames__default["default"].crop), style: {
4320                    minWidth: 0,
4321                    width: props.width,
4322                    flexGrow: props.grow,
4323                }, renderProps: renderProps, generatorName: 'resourceCellContent', customGenerator: colSpec.cellContent, defaultGenerator: renderResourceInner, classNameGenerator: colSpec.cellClass, didMount: colSpec.cellDidMount, willUnmount: colSpec.cellWillUnmount, children: (InnerContent) => (jsxRuntime.jsxs("div", { ref: this.innerElRef, className: publicApi.joinClassNames(classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, classNames__default["default"].flexRow), style: {
4324                        isolation: 'isolate', // TODO: className
4325                    }, children: [colSpec.isMain && Boolean(props.indent) && (jsxRuntime.jsx(ResourceIndent, { level: props.indent, indentWidth: props.indentWidth, style: { zIndex: 2 }, children: props.hasChildren && (jsxRuntime.jsx(ResourceExpander, { isExpanded: props.isExpanded, onExpanderClick: this.onExpanderClick })) })), jsxRuntime.jsx(InnerContent, { tag: 'div', className: protectedApi.generateClassName(colSpec.cellInnerClass, renderProps), style: { zIndex: 1 } })] })) }));
4326        }
4327        componentDidMount() {
4328            this._isUnmounting = false;
4329            this.disconnectHeight = protectedApi.watchHeight(this.innerElRef.current, (height) => {
4330                if (this._isUnmounting)
4331                    return;
4332                protectedApi.setRef(this.props.innerHeightRef, height);
4333            });
4334        }
4335        componentWillUnmount() {
4336            this._isUnmounting = true;
4337            this.disconnectHeight();
4338            protectedApi.setRef(this.props.innerHeightRef, null);
4339        }
4340    }
4341    function renderResourceInner(renderProps) {
4342        return renderProps.fieldValue || jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0" });
4343    }
4344    function refineRenderProps(input) {
4345        return {
4346            resource: new ResourceApi(input.context, input.resource),
4347            field: input.field,
4348            fieldValue: input.fieldValue,
4349            view: input.context.viewApi,
4350        };
4351    }
4352
4353    class ResourceSubrow extends protectedApi.BaseComponent {
4354        constructor() {
4355            super(...arguments);
4356            // refs
4357            this.innerHeightRefMap = new protectedApi.RefMap(() => {
4358                protectedApi.afterSize(this.handleInnerHeights);
4359            });
4360            this.currentInnerHeight = null;
4361            this.handleInnerHeights = () => {
4362                if (this._isUnmounting)
4363                    return;
4364                const innerHeightMap = this.innerHeightRefMap.current;
4365                let max = null;
4366                for (const innerHeight of innerHeightMap.values()) {
4367                    if (max == null || innerHeight > max) {
4368                        max = innerHeight;
4369                    }
4370                }
4371                if (this.currentInnerHeight !== max) {
4372                    this.currentInnerHeight = max;
4373                    protectedApi.setRef(this.props.innerHeightRef, max);
4374                }
4375            };
4376        }
4377        render() {
4378            const { props, innerHeightRefMap } = this;
4379            const { resource, resourceFields, colSpecs } = props;
4380            const { options } = this.context;
4381            const colWidths = props.colWidths || [];
4382            const colGrows = props.colGrows || [];
4383            let totalColWidth;
4384            let totalColGrow;
4385            if (props.totalX) {
4386                if (props.colWidths) {
4387                    totalColWidth = totalColDims(props.colWidths, props.colStartIndex, colSpecs.length);
4388                }
4389                if (props.colGrows) {
4390                    totalColGrow = totalColDims(props.colGrows, props.colStartIndex, colSpecs.length);
4391                }
4392            }
4393            return (jsxRuntime.jsx("div", { role: props.role, className: publicApi.joinClassNames(options.resourceRowClass, props.className, // what for???
4394                classNames__default["default"].flexRow, props.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone), style: {
4395                    top: props.top,
4396                    height: props.height,
4397                    width: totalColWidth,
4398                    flexGrow: totalColGrow,
4399                }, children: mapRange(props.colStartIndex, colSpecs.length, (i) => {
4400                    const colSpec = colSpecs[i];
4401                    const fieldValue = colSpec.field ? resourceFields[colSpec.field] :
4402                        (resource.title || getPublicId(resource.id));
4403                    return (jsxRuntime.jsx(ResourceCell, { cellIdPrefix: props.cellIdPrefix, cellRowIndex: props.cellRowIndex, cellColIndex: props.cellIdPrefix ? i : undefined, colSpec: colSpec, resource: resource, field: colSpec.field || 'title', fieldValue: fieldValue, indent: props.indent, hasChildren: props.hasChildren, isExpanded: props.isExpanded, innerHeightRef: innerHeightRefMap.createRef(i), width: colWidths[i], indentWidth: props.indentWidth, grow: colGrows[i], borderStart: props.borderStart || Boolean(i) }, i));
4404                }) }));
4405        }
4406        componentDidMount() {
4407            this._isUnmounting = false;
4408        }
4409        componentWillUnmount() {
4410            this._isUnmounting = true;
4411            this.currentInnerHeight = null;
4412        }
4413    }
4414    ResourceSubrow.addPropsEquality({
4415        colWidths: protectedApi.isArraysEqual,
4416    });
4417    // Utils
4418    // -------------------------------------------------------------------------------------------------
4419    function mapRange(start, end, func) {
4420        const items = [];
4421        for (let i = start; i < end; i++) {
4422            items.push(func(i));
4423        }
4424        return items;
4425    }
4426    function totalColDims(colDims, startIndex, endIndex) {
4427        let total = 0;
4428        for (let i = startIndex; i < endIndex; i++) {
4429            total += colDims[i];
4430        }
4431        return total;
4432    }
4433
4434    /*
4435    Takes up no vertical space. Fills itself within outer container.
4436    Caller is responsible for this.
4437    */
4438    class BodySection extends protectedApi.BaseComponent {
4439        render() {
4440            const { props } = this;
4441            const { rowInnerHeightRefMap, headerRowSpan } = props;
4442            const groupColCnt = props.groupCellCnts.length;
4443            const colWidths = props.colWidths || [];
4444            return (jsxRuntime.jsxs("div", { className: classNames__default["default"].flexRow, style: {
4445                    minWidth: props.canvasWidth,
4446                    minHeight: props.canvasHeight,
4447                }, children: [props.groupColPositions.map((groupCellPositions, colIndex) => (jsxRuntime.jsx("div", { role: 'presentation', className: classNames__default["default"].rel /* origin for abs-positioned rows */, style: {
4448                            height: 0,
4449                            minWidth: 0,
4450                            width: colWidths[colIndex],
4451                        }, children: groupCellPositions.map((groupCellPosition, groupIndex) => {
4452                            const groupCellLayout = groupCellPosition.item;
4453                            const group = groupCellLayout.entity;
4454                            const groupKey = groupCellPosition.key;
4455                            const isNotLast = groupIndex < groupCellPositions.length - 1;
4456                            const rowHeight = groupCellPosition.size;
4457                            return (jsxRuntime.jsx(ResourceGroupSubrow, { colSpec: group.spec, rowSpan: groupCellLayout.rowSpan, fieldValue: group.value, className: classNames__default["default"].fillX, borderStart: Boolean(colIndex), borderBottom: isNotLast, role: 'presentation', cellIdPrefix: props.cellIdPrefix, cellRowIndex: 1 + headerRowSpan + groupCellLayout.rowIndex, cellColIndex: colIndex, innerHeightRef: rowInnerHeightRefMap.createRef(groupKey), top: groupCellPosition.start, height: (row
4457Height != null && isNotLast)
4458                                    ? rowHeight + ROW_BORDER_WIDTH // considering bottom border, which is added to cell
4459                                    : rowHeight }, groupKey));
4460                        }) }, colIndex))), jsxRuntime.jsxs("div", { role: 'presentation', className: classNames__default["default"].rel, style: {
4461                            height: 0,
4462                            minWidth: 0,
4463                            width: sumArray(colWidths.slice(groupColCnt)),
4464                        }, children: [props.groupRowPositions.map((rowPosition) => {
4465                                const groupRowLayout = rowPosition.item;
4466                                const group = groupRowLayout.entity;
4467                                const groupKey = rowPosition.key;
4468                                // Compare against every expansion-filtered row, not only the virtualized subset.
4469                                const isNotLast = groupRowLayout.visibleIndex < props.displayedRowCnt - 1;
4470                                return (jsxRuntime.jsx("div", { role: 'presentation', className: publicApi.joinClassNames(classNames__default["default"].fillX, classNames__default["default"].flexRow), style: {
4471                                        top: rowPosition.start,
4472                                    }, children: jsxRuntime.jsx(ResourceGroupHeaderSubrow, { cellIdPrefix: props.cellIdPrefix, cellRowIndex: 1 + headerRowSpan + groupRowLayout.rowIndex, cellColIndex: 0, group: group, isExpanded: groupRowLayout.isExpanded, innerHeightRef: rowInnerHeightRefMap.createRef(groupKey), colSpan: props.colSpecs.length, borderBottom: isNotLast, height: rowPosition.size, indentWidth: props.indentWidth, className: classNames__default["default"].fillX }) }, groupKey));
4473                            }), props.resourceRowPositions.map((rowPosition) => {
4474                                const resourceLayout = rowPosition.item;
4475                                const resource = resourceLayout.entity;
4476                                const rowHeight = rowPosition.size;
4477                                // Compare against every expansion-filtered row, not only the virtualized subset.
4478                                const isNotLast = resourceLayout.visibleIndex < props.displayedRowCnt - 1;
4479                                return (jsxRuntime.jsx(ResourceSubrow, { resource: resource, resourceFields: resourceLayout.resourceFields, indent: resourceLayout.indent, hasChildren: resourceLayout.hasChildren, isExpanded: resourceLayout.isExpanded, colStartIndex: groupColCnt, colSpecs: props.colSpecs, colWidths: colWidths, innerHeightRef: rowInnerHeightRefMap.createRef(resource.id), className: classNames__default["default"].fillX, borderStart: Boolean(groupColCnt), borderBottom: isNotLast, role: 'presentation', cellIdPrefix: props.cellIdPrefix, cellRowIndex: 1 + headerRowSpan + resourceLayout.rowIndex, top: rowPosition.start, height: (rowHeight != null && isNotLast)
4480                                        ? rowHeight + ROW_BORDER_WIDTH // considering bottom border, which is added to cell
4481                                        : rowHeight, indentWidth: props.indentWidth }, resource.id /* TODO: use rowPosition.key? */));
4482                            })] })] }));
4483        }
4484    }
4485    // Utils
4486    // -------------------------------------------------------------------------------------------------
4487    function sumArray(a) {
4488        let sum = 0;
4489        for (const num of a) {
4490            sum += num;
4491        }
4492        return sum;
4493    }
4494
4495    class HeaderCell extends protectedApi.BaseComponent {
4496        constructor() {
4497            super(...arguments);
4498            // ref
4499            this.innerElRef = compat.createRef();
4500        }
4501        render() {
4502            let { props, context } = this;
4503            let { colSpec } = props;
4504            let renderProps = { view: context.viewApi };
4505            // need empty inner div for abs positioning for resizer
4506            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
4507                    ...buildAriaCellAttrs(props),
4508                    role: 'columnheader',
4509                }, className: publicApi.joinClassNames(classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone, 
4509classNames__default["default"].rel), style: {
4510                    minWidth: 0,
4511                    width: props.width,
4512                    flexGrow: props.grow,
4513                }, renderProps: renderProps, generatorName: "resourceColumnHeaderContent", customGenerator: colSpec.headerContent, defaultGenerator: colSpec.headerDefault, classNameGenerator: colSpec.headerClass, didMount: colSpec.headerDidMount, willUnmount: colSpec.headerWillUnmount, children: (InnerContent) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].crop, // must crop instead of outer
4514                            classNames__default["default"].flexCol, classNames__default["default"].alignStart), children: jsxRuntime.jsxs("div", { ref: this.innerElRef, className: publicApi.joinClassNames(classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, classNames__default["default"].flexRow), children: [this.props.indent && (jsxRuntime.jsx(ResourceIndent, { level: 1, indentWidth: props.indentWidth })), jsxRuntime.jsx(InnerContent, { tag: 'div', className: protectedApi.generateClassName(colSpec.headerInnerClass, renderProps) })] }) }), props.resizer && (jsxRuntime.jsx("div", { ref: props.resizerElRef, className: publicApi.joinClassNames(protectedApi.generateClassName(colSpec.headerResizerClass, renderProps), classNames__default["default"].z1, classNames__default["default"].cursorColResizer) }))] })) }));
4515        }
4516        componentDidMount() {
4517            this._isUnmounting = false;
4518            const innerEl = this.innerElRef.current;
4519            this.disconnectInnerHeight = protectedApi.watchHeight(innerEl, (height) => {
4520                if (this._isUnmounting)
4521                    return;
4522                protectedApi.setRef(this.props.innerHeightRef, height);
4523            });
4524        }
4525        componentWillUnmount() {
4526            this._isUnmounting = true;
4527            this.disconnectInnerHeight();
4528            protectedApi.setRef(this.props.innerHeightRef, null);
4529        }
4530    }
4531
4532    class HeaderRow extends protectedApi.BaseComponent {
4533        constructor() {
4534            super(...arguments);
4535            // refs
4536            this.resizerElRefMap = new protectedApi.RefMap((resizerEl, index) => {
4537                let { colDraggings } = this;
4538                if (!resizerEl) {
4539                    let dragging = colDraggings[index];
4540                    if (dragging) {
4541                        dragging.destroy();
4542                        delete colDraggings[index];
4543                    }
4544                }
4545                else {
4546                    let dragging = this.initColResizing(resizerEl, index);
4547                    if (dragging) {
4548                        colDraggings[index] = dragging;
4549                    }
4550                }
4551            });
4552            this.innerHeightRefMap = new protectedApi.RefMap(() => {
4553                protectedApi.afterSize(this.handleInnerHeights);
4554            });
4555            this.colDraggings = {};
4556            this.handleInnerHeights = () => {
4557                if (this._isUnmounting)
4558                    return;
4559                const { innerHeightRefMap } = this;
4560                let max = 0;
4561                for (const innerHeight of innerHeightRefMap.current.values()) {
4562                    max = Math.max(max, innerHeight);
4563                }
4564                if (this.currentInnerHeight !== max) {
4565                    this.currentInnerHeight = max;
4566                    protectedApi.setRef(this.props.innerHeightRef, max);
4567                }
4568            };
4569        }
4570        render() {
4571            const { props, innerHeightRefMap, resizerElRefMap } = this;
4572            const { colSpecs } = props;
4573            const { options } = this.context;
4574            const colWidths = props.colWidths || [];
4575            const colGrows = props.colGrows || [];
4576            return (jsxRuntime.jsx("div", { role: props.role || 'row', "aria-rowindex": props.role !== 'presentation' && props.rowIndex != null ? 1 + props.rowIndex : undefined, className: publicApi.joinClassNames(options.resourceHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, classNames__default["default"].borderNone), children: colSpecs.map((colSpec, colIndex) => {
4577                    return (jsxRuntime.jsx(HeaderCell, { cellIdPrefix: props.cellIdPrefix, cellRowIndex: props.rowIndex != null ? 1 + props.rowIndex : undefined, cellColIndex: props.cellIdPrefix ? colIndex : undefined, width: colWidths[colIndex], grow: colGrows[colIndex], colSpec: colSpec, resizer: colIndex < colSpecs.length - 1, indent: colSpec.isMain && props.indent, resizerElRef: resizerElRefMap.createRef(colIndex), innerHeightRef: innerHe
4577ightRefMap.createRef(colIndex), borderStart: Boolean(colIndex), indentWidth: props.indentWidth }, colIndex));
4578                }) }));
4579        }
4580        componentDidMount() {
4581            this._isUnmounting = false;
4582        }
4583        componentWillUnmount() {
4584            this._isUnmounting = true;
4585            this.currentInnerHeight = undefined;
4586            protectedApi.setRef(this.props.innerHeightRef, null);
4587        }
4588        // Dragging
4589        // -----------------------------------------------------------------------------------------------
4590        initColResizing(resizerEl, colIndex) {
4591            let { pluginHooks } = this.context;
4592            let ElementDraggingImpl = pluginHooks.elementDraggingImpl;
4593            if (ElementDraggingImpl) {
4594                let dragging = new ElementDraggingImpl(resizerEl);
4595                dragging.emitter.on('dragstart', () => {
4596                    const isRtl = protectedApi.computeElIsRtl(resizerEl);
4597                    const startWidth = this.props.colWidths[colIndex];
4598                    dragging.emitter.on('dragmove', (pev) => {
4599                        if (this.props.onColResize) {
4600                            this.props.onColResize(colIndex, startWidth + pev.deltaX * (isRtl ? -1 : 1));
4601                        }
4602                    });
4603                    // because gets weird with auto-scrolling time area
4604                    dragging.setAutoScrollEnabled(false);
4605                });
4606                return dragging;
4607            }
4608            return null;
4609        }
4610    }
4611
4612    /*
4613    TODO: make more DRY with HeaderCell. Almost exactly the same except doesn't need resizer
4614    */
4615    class SuperHeaderCell extends protectedApi.BaseComponent {
4616        constructor() {
4617            super(...arguments);
4618            // refs
4619            this.innerElRef = compat.createRef();
4620        }
4621        render() {
4622            let { renderHooks } = this.props;
4623            let renderProps = { view: this.context.viewApi };
4624            return (jsxRuntime.jsx(protectedApi.ContentContainer, { tag: "div", attrs: {
4625                    ...buildAriaCellAttrs(this.props),
4626                    role: 'columnheader',
4627                    'aria-colspan': this.props.colSpan,
4628                }, className: publicApi.joinClassNames(classNames__default["default"].liquid, classNames__default["default"].noMargin, classNames__default["default"].noPadding, classNames__default["default"].flexCol, classNames__default["default"].alignStart, classNames__default["default"].borderNone, classNames__default["default"].crop), renderProps: renderProps, generatorName: "resourceColumnHeaderContent", customGenerator: renderHooks.headerContent, defaultGenerator: renderHooks.headerDefault, classNameGenerator: renderHooks.headerClass, didMount: renderHooks.headerDidMount, willUnmount: renderHooks.headerWillUnmount, children: (InnerContent) => (jsxRuntime.jsxs("div", { ref: this.innerElRef, className: publicApi.joinClassNames(classNames__default["default"].noShrink, classNames__default["default"].whiteSpaceNoWrap, classNames__default["default"].flexRow), children: [this.props.indent && (jsxRuntime.jsx(ResourceIndent, { level: 1, indentWidth: this.props.indentWidth })), jsxRuntime.jsx(InnerContent, { tag: 'div', className: protectedApi.generateClassName(renderHooks.headerInnerClass, renderProps) })] })) }));
4629        }
4630        componentDidMount() {
4631            this._isUnmounting = false;
4632            const innerEl = this.innerElRef.current;
4633            this.disconnectInnerHeight = protectedApi.watchHeight(innerEl, (height) => {
4634                if (this._isUnmounting)
4635                    return;
4636                protectedApi.setRef(this.props.innerHeightRef, height);
4637            });
4638        }
4639        componentWillUnmount() {
4640            this._isUnmounting = true;
4641            this.disconnectInnerHeight();
4642            protectedApi.setRef(this.props.innerHeightRef, null);
4643        }
4644    }
4645
4646    class Virtualizer {
4647        constructor(getItemKey, getItemStart, getItemSize, requestRerender, overscan = 1) {
4648            this.getItemKey = getItemKey;
4649            this.getItemStart = getItemStart;
4650            this.getItemSize = getItemSize;
4651            this.requestRerender = requestRerender;
4652            this.overscan = overscan;
4653            // left exposed so they can be prepopulated :(
4654            this.viewportSize = 0;
4655            this.scroll = 0;
4656            this.items = [];
4657        }
4658        computePositions(items, disabled, forcedScroll) {
4659            if (disabled) {
4660                this.items = [];
4661                this.itemPositions = undefined; // affects _handleViewportChange
4662                return this.computeAllPositions(items);
4663            }
4664            this.items = items;
4665            if (forcedScroll !== undefined) {
4666                this.scroll = forcedScroll;
4667            }
4668            if (!this.viewportSize) {
4669                return [];
4670            }
4671            return (this.itemPositions = this.computePositionsInRange(items));
4672        }
4673        handleViewportSize(size) {
4674            if (size !== this.viewportSize) {
4675                this.viewportSize = size;
4676                this._handleViewportChange();
4677            }
4678        }
4679        handleScroll(scroll) {
4680            if (scroll !== this.scroll) {
4681                this.scroll = scroll;
4682                this._handleViewportChange();
4683            }
4684        }
4685        _handleViewportChange() {
4686            if (this.itemPositions) {
4687                const testItemPositions = this.computePositionsInRange(this.items);
4688                if (testItemPositions.length !== this.itemPositions.length || (testItemPositions.length &&
4689                    testItemPositions[0].index !== this.itemPositions[0].index)) {
4690                    this.requestRerender();
4691                }
4692            }
4693        }
4694        computePositionsInRange(items) {
4695            const { scroll, viewportSize, getItemKey, getItemStart, getItemSize, overscan } = this;
4696            const count = items.length;
4697            let itemPositions = [];
4698            let index = 0;
4699            let runningStart = 0;
4700            while (index < count) {
4701                const item = items[index];
4702                const key = getItemKey(item);
4703                const start = getItemStart ? getItemStart(key, index, item) : runningStart;
4704                const size = getItemSize(key, index, item);
4705                if (start + size > scroll) {
4706                    break;
4707                }
4708                index++;
4709                runningStart += size;
4710            }
4711            // back up for underscan
4712            const startIndex = Math.max(index - overscan, 0);
4713            while (index > startIndex) {
4714                const item = items[index - 1];
4715                const key = getItemKey(item);
4716                const size = getItemSize(key, index - 1, item);
4717                index--;
4718                runningStart -= size;
4719            }
4720            while (index < count) {
4721                const item = items[index];
4722                const key = getItemKey(item);
4723                const start = getItemStart ? getItemStart(key, index, item) : runningStart;
4724                const size = getItemSize(key, index, item);
4725                if (start >= scroll + viewportSize) {
4726                    break;
4727                }
4728                itemPositions.push({
4729                    item,
4730                    key,
4731                    index,
4732                    start,
4733                    size,
4734                });
4735                index++;
4736                runningStart += size;
4737            }
4738            const endIndex = Math.min(index + overscan, count);
4739            // overshoot for overscan
4740            while (index < endIndex) {
4741                const item = items[index];
4742                const key = getItemKey(item);
4743                const start = getItemStart ? getItemStart(key, index, item) : runningStart;
4744                const size = getItemSize(key, index, item);
4745                itemPositions.push({
4746                    item,
4747                    key,
4748                    index,
4749                    start,
4750                    size,
4751                });
4752                index++;
4753                runningStart += size;
4754            }
4755            return itemPositions;
4756        }
4757        computeAllPositions(items) {
4758            const { getItemKey, getItemStart, getItemSize } = this;
4759            let runningStart = 0;
4760            return items.map((item, index) => {
4761                const key = getItemKey(item);
4762                const start = getItemStart ? getItemStart(key, index, item) : runningStart;
4763                const size = getItemSize(key, index, item);
4764                runningStart += size;
4765                return {
4766                    item,
4767                    key,
4768                    index,
4769                    start,
4770                    size,
4771                };
4772            });
4773        }
4774    }
4775    function computeShift(itemPositions) {
4776        const count = itemPositions.length;
4777        if (count) {
4778            const start = itemPositions[0].start;
4779            const end = itemPositions[count - 1].start + itemPositions[count - 1].size;
4780            return [start, end, itemPositions[0].index];
4781        }
4782    }
4783
4784    const defaultOwnCellHeight = 40;
4785    const defaultSlotWidth = 50;
4786    class ResourceTimelineLayoutNormal extends protectedApi.DateComponent {
4787        constructor() {
4788            super(...arguments);
4789            this.state = {};
4790            // memoized
4791            this.buildResourceLayouts = protectedApi.memoize(buildResourceLayouts);
4792            // TODO: make this nice
4793            // This is a means to recompute row positioning when *HeightMaps change
4794            this.queuedHeightChange = false;
4795            this.handleHeightChange = () => {
4796                if (this._isUnmounting)
4797                    return;
4798                this.queuedHeightChange = true;
4799                protectedApi.afterSize(this.boundForceUpdate);
4800            };
4801            this.boundForceUpdate = () => {
4802                if (this._isUnmounting)
4803                    return;
4804                this.forceUpdate();
4805            };
4806            this.spreadsheetHeaderScrollerRef = compat.createRef();
4807            this.spreadsheetBodyScrollerRef = compat.createRef();
4808            this.spreadsheetFooterScrollerRef = compat.createRef();
4809            this.timeHeaderScrollerRef = compat.createRef();
4810            this.timeBodyScrollerRef = compat.createRef();
4811            this.timeFooterScrollerRef = compat.createRef();
4812            this.headerRowInnerWidthMap = new protectedApi.RefMap(() => {
4813                protectedApi.afterSize(this.handleSlotInnerWidths);
4814            });
4815            this.timelineHeaderRowInnerHeightMap = new protectedApi.RefMap(this.handleHeightChange);
4816            this.dataGridHeaderRowInnerHeightMap = new protectedApi.RefMap(this.handleHeightChange);
4817            // keyed by createEntityId
4818            // NOTE: ignoring deletes can cause memory issues if resources constantly change and have new keys
4819            // refactor in future. FWIW, TanStack Virtual doesn't garbage collect unnused dimensions either
4820            this.dataGridEntityInnerHeightMap = new protectedApi.RefMap(this.handleHeightChange, /* ignoreDeletes = */ true);
4821            this.timeEntityInnerHeightMap = new protectedApi.RefMap(this.handleHeightChange, /* ignoreDeletes = */ true);
4822            // updated in-place
4823            // .time takes precedence
4824            // to operate on pixel value (.x), clear time
4825            this.scroll = {};
4826            // virtualizers
4827            this.getEntityTop = (key) => this.bodyTops.get(key);
4828            this.getEntityHeight = (key) => this.bodyHeights.get(key);
4829            this.rowVirtualizer = new Virtualizer((rowLayout) => createEntityId(rowLayout.entity), this.getEntityTop, this.getEntityHeight, this.boundForceUpdate);
4830            this.groupColVirtualizers = [];
4831            this.slotVirtualizer = new Virtualizer((dateMarker) => dateMarker.toISOString(), (_key, index) => index * (this.props.slotWidth ?? defaultSlotWidth), () => this.props.slotWidth ?? defaultSlotWidth, this.boundForceUpdate, 
4832            /* overscan = */ 3);
4833            this.timeHeaderVirtualizers = [];
4834            // Sizing
4835            // -----------------------------------------------------------------------------------------------
4836            this.handleSpreadsheetClientWidth = (spreadsheetClientWidth) => {
4837                if (this._isUnmounting)
4838                    return;
4839                protectedApi.setRef(this.props.spreadsheetClientWidthRef, spreadsheetClientWidth);
4840            };
4841            this.handleSpreadsheetBottomScrollbarWidth = (spreadsheetBottomScrollbarWidth) => {
4842                this.setState({
4843                    spreadsheetBottomScrollbarWidth,
4844                });
4845            };
4846            this.handleTimeTotalWidth = (timeTotalWidth) => {
4847                if (this._isUnmounting)
4848                    return;
4849                this.setState({
4850                    timeTotalWidth,
4851                });
4852            };
4853            this.handleTimeClientWidth = (timeClientWidth) => {
4854                if (this._isUnmounting)
4855                    return;
4856                protectedApi.setRef(this.props.timeClientWidthRef, timeClientWidth);
4857                this.setState({
4858                    timeClientWidth,
4859                });
4860                this.slotVirtualizer.handleViewportSize(timeClientWidth);
4861                for (const timeHeaderVirtualizer of this.timeHeaderVirtualizers) {
4862                    timeHeaderVirtualizer.handleViewportSize(timeClientWidth);
4863                    // TODO: what about difference in scrollbar width?
4864                }
4865            };
4866            this.handleTimeClientHeight = (timeClientHeight) => {
4867                if (this._isUnmounting)
4868                    return;
4869                this.setState({
4870                    timeClientHeight,
4871                });
4872                if (!protectedApi.getIsHeightAuto(this.context.options)) {
4873                    this.setVirtualizerViewportSize(timeClientHeight);
4874                }
4875            };
4876            this.handleTimeBottomScrollbarWidth = (timeBottomScrollbarWidth) => {
4877                if (this._isUnmounting)
4878                    return;
4879                this.setState({
4880                    timeBottomScrollbarWidth,
4881                });
4882            };
4883            this.handleSlotInnerWidths = () => {
4884                if (this._isUnmounting)
4885                    return;
4886                const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.props.tDateProfile.cellRows.length - 1);
4887                if (headerSlotInnerWidth != null && headerSlotInnerWidth !== this.slotInnerWidth) {
4888                    this.slotInnerWidth = headerSlotInnerWidth;
4889                    protectedApi.setRef(this.props.slotInnerWidthRef, headerSlotInnerWidth);
4890                }
4891            };
4892            this.updateVirtualizerScroll = protectedApi.debounce(() => {
4893                const scroll = this.currentEntityScroll;
4894                this.rowVirtualizer.handleScroll(scroll);
4895                for (const groupColVirtualizer of this.groupColVirtualizers) {
4896                    groupColVirtualizer.handleScroll(scroll);
4897                }
4898            }, 10)[0];
4899            // Window Scrolling
4900            // -----------------------------------------------------------------------------------------------
4901            this.isTrackingWindowScroll = false;
4902            this.bodyOffset = 0;
4903            this.lastOffsetQuery = 0;
4904            this.handleWindowScroll = () => {
4905                const scrollY = window.scrollY;
4906                const now = Date.now();
4907                if (now - this.lastOffsetQuery > 200) {
4908                    this.lastOffsetQuery = now;
4909                    this.bodyOffset = this.bodyEl.getBoundingClientRect().top + scrollY;
4910                }
4911                this.setVirtualizerScroll(scrollY - this.bodyOffset);
4912            };
4913            this.handleTimeScrollRequest = (timeScroll) => {
4914                this.scroll.time = timeScroll;
4915                this.applyTimeScroll();
4916            };
4917            // HACKY
4918            // can't use isDevice because we want programmatic .scrollLeft assignments to work with scroll-virtualization too.
4919            // the main reason we use a isDevice gate is to prevent the initial-scroll assignment from triggering handleTimeScroll,
4920            // but the debounce luckily helps us in this case
4921            this.handleTimeScroll = (_isDevice, scroll) => {
4922                this.scroll.x = scroll;
4923                this._handleTimeScroll();
4924            };
4925            this._handleTimeScroll = protectedApi.debounce(() => {
4926                this.scroll.time = undefined; // prevents computeTimeScroll from computing based on time
4927                const scrollX = this.scroll.x;
4928                this.slotVirtualizer.handleScroll(scrollX);
4929                for (const timeHeaderVirtualizer of this.timeHeaderVirtualizers) {
4930                    timeHeaderVirtualizer.handleScroll(scrollX);
4931                }
4932            }, 10)[0];
4933            this.handleTimeScrollEnd = () => {
4934                protectedApi.setRef(this.props.scrollRef, this.scroll);
4935            };
4936            // ENTITY (RESOURCE) Scrolling
4937            // -----------------------------------------------------------------------------------------------
4938            this.handleResourceScrollRequest = (resourceId) => {
4939                this.scroll.entityId = resourceId;
4940                this.scroll.fromBottom = undefined;
4941                this.applyEntityScroll();
4942            };
4943            this.handleEntityScroll = (_isDevice, scroll) => {
4944                if (!protectedApi.getIsHeightAuto(this.context.options)) {
4945                    this.setVirtualizerScroll(scroll);
4946                }
4947            };
4948            this.handleEntityScrollStart = () => {
4949                this.scroll.entityId = undefined;
4950                this.scroll.fromBottom = undefined;
4951            };
4952            /*
4953            Captures current values
4954            */
4955            this.handleEntityScrollEnd = () => {
4956                if (!this.isTrackingWindowScroll) {
4957                    const { bodyLayouts, bodyTops, bodyHeights, scroll } = this;
4958                    const y = this.bodyScroller.y;
4959                    const coordRes = findEntityByCoord(bodyLayouts, bodyTops, bodyHeights, y, createEntityId);
4960                    if (coordRes) {
4961                        const [entity, elTop, elHeight] = coordRes;
4962                        scroll.entityId = createEntityId(entity);
4963                        scroll.fromBottom = y
4964                            ? elTop + elHeight - y
4965                            : undefined; // if already at top, keep at top
4966                    }
4967                }
4968            };
4969            // Hit System
4970            // -----------------------------------------------------------------------------------------------
4971            this.handleBodyEl = (el) => {
4972                this.bodyEl = el;
4973                if (el) {
4974                    this.context.registerInteractiveComponent(this, { el });
4975                }
4976                else {
4977                    this.context.unregisterInteractiveComponent(this);
4978                }
4979            };
4980        }
4981        render() {
4982            let { props, state, context } = this;
4983            let { dateProfile, hasNesting } = props;
4984            let { options, viewSpec } = context;
4985            const { borderlessX, borderlessTop, borderlessBottom } = protectedApi.computeViewBorderless(options);
4986            let { spreadsheetBottomScrollbarWidth, timeTotalWidth, timeClientWidth, timeClientHeight, timeBottomScrollbarWidth } = state;
4987            /* date */
4988            const { tDateProfile } = props;
4989            let { cellRows } = tDateProfile;
4990            /* table settings */
4991            let allowVerticalScrolling = !protectedApi.getIsHeightAuto(options);
4992            let tableHeaderSticky = protectedApi.getTableHeaderSticky(options);
4993            let footerScrollbarSticky = protectedApi.getFooterScrollbarSticky(options);
4994            const { colSpecs, groupColCnt, superHeaderRendering } = props;
4995            /* table hierarchy */
4996            const { resourceHierarchy } = props;
4997            const superHeaderRowSpan = superHeaderRendering ? 1 : 0;
4998            const totalHeaderRowSpan = superHeaderRowSpan + 1;
4999            /* table display */
5000            let { layouts: bodyLayouts, flatRowLayouts, flatGroupColLayouts, totalCnt, } = this.buildResourceLayouts(resourceHierarchy, hasNesting, props.resourceEntityExpansions, options.resourcesInitiallyExpanded);
5001            this.bodyLayouts = bodyLayouts;
5002            // update groupColVirtualizers
5003            let groupColVirtualizers = this.groupColVirtualizers;
5004            let oldLen = groupColVirtualizers.length;
5005            let newLen = flatGroupColLayouts.length;
5006            if (newLen > oldLen) {
5007                for (let i = oldLen; i < newLen; i++) {
5008                    groupColVirtualizers[i] = new Virtualizer((groupCellLayout) => createGroupId(groupCellLayout.entity), this.getEntityTop, this.getEntityHeight, this.boundForceUpdate);
5009                }
5010            }
5011            else if (newLen < oldLen) {
5012                groupColVirtualizers = groupColVirtualizers.slice(0, newLen);
5013            }
5014            this.groupColVirtualizers = groupColVirtualizers;
5015            // update timeHeaderVirtualizers
5016            let timeHeaderVirtualizers = this.timeHeaderVirtualizers;
5017            oldLen = timeHeaderVirtualizers.length;
5018            newLen = cellRows.length;
5019            if (newLen > oldLen) {
5020                for (let i = oldLen; i < newLen; i++) {
5021                    timeHeaderVirtualizers[i] = new Virtualizer((cell) => cell.rowUnit + ':' + cell.date.toISOString(), // TODO: DRY with TimelineHeaderRow
5022                    undefined, // getItemStart (let Virtualizer compute it)
5023                    (key, index, cell) => cell.colspan * (this.props.slotWidth ?? defaultSlotWidth), this.boundForceUpdate);
5024                    // HACK to prepopulate
5025                    timeHeaderVirtualizers[i].viewportSize = timeHeaderVirtualizers[0].viewportSize;
5026                    timeHeaderVirtualizers[i].scroll = timeHeaderVirtualizers[0].scroll;
5027                }
5028            }
5029            this.timeHeaderVirtualizers = timeHeaderVirtualizers;
5030            // Expansion-filtered rows, including those outside the virtualized viewport.
5031            // `totalCnt` also includes collapsed descendants; `rowPositions` below is the mounted subset.
5032            const displayedRowCnt = flatRowLayouts.length;
5033            /* table positions */
5034            // does not include bottom border between header and body
5035            const headerHeight = computeHeaderHeight(this.dataGridHeaderRowInnerHeightMap.current, this.timelineHeaderRowInnerHeightMap.current, Boolean(superHeaderRendering), tDateProfile.cellRows.length);
5036            let endScrollbarWidth = (timeTotalWidth != null && timeClientWidth != null)
5037                ? timeTotalWidth - timeClientWidth
5038                : undefined;
5039            // totalBodyHeight is the height WITHIN the scrollers
5040            let [bodyHeights, totalBodyHeight] = computeHeights(// TODO: memoize?
5041            bodyLayouts, createEntityId, (entityKey) => {
5042                const entitySpreadsheetHeight = this.dataGridEntityInnerHeightMap.current.get(entityKey) ?? defaultOwnCellHeight;
5043                // map doesn't contain group-column-cell heights, thus ||0
5044                // TODO: better use defaultOwnCellHeight
5045                const entityTimeHeight = this.timeEntityInnerHeightMap.current.get(entityKey) || 0;
5046                return Math.max(entitySpreadsheetHeight, entityTimeHeight);
5047            }, 
5048            /* minHeight = */ (allowVerticalScrolling && options.expandRows)
5049                ? timeClientHeight
5050                : undefined);
5051            this.bodyHeights = bodyHeights;
5052            let bodyTops = computeTopsFromHeights(bodyLayouts, createEntityId, bodyHeights);
5053            this.bodyTops = bodyTops;
5054            /* virtualize */
5055            const virtualizationDisabled = !options.virtualization;
5056            // ensure in-range items are computed with most recent scroll, even tho maybe not applied yet
5057            const forcedEntityScroll = this.computeEntityScroll();
5058            const rowPositions = this.rowVirtualizer.computePositions(flatRowLayouts, virtualizationDisabled, forcedEntityScroll);
5059            // Keep render loops component-type-specific for future row recycling.
5060            const groupRowPositions = [];
5061            const resourceRowPositions = [];
5062            for (const rowPosition of rowPositions) {
5063                if ('resourceFields' in rowPosition.item) {
5064                    resourceRowPositions.push(rowPosition);
5065                }
5066                else {
5067                    groupRowPositions.push(rowPosition);
5068                }
5069            }
5070            const groupColPositions = this.groupColVirtualizers.map((groupColVirtualizer, i) => {
5071                return groupColVirtualizer.computePositions(flatGroupColLayouts[i], virtualizationDisabled, forcedEntityScroll);
5072            });
5073            // Scope deterministic row/column cell IDs to this calendar instance.
5074            const cellIdPrefix = `${context.baseId}rt`;
5075            const ariaBodyRows = buildAriaBodyRows(flatRowLayouts, groupColPositions, rowPositions, groupColCnt, colSpecs.length);
5076            // Only paint vertical fills/lines that are in view
5077            // Big performance impact for very tall virtualized lists
5078            // NOTE: could be zero rows and groups!
5079            const rowPositionShift = computeShift(rowPositions);
5080            const yFillTop = rowPositionShift?.[0] ?? 0;
5081            const yFillBottom = rowPositionShift?.[1] ?? 0;
5082            let yFillHeight;
5083            if (allowVerticalScrolling) {
5084                yFillHeight = timeClientHeight != null
5085                    ? Math.max(yFillBottom, timeClientHeight) - yFillTop
5086                    : undefined;
5087            }
5088            else {
5089                yFillHeight = yFillBottom - yFillTop;
5090            }
5091            const forcedTimeScroll = this.computeTimeScroll();
5092            const slotDatePositions = this.slotVirtualizer.computePositions(tDateProfile.slotDates, virtualizationDisabled, forcedTimeScroll);
5093            const slotDateShift = computeShift(slotDatePositions);
5094            /* */
5095            const { timeCanvasWidth } = props;
5096            const slotWidth = props.slotWidth ?? defaultSlotWidth;
5097            const { spreadsheetColWidths, spreadsheetCanvasWidth } = props;
5098            /* event display */
5099            const { splitProps, bgSlicedProps } = props;
5100            /* business hour display */
5101            const { hasResourceBusinessHours, fallbackBusinessHours } = props;
5102            const enableNowIndicator = // TODO: DRY
5103             options.nowIndicator &&
5104                protectedApi.rangeContainsMarker(props.dateProfile.currentRange, props.nowDate);
5105            /* filler */
5106            const spreadsheetBottomFiller = Math.max(0, (timeBottomScrollbarWidth || 0) -
5107                (spreadsheetBottomScrollbarWidth || 0));
5108            const timelineBottomFiller = Math.max(0, (spreadsheetBottomScrollbarWidth || 0) -
5109                (timeBottomScrollbarWidth || 0));
5110            const maxVScroll = this.maxVScroll =
5111                (allowVerticalScrolling && timeClientHeight != null)
5112                    ? Math.max(0, totalBodyHeight - timeClientHeight)
5113                    : 0;
5114            const allowBottomFiller = !options.expandRows && // if rows expand, then we never need filler
5115                !maxVScroll; // no scrolling possible
5116            const spreadsheetNeedsBottomFiller = allowBottomFiller || Boolean(spreadsheetBottomFiller);
5117            const timelineNeedsBottomFiller = allowBottomFiller || Boolean(timelineBottomFiller);
5118            return (jsxRuntime.jsxs(protectedApi.ViewContainer, { viewSpec: viewSpec, attrs: {
5119                    role: hasNesting ? 'treegrid' : 'grid', // TODO: DRY
5120                    'aria-rowcount': totalHeaderRowSpan + totalCnt,
5121                    'aria-colcount': colSpecs.length + 1,
5122                    'aria-label': props.labelStr,
5123                    'aria-labelledby': props.labelId,
5124                }, className: publicApi.joinClassNames(props.className, protectedApi.generateClassName(options.tableClass, {
5125                    borderlessX,
5126                    borderlessTop,
5127                    borderlessBottom,
5128                    multiMonthColumns: 0,
5129                }), classNames__default["default"].flexCol), children: [jsxRuntime.jsx(AriaProxyRows, { cellIdPrefix: cellIdPrefix, bodyRows: ariaBodyRows, resourceColCnt: colSpecs.length, superHeaderRowSpan: superHeaderRowSpan, hasNesting: hasNesting }), jsxRuntime.jsx(ResizableTwoCol, { initialStartWidth: props.initialSpreadsheetWidth, resizedWidthRef: props.spreadsheetResizedWidthRef, className: allowVerticalScrolling ? classNames__default["default"].liquid : '', 
5130                        /* spreadsheet
5131                        --------------------------------------------------------------------------------- */
5132                        startClassName: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].isolate), startContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { role: 'presentation', className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableHeaderClass, {
5133                                        isSticky: tableHeaderSticky,
5134                                        borderlessX,
5135                                        borderlessTop,
5136                                        borderlessBottom,
5137                                        multiMonthColumns: 0,
5138                                    }), classNames__default["default"].flexCol, tableHeaderSticky && classNames__default["default"].tableHeaderSticky), style: {
5139                                        zIndex: 1,
5140                                    }, children: [jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].contentBox), style: {
5141                                                height: headerHeight,
5142                                            }, children: [Boolean(superHeaderRendering) && (jsxRuntime.jsx("div", { role: 'presentation', className: publicApi.joinClassNames(options.resourceHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, classNames__default["default"].borderOnlyB), children: jsxRuntime.jsx(SuperHeaderCell, { cellIdPrefix: cellIdPrefix, cellRowIndex: 1, cellColIndex: 0, renderHooks: superHeaderRendering, indent: hasNesting && !groupColCnt /* group-cols are leftmost, making expander alignment irrelevant */, innerHeightRef: this.dataGridHeaderRowInnerHeightMap.createRef(true), colSpan: colSpecs.length, indentWidth: props.indentWidth }) })), jsxRuntime.jsx(protectedApi.Scroller, { horizontal: true, hideScrollbars: true, className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].grow), ref: this.spreadsheetHeaderScrollerRef, children: jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].grow), style: { minWidth: spreadsheetCanvasWidth }, children: jsxRuntime.jsx(HeaderRow, { role: 'presentation', colSpecs: colSpecs, colWidths: spreadsheetColWidths, indent: hasNesting, indentWidth: props.indentWidth, rowIndex: superHeaderRowSpan, cellIdPrefix: cellIdPrefix, 
5143                                                            // refs
5144                                                            innerHeightRef: this.dataGridHeaderRowInnerHeightMap.createRef(false), onColResize: props.onColResize }) }) })] }), jsxRuntime.jsx("div", { className: protectedApi.generateClassName(options.slotHeaderDividerClass, {
5145                                                inTableHeader: true,
5146                                                options: { dayMinWidth: options.dayMinWidth },
5147                                            }) })] }), jsxRuntime.jsxs(protectedApi.Scroller, { vertical: allowVerticalScrolling, horizontal: true, hideScrollbars: true, className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableBodyClass, {
5148                                        borderlessX,
5149                                        borderlessTop,
5150                                        borderlessBottom,
5151                                        multiMonthColumns: 0,
5152                                    }), classNames__default["default"].flexCol, classNames__default["default"].rel, // for Ruler.fillStart
5153                                    allowVerticalScrolling && classNames__default["default"].liquid), style: {
5154                                        zIndex: 0,
5155                                    }, ref: this.spreadsheetBodyScrollerRef, clientWidthRef: this.handleSpreadsheetClientWidth, children: [jsxRuntime.jsx(BodySection, { cellIdPrefix: cellIdPrefix, groupRowPositions: groupRowPositions, resourceRowPositions: resourceRowPositions, groupColPositions: groupColPositions, displayedRowCnt: displayedRowCnt, groupCellCnts: flatGroupColLayouts.map((flatGroupCellLayouts) => flatGroupCellLayouts.length), colWidths: spreadsheetColWidths, c
5155olSpecs: colSpecs, rowInnerHeightRefMap: this.dataGridEntityInnerHeightMap, headerRowSpan: totalHeaderRowSpan, indentWidth: props.indentWidth, canvasWidth: spreadsheetCanvasWidth, canvasHeight: totalBodyHeight }), spreadsheetNeedsBottomFiller && (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.fillerClass, { inTableHeader: false }), classNames__default["default"].borderOnlyT), style: { minHeight: spreadsheetBottomFiller } }))] }), jsxRuntime.jsx(protectedApi.FooterScrollbar, { isSticky: footerScrollbarSticky, canvasWidth: spreadsheetCanvasWidth, scrollerRef: this.spreadsheetFooterScrollerRef, scrollbarWidthRef: this.handleSpreadsheetBottomScrollbarWidth })] }), 
5156                        /* time-area (TODO: try to make this DRY-er with TimelineView???)
5157                        --------------------------------------------------------------------------------- */
5158                        endClassName: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].isolate), endContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableHeaderClass, {
5159                                        isSticky: tableHeaderSticky,
5160                                        borderlessX,
5161                                        borderlessTop,
5162                                        borderlessBottom,
5163                                        multiMonthColumns: 0,
5164                                    }), classNames__default["default"].flexCol, tableHeaderSticky && classNames__default["default"].tableHeaderSticky), style: {
5165                                        zIndex: 1,
5166                                    }, children: [jsxRuntime.jsxs(protectedApi.Scroller, { ref: this.timeHeaderScrollerRef, horizontal: true, hideScrollbars: true, className: publicApi.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].contentBox), style: {
5167                                                height: headerHeight,
5168                                            }, children: [jsxRuntime.jsx("div", { role: 'presentation', children: jsxRuntime.jsx("div", { ...buildAriaCellAttrs({
5169                                                            cellIdPrefix,
5170                                                            cellRowIndex: 1,
5171                                                            cellColIndex: colSpecs.length,
5172                                                        }), role: 'columnheader', "aria-rowspan": totalHeaderRowSpan, "aria-label": options.eventsHint }) }), jsxRuntime.jsxs("div", { "aria-hidden": true, className: publicApi.joinClassNames(// TODO: DRY
5173                                                    classNames__default["default"].rel, // origin for now-indicator
5174                                                    classNames__default["default"].flexCol, timeCanvasWidth == null && classNames__default["default"].liquid, 
5175                                                    // slotLiquid:true implies that slots are expanding bigger than their min,
5176                                                    // and that there are NOT any horizontal scrollbars. if so, then no worries
5177                                                    // about sticky-event-titles. if so, we're able to apply cropping to prevent
5178                                                    // event hover-resizers and whatnot from bleeding out of the canvas and causing
5179                                                    // horizontal scrollbars when there normally wouldn't be any
5180                                                    props.slotLiquid && classNames__default["default"].crop), style: { minWidth: timeCanvasWidth }, children: [cellRows.map((cells, rowIndex) => {
5181                                                            const rowLevel = cellRows.length - rowIndex - 1;
5182                                                            const headerCellPositions = timeHeaderVirtualizers[rowIndex].computePositions(cells, virtualizationDisabled, forcedTimeScroll);
5183                                                            const shift = computeShift(headerCellPositions);
5184                                                            if (shift) {
5185                                                                timeHeaderVirtualizers[rowIndex].computePositions(cells, virtualizationDisabled, forcedTimeScroll);
5186                                                            }
5187                                                            return (jsxRuntime.jsx(TimelineHeaderRow, { className: classNames__default["default"].rel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, rowLevel: rowLevel, cells: cells, slotWidth: 
5187slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowIndex), innerHeighRef: this.timelineHeaderRowInnerHeightMap.createRef(rowIndex), insetInlineStart: shift && shift[0], width: shift ? (shift[1] - shift[0]) : undefined, cellStartIndex: shift && shift[2], cellCount: headerCellPositions.length }, rowIndex));
5188                                                        }), enableNowIndicator && (
5189                                                        // TODO: make this positioned WITHIN padding
5190                                                        jsxRuntime.jsx(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: props.nowDate, slotWidth: slotWidth }))] }), Boolean(endScrollbarWidth) && (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.fillerClass, { inTableHeader: true }), classNames__default["default"].borderOnlyS), style: { minWidth: endScrollbarWidth } }))] }), jsxRuntime.jsx("div", { className: protectedApi.generateClassName(options.slotHeaderDividerClass, {
5191                                                inTableHeader: false,
5192                                                options: { dayMinWidth: options.dayMinWidth },
5193                                            }) })] }), jsxRuntime.jsx(protectedApi.Scroller, { vertical: allowVerticalScrolling &&
5194                                        // infer whether scrolling would happen. prevents clientHeight thrashing due to horizontal scrollbars
5195                                        (totalBodyHeight != null && timeClientHeight != null && totalBodyHeight > timeClientHeight), horizontal: true, hideScrollbars: footerScrollbarSticky /* FYI, this view is never print */, className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableBodyClass, {
5196                                        borderlessX,
5197                                        borderlessTop,
5198                                        borderlessBottom,
5199                                        multiMonthColumns: 0,
5200                                    }), classNames__default["default"].flexCol, classNames__default["default"].rel, // for Ruler.fillStart
5201                                    allowVerticalScrolling && classNames__default["default"].liquid), style: {
5202                                        zIndex: 0,
5203                                    }, ref: this.timeBodyScrollerRef, clientWidthRef: this.handleTimeClientWidth, clientHeightRef: allowVerticalScrolling
5204                                        ? this.handleTimeClientHeight
5205                                        : undefined // for when height:auto, which will have dynamic height and needlessly fire ResizeObserver
5206                                    , bottomScrollbarWidthRef: allowVerticalScrolling
5207                                        ? this.handleTimeBottomScrollbarWidth
5208                                        : undefined // for when height:auto, which will have dynamic height and needlessly fire ResizeObserver
5209                                    , children: jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(classNames__default["default"].rel, // origin for canvas?
5210                                        classNames__default["default"].grow, classNames__default["default"].flexCol, 
5211                                        // slotLiquid:true implies that slots are expanding bigger than their min,
5212                                        // and that there are NOT any horizontal scrollbars. if so, then no worries
5213                                        // about sticky-event-titles. if so, we're able to apply cropping to prevent
5214                                        // event hover-resizers and whatnot from bleeding out of the canvas and causing
5215                                        // horizontal scrollbars when there normally wouldn't be any
5216                                        props.slotLiquid && classNames__default["default"].crop), style: {
5217                                            minWidth: timeCanvasWidth,
5218                                            minHeight: totalBodyHeight,
5219                                        }, ref: this.handleBodyEl, children: [jsxRuntime.jsxs("div", { 
5220                                                // roving-origin for anything that fills y-axis,
5221                                                // like slots or non-resource bg events
5222                                                // is ZERO-width, but has defined top/bottom
5223                                                className: classNames__default["default"].abs, style: {
5224                                                    top: yFillTop,
5225                                                    height: yFillHeight,
5226                                                    bottom: yFillHeight == null ? 0 : undefined,
5227                                                    insetInlineStart: slotDateShift?.[0],
5228                                                }, children: [jsxRuntime.jsx(TimelineSlats, { dateProfile: dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, slatStartIndex: slotDateShift?.[2], slatCount: slotDatePositions.length, slotWidth: 
5228slotWidth }), jsxRuntime.jsx(TimelineBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, 
5229                                                        // content
5230                                                        bgEventSegs: bgSlicedProps.bgEventSegs, businessHourSegs: hasResourceBusinessHours ? null : bgSlicedProps.businessHourSegs, dateSelectionSegs: bgSlicedProps.dateSelectionSegs, eventResizeSegs: (bgSlicedProps.eventResize ? bgSlicedProps.eventResize.segs : null), 
5231                                                        // dimensions
5232                                                        slotWidth: slotWidth, 
5233                                                        // virtualization
5234                                                        clipStart: slotDateShift?.[0], clipEnd: slotDateShift?.[1] }), enableNowIndicator && (jsxRuntime.jsx(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: props.nowDate, slotWidth: slotWidth, clipStart: slotDateShift?.[0] }))] }), jsxRuntime.jsxs("div", { 
5235                                                // roving-origin for all types of resource(-group)-rows
5236                                                // is ZERO-height, always fixed to top, but has definet x-coordinate and width
5237                                                role: 'presentation', className: classNames__default["default"].abs, style: {
5238                                                    top: 0,
5239                                                    insetInlineStart: slotDateShift?.[0],
5240                                                    width: slotDateShift ? (slotDateShift[1] - slotDateShift[0]) : undefined,
5241                                                }, children: [groupRowPositions.map((rowPosition) => {
5242                                                        const rowLayout = rowPosition.item;
5243                                                        const group = rowLayout.entity;
5244                                                        const groupKey = rowPosition.key;
5245                                                        return (jsxRuntime.jsx("div", { role: 'presentation', className: publicApi.joinClassNames(classNames__default["default"].fillX, classNames__default["default"].flexRow), style: {
5246                                                                top: rowPosition.start,
5247                                                            }, children: jsxRuntime.jsx(GroupLane, { cellIdPrefix: cellIdPrefix, cellRowIndex: 1 + totalHeaderRowSpan + rowLayout.rowIndex, cellColIndex: colSpecs.length, group: group, borderBottom: rowLayout.visibleIndex < displayedRowCnt - 1, innerHeightRef: this.timeEntityInnerHeightMap.createRef(groupKey), height: rowPosition.size }) }, groupKey));
5248                                                    }), resourceRowPositions.map((rowPosition) => {
5249                                                        const resourceLayout = rowPosition.item;
5250                                                        const resource = resourceLayout.entity;
5251                                                        return (compat.createElement(ResourceLane, { ...splitProps[resource.id], key: resource.id /* TODO: use rowPosition.key? */, role: 'presentation', cellIdPrefix: cellIdPrefix, cellRowIndex: 1 + totalHeaderRowSpan + resourceLayout.rowIndex, cellColIndex: colSpecs.length, className: classNames__default["default"].fillX, resource: resource, dateProfile: dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, businessHours: resource.businessHours || fallbackBusinessHours, borderBottom: resourceLayout.visibleIndex < displayedRowCnt - 1, 
5252                                                            // ref
5253                                                            heightRef: this.timeEntityInnerHeightMap.createRef(resource.id), 
5254                                                            // dimensions
5255                                                            slotWidth: slotWidth, 
5256                                                            // position
5257                                                            top: rowPosition.start, height: rowPosition.size, 
5258                                                            // virtualization
5259                                                            clipStart: slotDateShift?.[0], clipEnd: slotDateShift?.[1] }));
5260                                                    })] }), timelineNeedsBottomFiller && (jsxRuntime.jsx("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.fillerClass, { inTableHeader: false }), classNames__default["default"].borderOnlyT, classNames__default["default"].fillX), style: {
5261                                                    top: yFillBottom,
5262                                                    minHeight: timelineBottomFiller
5263                                                } }))] }) }), Boolean(footerScrollbarSticky) && (jsxRuntime.jsx(protectedApi.FooterScrollbar, { isSticky: true, canvasWidth: timeCanvasWidth, scrollbarWidthRef: this.handleTimeBottomScrollbarWidth, scrollerRef: this.timeFooterScrollerRef })), jsxRuntime.jsx(protectedApi.Ruler, { widthRef: this.handleTimeTotalWidth })] }) })] }));
5264        }
5265        // Lifecycle
5266        // -----------------------------------------------------------------------------------------------
5267        componentDidMount() {
5268            const { props, context } = this;
5269            this._isUnmounting = false;
5270            this.timeScroller = new ScrollerSyncer(true); // horizontal=true
5271            this.bodyScroller = new ScrollerSyncer(); // horizontal=false
5272            this.spreadsheetScroller = new ScrollerSyncer(true); // horizontal=true
5273            this.updateScrollersSyncers();
5274            this.updateWindowScrolling();
5275            if (props.initialScroll) {
5276                this.scroll = { ...props.initialScroll }; // copy
5277                this.applyEntityScroll();
5278                this.applyTimeScroll();
5279            }
5280            else {
5281                this.resetTimeScroll();
5282            }
5283            this.timeScroller.addScrollListener(this.handleTimeScroll);
5284            this.timeScroller.addScrollEndListener(this.handleTimeScrollEnd);
5285            this.bodyScroller.addScrollStartListener(this.handleEntityScrollStart);
5286            this.bodyScroller.addScrollListener(this.handleEntityScroll);
5287            this.bodyScroller.addScrollEndListener(this.handleEntityScrollEnd);
5288            context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
5289            context.emitter.on('_resourceScrollRequest', this.handleResourceScrollRequest);
5290        }
5291        componentDidUpdate(prevProps, prevState) {
5292            const { props, state } = this;
5293            const { options } = this.context;
5294            this.updateScrollersSyncers();
5295            this.updateWindowScrolling();
5296            const dateProfileChange = prevProps.dateProfile !== props.dateProfile;
5297            const slotWidthChange = prevProps.slotWidth !== props.slotWidth;
5298            const timeCanvasWidthChange = prevProps.timeCanvasWidth !== props.timeCanvasWidth;
5299            const timeClientWidthChange = prevState.timeClientWidth !== state.timeClientWidth;
5300            if (dateProfileChange || slotWidthChange || timeCanvasWidthChange || timeClientWidthChange) {
5301                if (dateProfileChange && options.scrollTimeReset) {
5302                    this.resetTimeScroll();
5303                }
5304                else {
5305                    this.applyTimeScroll();
5306                }
5307            }
5308            /*
5309            Unfortunately this will execute after auto-scroll finished but before scrollEnd can record
5310            the updated scroll positioning, causing a scroll-jump to the last recorded entityScroll.
5311            */
5312            if (this.queuedHeightChange) {
5313                this.queuedHeightChange = false;
5314                this.applyEntityScroll();
5315            }
5316        }
5317        componentWillUnmount() {
5318            this._isUnmounting = true;
5319            this.slotInnerWidth = undefined;
5320            this.timeScroller.destroy();
5321            this.bodyScroller.destroy();
5322            this.spreadsheetScroller.destroy();
5323            this.destroyWindowScrolling();
5324            this.bodyScroller.removeScrollEndListener(this.handleEntityScrollEnd);
5325            this.bodyScroller.removeScrollListener(this.handleEntityScroll);
5326            this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
5327            this.context.emitter.off('_resourceScrollRequest', this.handleResourceScrollRequest);
5328        }
5329        // Virtualizer Utils
5330        // -----------------------------------------------------------------------------------------------
5331        setVirtualizerViewportSize(height) {
5332            this.rowVirtualizer.handleViewportSize(height);
5333            for (const groupColVirtualizer of this.groupColVirtualizers) {
5334                groupColVirtualizer.handleViewportSize(height);
5335            }
5336        }
5337        setVirtualizerScroll(scroll) {
5338            this.currentEntityScroll = scroll;
5339            this.updateVirtualizerScroll();
5340        }
5341        updateWindowScrolling() {
5342            if (protectedApi.getIsHeightAuto(this.context.options)) {
5343                if (!this.isTrackingWindowScroll) {
5344                    this.isTrackingWindowScroll = true;
5345                    this.setVirtualizerViewportSize(window.innerHeight);
5346                    ([this.handleWindowResize, this.cancelWindowResize] = protectedApi.debounce(() => {
5347                        this.setVirtualizerViewportSize(window.innerHeight);
5348                    }, 200));
5349                    window.addEventListener('resize', this.handleWindowResize);
5350                    window.addEventListener('scroll', this.handleWindowScroll, { passive: true });
5351                }
5352            }
5353            else {
5354                this.destroyWindowScrolling();
5355            }
5356        }
5357        destroyWindowScrolling() {
5358            if (this.isTrackingWindowScroll) {
5359                this.isTrackingWindowScroll = false;
5360                this.cancelWindowResize();
5361                window.removeEventListener('resize', this.handleWindowResize);
5362                window.removeEventListener('scroll', this.handleWindowScroll, { passive: true });
5363            }
5364        }
5365        // Scrolling
5366        // -----------------------------------------------------------------------------------------------
5367        updateScrollersSyncers() {
5368            this.timeScroller.handleChildren([
5369                this.timeHeaderScrollerRef.current,
5370                this.timeBodyScrollerRef.current,
5371                this.timeFooterScrollerRef.current,
5372            ]);
5373            this.bodyScroller.handleChildren([
5374                this.spreadsheetBodyScrollerRef.current,
5375                this.timeBodyScrollerRef.current,
5376            ]);
5377            this.spreadsheetScroller.handleChildren([
5378                this.spreadsheetHeaderScrollerRef.current,
5379                this.spreadsheetBodyScrollerRef.current,
5380                this.spreadsheetFooterScrollerRef.current,
5381            ]);
5382        }
5383        // TIME Scrolling
5384        // -----------------------------------------------------------------------------------------------
5385        resetTimeScroll() {
5386            this.handleTimeScrollRequest(this.context.options.scrollTime);
5387        }
5388        applyTimeScroll() {
5389            const x = this.computeTimeScroll();
5390            if (x !== undefined) {
5391                this.timeScroller.scrollTo({ x });
5392                if (this.scroll.x !== x) {
5393                    this.scroll.x = x;
5394                    protectedApi.setRef(this.props.scrollRef, this.scroll);
5395                }
5396            }
5397        }
5398        // used by render() for virtualization too
5399        computeTimeScroll() {
5400            const { props, context, scroll } = this;
5401            const { tDateProfile } = props;
5402            const slotWidth = props.slotWidth ?? defaultSlotWidth;
5403            let { x, time } = scroll;
5404            if (time) {
5405                x = timeToCoord(time, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
5406                if (x) {
5407                    x += 1; // overcome border. TODO: DRY this up
5408                }
5409            }
5410            return x;
5411        }
5412        applyEntityScroll() {
5413            const scrollTop = this.computeEntityScroll();
5414            if (scrollTop !== undefined) {
5415                this.bodyScroller.scrollTo({ y: scrollTop });
5416            }
5417        }
5418        computeEntityScroll() {
5419            const { bodyTops, bodyHeights, scroll } = this;
5420            const { entityId, fromBottom } = scroll;
5421            if (entityId) {
5422                const top = bodyTops.get(entityId);
5423                const height = bodyHeights.get(entityId);
5424                if (top != null) {
5425                    const bottom = top + height;
5426                    const requestedScrollTop = fromBottom != null ?
5427                        bottom - fromBottom : // pixels from bottom edge
5428                        top + // just use top edge
5429                            (top ? 1 : 0); // overcome top border
5430                    return Math.min(this.maxVScroll, requestedScrollTop);
5431                }
5432            }
5433        }
5434        prepareHits() {
5435            /*
5436            HACK for queuedHeightChange usage in componentDidUpdate
5437            This executes when a drag/resize starts and clears the last recorded entity scroll
5438            */
5439            this.scroll.entityId = undefined;
5440            this.scroll.fromBottom = undefined;
5441        }
5442        queryHit(isRtl, positionLeft, positionTop) {
5443            let { props, context, bodyLayouts, bodyTops, bodyHeights } = this;
5444            const { dateProfile, tDateProfile, timeCanvasWidth } = props;
5445            const slotWidth = props.slotWidth ?? defaultSlotWidth;
5446            const { dateEnv } = context;
5447            let coordRes = findEntityByCoord(bodyLayouts, bodyTops, bodyHeights, positionTop, createEntityId);
5448            if (coordRes) {
5449                let [entityAtTop, top, height] = coordRes;
5450                if (!isEntityGroup(entityAtTop)) {
5451                    let resource = entityAtTop;
5452                    let bottom = top + height;
5453                    /*
5454                    TODO: DRY-up ith TimelineView!!!
5455                    */
5456                    const x = isRtl ? timeCanvasWidth - positionLeft : positionLeft;
5457                    const slatIndex = Math.floor(x / slotWidth);
5458                    const slatLeft = slatIndex * slotWidth;
5459                    const partial = (x - slatLeft) / slotWidth; // floating point number between 0 and 1
5460                    const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
5461                    let start = dateEnv.add(tDateProfile.slotDates[slatIndex], protectedApi.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
5462                    let end = dateEnv.add(start, tDateProfile.snapDuration);
5463                    // TODO: generalize this coord stuff to TimeGrid?
5464                    let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
5465                    let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
5466                    let endCoord = startCoord + snapWidth;
5467                    let left, right;
5468                    if (isRtl) {
5469                        left = timeCanvasWidth - endCoord;
5470                        right = timeCanvasWidth - startCoord;
5471                    }
5472                    else {
5473                        left = startCoord;
5474                        right = endCoord;
5475                    }
5476                    return {
5477                        dateProfile,
5478                        dateSpan: {
5479                            range: { start, end },
5480                            allDay: !tDateProfile.isTimeScale,
5481                            resourceId: resource.id,
5482                        },
5483                        rect: {
5484                            left,
5485                            right,
5486                            top,
5487                            bottom,
5488                        },
5489                        getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
5490                        layer: 0,
5491                    };
5492                }
5493            }
5494            return null;
5495        }
5496    }
5497    function computeHeaderHeight(dataGridHeaderRowInnerHeightMap, timelineHeaderRowInnerHeightMap, hasDateGridSuperHeader, timelineHeaderRowCnt) {
5498        const dataGridKeys = (hasDateGridSuperHeader ? [true] : []).concat(false);
5499        let dataGridH = 0;
5500        // TODO: use summing util?
5501        for (const key of dataGridKeys) {
5502            const rowHeight = dataGridHeaderRowInnerHeightMap.get(key);
5503            if (rowHeight == null) {
5504                return;
5505            }
5506            dataGridH += rowHeight;
5507        }
5508        let timelineH = 0;
5509        // TODO: use summing util?
5510        for (let row = 0; row < timelineHeaderRowCnt; row++) {
5511            const rowHeight = timelineHeaderRowInnerHeightMap.get(row);
5512            if (rowHeight == null) {
5513                return;
5514            }
5515            timelineH += rowHeight;
5516        }
5517        // add in-between borders too
5518        return Math.max(dataGridH + (hasDateGridSuperHeader ? ROW_BORDER_WIDTH : 0), timelineH + ROW_BORDER_WIDTH * (timelineHeaderRowCnt - 1));
5519    }
5520
5521    /*
5522    TODO: test multiple levels of group-col nesting
5523    */
5524    function buildPrintLayouts(hierarchy, hasNesting, expansions, expansionDefault) {
5525        const layouts = [];
5526        function processNodes(nodes, indent, colGroups) {
5527            let totalRowCnt = 0;
5528            for (const node of nodes) {
5529                if (node.resourceFields) { // resource
5530                    const isExpanded = expansions[node.entity.id] ?? expansionDefault;
5531                    const hasChildren = Boolean(node.children.length);
5532                    layouts.push({
5533                        entity: node.entity,
5534                        resourceFields: node.resourceFields,
5535                        colGroups,
5536                        isExpanded,
5537                        hasChildren,
5538                        indent,
5539                    });
5540                    totalRowCnt += 1;
5541                    totalRowCnt += processNodes(node.children, indent + 1, colGroups);
5542                }
5543                else if (node.pooledHeight) { // column-group
5544                    totalRowCnt += processNodes(node.children, indent, colGroups.concat(node.entity));
5545                }
5546                else { // row-group (FYI, can't be within a column-group)
5547                    const isExpanded = expansions[createGroupId(node.entity)] ?? expansionDefault;
5548                    const hasChildren = Boolean(node.children.length);
5549                    layouts.push({
5550                        entity: node.entity,
5551                        isExpanded,
5552                        hasChildren,
5553                        indent,
5554                    });
5555                    totalRowCnt += 1;
5556                    totalRowCnt += processNodes(node.children, indent + 1, colGroups);
5557                }
5558            }
5559            return totalRowCnt;
5560        }
5561        processNodes(hierarchy, hasNesting ? 1 : 0, []);
5562        return layouts;
5563    }
5564
5565    /*
5566    Just for print
5567    */
5568    class ResourceGroupSubrows extends protectedApi.BaseComponent {
5569        render() {
5570            const { props, context } = this;
5571            const { options } = context;
5572            const { colGroups, colGroupStats } = props;
5573            const colWidths = props.colWidths || [];
5574            const colGrows = props.colGrows || [];
5575            return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: colGroups.map((colGroup, i) => {
5576                    const stats = colGroupStats[i];
5577                    return (stats.render ? (jsxRuntime.jsx(ResourceGroupSubrow, { colSpec: colGroup.spec, fieldValue: colGroup.value, width: colWidths[i], grow: colGrows[i], borderStart: Boolean(i), borderBottom: stats.borderBottom })) : (jsxRuntime.jsx("div", { 
5578                        // TODO: make className DRY somehow?
5579                        className: publicApi.joinClassNames(options.resourceRowClass, stats.borderBottom ? classNames__default["default"].borderOnlyB : classNames__default["default"].borderNone), style: {
5580                            minWidth: 0,
5581                            width: colWidths[i],
5582                            flexGrow: colGrows[i],
5583                        } })));
5584                }) }));
5585        }
5586    }
5587
5588    const BG_HEIGHT = 100000;
5589    /*
5590    ARIA considerations:
5591    try to put correct role="" on things, just for readability, but it's pointless to pursue more
5592    complex things like indexes, level, complex-header relationships (via aria-labelledby)
5593    */
5594    class ResourceTimelineLayoutPrint extends protectedApi.BaseComponent {
5595        constructor() {
5596            super(...arguments);
5597            // memoized
5598            this.buildPrintLayouts = protectedApi.memoize(buildPrintLayouts);
5599            this.compileColWidths = protectedApi.memoize(compileColWidths);
5600        }
5601        render() {
5602            let { props, context } = this;
5603            let { dateProfile } = props;
5604            let { options, viewSpec } = context;
5605            const { borderlessX, borderlessTop, borderlessBottom } = protectedApi.computeViewBorderless(options);
5606            const { tDateProfile, todayRange, nowDate, hasNesting } = props;
5607            const { slotWidth, timeCanvasWidth } = props;
5608            const { splitProps, bgSlicedProps, timeAreaOffset } = props;
5609            const { superHeaderRendering, groupColCnt, colSpecs } = props;
5610            const [colWidths, colGrows, spreadsheetCanvasWidth] = this.compileColWidths(props.spreadsheetColWidthConfigs, props.spreadsheetColWidths);
5611            const { cellRows } = tDateProfile;
5612            const { resourceHierarchy } = props;
5613            let printLayouts = this.buildPrintLayouts(resourceHierarchy, hasNesting, props.resourceEntityExpansions, options.resourcesInitiallyExpanded);
5614            if (printLayouts.length > options.printMaxRows) {
5615                printLayouts = printLayouts.slice(0, options.printMaxRows);
5616                protectedApi.warn(`Rows truncated for print. Only included the first ${options.printMaxRows} rows.`);
5617            }
5618            return (jsxRuntime.jsxs(protectedApi.ViewContainer, { viewSpec: viewSpec, attrs: {
5619                    role: hasNesting ? 'treegrid' : 'grid', // TODO: DRY
5620                    'aria-label': props.labelStr,
5621                    'aria-labelledby': props.labelId,
5622                }, className: publicApi.joinClassNames(props.className, protectedApi.generateClassName(options.tableClass, {
5623                    borderlessX,
5624                    borderlessTop,
5625                    borderlessBottom,
5626                    multiMonthColumns: 0,
5627                }), classNames__default["default"].printRoot), children: [jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableHeaderClass, {
5628                            isSticky: false,
5629                            borderlessX,
5630                            borderlessTop,
5631                            borderlessBottom,
5632                            multiMonthColumns: 0,
5633                        }), classNames__default["default"].printHeader), children: [jsxRuntime.jsxs("div", { className: classNames__default["default"].flexRow, children: [jsxRuntime.jsxs("div", { role: 'rowgroup', className: classNames__default["default"].flexCol, style: { width: props.spreadsheetWidth }, children: [Boolean(superHeaderRendering) && (jsxRuntime.jsx("div", { role: 'row', className: publicApi.joinClassNames(options.resourceHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, classNames__default["default"].borderOnlyB), children: jsxRuntime.jsx(SuperHeaderCell, { renderHooks: superHeaderRendering, indent: hasNesting && !groupColCnt /* group-cols are leftmost, making expander alignment irrelevant */, colSpan: colSpecs.length, indentWidth: props.indentWidth }) })), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].grow, classNames__default["default"].crop), children: jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].grow, // height-grow, for matching height with timeline axis
5634                                                    classNames__default["default"].flexCol), style: { minWidth: spreadsheetCanvasWidth }, children: jsxRuntime.jsx(HeaderRow, { colSpecs: colSpecs, colWidths: colWidths, colGrows: colGrows, indent: hasNesting, indentWidth: props.indentWidth }) }) })] }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.resourceColumnDividerClass) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].liquid, classNames__default["default"].flexRow, classNames__default["default"].crop), children: jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].rel), style: {
5635                                                width: timeCanvasWidth,
5636                                                insetInlineStart: -timeAreaOffset,
5637                                            }, children: cellRows.map((cells, rowIndex) => {
5638                                                const rowLevel = cellRows.length - rowIndex - 1;
5639                                                return (jsxRuntime.jsx(TimelineHeaderRow, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, cells: cells, slotWidth: 
5639slotWidth }, rowLevel));
5640                                            }) }) })] }), jsxRuntime.jsx("div", { className: protectedApi.generateClassName(options.slotHeaderDividerClass, {
5641                                    inTableHeader: true,
5642                                    options: { dayMinWidth: options.dayMinWidth },
5643                                }) })] }), jsxRuntime.jsxs("div", { role: 'rowgroup', className: publicApi.joinClassNames(protectedApi.generateClassName(options.tableBodyClass, {
5644                            borderlessX,
5645                            borderlessTop,
5646                            borderlessBottom,
5647                            multiMonthColumns: 0,
5648                        }), 
5649                        // leave display:block for print!
5650                        classNames__default["default"].rel, classNames__default["default"].crop), children: [jsxRuntime.jsxs("div", { "aria-hidden": true, className: publicApi.joinClassNames(classNames__default["default"].fillY, classNames__default["default"].flexRow), style: {
5651                                    // HACK for print where header-height prevents absolutely-positioned events
5652                                    // from falling short in height for subsequent pages
5653                                    height: BG_HEIGHT,
5654                                    // TODO: add the divider width too!!!
5655                                    insetInlineStart: props.spreadsheetWidth,
5656                                    insetInlineEnd: 0,
5657                                }, children: [jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.resourceColumnDividerClass, classNames__default["default"].invisible) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].liquidX, classNames__default["default"].crop, classNames__default["default"].rel), children: jsxRuntime.jsxs("div", { className: classNames__default["default"].fillY, style: {
5658                                                width: timeCanvasWidth,
5659                                                height: BG_HEIGHT, // HACK
5660                                                insetInlineStart: -timeAreaOffset,
5661                                            }, children: [jsxRuntime.jsx(TimelineSlats, { dateProfile: dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, height: BG_HEIGHT, 
5662                                                    // dimensions
5663                                                    slotWidth: slotWidth }), jsxRuntime.jsx(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, 
5664                                                    // content
5665                                                    bgEventSegs: bgSlicedProps.bgEventSegs, businessHourSegs: null, dateSelectionSegs: null, eventResizeSegs: null, 
5666                                                    // dimensions
5667                                                    slotWidth: slotWidth })] }) })] }), printLayouts.map((printLayout, rowIndex0) => {
5668                                const isNotLast = rowIndex0 < printLayouts.length - 1;
5669                                if (printLayout.colGroups) {
5670                                    const resource = printLayout.entity;
5671                                    const colGroupStats = createColGroupStats(printLayout, printLayouts[rowIndex0 - 1], printLayouts[rowIndex0 + 1]);
5672                                    return (jsxRuntime.jsxs("div", { role: 'row', "aria-level": hasNesting ? printLayout.indent : undefined, "aria-expanded": printLayout.hasChildren
5673                                            ? printLayout.isExpanded
5674                                            : undefined, className: publicApi.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].breakInsideAvoid), children: [jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, // mimics what non-print scrollpane does
5675                                                classNames__default["default"].crop), style: { width: props.spreadsheetWidth }, children: jsxRuntime.jsxs("div", { className: publicApi.joinClassNames(classNames__default["default"].grow, // he
5675ight-grow, for matching tall timeline heights
5676                                                    classNames__default["default"].flexRow), style: { minWidth: spreadsheetCanvasWidth }, children: [jsxRuntime.jsx(ResourceGroupSubrows // usually does NOT have bottom-line
5677                                                        , { colGroups: printLayout.colGroups, colGroupStats: colGroupStats, colWidths: colWidths, colGrows: colGrows }), jsxRuntime.jsx(ResourceSubrow // almost always has bottom-line
5678                                                        , { resource: resource, resourceFields: printLayout.resourceFields, indent: printLayout.indent, hasChildren: printLayout.hasChildren, isExpanded: printLayout.isExpanded, colStartIndex: groupColCnt, colSpecs: colSpecs, colWidths: colWidths, colGrows: colGrows, borderStart: Boolean(groupColCnt), borderBottom: isNotLast, indentWidth: props.indentWidth, totalX // set width and flexgrow on this subrow
5679                                                            : true })] }) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.resourceColumnDividerClass) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].crop, classNames__default["default"].liquid), children: jsxRuntime.jsx(ResourceLane, { ...splitProps[resource.id], className: publicApi.joinClassNames(classNames__default["default"].rel, classNames__default["default"].grow), resource: resource, dateProfile: dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, businessHours: null, dateSelection: null, eventDrag: null, eventResize: null, width: timeCanvasWidth, slotWidth: slotWidth, insetInlineStart: -timeAreaOffset, borderBottom: isNotLast }) })] }, resource.id));
5680                                }
5681                                else {
5682                                    const group = printLayout.entity;
5683                                    const groupKey = createGroupId(group);
5684                                    return (jsxRuntime.jsxs("div", { role: 'row', "aria-expanded": printLayout.isExpanded, className: publicApi.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].breakInsideAvoid), children: [jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].crop, classNames__default["default"].flexRow), style: { width: props.spreadsheetWidth }, children: jsxRuntime.jsx(ResourceGroupHeaderSubrow, { group: group, isExpanded: printLayout.isExpanded, colSpan: props.colSpecs.length, borderBottom: isNotLast, indentWidth: props.indentWidth }) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(options.resourceColumnDividerClass) }), jsxRuntime.jsx("div", { className: publicApi.joinClassNames(classNames__default["default"].crop, classNames__default["default"].flexRow, classNames__default["default"].liquid), children: jsxRuntime.jsx(GroupLane, { group: group, borderBottom: isNotLast }) })] }, groupKey));
5685                                }
5686                            })] })] }));
5687        }
5688    }
5689    function createColGroupStats(currentLayout, prevLayout, nextLayout) {
5690        return currentLayout.colGroups.map((colGroup, colGroupI) => ({
5691            render: !prevLayout ||
5692                !prevLayout.colGroups ||
5693                prevLayout.colGroups[colGroupI] !== colGroup,
5694            borderBottom: nextLayout && (!nextLayout.colGroups ||
5695                nextLayout.colGroups[colGroupI] !== colGroup),
5696        }));
5697    }
5698    function compileColWidths(colWidthConfigs, colWidths) {
5699        if (colWidths != null) {
5700            return flexifyDimConfigs(colWidthConfigs, colWidths);
5701        }
5702        return [undefined, undefined, undefined];
5703    }
5704
5705    const SPREADSHEET_COL_MIN_WIDTH = 50;
5706    function processColOptions(options) {
5707        let allColSpecs = options.resourceColumns || [];
5708        let superHeaderRendering = null;
5709        if (!allColSpecs.length) {
5710            allColSpecs.push({
5711                headerDefault: () => 'Resources', // TODO: view.defaults
5712            });
5713        }
5714        else if (options.resourceColumnHeaderContent) { // weird way to determine if content
5715            superHeaderRendering = {
5716                headerClass: options.resourceColumnHeaderClass,
5717                headerInnerClass: options.resourceColumnHeaderInnerClass,
5718                headerContent: options.resourceColumnHeaderContent,
5719                headerDidMount: options.resourceColumnHeaderDidMount,
5720                headerWillUnmount: options.resourceColumnHeaderWillUnmount,
5721            };
5722        }
5723        let resourceColSpecs = [];
5724        let groupColSpecs = []; // part of the colSpecs, but filtered out in order to put first
5725        let groupSpecs = [];
5726        let groupRowDepth = 0;
5727        let foundMainColSpec = false;
5728        for (let colSpec of allColSpecs) {
5729            (colSpec.group ? groupColSpecs : resourceColSpecs).push({
5730                ...colSpec,
5731                isMain: ((!colSpec.group && !foundMainColSpec)
5732                    ? (foundMainColSpec = true)
5733                    : false),
5734                headerClass: protectedApi.joinFuncishClassNames(options.resourceColumnHeaderClass, colSpec.headerClass, 'resourceColumnHeaderClass'),
5735                headerInnerClass: protectedApi.joinFuncishClassNames(options.resourceColumnHeaderInnerClass, colSpec.headerInnerClass, 'resourceColumnHeaderInnerClass'),
5736                headerResizerClass: protectedApi.joinFuncishClassNames(options.resourceColumnResizerClass, colSpec.headerResizerClass, 'resourceColumnResizerClass'),
5737                headerContent: protectedApi.mergeContentInjectors(options.resourceColumnHeaderContent, colSpec.headerContent),
5738                headerDidMount: protectedApi.mergeLifecycleCallbacks(options.resourceColumnHeaderDidMount, colSpec.headerDidMount),
5739                headerWillUnmount: protectedApi.mergeLifecycleCallbacks(options.resourceColumnHeaderWillUnmount, colSpec.headerWillUnmount),
5740                cellClass: protectedApi.joinFuncishClassNames(options.resourceCellClass, colSpec.cellClass, 'resourceCellClass'),
5741                cellInnerClass: protectedApi.joinFuncishClassNames(options.resourceCellInnerClass, colSpec.cellInnerClass, 'resourceCellInnerClass'),
5742                cellContent: protectedApi.mergeContentInjectors(options.resourceCellContent, colSpec.cellContent),
5743                cellDidMount: protectedApi.mergeLifecycleCallbacks(options.resourceCellDidMount, colSpec.cellDidMount),
5744                cellWillUnmount: protectedApi.mergeLifecycleCallbacks(options.resourceCellWillUnmount, colSpec.cellWillUnmount),
5745            });
5746        }
5747        if (groupColSpecs.length) {
5748            groupSpecs = groupColSpecs;
5749        }
5750        else {
5751            groupRowDepth = 1;
5752            let hGroupField = options.resourceGroupField;
5753            if (hGroupField) {
5754                groupSpecs.push({
5755                    field: hGroupField,
5756                    labelClass: options.resourceGroupHeaderClass,
5757                    labelInnerClass: options.resourceGroupHeaderInnerClass,
5758                    labelContent: options.resourceGroupHeaderContent,
5759                    labelDidMount: options.resourceGroupHeaderDidMount,
5760                    labelWillUnmount: options.resourceGroupHeaderWillUnmount,
5761                    laneClass: options.resourceGroupLaneClass,
5762                    laneInnerClass: options.resourceGroupLaneInnerClass,
5763                    laneContent: options.resourceGroupLaneContent,
5764                    laneDidMount: options.resourceGroupLaneDidMount,
5765                    laneWillUnmount: options.resourceGroupLaneWillUnmount,
5766                });
5767            }
5768        }
5769        let allOrderSpecs = options.resourceOrder || DEFAULT_RESOURCE_ORDER;
5770        let plainOrderSpecs = [];
5771        for (let orderSpec of allOrderSpecs) {
5772            let isGroup = false;
5773            for (let groupSpec of groupSpecs) {
5774                if (groupSpec.field === orderSpec.field) {
5775                    groupSpec.order = orderSpec.order; // -1, 0, 1
5776                    isGroup = true;
5777                    break;
5778                }
5779            }
5780            if (!isGroup) {
5781                plainOrderSpecs.push(orderSpec);
5782            }
5783        }
5784        const colSpecs = groupColSpecs.concat(resourceColSpecs);
5785        const colWidthConfigs = colSpecs.map((colSpec) => (parseSiblingDimConfig(colSpec.width, 
5786        /* grow = */ undefined, SPREADSHEET_COL_MIN_WIDTH)));
5787        ensureDimConfigsGrow(colWidthConfigs);
5788        return {
5789            groupSpecs,
5790            groupRowDepth,
5791            orderSpecs: plainOrderSpecs,
5792            colSpecs,
5793            groupColCnt: groupColSpecs.length,
5794            colWidthConfigs,
5795            superHeaderRendering,
5796        };
5797    }
5798
5799    class ResourceTimelineView extends protectedApi.DateComponent {
5800        constructor() {
5801            super(...arguments);
5802            this.state = {};
5803            // memoized
5804            this.buildTimelineDateProfile = protectedApi.memoize(buildTimelineDateProfile);
5805            this.processColOptions = protectedApi.memoize(processColOptions);
5806            this.buildResourceHierarchy = protectedApi.memoize(buildResourceHierarchy);
5807            this.computeSlotWidth = protectedApi.memoize(computeSlotWidth);
5808            this.computeHasNesting = protectedApi.memoize(computeHasNesting);
5809            this.computeHasResourceBusinessHours = protectedApi.memoize(computeHasResourceBusinessHours);
5810            // ref
5811            this.scrollRef = compat.createRef();
5812            this.spreadsheetResizedWidthRef = compat.createRef(); // the CSS dimension. could be percent
5813            this.resourceSplitter = new ResourceSplitter();
5814            this.bgSlicer = new TimelineLaneSlicer();
5815            this.handleColResize = (colIndex, newWidth) => {
5816                if (this._isUnmounting)
5817                    return;
5818                const colWidthOverrides = resizeSiblingDimConfig(this.spreadsheetColWidthConfigs, this.spreadsheetColWidths, this.state.spreadsheetClientWidth, colIndex, newWidth);
5819                this.setState({ colWidthOverrides });
5820            };
5821            this.handleSpreadsheetClientWidth = (spreadsheetClientWidth) => {
5822                if (this._isUnmounting)
5823                    return;
5824                if (spreadsheetClientWidth != null) {
5825                    this.setState({ spreadsheetClientWidth });
5826                }
5827            };
5828            this.handleTimeClientWidth = (timeClientWidth) => {
5829                if (this._isUnmounting)
5830                    return;
5831                if (timeClientWidth != null) {
5832                    this.setState({ timeClientWidth });
5833                }
5834            };
5835            this.handleSlotInnerWidth = (slotInnerWidth) => {
5836                if (this._isUnmounting)
5837                    return;
5838                if (slotInnerWidth != null) {
5839                    this.setState({ slotInnerWidth });
5840                }
5841            };
5842            this.handleExpanderEl = (expanderEl) => {
5843                if (this.disconnectExpanderWidth) {
5844                    this.disconnectExpanderWidth();
5845                    this.disconnectExpanderWidth = undefined;
5846                }
5847                if (expanderEl) {
5848                    this.disconnectExpanderWidth = protectedApi.watchWidth(expanderEl, (width) => {
5849                        if (this._isUnmounting)
5850                            return;
5851                        this.setState({ expanderWidth: width });
5852                    });
5853                }
5854            };
5855        }
5856        render() {
5857            let { props, state, context } = this;
5858            let { dateProfile } = props;
5859            let { options } = context;
5860            /* date */
5861            let tDateProfile = this.buildTimelineDateProfile(dateProfile, context.dateEnv, options, context.dateProfileGenerator);
5862            let { unit: timerUnit, value: timeUnitValue } = protectedApi.greatestDurationDenominator(tDateProfile.slotDuration);
5863            /* table settings */
5864            let { groupSpecs, groupRowDepth, orderSpecs, colSpecs, groupColCnt, colWidthConfigs: initialColWidthConfigs, superHeaderRendering, } = this.processColOptions(context.options);
5865            /* spreadsheet col widths */
5866            let spreadsheetColWidthConfigs = state.colWidthOverrides || initialColWidthConfigs;
5867            let [spreadsheetColWidths, spreadsheetCanvasWidth] = state.spreadsheetClientWidth != null
5868                ? pixelizeDimConfigs(spreadsheetColWidthConfigs, state.spreadsheetClientWidth)
5869                : [undefined, undefined];
5870            this.spreadsheetColWidthConfigs = spreadsheetColWidthConfigs;
5871            this.spreadsheetColWidths = spreadsheetColWidths;
5872            /* table hierarchy */
5873            let resourceHierarchy = this.buildResourceHierarchy(props.resourceStore, orderSpecs, groupSpecs, groupRowDepth);
5874            let hasNesting = this.computeHasNesting(resourceHierarchy);
5875            /* table positions */
5876            let [timeCanvasWidth, slotWidth, slotLiquid] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the last-level label width. rename?
5877            state.timeClientWidth);
5878            /* event display */
5879            let splitProps = this.resourceSplitter.splitProps(props);
5880            let bgLaneProps = splitProps[''];
5881            let bgSlicedProps = this.bgSlicer.sliceProps(bgLaneProps, dateProfile, tDateProfile.isTimeScale ? null : options.nextDayThreshold, context, // wish we didn't need to pass in the rest of these args...
5882            dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
5883            /* business hour display */
5884            let hasResourceBusinessHours = this.computeHasResourceBusinessHours(resourceHierarchy);
5885            let fallbackBusinessHours = hasResourceBusinessHours ? props.businessHours : null;
5886            return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(protectedApi.NowTimer, { unit: timerUnit, unitValue: timeUnitValue, children: (nowDate, todayRange) => {
5887                            const baseProps = {
5888                                className: props.className,
5889                                labelId: props.labelId,
5890                                labelStr: props.labelStr,
5891                                tDateProfile,
5892                                dateProfile,
5893                                resourceHierarchy,
5894                                resourceEntityExpansions: props.resourceEntityExpansions,
5895                                hasNesting,
5896                                nowDate,
5897                                todayRange,
5898                                colSpecs,
5899                                groupColCnt,
5900                                superHeaderRendering,
5901                                splitProps,
5902                                bgSlicedProps,
5903                                eventSelection: props.eventSelection,
5904                                hasResourceBusinessHours,
5905                                fallbackBusinessHours,
5906                                slotWidth,
5907                                timeCanvasWidth,
5908                                spreadsheetColWidths,
5909                            };
5910                            return props.forPrint ? (jsxRuntime.jsx(ResourceTimelineLayoutPrint, { ...baseProps, spreadsheetWidth: this.spreadsheetResizedWidthRef.current ??
5911                                    options.resourceColumnsWidth, spreadsheetColWidthConfigs: spreadsheetColWidthConfigs, timeAreaOffset: this.scrollRef.current.x /* for simulating horizontal scroll */, indentWidth: state.expanderWidth })) : (jsxRuntime.jsx(ResourceTimelineLayoutNormal, { ...baseProps, slotLiquid: slotLiquid, timeClientWidthRef: this.handleTimeClientWidth, slotInnerWidthRef: this.handleSlotInnerWidth, initialSpreadsheetWidth: this.spreadsheetResizedWidthRef.current ??
5912                                    options.resourceColumnsWidth, spreadsheetCanvasWidth: spreadsheetCanvasWidth, initialScroll: this.scrollRef.current /* for reviving after print-view */, 
5913                                // refs
5914                                spreadsheetResizedWidthRef: this.spreadsheetResizedWidthRef, spreadsheetClientWidthRef: this.handleSpreadsheetClientWidth, scrollRef: this.scrollRef, 
5915                                // handlers
5916                                onColResize: this.handleColResize, indentWidth: state.expanderWidth }));
5917                        } }), jsxRuntime.jsx(ResourceExpander // for probing size
5918                    , { isExpanded: true, elRef: this.handleExpanderEl, className: classNames__default["default"].offscreen })] }));
5919        }
5920        componentDidMount() {
5921            this._isUnmounting = false;
5922        }
5923        componentWillUnmount() {
5924            this._isUnmounting = true;
5925        }
5926    }
5927    ResourceTimelineView.addStateEquality({
5928        colWidthOverrides: protectedApi.isArraysEqual,
5929    });
5930    function computeHasResourceBusinessHours(resourceHierarchy) {
5931        for (const node of resourceHierarchy) {
5932            if ((node.resourceFields && node.entity.businessHours) ||
5933                computeHasResourceBusinessHours(node.children)) {
5934                return true;
5935            }
5936        }
5937        return false;
5938    }
5939    function computeHasNesting(resourceHierarchy) {
5940        for (const node of resourceHierarchy) {
5941            if ((node.children.length && !node.pooledHeight) || // has children, but NOT a col-spanning group
5942                computeHasNesting(node.children)) {
5943                return true;
5944            }
5945        }
5946        return false;
5947    }
5948
5949    var resourceTimelinePlugin = {
5950        name: 'resource-timeline',
5951        premiumReleaseDate: '2026-07-24',
5952        deps: [
5953            premiumCommonPlugin,
5954            resourcePlugin,
5955            timelinePlugin,
5956        ],
5957        initialView: 'resourceTimelineDay',
5958        views: {
5959            resourceTimeline: {
5960                type: 'timeline', // inherit configuration
5961                component: ResourceTimelineView,
5962                needsResourceData: true,
5963                resourceColumnsWidth: '30%',
5964                resourcesInitiallyExpanded: true,
5965                eventResizableFromStart: true, // TODO: not DRY with this same setting in the main timeline config
5966            },
5967            resourceTimelineDay: {
5968                type: 'resourceTimeline',
5969                duration: { days: 1 },
5970            },
5971            resourceTimelineWeek: {
5972                type: 'resourceTimeline',
5973                duration: { weeks: 1 },
5974            },
5975            resourceTimelineMonth: {
5976                type: 'resourceTimeline',
5977                duration: { months: 1 },
5978            },
5979            resourceTimelineYear: {
5980                type: 'resourceTimeline',
5981                duration: { years: 1 },
5982            },
5983        },
5984    };
5985
5986    var scrollGridPlugin = {
5987        name: 'scrollgrid',
5988        premiumReleaseDate: '2026-07-24',
5989        deps: [premiumCommonPlugin],
5990        scrollerSyncerClass: ScrollerSyncer
5991    };
5992
5993    let contexts = [];
5994    function contextInit(context) {
5995        if (!contexts.length) {
5996            attachGlobalHandlers();
5997        }
5998        contexts.push(context);
5999        context.calendarApi.on('_unmount', () => {
6000            protectedApi.removeExact(contexts, context);
6001            if (!contexts.length) {
6002                removeGlobalHandlers();
6003            }
6004        });
6005    }
6006    function attachGlobalHandlers() {
6007        window.addEventListener('beforeprint', handleBeforePrint);
6008        window.addEventListener('afterprint', handleAfterPrint);
6009        /*
6010        // for testing
6011        let forPrint = false
6012        document.addEventListener('keypress', (ev) => {
6013          if (ev.key === 'p') {
6014            forPrint = !forPrint
6015            if (forPrint) {
6016              handleBeforePrint()
6017            } else {
6018              handleAfterPrint()
6019            }
6020          }
6021        })
6022        */
6023    }
6024    function removeGlobalHandlers() {
6025        window.removeEventListener('beforeprint', handleBeforePrint);
6026        window.removeEventListener('afterprint', handleAfterPrint);
6027    }
6028    function handleBeforePrint() {
6029        for (let context of contexts) {
6030            context.emitter.trigger('_beforeprint');
6031        }
6032    }
6033    function handleAfterPrint() {
6034        for (let context of contexts) {
6035            context.emitter.trigger('_afterprint');
6036        }
6037    }
6038
6039    var adaptivePlugin = {
6040        name: 'adaptive',
6041        premiumReleaseDate: '2026-07-24',
6042        deps: [premiumCommonPlugin],
6043        contextInit,
6044    };
6045
6046    publicApi.globalPlugins.push(resourceDayGridPlugin, resourceTimeGridPlugin, resourceTimelinePlugin, scrollGridPlugin, timelinePlugin, adaptivePlugin);
6047
6048})(FullCalendar.ProtectedApi, FullCalendar.DayGrid, FullCalendar.PreactJSXRuntime, FullCalendar, FullCalendar.ProtectedStyles, FullCalendar.Preact, FullCalendar.TimeGrid);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.