index.global.js 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. /*!
  2. FullCalendar Time Grid Plugin v6.1.14
  3. Docs & License: https://fullcalendar.io/docs/timegrid-view
  4. (c) 2024 Adam Shaw
  5. */
  6. FullCalendar.TimeGrid = (function (exports, core, internal$1, preact, internal$2) {
  7. 'use strict';
  8. class AllDaySplitter extends internal$1.Splitter {
  9. getKeyInfo() {
  10. return {
  11. allDay: {},
  12. timed: {},
  13. };
  14. }
  15. getKeysForDateSpan(dateSpan) {
  16. if (dateSpan.allDay) {
  17. return ['allDay'];
  18. }
  19. return ['timed'];
  20. }
  21. getKeysForEventDef(eventDef) {
  22. if (!eventDef.allDay) {
  23. return ['timed'];
  24. }
  25. if (internal$1.hasBgRendering(eventDef)) {
  26. return ['timed', 'allDay'];
  27. }
  28. return ['allDay'];
  29. }
  30. }
  31. const DEFAULT_SLAT_LABEL_FORMAT = internal$1.createFormatter({
  32. hour: 'numeric',
  33. minute: '2-digit',
  34. omitZeroMinute: true,
  35. meridiem: 'short',
  36. });
  37. function TimeColsAxisCell(props) {
  38. let classNames = [
  39. 'fc-timegrid-slot',
  40. 'fc-timegrid-slot-label',
  41. props.isLabeled ? 'fc-scrollgrid-shrink' : 'fc-timegrid-slot-minor',
  42. ];
  43. return (preact.createElement(internal$1.ViewContextType.Consumer, null, (context) => {
  44. if (!props.isLabeled) {
  45. return (preact.createElement("td", { className: classNames.join(' '), "data-time": props.isoTimeStr }));
  46. }
  47. let { dateEnv, options, viewApi } = context;
  48. let labelFormat = // TODO: fully pre-parse
  49. options.slotLabelFormat == null ? DEFAULT_SLAT_LABEL_FORMAT :
  50. Array.isArray(options.slotLabelFormat) ? internal$1.createFormatter(options.slotLabelFormat[0]) :
  51. internal$1.createFormatter(options.slotLabelFormat);
  52. let renderProps = {
  53. level: 0,
  54. time: props.time,
  55. date: dateEnv.toDate(props.date),
  56. view: viewApi,
  57. text: dateEnv.format(props.date, labelFormat),
  58. };
  59. return (preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: classNames, elAttrs: {
  60. 'data-time': props.isoTimeStr,
  61. }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact.createElement("div", { className: "fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame" },
  62. preact.createElement(InnerContent, { elTag: "div", elClasses: [
  63. 'fc-timegrid-slot-label-cushion',
  64. 'fc-scrollgrid-shrink-cushion',
  65. ] })))));
  66. }));
  67. }
  68. function renderInnerContent(props) {
  69. return props.text;
  70. }
  71. class TimeBodyAxis extends internal$1.BaseComponent {
  72. render() {
  73. return this.props.slatMetas.map((slatMeta) => (preact.createElement("tr", { key: slatMeta.key },
  74. preact.createElement(TimeColsAxisCell, Object.assign({}, slatMeta)))));
  75. }
  76. }
  77. const DEFAULT_WEEK_NUM_FORMAT = internal$1.createFormatter({ week: 'short' });
  78. const AUTO_ALL_DAY_MAX_EVENT_ROWS = 5;
  79. class TimeColsView extends internal$1.DateComponent {
  80. constructor() {
  81. super(...arguments);
  82. this.allDaySplitter = new AllDaySplitter(); // for use by subclasses
  83. this.headerElRef = preact.createRef();
  84. this.rootElRef = preact.createRef();
  85. this.scrollerElRef = preact.createRef();
  86. this.state = {
  87. slatCoords: null,
  88. };
  89. this.handleScrollTopRequest = (scrollTop) => {
  90. let scrollerEl = this.scrollerElRef.current;
  91. if (scrollerEl) { // TODO: not sure how this could ever be null. weirdness with the reducer
  92. scrollerEl.scrollTop = scrollTop;
  93. }
  94. };
  95. /* Header Render Methods
  96. ------------------------------------------------------------------------------------------------------------------*/
  97. this.renderHeadAxis = (rowKey, frameHeight = '') => {
  98. let { options } = this.context;
  99. let { dateProfile } = this.props;
  100. let range = dateProfile.renderRange;
  101. let dayCnt = internal$1.diffDays(range.start, range.end);
  102. // only do in day views (to avoid doing in week views that dont need it)
  103. let navLinkAttrs = (dayCnt === 1)
  104. ? internal$1.buildNavLinkAttrs(this.context, range.start, 'week')
  105. : {};
  106. if (options.weekNumbers && rowKey === 'day') {
  107. return (preact.createElement(internal$1.WeekNumberContainer, { elTag: "th", elClasses: [
  108. 'fc-timegrid-axis',
  109. 'fc-scrollgrid-shrink',
  110. ], elAttrs: {
  111. 'aria-hidden': true,
  112. }, date: range.start, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }, (InnerContent) => (preact.createElement("div", { className: [
  113. 'fc-timegrid-axis-frame',
  114. 'fc-scrollgrid-shrink-frame',
  115. 'fc-timegrid-axis-frame-liquid',
  116. ].join(' '), style: { height: frameHeight } },
  117. preact.createElement(InnerContent, { elTag: "a", elClasses: [
  118. 'fc-timegrid-axis-cushion',
  119. 'fc-scrollgrid-shrink-cushion',
  120. 'fc-scrollgrid-sync-inner',
  121. ], elAttrs: navLinkAttrs })))));
  122. }
  123. return (preact.createElement("th", { "aria-hidden": true, className: "fc-timegrid-axis" },
  124. preact.createElement("div", { className: "fc-timegrid-axis-frame", style: { height: frameHeight } })));
  125. };
  126. /* Table Component Render Methods
  127. ------------------------------------------------------------------------------------------------------------------*/
  128. // only a one-way height sync. we don't send the axis inner-content height to the DayGrid,
  129. // but DayGrid still needs to have classNames on inner elements in order to measure.
  130. this.renderTableRowAxis = (rowHeight) => {
  131. let { options, viewApi } = this.context;
  132. let renderProps = {
  133. text: options.allDayText,
  134. view: viewApi,
  135. };
  136. return (
  137. // TODO: make reusable hook. used in list view too
  138. preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: [
  139. 'fc-timegrid-axis',
  140. 'fc-scrollgrid-shrink',
  141. ], elAttrs: {
  142. 'aria-hidden': true,
  143. }, renderProps: renderProps, generatorName: "allDayContent", customGenerator: options.allDayContent, defaultGenerator: renderAllDayInner, classNameGenerator: options.allDayClassNames, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, (InnerContent) => (preact.createElement("div", { className: [
  144. 'fc-timegrid-axis-frame',
  145. 'fc-scrollgrid-shrink-frame',
  146. rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : '',
  147. ].join(' '), style: { height: rowHeight } },
  148. preact.createElement(InnerContent, { elTag: "span", elClasses: [
  149. 'fc-timegrid-axis-cushion',
  150. 'fc-scrollgrid-shrink-cushion',
  151. 'fc-scrollgrid-sync-inner',
  152. ] })))));
  153. };
  154. this.handleSlatCoords = (slatCoords) => {
  155. this.setState({ slatCoords });
  156. };
  157. }
  158. // rendering
  159. // ----------------------------------------------------------------------------------------------------
  160. renderSimpleLayout(headerRowContent, allDayContent, timeContent) {
  161. let { context, props } = this;
  162. let sections = [];
  163. let stickyHeaderDates = internal$1.getStickyHeaderDates(context.options);
  164. if (headerRowContent) {
  165. sections.push({
  166. type: 'header',
  167. key: 'header',
  168. isSticky: stickyHeaderDates,
  169. chunk: {
  170. elRef: this.headerElRef,
  171. tableClassName: 'fc-col-header',
  172. rowContent: headerRowContent,
  173. },
  174. });
  175. }
  176. if (allDayContent) {
  177. sections.push({
  178. type: 'body',
  179. key: 'all-day',
  180. chunk: { content: allDayContent },
  181. });
  182. sections.push({
  183. type: 'body',
  184. key: 'all-day-divider',
  185. outerContent: ( // TODO: rename to cellContent so don't need to define <tr>?
  186. preact.createElement("tr", { role: "presentation", className: "fc-scrollgrid-section" },
  187. preact.createElement("td", { className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))),
  188. });
  189. }
  190. sections.push({
  191. type: 'body',
  192. key: 'body',
  193. liquid: true,
  194. expandRows: Boolean(context.options.expandRows),
  195. chunk: {
  196. scrollerElRef: this.scrollerElRef,
  197. content: timeContent,
  198. },
  199. });
  200. return (preact.createElement(internal$1.ViewContainer, { elRef: this.rootElRef, elClasses: ['fc-timegrid'], viewSpec: context.viewSpec },
  201. preact.createElement(internal$1.SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: props.forPrint, cols: [{ width: 'shrink' }], sections: sections })));
  202. }
  203. renderHScrollLayout(headerRowContent, allDayContent, timeContent, colCnt, dayMinWidth, slatMetas, slatCoords) {
  204. let ScrollGrid = this.context.pluginHooks.scrollGridImpl;
  205. if (!ScrollGrid) {
  206. throw new Error('No ScrollGrid implementation');
  207. }
  208. let { context, props } = this;
  209. let stickyHeaderDates = !props.forPrint && internal$1.getStickyHeaderDates(context.options);
  210. let stickyFooterScrollbar = !props.forPrint && internal$1.getStickyFooterScrollbar(context.options);
  211. let sections = [];
  212. if (headerRowContent) {
  213. sections.push({
  214. type: 'header',
  215. key: 'header',
  216. isSticky: stickyHeaderDates,
  217. syncRowHeights: true,
  218. chunks: [
  219. {
  220. key: 'axis',
  221. rowContent: (arg) => (preact.createElement("tr", { role: "presentation" }, this.renderHeadAxis('day', arg.rowSyncHeights[0]))),
  222. },
  223. {
  224. key: 'cols',
  225. elRef: this.headerElRef,
  226. tableClassName: 'fc-col-header',
  227. rowContent: headerRowContent,
  228. },
  229. ],
  230. });
  231. }
  232. if (allDayContent) {
  233. sections.push({
  234. type: 'body',
  235. key: 'all-day',
  236. syncRowHeights: true,
  237. chunks: [
  238. {
  239. key: 'axis',
  240. rowContent: (contentArg) => (preact.createElement("tr", { role: "presentation" }, this.renderTableRowAxis(contentArg.rowSyncHeights[0]))),
  241. },
  242. {
  243. key: 'cols',
  244. content: allDayContent,
  245. },
  246. ],
  247. });
  248. sections.push({
  249. key: 'all-day-divider',
  250. type: 'body',
  251. outerContent: ( // TODO: rename to cellContent so don't need to define <tr>?
  252. preact.createElement("tr", { role: "presentation", className: "fc-scrollgrid-section" },
  253. preact.createElement("td", { colSpan: 2, className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))),
  254. });
  255. }
  256. let isNowIndicator = context.options.nowIndicator;
  257. sections.push({
  258. type: 'body',
  259. key: 'body',
  260. liquid: true,
  261. expandRows: Boolean(context.options.expandRows),
  262. chunks: [
  263. {
  264. key: 'axis',
  265. content: (arg) => (
  266. // TODO: make this now-indicator arrow more DRY with TimeColsContent
  267. preact.createElement("div", { className: "fc-timegrid-axis-chunk" },
  268. preact.createElement("table", { "aria-hidden": true, style: { height: arg.expandRows ? arg.clientHeight : '' } },
  269. arg.tableColGroupNode,
  270. preact.createElement("tbody", null,
  271. preact.createElement(TimeBodyAxis, { slatMetas: slatMetas }))),
  272. preact.createElement("div", { className: "fc-timegrid-now-indicator-container" },
  273. preact.createElement(internal$1.NowTimer, { unit: isNowIndicator ? 'minute' : 'day' /* hacky */ }, (nowDate) => {
  274. let nowIndicatorTop = isNowIndicator &&
  275. slatCoords &&
  276. slatCoords.safeComputeTop(nowDate); // might return void
  277. if (typeof nowIndicatorTop === 'number') {
  278. return (preact.createElement(internal$1.NowIndicatorContainer, { elClasses: ['fc-timegrid-now-indicator-arrow'], elStyle: { top: nowIndicatorTop }, isAxis: true, date: nowDate }));
  279. }
  280. return null;
  281. })))),
  282. },
  283. {
  284. key: 'cols',
  285. scrollerElRef: this.scrollerElRef,
  286. content: timeContent,
  287. },
  288. ],
  289. });
  290. if (stickyFooterScrollbar) {
  291. sections.push({
  292. key: 'footer',
  293. type: 'footer',
  294. isSticky: true,
  295. chunks: [
  296. {
  297. key: 'axis',
  298. content: internal$1.renderScrollShim,
  299. },
  300. {
  301. key: 'cols',
  302. content: internal$1.renderScrollShim,
  303. },
  304. ],
  305. });
  306. }
  307. return (preact.createElement(internal$1.ViewContainer, { elRef: this.rootElRef, elClasses: ['fc-timegrid'], viewSpec: context.viewSpec },
  308. preact.createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, forPrint: props.forPrint, collapsibleWidth: false, colGroups: [
  309. { width: 'shrink', cols: [{ width: 'shrink' }] },
  310. { cols: [{ span: colCnt, minWidth: dayMinWidth }] },
  311. ], sections: sections })));
  312. }
  313. /* Dimensions
  314. ------------------------------------------------------------------------------------------------------------------*/
  315. getAllDayMaxEventProps() {
  316. let { dayMaxEvents, dayMaxEventRows } = this.context.options;
  317. if (dayMaxEvents === true || dayMaxEventRows === true) { // is auto?
  318. dayMaxEvents = undefined;
  319. dayMaxEventRows = AUTO_ALL_DAY_MAX_EVENT_ROWS; // make sure "auto" goes to a real number
  320. }
  321. return { dayMaxEvents, dayMaxEventRows };
  322. }
  323. }
  324. function renderAllDayInner(renderProps) {
  325. return renderProps.text;
  326. }
  327. class TimeColsSlatsCoords {
  328. constructor(positions, dateProfile, slotDuration) {
  329. this.positions = positions;
  330. this.dateProfile = dateProfile;
  331. this.slotDuration = slotDuration;
  332. }
  333. safeComputeTop(date) {
  334. let { dateProfile } = this;
  335. if (internal$1.rangeContainsMarker(dateProfile.currentRange, date)) {
  336. let startOfDayDate = internal$1.startOfDay(date);
  337. let timeMs = date.valueOf() - startOfDayDate.valueOf();
  338. if (timeMs >= internal$1.asRoughMs(dateProfile.slotMinTime) &&
  339. timeMs < internal$1.asRoughMs(dateProfile.slotMaxTime)) {
  340. return this.computeTimeTop(internal$1.createDuration(timeMs));
  341. }
  342. }
  343. return null;
  344. }
  345. // Computes the top coordinate, relative to the bounds of the grid, of the given date.
  346. // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
  347. computeDateTop(when, startOfDayDate) {
  348. if (!startOfDayDate) {
  349. startOfDayDate = internal$1.startOfDay(when);
  350. }
  351. return this.computeTimeTop(internal$1.createDuration(when.valueOf() - startOfDayDate.valueOf()));
  352. }
  353. // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
  354. // This is a makeshify way to compute the time-top. Assumes all slatMetas dates are uniform.
  355. // Eventually allow computation with arbirary slat dates.
  356. computeTimeTop(duration) {
  357. let { positions, dateProfile } = this;
  358. let len = positions.els.length;
  359. // floating-point value of # of slots covered
  360. let slatCoverage = (duration.milliseconds - internal$1.asRoughMs(dateProfile.slotMinTime)) / internal$1.asRoughMs(this.slotDuration);
  361. let slatIndex;
  362. let slatRemainder;
  363. // compute a floating-point number for how many slats should be progressed through.
  364. // from 0 to number of slats (inclusive)
  365. // constrained because slotMinTime/slotMaxTime might be customized.
  366. slatCoverage = Math.max(0, slatCoverage);
  367. slatCoverage = Math.min(len, slatCoverage);
  368. // an integer index of the furthest whole slat
  369. // from 0 to number slats (*exclusive*, so len-1)
  370. slatIndex = Math.floor(slatCoverage);
  371. slatIndex = Math.min(slatIndex, len - 1);
  372. // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
  373. // could be 1.0 if slatCoverage is covering *all* the slots
  374. slatRemainder = slatCoverage - slatIndex;
  375. return positions.tops[slatIndex] +
  376. positions.getHeight(slatIndex) * slatRemainder;
  377. }
  378. }
  379. class TimeColsSlatsBody extends internal$1.BaseComponent {
  380. render() {
  381. let { props, context } = this;
  382. let { options } = context;
  383. let { slatElRefs } = props;
  384. return (preact.createElement("tbody", null, props.slatMetas.map((slatMeta, i) => {
  385. let renderProps = {
  386. time: slatMeta.time,
  387. date: context.dateEnv.toDate(slatMeta.date),
  388. view: context.viewApi,
  389. };
  390. return (preact.createElement("tr", { key: slatMeta.key, ref: slatElRefs.createRef(slatMeta.key) },
  391. props.axis && (preact.createElement(TimeColsAxisCell, Object.assign({}, slatMeta))),
  392. preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: [
  393. 'fc-timegrid-slot',
  394. 'fc-timegrid-slot-lane',
  395. !slatMeta.isLabeled && 'fc-timegrid-slot-minor',
  396. ], elAttrs: {
  397. 'data-time': slatMeta.isoTimeStr,
  398. }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount })));
  399. })));
  400. }
  401. }
  402. /*
  403. for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
  404. */
  405. class TimeColsSlats extends internal$1.BaseComponent {
  406. constructor() {
  407. super(...arguments);
  408. this.rootElRef = preact.createRef();
  409. this.slatElRefs = new internal$1.RefMap();
  410. }
  411. render() {
  412. let { props, context } = this;
  413. return (preact.createElement("div", { ref: this.rootElRef, className: "fc-timegrid-slots" },
  414. preact.createElement("table", { "aria-hidden": true, className: context.theme.getClass('table'), style: {
  415. minWidth: props.tableMinWidth,
  416. width: props.clientWidth,
  417. height: props.minHeight,
  418. } },
  419. props.tableColGroupNode /* relies on there only being a single <col> for the axis */,
  420. preact.createElement(TimeColsSlatsBody, { slatElRefs: this.slatElRefs, axis: props.axis, slatMetas: props.slatMetas }))));
  421. }
  422. componentDidMount() {
  423. this.updateSizing();
  424. }
  425. componentDidUpdate() {
  426. this.updateSizing();
  427. }
  428. componentWillUnmount() {
  429. if (this.props.onCoords) {
  430. this.props.onCoords(null);
  431. }
  432. }
  433. updateSizing() {
  434. let { context, props } = this;
  435. if (props.onCoords &&
  436. props.clientWidth !== null // means sizing has stabilized
  437. ) {
  438. let rootEl = this.rootElRef.current;
  439. if (rootEl.offsetHeight) { // not hidden by css
  440. props.onCoords(new TimeColsSlatsCoords(new internal$1.PositionCache(this.rootElRef.current, collectSlatEls(this.slatElRefs.currentMap, props.slatMetas), false, true), this.props.dateProfile, context.options.slotDuration));
  441. }
  442. }
  443. }
  444. }
  445. function collectSlatEls(elMap, slatMetas) {
  446. return slatMetas.map((slatMeta) => elMap[slatMeta.key]);
  447. }
  448. function splitSegsByCol(segs, colCnt) {
  449. let segsByCol = [];
  450. let i;
  451. for (i = 0; i < colCnt; i += 1) {
  452. segsByCol.push([]);
  453. }
  454. if (segs) {
  455. for (i = 0; i < segs.length; i += 1) {
  456. segsByCol[segs[i].col].push(segs[i]);
  457. }
  458. }
  459. return segsByCol;
  460. }
  461. function splitInteractionByCol(ui, colCnt) {
  462. let byRow = [];
  463. if (!ui) {
  464. for (let i = 0; i < colCnt; i += 1) {
  465. byRow[i] = null;
  466. }
  467. }
  468. else {
  469. for (let i = 0; i < colCnt; i += 1) {
  470. byRow[i] = {
  471. affectedInstances: ui.affectedInstances,
  472. isEvent: ui.isEvent,
  473. segs: [],
  474. };
  475. }
  476. for (let seg of ui.segs) {
  477. byRow[seg.col].segs.push(seg);
  478. }
  479. }
  480. return byRow;
  481. }
  482. class TimeColMoreLink extends internal$1.BaseComponent {
  483. render() {
  484. let { props } = this;
  485. return (preact.createElement(internal$1.MoreLinkContainer, { elClasses: ['fc-timegrid-more-link'], elStyle: {
  486. top: props.top,
  487. bottom: props.bottom,
  488. }, allDayDate: null, moreCnt: props.hiddenSegs.length, allSegs: props.hiddenSegs, hiddenSegs: props.hiddenSegs, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, todayRange: props.todayRange, popoverContent: () => renderPlainFgSegs(props.hiddenSegs, props), defaultGenerator: renderMoreLinkInner, forceTimed: true }, (InnerContent) => (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timegrid-more-link-inner', 'fc-sticky'] }))));
  489. }
  490. }
  491. function renderMoreLinkInner(props) {
  492. return props.shortText;
  493. }
  494. // segInputs assumed sorted
  495. function buildPositioning(segInputs, strictOrder, maxStackCnt) {
  496. let hierarchy = new internal$1.SegHierarchy();
  497. if (strictOrder != null) {
  498. hierarchy.strictOrder = strictOrder;
  499. }
  500. if (maxStackCnt != null) {
  501. hierarchy.maxStackCnt = maxStackCnt;
  502. }
  503. let hiddenEntries = hierarchy.addSegs(segInputs);
  504. let hiddenGroups = internal$1.groupIntersectingEntries(hiddenEntries);
  505. let web = buildWeb(hierarchy);
  506. web = stretchWeb(web, 1); // all levelCoords/thickness will have 0.0-1.0
  507. let segRects = webToRects(web);
  508. return { segRects, hiddenGroups };
  509. }
  510. function buildWeb(hierarchy) {
  511. const { entriesByLevel } = hierarchy;
  512. const buildNode = cacheable((level, lateral) => level + ':' + lateral, (level, lateral) => {
  513. let siblingRange = findNextLevelSegs(hierarchy, level, lateral);
  514. let nextLevelRes = buildNodes(siblingRange, buildNode);
  515. let entry = entriesByLevel[level][lateral];
  516. return [
  517. Object.assign(Object.assign({}, entry), { nextLevelNodes: nextLevelRes[0] }),
  518. entry.thickness + nextLevelRes[1], // the pressure builds
  519. ];
  520. });
  521. return buildNodes(entriesByLevel.length
  522. ? { level: 0, lateralStart: 0, lateralEnd: entriesByLevel[0].length }
  523. : null, buildNode)[0];
  524. }
  525. function buildNodes(siblingRange, buildNode) {
  526. if (!siblingRange) {
  527. return [[], 0];
  528. }
  529. let { level, lateralStart, lateralEnd } = siblingRange;
  530. let lateral = lateralStart;
  531. let pairs = [];
  532. while (lateral < lateralEnd) {
  533. pairs.push(buildNode(level, lateral));
  534. lateral += 1;
  535. }
  536. pairs.sort(cmpDescPressures);
  537. return [
  538. pairs.map(extractNode),
  539. pairs[0][1], // first item's pressure
  540. ];
  541. }
  542. function cmpDescPressures(a, b) {
  543. return b[1] - a[1];
  544. }
  545. function extractNode(a) {
  546. return a[0];
  547. }
  548. function findNextLevelSegs(hierarchy, subjectLevel, subjectLateral) {
  549. let { levelCoords, entriesByLevel } = hierarchy;
  550. let subjectEntry = entriesByLevel[subjectLevel][subjectLateral];
  551. let afterSubject = levelCoords[subjectLevel] + subjectEntry.thickness;
  552. let levelCnt = levelCoords.length;
  553. let level = subjectLevel;
  554. // skip past levels that are too high up
  555. for (; level < levelCnt && levelCoords[level] < afterSubject; level += 1)
  556. ; // do nothing
  557. for (; level < levelCnt; level += 1) {
  558. let entries = entriesByLevel[level];
  559. let entry;
  560. let searchIndex = internal$1.binarySearch(entries, subjectEntry.span.start, internal$1.getEntrySpanEnd);
  561. let lateralStart = searchIndex[0] + searchIndex[1]; // if exact match (which doesn't collide), go to next one
  562. let lateralEnd = lateralStart;
  563. while ( // loop through entries that horizontally intersect
  564. (entry = entries[lateralEnd]) && // but not past the whole seg list
  565. entry.span.start < subjectEntry.span.end) {
  566. lateralEnd += 1;
  567. }
  568. if (lateralStart < lateralEnd) {
  569. return { level, lateralStart, lateralEnd };
  570. }
  571. }
  572. return null;
  573. }
  574. function stretchWeb(topLevelNodes, totalThickness) {
  575. const stretchNode = cacheable((node, startCoord, prevThickness) => internal$1.buildEntryKey(node), (node, startCoord, prevThickness) => {
  576. let { nextLevelNodes, thickness } = node;
  577. let allThickness = thickness + prevThickness;
  578. let thicknessFraction = thickness / allThickness;
  579. let endCoord;
  580. let newChildren = [];
  581. if (!nextLevelNodes.length) {
  582. endCoord = totalThickness;
  583. }
  584. else {
  585. for (let childNode of nextLevelNodes) {
  586. if (endCoord === undefined) {
  587. let res = stretchNode(childNode, startCoord, allThickness);
  588. endCoord = res[0];
  589. newChildren.push(res[1]);
  590. }
  591. else {
  592. let res = stretchNode(childNode, endCoord, 0);
  593. newChildren.push(res[1]);
  594. }
  595. }
  596. }
  597. let newThickness = (endCoord - startCoord) * thicknessFraction;
  598. return [endCoord - newThickness, Object.assign(Object.assign({}, node), { thickness: newThickness, nextLevelNodes: newChildren })];
  599. });
  600. return topLevelNodes.map((node) => stretchNode(node, 0, 0)[1]);
  601. }
  602. // not sorted in any particular order
  603. function webToRects(topLevelNodes) {
  604. let rects = [];
  605. const processNode = cacheable((node, levelCoord, stackDepth) => internal$1.buildEntryKey(node), (node, levelCoord, stackDepth) => {
  606. let rect = Object.assign(Object.assign({}, node), { levelCoord,
  607. stackDepth, stackForward: 0 });
  608. rects.push(rect);
  609. return (rect.stackForward = processNodes(node.nextLevelNodes, levelCoord + node.thickness, stackDepth + 1) + 1);
  610. });
  611. function processNodes(nodes, levelCoord, stackDepth) {
  612. let stackForward = 0;
  613. for (let node of nodes) {
  614. stackForward = Math.max(processNode(node, levelCoord, stackDepth), stackForward);
  615. }
  616. return stackForward;
  617. }
  618. processNodes(topLevelNodes, 0, 0);
  619. return rects; // TODO: sort rects by levelCoord to be consistent with toRects?
  620. }
  621. // TODO: move to general util
  622. function cacheable(keyFunc, workFunc) {
  623. const cache = {};
  624. return (...args) => {
  625. let key = keyFunc(...args);
  626. return (key in cache)
  627. ? cache[key]
  628. : (cache[key] = workFunc(...args));
  629. };
  630. }
  631. function computeSegVCoords(segs, colDate, slatCoords = null, eventMinHeight = 0) {
  632. let vcoords = [];
  633. if (slatCoords) {
  634. for (let i = 0; i < segs.length; i += 1) {
  635. let seg = segs[i];
  636. let spanStart = slatCoords.computeDateTop(seg.start, colDate);
  637. let spanEnd = Math.max(spanStart + (eventMinHeight || 0), // :(
  638. slatCoords.computeDateTop(seg.end, colDate));
  639. vcoords.push({
  640. start: Math.round(spanStart),
  641. end: Math.round(spanEnd), //
  642. });
  643. }
  644. }
  645. return vcoords;
  646. }
  647. function computeFgSegPlacements(segs, segVCoords, // might not have for every seg
  648. eventOrderStrict, eventMaxStack) {
  649. let segInputs = [];
  650. let dumbSegs = []; // segs without coords
  651. for (let i = 0; i < segs.length; i += 1) {
  652. let vcoords = segVCoords[i];
  653. if (vcoords) {
  654. segInputs.push({
  655. index: i,
  656. thickness: 1,
  657. span: vcoords,
  658. });
  659. }
  660. else {
  661. dumbSegs.push(segs[i]);
  662. }
  663. }
  664. let { segRects, hiddenGroups } = buildPositioning(segInputs, eventOrderStrict, eventMaxStack);
  665. let segPlacements = [];
  666. for (let segRect of segRects) {
  667. segPlacements.push({
  668. seg: segs[segRect.index],
  669. rect: segRect,
  670. });
  671. }
  672. for (let dumbSeg of dumbSegs) {
  673. segPlacements.push({ seg: dumbSeg, rect: null });
  674. }
  675. return { segPlacements, hiddenGroups };
  676. }
  677. const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
  678. hour: 'numeric',
  679. minute: '2-digit',
  680. meridiem: false,
  681. });
  682. class TimeColEvent extends internal$1.BaseComponent {
  683. render() {
  684. return (preact.createElement(internal$1.StandardEvent, Object.assign({}, this.props, { elClasses: [
  685. 'fc-timegrid-event',
  686. 'fc-v-event',
  687. this.props.isShort && 'fc-timegrid-event-short',
  688. ], defaultTimeFormat: DEFAULT_TIME_FORMAT })));
  689. }
  690. }
  691. class TimeCol extends internal$1.BaseComponent {
  692. constructor() {
  693. super(...arguments);
  694. this.sortEventSegs = internal$1.memoize(internal$1.sortEventSegs);
  695. }
  696. // TODO: memoize event-placement?
  697. render() {
  698. let { props, context } = this;
  699. let { options } = context;
  700. let isSelectMirror = options.selectMirror;
  701. let mirrorSegs = // yuck
  702. (props.eventDrag && props.eventDrag.segs) ||
  703. (props.eventResize && props.eventResize.segs) ||
  704. (isSelectMirror && props.dateSelectionSegs) ||
  705. [];
  706. let interactionAffectedInstances = // TODO: messy way to compute this
  707. (props.eventDrag && props.eventDrag.affectedInstances) ||
  708. (props.eventResize && props.eventResize.affectedInstances) ||
  709. {};
  710. let sortedFgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
  711. return (preact.createElement(internal$1.DayCellContainer, { elTag: "td", elRef: props.elRef, elClasses: [
  712. 'fc-timegrid-col',
  713. ...(props.extraClassNames || []),
  714. ], elAttrs: Object.assign({ role: 'gridcell' }, props.extraDataAttrs), date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraRenderProps: props.extraRenderProps }, (InnerContent) => (preact.createElement("div", { className: "fc-timegrid-col-frame" },
  715. preact.createElement("div", { className: "fc-timegrid-col-bg" },
  716. this.renderFillSegs(props.businessHourSegs, 'non-business'),
  717. this.renderFillSegs(props.bgEventSegs, 'bg-event'),
  718. this.renderFillSegs(props.dateSelectionSegs, 'highlight')),
  719. preact.createElement("div", { className: "fc-timegrid-col-events" }, this.renderFgSegs(sortedFgSegs, interactionAffectedInstances, false, false, false)),
  720. preact.createElement("div", { className: "fc-timegrid-col-events" }, this.renderFgSegs(mirrorSegs, {}, Boolean(props.eventDrag), Boolean(props.eventResize), Boolean(isSelectMirror), 'mirror')),
  721. preact.createElement("div", { className: "fc-timegrid-now-indicator-container" }, this.renderNowIndicator(props.nowIndicatorSegs)),
  722. internal$1.hasCustomDayCellContent(options) && (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timegrid-col-misc'] }))))));
  723. }
  724. renderFgSegs(sortedFgSegs, segIsInvisible, isDragging, isResizing, isDateSelecting, forcedKey) {
  725. let { props } = this;
  726. if (props.forPrint) {
  727. return renderPlainFgSegs(sortedFgSegs, props);
  728. }
  729. return this.renderPositionedFgSegs(sortedFgSegs, segIsInvisible, isDragging, isResizing, isDateSelecting, forcedKey);
  730. }
  731. renderPositionedFgSegs(segs, // if not mirror, needs to be sorted
  732. segIsInvisible, isDragging, isResizing, isDateSelecting, forcedKey) {
  733. let { eventMaxStack, eventShortHeight, eventOrderStrict, eventMinHeight } = this.context.options;
  734. let { date, slatCoords, eventSelection, todayRange, nowDate } = this.props;
  735. let isMirror = isDragging || isResizing || isDateSelecting;
  736. let segVCoords = computeSegVCoords(segs, date, slatCoords, eventMinHeight);
  737. let { segPlacements, hiddenGroups } = computeFgSegPlacements(segs, segVCoords, eventOrderStrict, eventMaxStack);
  738. return (preact.createElement(preact.Fragment, null,
  739. this.renderHiddenGroups(hiddenGroups, segs),
  740. segPlacements.map((segPlacement) => {
  741. let { seg, rect } = segPlacement;
  742. let instanceId = seg.eventRange.instance.instanceId;
  743. let isVisible = isMirror || Boolean(!segIsInvisible[instanceId] && rect);
  744. let vStyle = computeSegVStyle(rect && rect.span);
  745. let hStyle = (!isMirror && rect) ? this.computeSegHStyle(rect) : { left: 0, right: 0 };
  746. let isInset = Boolean(rect) && rect.stackForward > 0;
  747. let isShort = Boolean(rect) && (rect.span.end - rect.span.start) < eventShortHeight; // look at other places for this problem
  748. return (preact.createElement("div", { className: 'fc-timegrid-event-harness' +
  749. (isInset ? ' fc-timegrid-event-harness-inset' : ''), key: forcedKey || instanceId, style: Object.assign(Object.assign({ visibility: isVisible ? '' : 'hidden' }, vStyle), hStyle) },
  750. preact.createElement(TimeColEvent, Object.assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, isShort: isShort }, internal$1.getSegMeta(seg, todayRange, nowDate)))));
  751. })));
  752. }
  753. // will already have eventMinHeight applied because segInputs already had it
  754. renderHiddenGroups(hiddenGroups, segs) {
  755. let { extraDateSpan, dateProfile, todayRange, nowDate, eventSelection, eventDrag, eventResize } = this.props;
  756. return (preact.createElement(preact.Fragment, null, hiddenGroups.map((hiddenGroup) => {
  757. let positionCss = computeSegVStyle(hiddenGroup.span);
  758. let hiddenSegs = compileSegsFromEntries(hiddenGroup.entries, segs);
  759. return (preact.createElement(TimeColMoreLink, { key: internal$1.buildIsoString(internal$1.computeEarliestSegStart(hiddenSegs)), hiddenSegs: hiddenSegs, top: positionCss.top, bottom: positionCss.bottom, extraDateSpan: extraDateSpan, dateProfile: dateProfile, todayRange: todayRange, nowDate: nowDate, eventSelection: eventSelection, eventDrag: eventDrag, eventResize: eventResize }));
  760. })));
  761. }
  762. renderFillSegs(segs, fillType) {
  763. let { props, context } = this;
  764. let segVCoords = computeSegVCoords(segs, props.date, props.slatCoords, context.options.eventMinHeight); // don't assume all populated
  765. let children = segVCoords.map((vcoords, i) => {
  766. let seg = segs[i];
  767. return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: "fc-timegrid-bg-harness", style: computeSegVStyle(vcoords) }, fillType === 'bg-event' ?
  768. preact.createElement(internal$1.BgEvent, Object.assign({ seg: seg }, internal$1.getSegMeta(seg, props.todayRange, props.nowDate))) :
  769. internal$1.renderFill(fillType)));
  770. });
  771. return preact.createElement(preact.Fragment, null, children);
  772. }
  773. renderNowIndicator(segs) {
  774. let { slatCoords, date } = this.props;
  775. if (!slatCoords) {
  776. return null;
  777. }
  778. return segs.map((seg, i) => (preact.createElement(internal$1.NowIndicatorContainer
  779. // key doesn't matter. will only ever be one
  780. , {
  781. // key doesn't matter. will only ever be one
  782. key: i, elClasses: ['fc-timegrid-now-indicator-line'], elStyle: {
  783. top: slatCoords.computeDateTop(seg.start, date),
  784. }, isAxis: false, date: date })));
  785. }
  786. computeSegHStyle(segHCoords) {
  787. let { isRtl, options } = this.context;
  788. let shouldOverlap = options.slotEventOverlap;
  789. let nearCoord = segHCoords.levelCoord; // the left side if LTR. the right side if RTL. floating-point
  790. let farCoord = segHCoords.levelCoord + segHCoords.thickness; // the right side if LTR. the left side if RTL. floating-point
  791. let left; // amount of space from left edge, a fraction of the total width
  792. let right; // amount of space from right edge, a fraction of the total width
  793. if (shouldOverlap) {
  794. // double the width, but don't go beyond the maximum forward coordinate (1.0)
  795. farCoord = Math.min(1, nearCoord + (farCoord - nearCoord) * 2);
  796. }
  797. if (isRtl) {
  798. left = 1 - farCoord;
  799. right = nearCoord;
  800. }
  801. else {
  802. left = nearCoord;
  803. right = 1 - farCoord;
  804. }
  805. let props = {
  806. zIndex: segHCoords.stackDepth + 1,
  807. left: left * 100 + '%',
  808. right: right * 100 + '%',
  809. };
  810. if (shouldOverlap && !segHCoords.stackForward) {
  811. // add padding to the edge so that forward stacked events don't cover the resizer's icon
  812. props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
  813. }
  814. return props;
  815. }
  816. }
  817. function renderPlainFgSegs(sortedFgSegs, { todayRange, nowDate, eventSelection, eventDrag, eventResize }) {
  818. let hiddenInstances = (eventDrag ? eventDrag.affectedInstances : null) ||
  819. (eventResize ? eventResize.affectedInstances : null) ||
  820. {};
  821. return (preact.createElement(preact.Fragment, null, sortedFgSegs.map((seg) => {
  822. let instanceId = seg.eventRange.instance.instanceId;
  823. return (preact.createElement("div", { key: instanceId, style: { visibility: hiddenInstances[instanceId] ? 'hidden' : '' } },
  824. preact.createElement(TimeColEvent, Object.assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === eventSelection, isShort: false }, internal$1.getSegMeta(seg, todayRange, nowDate)))));
  825. })));
  826. }
  827. function computeSegVStyle(segVCoords) {
  828. if (!segVCoords) {
  829. return { top: '', bottom: '' };
  830. }
  831. return {
  832. top: segVCoords.start,
  833. bottom: -segVCoords.end,
  834. };
  835. }
  836. function compileSegsFromEntries(segEntries, allSegs) {
  837. return segEntries.map((segEntry) => allSegs[segEntry.index]);
  838. }
  839. class TimeColsContent extends internal$1.BaseComponent {
  840. constructor() {
  841. super(...arguments);
  842. this.splitFgEventSegs = internal$1.memoize(splitSegsByCol);
  843. this.splitBgEventSegs = internal$1.memoize(splitSegsByCol);
  844. this.splitBusinessHourSegs = internal$1.memoize(splitSegsByCol);
  845. this.splitNowIndicatorSegs = internal$1.memoize(splitSegsByCol);
  846. this.splitDateSelectionSegs = internal$1.memoize(splitSegsByCol);
  847. this.splitEventDrag = internal$1.memoize(splitInteractionByCol);
  848. this.splitEventResize = internal$1.memoize(splitInteractionByCol);
  849. this.rootElRef = preact.createRef();
  850. this.cellElRefs = new internal$1.RefMap();
  851. }
  852. render() {
  853. let { props, context } = this;
  854. let nowIndicatorTop = context.options.nowIndicator &&
  855. props.slatCoords &&
  856. props.slatCoords.safeComputeTop(props.nowDate); // might return void
  857. let colCnt = props.cells.length;
  858. let fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, colCnt);
  859. let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, colCnt);
  860. let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, colCnt);
  861. let nowIndicatorSegsByRow = this.splitNowIndicatorSegs(props.nowIndicatorSegs, colCnt);
  862. let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, colCnt);
  863. let eventDragByRow = this.splitEventDrag(props.eventDrag, colCnt);
  864. let eventResizeByRow = this.splitEventResize(props.eventResize, colCnt);
  865. return (preact.createElement("div", { className: "fc-timegrid-cols", ref: this.rootElRef },
  866. preact.createElement("table", { role: "presentation", style: {
  867. minWidth: props.tableMinWidth,
  868. width: props.clientWidth,
  869. } },
  870. props.tableColGroupNode,
  871. preact.createElement("tbody", { role: "presentation" },
  872. preact.createElement("tr", { role: "row" },
  873. props.axis && (preact.createElement("td", { "aria-hidden": true, className: "fc-timegrid-col fc-timegrid-axis" },
  874. preact.createElement("div", { className: "fc-timegrid-col-frame" },
  875. preact.createElement("div", { className: "fc-timegrid-now-indicator-container" }, typeof nowIndicatorTop === 'number' && (preact.createElement(internal$1.NowIndicatorContainer, { elClasses: ['fc-timegrid-now-indicator-arrow'], elStyle: { top: nowIndicatorTop }, isAxis: true, date: props.nowDate })))))),
  876. props.cells.map((cell, i) => (preact.createElement(TimeCol, { key: cell.key, elRef: this.cellElRefs.createRef(cell.key), dateProfile: props.dateProfile, date: cell.date, nowDate: props.nowDate, todayRange: props.todayRange, extraRenderProps: cell.extraRenderProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, extraDateSpan: cell.extraDateSpan, fgEventSegs: fgEventSegsByRow[i], bgEventSegs: bgEventSegsByRow[i], businessHourSegs: businessHourSegsByRow[i], nowIndicatorSegs: nowIndicatorSegsByRow[i], dateSelectionSegs: dateSelectionSegsByRow[i], eventDrag: eventDragByRow[i], eventResize: eventResizeByRow[i], slatCoords: props.slatCoords, eventSelection: props.eventSelection, forPrint: props.forPrint }))))))));
  877. }
  878. componentDidMount() {
  879. this.updateCoords();
  880. }
  881. componentDidUpdate() {
  882. this.updateCoords();
  883. }
  884. updateCoords() {
  885. let { props } = this;
  886. if (props.onColCoords &&
  887. props.clientWidth !== null // means sizing has stabilized
  888. ) {
  889. props.onColCoords(new internal$1.PositionCache(this.rootElRef.current, collectCellEls(this.cellElRefs.currentMap, props.cells), true, // horizontal
  890. false));
  891. }
  892. }
  893. }
  894. function collectCellEls(elMap, cells) {
  895. return cells.map((cell) => elMap[cell.key]);
  896. }
  897. /* A component that renders one or more columns of vertical time slots
  898. ----------------------------------------------------------------------------------------------------------------------*/
  899. class TimeCols extends internal$1.DateComponent {
  900. constructor() {
  901. super(...arguments);
  902. this.processSlotOptions = internal$1.memoize(processSlotOptions);
  903. this.state = {
  904. slatCoords: null,
  905. };
  906. this.handleRootEl = (el) => {
  907. if (el) {
  908. this.context.registerInteractiveComponent(this, {
  909. el,
  910. isHitComboAllowed: this.props.isHitComboAllowed,
  911. });
  912. }
  913. else {
  914. this.context.unregisterInteractiveComponent(this);
  915. }
  916. };
  917. this.handleScrollRequest = (request) => {
  918. let { onScrollTopRequest } = this.props;
  919. let { slatCoords } = this.state;
  920. if (onScrollTopRequest && slatCoords) {
  921. if (request.time) {
  922. let top = slatCoords.computeTimeTop(request.time);
  923. top = Math.ceil(top); // zoom can give weird floating-point values. rather scroll a little bit further
  924. if (top) {
  925. top += 1; // to overcome top border that slots beyond the first have. looks better
  926. }
  927. onScrollTopRequest(top);
  928. }
  929. return true;
  930. }
  931. return false;
  932. };
  933. this.handleColCoords = (colCoords) => {
  934. this.colCoords = colCoords;
  935. };
  936. this.handleSlatCoords = (slatCoords) => {
  937. this.setState({ slatCoords });
  938. if (this.props.onSlatCoords) {
  939. this.props.onSlatCoords(slatCoords);
  940. }
  941. };
  942. }
  943. render() {
  944. let { props, state } = this;
  945. return (preact.createElement("div", { className: "fc-timegrid-body", ref: this.handleRootEl, style: {
  946. // these props are important to give this wrapper correct dimensions for interactions
  947. // TODO: if we set it here, can we avoid giving to inner tables?
  948. width: props.clientWidth,
  949. minWidth: props.tableMinWidth,
  950. } },
  951. preact.createElement(TimeColsSlats, { axis: props.axis, dateProfile: props.dateProfile, slatMetas: props.slatMetas, clientWidth: props.clientWidth, minHeight: props.expandRows ? props.clientHeight : '', tableMinWidth: props.tableMinWidth, tableColGroupNode: props.axis ? props.tableColGroupNode : null /* axis depends on the colgroup's shrinking */, onCoords: this.handleSlatCoords }),
  952. preact.createElement(TimeColsContent, { cells: props.cells, axis: props.axis, dateProfile: props.dateProfile, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange, nowDate: props.nowDate, nowIndicatorSegs: props.nowIndicatorSegs, clientWidth: props.clientWidth, tableMinWidth: props.tableMinWidth, tableColGroupNode: props.tableColGroupNode, slatCoords: state.slatCoords, onColCoords: this.handleColCoords, forPrint: props.forPrint })));
  953. }
  954. componentDidMount() {
  955. this.scrollResponder = this.context.createScrollResponder(this.handleScrollRequest);
  956. }
  957. componentDidUpdate(prevProps) {
  958. this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile);
  959. }
  960. componentWillUnmount() {
  961. this.scrollResponder.detach();
  962. }
  963. queryHit(positionLeft, positionTop) {
  964. let { dateEnv, options } = this.context;
  965. let { colCoords } = this;
  966. let { dateProfile } = this.props;
  967. let { slatCoords } = this.state;
  968. let { snapDuration, snapsPerSlot } = this.processSlotOptions(this.props.slotDuration, options.snapDuration);
  969. let colIndex = colCoords.leftToIndex(positionLeft);
  970. let slatIndex = slatCoords.positions.topToIndex(positionTop);
  971. if (colIndex != null && slatIndex != null) {
  972. let cell = this.props.cells[colIndex];
  973. let slatTop = slatCoords.positions.tops[slatIndex];
  974. let slatHeight = slatCoords.positions.getHeight(slatIndex);
  975. let partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
  976. let localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
  977. let snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
  978. let dayDate = this.props.cells[colIndex].date;
  979. let time = internal$1.addDurations(dateProfile.slotMinTime, internal$1.multiplyDuration(snapDuration, snapIndex));
  980. let start = dateEnv.add(dayDate, time);
  981. let end = dateEnv.add(start, snapDuration);
  982. return {
  983. dateProfile,
  984. dateSpan: Object.assign({ range: { start, end }, allDay: false }, cell.extraDateSpan),
  985. dayEl: colCoords.els[colIndex],
  986. rect: {
  987. left: colCoords.lefts[colIndex],
  988. right: colCoords.rights[colIndex],
  989. top: slatTop,
  990. bottom: slatTop + slatHeight,
  991. },
  992. layer: 0,
  993. };
  994. }
  995. return null;
  996. }
  997. }
  998. function processSlotOptions(slotDuration, snapDurationOverride) {
  999. let snapDuration = snapDurationOverride || slotDuration;
  1000. let snapsPerSlot = internal$1.wholeDivideDurations(slotDuration, snapDuration);
  1001. if (snapsPerSlot === null) {
  1002. snapDuration = slotDuration;
  1003. snapsPerSlot = 1;
  1004. // TODO: say warning?
  1005. }
  1006. return { snapDuration, snapsPerSlot };
  1007. }
  1008. class DayTimeColsSlicer extends internal$1.Slicer {
  1009. sliceRange(range, dayRanges) {
  1010. let segs = [];
  1011. for (let col = 0; col < dayRanges.length; col += 1) {
  1012. let segRange = internal$1.intersectRanges(range, dayRanges[col]);
  1013. if (segRange) {
  1014. segs.push({
  1015. start: segRange.start,
  1016. end: segRange.end,
  1017. isStart: segRange.start.valueOf() === range.start.valueOf(),
  1018. isEnd: segRange.end.valueOf() === range.end.valueOf(),
  1019. col,
  1020. });
  1021. }
  1022. }
  1023. return segs;
  1024. }
  1025. }
  1026. class DayTimeCols extends internal$1.DateComponent {
  1027. constructor() {
  1028. super(...arguments);
  1029. this.buildDayRanges = internal$1.memoize(buildDayRanges);
  1030. this.slicer = new DayTimeColsSlicer();
  1031. this.timeColsRef = preact.createRef();
  1032. }
  1033. render() {
  1034. let { props, context } = this;
  1035. let { dateProfile, dayTableModel } = props;
  1036. let { nowIndicator, nextDayThreshold } = context.options;
  1037. let dayRanges = this.buildDayRanges(dayTableModel, dateProfile, context.dateEnv);
  1038. // give it the first row of cells
  1039. // TODO: would move this further down hierarchy, but sliceNowDate needs it
  1040. return (preact.createElement(internal$1.NowTimer, { unit: nowIndicator ? 'minute' : 'day' }, (nowDate, todayRange) => (preact.createElement(TimeCols, Object.assign({ ref: this.timeColsRef }, this.slicer.sliceProps(props, dateProfile, null, context, dayRanges), { forPrint: props.forPrint, axis: props.axis, dateProfile: dateProfile, slatMetas: props.slatMetas, slotDuration: props.slotDuration, cells: dayTableModel.cells[0], tableColGroupNode: props.tableColGroupNode, tableMinWidth: props.tableMinWidth, clientWidth: props.clientWidth, clientHeight: props.clientHeight, expandRows: props.expandRows, nowDate: nowDate, nowIndicatorSegs: nowIndicator && this.slicer.sliceNowDate(nowDate, dateProfile, nextDayThreshold, context, dayRanges), todayRange: todayRange, onScrollTopRequest: props.onScrollTopRequest, onSlatCoords: props.onSlatCoords })))));
  1041. }
  1042. }
  1043. function buildDayRanges(dayTableModel, dateProfile, dateEnv) {
  1044. let ranges = [];
  1045. for (let date of dayTableModel.headerDates) {
  1046. ranges.push({
  1047. start: dateEnv.add(date, dateProfile.slotMinTime),
  1048. end: dateEnv.add(date, dateProfile.slotMaxTime),
  1049. });
  1050. }
  1051. return ranges;
  1052. }
  1053. // potential nice values for the slot-duration and interval-duration
  1054. // from largest to smallest
  1055. const STOCK_SUB_DURATIONS = [
  1056. { hours: 1 },
  1057. { minutes: 30 },
  1058. { minutes: 15 },
  1059. { seconds: 30 },
  1060. { seconds: 15 },
  1061. ];
  1062. function buildSlatMetas(slotMinTime, slotMaxTime, explicitLabelInterval, slotDuration, dateEnv) {
  1063. let dayStart = new Date(0);
  1064. let slatTime = slotMinTime;
  1065. let slatIterator = internal$1.createDuration(0);
  1066. let labelInterval = explicitLabelInterval || computeLabelInterval(slotDuration);
  1067. let metas = [];
  1068. while (internal$1.asRoughMs(slatTime) < internal$1.asRoughMs(slotMaxTime)) {
  1069. let date = dateEnv.add(dayStart, slatTime);
  1070. let isLabeled = internal$1.wholeDivideDurations(slatIterator, labelInterval) !== null;
  1071. metas.push({
  1072. date,
  1073. time: slatTime,
  1074. key: date.toISOString(),
  1075. isoTimeStr: internal$1.formatIsoTimeString(date),
  1076. isLabeled,
  1077. });
  1078. slatTime = internal$1.addDurations(slatTime, slotDuration);
  1079. slatIterator = internal$1.addDurations(slatIterator, slotDuration);
  1080. }
  1081. return metas;
  1082. }
  1083. // Computes an automatic value for slotLabelInterval
  1084. function computeLabelInterval(slotDuration) {
  1085. let i;
  1086. let labelInterval;
  1087. let slotsPerLabel;
  1088. // find the smallest stock label interval that results in more than one slots-per-label
  1089. for (i = STOCK_SUB_DURATIONS.length - 1; i >= 0; i -= 1) {
  1090. labelInterval = internal$1.createDuration(STOCK_SUB_DURATIONS[i]);
  1091. slotsPerLabel = internal$1.wholeDivideDurations(labelInterval, slotDuration);
  1092. if (slotsPerLabel !== null && slotsPerLabel > 1) {
  1093. return labelInterval;
  1094. }
  1095. }
  1096. return slotDuration; // fall back
  1097. }
  1098. class DayTimeColsView extends TimeColsView {
  1099. constructor() {
  1100. super(...arguments);
  1101. this.buildTimeColsModel = internal$1.memoize(buildTimeColsModel);
  1102. this.buildSlatMetas = internal$1.memoize(buildSlatMetas);
  1103. }
  1104. render() {
  1105. let { options, dateEnv, dateProfileGenerator } = this.context;
  1106. let { props } = this;
  1107. let { dateProfile } = props;
  1108. let dayTableModel = this.buildTimeColsModel(dateProfile, dateProfileGenerator);
  1109. let splitProps = this.allDaySplitter.splitProps(props);
  1110. let slatMetas = this.buildSlatMetas(dateProfile.slotMinTime, dateProfile.slotMaxTime, options.slotLabelInterval, options.slotDuration, dateEnv);
  1111. let { dayMinWidth } = options;
  1112. let hasAttachedAxis = !dayMinWidth;
  1113. let hasDetachedAxis = dayMinWidth;
  1114. let headerContent = options.dayHeaders && (preact.createElement(internal$1.DayHeader, { dates: dayTableModel.headerDates, dateProfile: dateProfile, datesRepDistinctDays: true, renderIntro: hasAttachedAxis ? this.renderHeadAxis : null }));
  1115. let allDayContent = (options.allDaySlot !== false) && ((contentArg) => (preact.createElement(internal$2.DayTable, Object.assign({}, splitProps.allDay, { dateProfile: dateProfile, dayTableModel: dayTableModel, nextDayThreshold: options.nextDayThreshold, tableMinWidth: contentArg.tableMinWidth, colGroupNode: contentArg.tableColGroupNode, renderRowIntro: hasAttachedAxis ? this.renderTableRowAxis : null, showWeekNumbers: false, expandRows: false, headerAlignElRef: this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint }, this.getAllDayMaxEventProps()))));
  1116. let timeGridContent = (contentArg) => (preact.createElement(DayTimeCols, Object.assign({}, splitProps.timed, { dayTableModel: dayTableModel, dateProfile: dateProfile, axis: hasAttachedAxis, slotDuration: options.slotDuration, slatMetas: slatMetas, forPrint: props.forPrint, tableColGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, onSlatCoords: this.handleSlatCoords, expandRows: contentArg.expandRows, onScrollTopRequest: this.handleScrollTopRequest })));
  1117. return hasDetachedAxis
  1118. ? this.renderHScrollLayout(headerContent, allDayContent, timeGridContent, dayTableModel.colCnt, dayMinWidth, slatMetas, this.state.slatCoords)
  1119. : this.renderSimpleLayout(headerContent, allDayContent, timeGridContent);
  1120. }
  1121. }
  1122. function buildTimeColsModel(dateProfile, dateProfileGenerator) {
  1123. let daySeries = new internal$1.DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
  1124. return new internal$1.DayTableModel(daySeries, false);
  1125. }
  1126. const OPTION_REFINERS = {
  1127. allDaySlot: Boolean,
  1128. };
  1129. var css_248z = ".fc-v-event{background-color:var(--fc-event-bg-color);border:1px solid var(--fc-event-border-color);display:block}.fc-v-event .fc-event-main{color:var(--fc-event-text-color);height:100%}.fc-v-event .fc-event-main-frame{display:flex;flex-direction:column;height:100%}.fc-v-event .fc-event-time{flex-grow:0;flex-shrink:0;max-height:100%;overflow:hidden}.fc-v-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-height:0}.fc-v-event .fc-event-title{bottom:0;max-height:100%;overflow:hidden;top:0}.fc-v-event:not(.fc-event-start){border-top-left-radius:0;border-top-right-radius:0;border-top-width:0}.fc-v-event:not(.fc-event-end){border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom-width:0}.fc-v-event.fc-event-selected:before{left:-10px;right:-10px}.fc-v-event .fc-event-resizer-start{cursor:n-resize}.fc-v-event .fc-event-resizer-end{cursor:s-resize}.fc-v-event:not(.fc-event-selected) .fc-event-resizer{height:var(--fc-event-resizer-thickness);left:0;right:0}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-start{top:calc(var(--fc-event-resizer-thickness)/-2)}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-end{bottom:calc(var(--fc-event-resizer-thickness)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer{left:50%;margin-left:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer-start{top:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer-end{bottom:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc .fc-timegrid .fc-daygrid-body{z-index:2}.fc .fc-timegrid-divider{padding:0 0 2px}.fc .fc-timegrid-body{min-height:100%;position:relative;z-index:1}.fc .fc-timegrid-axis-chunk{position:relative}.fc .fc-timegrid-axis-chunk>table,.fc .fc-timegrid-slots{position:relative;z-index:1}.fc .fc-timegrid-slot{border-bottom:0;height:1.5em}.fc .fc-timegrid-slot:empty:before{content:\"\\00a0\"}.fc .fc-timegrid-slot-minor{border-top-style:dotted}.fc .fc-timegrid-slot-label-cushion{display:inline-block;white-space:nowrap}.fc .fc-timegrid-slot-label{vertical-align:middle}.fc .fc-timegrid-axis-cushion,.fc .fc-timegrid-slot-label-cushion{padding:0 4px}.fc .fc-timegrid-axis-frame-liquid{height:100%}.fc .fc-timegrid-axis-frame{align-items:center;display:flex;justify-content:flex-end;overflow:hidden}.fc .fc-timegrid-axis-cushion{flex-shrink:0;max-width:60px}.fc-direction-ltr .fc-timegrid-slot-label-frame{text-align:right}.fc-direction-rtl .fc-timegrid-slot-label-frame{text-align:left}.fc-liquid-hack .fc-timegrid-axis-frame-liquid{bottom:0;height:auto;left:0;position:absolute;right:0;top:0}.fc .fc-timegrid-col.fc-day-today{background-color:var(--fc-today-bg-color)}.fc .fc-timegrid-col-frame{min-height:100%;position:relative}.fc-media-screen.fc-liquid-hack .fc-timegrid-col-frame{bottom:0;height:auto;left:0;position:absolute;right:0;top:0}.fc-media-screen .fc-timegrid-cols{bottom:0;left:0;position:absolute;right:0;top:0}.fc-media-screen .fc-timegrid-cols>table{height:100%}.fc-media-screen .fc-timegrid-col-bg,.fc-media-screen .fc-timegrid-col-events,.fc-media-screen .fc-timegrid-now-indicator-container{left:0;position:absolute;right:0;top:0}.fc .fc-timegrid-col-bg{z-index:2}.fc .fc-timegrid-col-bg .fc-non-business{z-index:1}.fc .fc-timegrid-col-bg .fc-bg-event{z-index:2}.fc .fc-timegrid-col-bg .fc-highlight{z-index:3}.fc .fc-timegrid-bg-harness{left:0;position:absolute;right:0}.fc .fc-timegrid-col-events{z-index:3}.fc .fc-timegrid-now-indicator-container{bottom:0;overflow:hidden}.fc-direction-ltr .fc-timegrid-col-events{margin:0 2.5% 0 2px}.fc-direction-rtl .fc-timegrid-col-events{margin:0 2px 0 2.5%}.fc-timegrid-event-harness{position:absolute}.fc-timegrid-event-harness>.fc-timegrid-event{bottom:0;left:0;position:absolute;right:0;top:0}.fc-timegrid-event-harness-inset .fc-timegrid-event,.fc-timegrid-event.fc-event-mirror,.fc-timegrid-more-link{box-shadow:0 0 0 1px var(--fc-page-bg-color)}.fc-timegrid-event,.fc-timegrid-more-link{border-radius:3px;font-size:var(--fc-small-font-size)}.fc-timegrid-event{margin-bottom:1px}.fc-timegrid-event .fc-event-main{padding:1px 1px 0}.fc-timegrid-event .fc-event-time{font-size:var(--fc-small-font-size);margin-bottom:1px;white-space:nowrap}.fc-timegrid-event-short .fc-event-main-frame{flex-direction:row;overflow:hidden}.fc-timegrid-event-short .fc-event-time:after{content:\"\\00a0-\\00a0\"}.fc-timegrid-event-short .fc-event-title{font-size:var(--fc-small-font-size)}.fc-timegrid-more-link{background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;margin-bottom:1px;position:absolute;z-index:9999}.fc-timegrid-more-link-inner{padding:3px 2px;top:0}.fc-direction-ltr .fc-timegrid-more-link{right:0}.fc-direction-rtl .fc-timegrid-more-link{left:0}.fc .fc-timegrid-now-indicator-line{border-color:var(--fc-now-indicator-color);border-style:solid;border-width:1px 0 0;left:0;position:absolute;right:0;z-index:4}.fc .fc-timegrid-now-indicator-arrow{border-color:var(--fc-now-indicator-color);border-style:solid;margin-top:-5px;position:absolute;z-index:4}.fc-direction-ltr .fc-timegrid-now-indicator-arrow{border-bottom-color:transparent;border-top-color:transparent;border-width:5px 0 5px 6px;left:0}.fc-direction-rtl .fc-timegrid-now-indicator-arrow{border-bottom-color:transparent;border-top-color:transparent;border-width:5px 6px 5px 0;right:0}";
  1130. internal$1.injectStyles(css_248z);
  1131. var plugin = core.createPlugin({
  1132. name: '@fullcalendar/timegrid',
  1133. initialView: 'timeGridWeek',
  1134. optionRefiners: OPTION_REFINERS,
  1135. views: {
  1136. timeGrid: {
  1137. component: DayTimeColsView,
  1138. usesMinMaxTime: true,
  1139. allDaySlot: true,
  1140. slotDuration: '00:30:00',
  1141. slotEventOverlap: true, // a bad name. confused with overlap/constraint system
  1142. },
  1143. timeGridDay: {
  1144. type: 'timeGrid',
  1145. duration: { days: 1 },
  1146. },
  1147. timeGridWeek: {
  1148. type: 'timeGrid',
  1149. duration: { weeks: 1 },
  1150. },
  1151. },
  1152. });
  1153. var internal = {
  1154. __proto__: null,
  1155. TimeColsView: TimeColsView,
  1156. DayTimeColsView: DayTimeColsView,
  1157. buildTimeColsModel: buildTimeColsModel,
  1158. DayTimeCols: DayTimeCols,
  1159. buildDayRanges: buildDayRanges,
  1160. DayTimeColsSlicer: DayTimeColsSlicer,
  1161. TimeCols: TimeCols,
  1162. buildSlatMetas: buildSlatMetas,
  1163. TimeColsSlatsCoords: TimeColsSlatsCoords
  1164. };
  1165. core.globalPlugins.push(plugin);
  1166. exports.Internal = internal;
  1167. exports["default"] = plugin;
  1168. Object.defineProperty(exports, '__esModule', { value: true });
  1169. return exports;
  1170. })({}, FullCalendar, FullCalendar.Internal, FullCalendar.Preact, FullCalendar.DayGrid.Internal);