Преглед изворни кода

morelinks as separate components

Adam Shaw пре 4 година
родитељ
комит
8876efc8c6

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit 637300c09c76d561d83552ba9af482de213ff72c
+Subproject commit 8b7794f44fbe872dd7e63ca33439f7bcb14e9014

+ 132 - 36
packages/common/src/common/MoreLinkRoot.tsx

@@ -1,12 +1,24 @@
-import { ComponentChildren, createElement } from '../vdom'
+import { ComponentChildren, createElement, Ref, RefObject } from '../vdom'
+import { BaseComponent } from '../vdom-util'
 import { ViewApi } from '../ViewApi'
 import { ViewContext, ViewContextType } from '../ViewContext'
-import { MountArg, RenderHook, RenderHookPropsChildren } from './render-hook'
+import { EventSegment } from './more-link'
+import { MountArg, RenderHook } from './render-hook'
+
+export type MoreLinkChildren = (
+  rootElRef: Ref<any>,
+  classNames: string[],
+  innerElRef: Ref<any>,
+  innerContent: ComponentChildren,
+  handleClick: () => void,
+) => ComponentChildren
 
 export interface MoreLinkRootProps { // what the MoreLinkRoot component receives
-  moreCnt: number
+  allSegs: EventSegment[]
+  hiddenSegs: EventSegment[]
+  positionElRef: RefObject<HTMLElement>
   defaultContent?: (hookProps: MoreLinkContentArg) => ComponentChildren // not used by anyone yet
-  children: RenderHookPropsChildren
+  children: MoreLinkChildren
 }
 
 export interface MoreLinkContentArg { // what the render-hooks receive
@@ -18,40 +30,124 @@ export interface MoreLinkContentArg { // what the render-hooks receive
 
 export type MoreLinkMountArg = MountArg<MoreLinkContentArg>
 
-export const MoreLinkRoot = (props: MoreLinkRootProps) => (
-  <ViewContextType.Consumer>
-    {(context: ViewContext) => {
-      let { viewApi, options, calendarApi } = context
-      let { moreLinkText } = options
-      let { moreCnt } = props
-
-      let hookProps: MoreLinkContentArg = {
-        num: moreCnt,
-        shortText: `+${moreCnt}`, // TODO: offer hook or i18n?
-        text: typeof moreLinkText === 'function'
-          ? moreLinkText.call(calendarApi, moreCnt)
-          : `+${moreCnt} ${moreLinkText}`,
-        view: viewApi,
-      }
+export class MoreLinkRoot extends BaseComponent<MoreLinkRootProps> {
+  render(props: MoreLinkRootProps) {
+    return (
+      <ViewContextType.Consumer>
+        {(context: ViewContext) => {
+          let { viewApi, options, calendarApi } = context
+          let { moreLinkText } = options
+          let { hiddenSegs } = props
+          let moreCnt = hiddenSegs.length
+
+          let hookProps: MoreLinkContentArg = {
+            num: moreCnt,
+            shortText: `+${moreCnt}`, // TODO: offer hook or i18n?
+            text: typeof moreLinkText === 'function'
+              ? moreLinkText.call(calendarApi, moreCnt)
+              : `+${moreCnt} ${moreLinkText}`,
+            view: viewApi,
+          }
 
-      return (
-        <RenderHook<MoreLinkContentArg>
-          hookProps={hookProps}
-          classNames={options.moreLinkClassNames}
-          content={options.moreLinkContent}
-          defaultContent={props.defaultContent || renderMoreLinkInner}
-          didMount={options.moreLinkDidMount}
-          willUnmount={options.moreLinkWillUnmount}
-        >
-          {(rootElRef, customClassNames, innerElRef, innerContent) => props.children(
-            rootElRef, ['fc-event-more'].concat(customClassNames), innerElRef, innerContent,
-          )}
-        </RenderHook>
-      )
-    }}
-  </ViewContextType.Consumer>
-)
+          return (
+            <RenderHook<MoreLinkContentArg>
+              hookProps={hookProps}
+              classNames={options.moreLinkClassNames}
+              content={options.moreLinkContent}
+              defaultContent={props.defaultContent || renderMoreLinkInner}
+              didMount={options.moreLinkDidMount}
+              willUnmount={options.moreLinkWillUnmount}
+            >
+              {(rootElRef, customClassNames, innerElRef, innerContent) => props.children(
+                rootElRef, ['fc-event-more'].concat(customClassNames), innerElRef, innerContent, this.handleClick
+              )}
+            </RenderHook>
+          )
+        }}
+      </ViewContextType.Consumer>
+    )
+  }
+
+  handleClick = () => {
+    console.log('handle click', this.props.hiddenSegs, this.props.positionElRef.current)
+  }
+}
 
 function renderMoreLinkInner(props: MoreLinkContentArg) {
   return props.text
 }
+
+/*
+let { props, context } = this
+let { options, dateEnv } = context
+let { moreLinkClick } = options
+let allSegs: EventSegment[] = []
+let hiddenSegs: EventSegment[] = []
+
+function segForPublic(seg: TableSeg) {
+  let { def, instance, range } = seg.eventRange
+  return {
+    event: new EventApi(context, def, instance),
+    start: dateEnv.toDate(range.start),
+    end: dateEnv.toDate(range.end),
+    isStart: seg.isStart,
+    isEnd: seg.isEnd,
+  }
+}
+
+for (let placement of props.singlePlacements) {
+  let publicSeg = segForPublic(placement.seg)
+  allSegs.push(publicSeg)
+  if (!placement.isVisible) {
+    hiddenSegs.push(publicSeg)
+  }
+}
+
+if (typeof moreLinkClick === 'function') {
+  moreLinkClick = moreLinkClick({
+    date: context.dateEnv.toDate(props.date),
+    allDay: true,
+    allSegs,
+    hiddenSegs,
+    jsEvent: ev,
+    view: context.viewApi,
+  }) as string | undefined
+}
+
+if (!moreLinkClick || moreLinkClick === 'popover') {
+  console.log('TODO: open popover', allSegs, this.rootEl)
+
+  (!props.forPrint && (
+    <MorePopover
+      ref={this.morePopoverRef}
+      date={morePopoverState.date}
+      dateProfile={dateProfile}
+      segs={morePopoverState.allSegs}
+      alignmentEl={morePopoverState.dayEl}
+      topAlignmentEl={rowCnt === 1 ? props.headerAlignElRef.current : null}
+      selectedInstanceId={props.eventSelection}
+      hiddenInstances={// yuck
+        (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
+        (props.eventResize ? props.eventResize.affectedInstances : null) ||
+        {}
+      }
+      todayRange={todayRange}
+    />
+  )
+
+  let morePopoverHit = morePopover ? morePopover.positionToHit(leftPosition, topPosition, this.rootEl) : null
+  let { morePopoverState } = this.state
+  if (morePopoverHit) {
+    return {
+      row: morePopoverState.fromRow,
+      col: morePopoverState.fromCol,
+      ...morePopoverHit,
+    }
+  }
+
+} else if (typeof moreLinkClick === 'string') { // a view name
+  context.calendarApi.zoomTo(props.date, moreLinkClick)
+}
+*/
+
+// TODO: address ticket where event refreshing closes popover

+ 20 - 0
packages/common/src/common/more-link.ts

@@ -1,6 +1,8 @@
 import { EventApi } from '../api/EventApi'
+import { EventRenderRange } from '../component/event-rendering'
 import { VUIEvent } from '../vdom'
 import { ViewApi } from '../ViewApi'
+import { ViewContext } from '../ViewContext'
 
 export interface EventSegment {
   event: EventApi
@@ -23,3 +25,21 @@ export interface MoreLinkArg {
 }
 
 export type MoreLinkHandler = (arg: MoreLinkArg) => MoreLinkSimpleAction | void
+
+export function buildPublicSeg(
+  seg: {
+    eventRange?: EventRenderRange, // needed because Seg's prop is optional. why?
+    isStart: boolean,
+    isEnd: boolean
+  },
+  context: ViewContext
+) {
+  let { def, instance, range } = seg.eventRange
+  return {
+    event: new EventApi(context, def, instance),
+    start: context.dateEnv.toDate(range.start),
+    end: context.dateEnv.toDate(range.end),
+    isStart: seg.isStart,
+    isEnd: seg.isEnd,
+  }
+}

+ 2 - 1
packages/common/src/main.ts

@@ -264,7 +264,8 @@ export { DayCellContent, DayCellContentProps } from './common/DayCellContent'
 export { EventRoot, MinimalEventProps } from './common/EventRoot'
 export { renderFill, BgEvent, BgEventProps } from './common/bg-fill'
 export { WeekNumberRoot, WeekNumberRootProps } from './common/WeekNumberRoot'
-export { MoreLinkRoot, MoreLinkContentArg, MoreLinkMountArg } from './common/MoreLinkRoot'
+export { buildPublicSeg } from './common/more-link'
+export { MoreLinkRoot, MoreLinkRootProps, MoreLinkContentArg, MoreLinkMountArg } from './common/MoreLinkRoot'
 
 export { ViewRoot, ViewRootProps } from './common/ViewRoot'
 export { triggerDateSelect, DatePointTransform, DateSpanTransform, DateSelectionApi, getDefaultEventEnd } from './calendar-utils'

+ 11 - 100
packages/daygrid/src/TableCell.tsx

@@ -10,16 +10,13 @@ import {
   WeekNumberRoot,
   DayCellRoot,
   DateProfile,
-  VUIEvent,
   setRef,
   createFormatter,
   Dictionary,
-  MoreLinkRoot,
-  EventApi,
-  EventSegment,
+  createRef,
 } from '@fullcalendar/common'
-import { TableSeg } from './TableSeg'
 import { TableCellTop } from './TableCellTop'
+import { TableCellMoreLink } from './TableCellMoreLink'
 import { TableSegPlacement } from './event-placement'
 
 export interface TableCellProps {
@@ -34,7 +31,6 @@ export interface TableCellProps {
   fgContentElRef?: Ref<HTMLDivElement> // TODO: rename!!! classname confusion. is the "event" div
   fgContent: ComponentChildren
   fgPaddingBottom: CssDimValue
-  moreCnt: number
   moreMarginTop: number
   showDayNumber: boolean
   showWeekNumber: boolean
@@ -54,11 +50,11 @@ export interface TableCellModel { // TODO: move somewhere else. combine with Day
 const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' })
 
 export class TableCell extends DateComponent<TableCellProps> {
-  private rootEl: HTMLElement
+  private rootElRef = createRef<HTMLElement>()
 
   render() {
-    let { options } = this.context
-    let { props } = this
+    let { props, context, rootElRef } = this
+    let { options } = context
     let { date, dateProfile } = props
     let navLinkAttrs = options.navLinks
       ? { 'data-navlink': buildNavLinkData(date, 'week'), tabIndex: 0 }
@@ -110,21 +106,11 @@ export class TableCell extends DateComponent<TableCellProps> {
                 style={{ paddingBottom: props.fgPaddingBottom }}
               >
                 {props.fgContent}
-                {Boolean(props.moreCnt) && (
-                  <div className="fc-daygrid-day-bottom" style={{ marginTop: props.moreMarginTop }}>
-                    <MoreLinkRoot moreCnt={props.moreCnt}>
-                      {(rootElRef, classNames, innerElRef, innerContent) => (
-                        <a
-                          ref={rootElRef}
-                          className={['fc-daygrid-more-link'].concat(classNames).join(' ')}
-                          onClick={this.handleMoreLinkClick}
-                        >
-                          {innerContent}
-                        </a>
-                      )}
-                    </MoreLinkRoot>
-                  </div>
-                )}
+                <TableCellMoreLink
+                  singlePlacements={props.singlePlacements}
+                  marginTop={props.moreMarginTop}
+                  positionElRef={rootElRef}
+                />
               </div>
               <div className="fc-daygrid-day-bg">
                 {props.bgContent}
@@ -137,82 +123,7 @@ export class TableCell extends DateComponent<TableCellProps> {
   }
 
   handleRootEl = (el: HTMLElement) => {
-    this.rootEl = el
+    setRef(this.rootElRef, el)
     setRef(this.props.elRef, el)
   }
-
-  handleMoreLinkClick = (ev: VUIEvent) => {
-    let { props, context } = this
-    let { options, dateEnv } = context
-    let { moreLinkClick } = options
-    let allSegs: EventSegment[] = []
-    let hiddenSegs: EventSegment[] = []
-
-    function segForPublic(seg: TableSeg) {
-      let { def, instance, range } = seg.eventRange
-      return {
-        event: new EventApi(context, def, instance),
-        start: dateEnv.toDate(range.start),
-        end: dateEnv.toDate(range.end),
-        isStart: seg.isStart,
-        isEnd: seg.isEnd,
-      }
-    }
-
-    for (let placement of props.singlePlacements) {
-      let publicSeg = segForPublic(placement.seg)
-      allSegs.push(publicSeg)
-      if (!placement.isVisible) {
-        hiddenSegs.push(publicSeg)
-      }
-    }
-
-    if (typeof moreLinkClick === 'function') {
-      moreLinkClick = moreLinkClick({
-        date: context.dateEnv.toDate(props.date),
-        allDay: true,
-        allSegs,
-        hiddenSegs,
-        jsEvent: ev,
-        view: context.viewApi,
-      }) as string | undefined
-    }
-
-    if (!moreLinkClick || moreLinkClick === 'popover') {
-      console.log('TODO: open popover', allSegs, this.rootEl)
-      /*
-      (!props.forPrint && (
-        <MorePopover
-          ref={this.morePopoverRef}
-          date={morePopoverState.date}
-          dateProfile={dateProfile}
-          segs={morePopoverState.allSegs}
-          alignmentEl={morePopoverState.dayEl}
-          topAlignmentEl={rowCnt === 1 ? props.headerAlignElRef.current : null}
-          selectedInstanceId={props.eventSelection}
-          hiddenInstances={// yuck
-            (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
-            (props.eventResize ? props.eventResize.affectedInstances : null) ||
-            {}
-          }
-          todayRange={todayRange}
-        />
-      )
-
-      let morePopoverHit = morePopover ? morePopover.positionToHit(leftPosition, topPosition, this.rootEl) : null
-      let { morePopoverState } = this.state
-      if (morePopoverHit) {
-        return {
-          row: morePopoverState.fromRow,
-          col: morePopoverState.fromCol,
-          ...morePopoverHit,
-        }
-      }
-
-      TODO: address ticket where event refreshing closes popover
-      */
-    } else if (typeof moreLinkClick === 'string') { // a view name
-      context.calendarApi.zoomTo(props.date, moreLinkClick)
-    }
-  }
 }

+ 1 - 2
packages/daygrid/src/TableRow.tsx

@@ -75,7 +75,7 @@ export class TableRow extends DateComponent<TableRowProps, TableRowState> {
     let highlightSegsByCol = splitSegsByFirstCol(this.getHighlightSegs(), colCnt)
     let mirrorSegsByCol = splitSegsByFirstCol(this.getMirrorSegs(), colCnt)
 
-    let { singleColPlacements, multiColPlacements, moreCnts, moreMarginTops, cellPaddingBottoms } = computeFgSegPlacement(
+    let { singleColPlacements, multiColPlacements, moreMarginTops, cellPaddingBottoms } = computeFgSegPlacement(
       sortEventSegs(props.fgEventSegs, context.options.eventOrder) as TableSeg[],
       props.dayMaxEvents,
       props.dayMaxEventRows,
@@ -124,7 +124,6 @@ export class TableRow extends DateComponent<TableRowProps, TableRowState> {
               extraHookProps={cell.extraHookProps}
               extraDataAttrs={cell.extraDataAttrs}
               extraClassNames={cell.extraClassNames}
-              moreCnt={moreCnts[col]}
               moreMarginTop={moreMarginTops[col]}
               singlePlacements={singleColPlacements[col]}
               fgPaddingBottom={cellPaddingBottoms[col]}

+ 1 - 1
packages/daygrid/src/event-placement.ts

@@ -128,7 +128,7 @@ export function computeFgSegPlacement(
     }
   }
 
-  return { singleColPlacements, multiColPlacements, moreCnts, moreMarginTops, cellPaddingBottoms }
+  return { singleColPlacements, multiColPlacements, moreMarginTops, cellPaddingBottoms }
 }
 
 // rects ordered by top coord, then left

+ 7 - 23
packages/timegrid/src/TimeCol.tsx

@@ -2,8 +2,8 @@ import {
   Ref, DateMarker, BaseComponent, createElement, EventSegUiInteractionState, Seg, getSegMeta,
   DateRange, Fragment, DayCellRoot, NowIndicatorRoot, BgEvent, renderFill,
   DateProfile, config, buildEventRangeKey, sortEventSegs, SegInput, memoize, SegEntryGroup,
-  MoreLinkRoot, MoreLinkContentArg,
 } from '@fullcalendar/common'
+import { TimeColMoreLink } from './TimeColMoreLink'
 import { TimeColsSeg } from './TimeColsSeg'
 import { TimeColsSlatsCoords } from './TimeColsSlatsCoords'
 import { computeFgSegPlacements, TimeColSegRect } from './event-placement'
@@ -205,28 +205,16 @@ export class TimeCol extends BaseComponent<TimeColProps> {
       let positionCss = this.computeSegTopBottomCss(hiddenGroup)
 
       return (
-        <MoreLinkRoot moreCnt={hiddenGroup.entries.length} defaultContent={renderMoreLinkInner}>
-          {(rootElRef, classNames, innerElRef, innerContent) => (
-            <a
-              ref={rootElRef}
-              className={['fc-timegrid-event-more'].concat(classNames).join(' ')}
-              style={positionCss}
-              onClick={this.handleMoreLinkClick}
-            >
-              <div ref={innerElRef} className="fc-timegrid-event-more-inner fc-sticky">
-                {innerContent}
-              </div>
-            </a>
-          )}
-        </MoreLinkRoot>
+        <TimeColMoreLink
+          segEntries={hiddenGroup.entries}
+          segs={segs}
+          top={positionCss.top}
+          bottom={positionCss.bottom}
+        />
       )
     })
   }
 
-  handleMoreLinkClick = () => {
-    console.log('more click')
-  }
-
   buildSegInputs(segs: TimeColsSeg[]): SegInput[] {
     let { date, slatCoords } = this.props
     let { eventMinHeight } = this.context.options
@@ -338,7 +326,3 @@ export class TimeCol extends BaseComponent<TimeColProps> {
     return props
   }
 }
-
-function renderMoreLinkInner(props: MoreLinkContentArg) {
-  return props.shortText
-}