internal.cjs 62 KB

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