From a618db32efba78510eafd960315370af4f3ec83e Mon Sep 17 00:00:00 2001 From: Andrey Los Date: Fri, 27 Sep 2019 18:26:20 +0200 Subject: [PATCH] [react-calendar-timeline] Updated types to version 0.24.6 (#38446) * [react-calendar-timeline] Updated types to version 0.24.6 - Added absent types - More test cases - removed old non existent now props * - few changes after review * - few changes after review * - test fix * - fixes to header * - fixes to labelFormat typings * - fixes to labelFormat typings * - fixes to typings --- types/react-calendar-timeline/index.d.ts | 469 +++++++++++++----- types/react-calendar-timeline/package.json | 6 + .../react-calendar-timeline-tests.tsx | 330 +++++++++++- types/react-calendar-timeline/tsconfig.json | 2 +- 4 files changed, 666 insertions(+), 141 deletions(-) create mode 100644 types/react-calendar-timeline/package.json diff --git a/types/react-calendar-timeline/index.d.ts b/types/react-calendar-timeline/index.d.ts index cf97e70f50..9591945b64 100644 --- a/types/react-calendar-timeline/index.d.ts +++ b/types/react-calendar-timeline/index.d.ts @@ -1,131 +1,366 @@ -// Type definitions for react-calendar-timeline v0.16.3 +// Type definitions for react-calendar-timeline v0.26.6 // Project: https://github.com/namespace-ee/react-calendar-timeline // Definitions by: Rajab Shakirov // Alex Maclean +// Andrii Los // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 +// TypeScript Version: 3.5 -/// +import * as React from 'react'; +import { Moment } from 'moment'; -declare module "react-calendar-timeline" { - - export interface TimelineGroup { - id: number; - title: React.ReactNode; - rightTitle?: React.ReactNode; - } +declare module 'react-calendar-timeline' { + export interface TimelineGroupBase { + id: number; + title: React.ReactNode; + rightTitle?: React.ReactNode; + } - export interface TimelineItem { - id: number; - group: number; - title?: React.ReactNode; - start_time: any; - end_time: any; - canMove?: boolean; - canResize?: boolean | "left" | "right" | "both"; - canChangeGroup?: boolean; - className?: string; - itemProps?: {}; - } + export interface TimelineItemBase { + id: number; + group: number; + title?: React.ReactNode; + start_time: DateType; + end_time: DateType; + canMove?: boolean; + canResize?: boolean | 'left' | 'right' | 'both'; + canChangeGroup?: boolean; + className?: string; + style?: React.CSSProperties; + itemProps?: React.HTMLAttributes; + } - export interface TimelineContext { - visibletimeStart: number, - visibleTimeEnd: number, - timelineWidth: number - } + export type TimelineItem = TimelineItemBase & CustomItemFields; + export type TimelineGroup = TimelineGroupBase & CustomGroupFields; - export interface TimelineTimeSteps { - second: number, - minute: number, - hour: number, - day: number, - month: number, - year: number - } + export interface TimelineContext { + timelineWidth: number; + visibleTimeStart: number; + visibleTimeEnd: number; + canvasTimeStart: number; + canvasTimeEnd: number; + } - export interface TimelineHeaderLabelFormat { - yearShort: string, - yearLong: string, - monthShort: string, - monthMedium: string, - monthMediumLong: string, - monthLong: string, - dayShort: string, - dayLong: string, - hourShort: string, - hourMedium: string, - hourMediumLong: string, - hourLong: string, - time: string - } + export interface ItemContext { + dimensions: { + collisionLeft: number; + collisionWidth: number; + height: number; + isDragging: boolean; + left: number; + order: { + group: { + id: string; + }; + index: number; + }; + originalLeft: number; + stack: boolean; + top: number | null; + width: number; + }; + useResizeHandle: boolean; + title: string; + canMove: boolean; + canResizeLeft: boolean; + canResizeRight: boolean; + selected: boolean; + dragging: boolean; + dragStart: { + x: number; + y: number; + }; + dragTime: number; + dragGroupDelta: number; + resizing: boolean; + resizeEdge: 'left' | 'right'; + resizeStart: number; + resizeTime: number; + width: boolean; + } - export interface ReactCalendarTimelineProps { - groups: TimelineGroup[]; - items: TimelineItem[]; - keys?:{ - groupIdKey: string; - groupTitleKey: string; - itemIdKey: string; - itemTitleKey: string; - itemGroupKey: string; - itemTimeStartKey: string; - itemTimeEndKey: string; - }; - defaultTimeStart?: any; - defaultTimeEnd?: any; - visibleTimeStart?: any; - visibleTimeEnd?: any; - selected?: number[]; - sidebarWidth?: number; - sidebarContent?: React.ReactNode; - rightSidebarWidth?: number; - rightSidebarContent?: React.ReactNode; - dragSnap?: number; - minResizeWidth?: number; - stickyOffset?: number; - stickyHeader?: boolean; - headerRef?: any; - lineHeight?: number; - headerLabelGroupHeight?: number; - headerLabelHeight?: number; - itemHeightRatio?: number; - minZoom?: number; - maxZoom?: number; - clickTolerance?: number; - canMove?: boolean; - canChangeGroup?: boolean; - canResize?: boolean | "left" | "right" | "both"; - useResizeHandle?: boolean; - showCursorLine?: boolean; - stackItems?: boolean; - traditionalZoom?: boolean; - itemTouchSendsClick?: boolean; - timeSteps?: TimelineTimeSteps, - onItemMove?(itemId: number, dragTime: number, newGroupOrder: number): any; - onItemResize?(itemId: number, newResizeEnd: number, edge: "left" | "right"): any; - onItemSelect?(itemId: number, e: any, time: number): any; - onItemClick?(itemId: number, e: any, time: number): any; - onItemDoubleClick?(itemId: number, e: any, time: number): any; - onCanvasClick?(groupId:number, time:number, e:any): any; - onCanvasDoubleClick?(group: TimelineGroup, time: number, e: any): any; - onCanvasContextMenu?(group: TimelineGroup, time: number, e: any): any; - onZoom?(timelineContext: TimelineContext): any; - moveResizeValidator?(action:"move" | "resize", itemId: number, time: number, resizeEdge: "left" | "right"): any; - headerLabelFormats?: TimelineHeaderLabelFormat, - subHeaderLabelFormats?: TimelineHeaderLabelFormat, - onTimeChange?(visibleTimeStart: number, visibleTimeEnd: number, updateScrollCanvas: (start: number, end: number) => void): any; - onTimeInit?(canvasTimeStart: number, canvasTimeEnd: number): any; - onBoundsChange?(canvasTimeStart: number, canvasTimeEnd: number): any; - itemRenderer?: (props: {item: TimelineItem, context: TimelineContext}) => React.ReactElement<{}>; - groupRenderer?: (props: {group: TimelineGroup, isRightSidebar: boolean}) => React.ReactElement<{}>; - minimumWidthForItemContentVisibility?: number; - children?: any; - } + export interface TimeFormat { + long: string; + mediumLong: string; + medium: string; + short: string; + } - export const defaultHeaderLabelFormats: TimelineHeaderLabelFormat; - export const defaultSubHeaderLabelFormats: TimelineHeaderLabelFormat; + export interface LabelFormat { + year: TimeFormat; + month: TimeFormat; + week: TimeFormat; + day: TimeFormat; + hour: TimeFormat; + minute: TimeFormat; + } - let ReactCalendarTimeline : React.ClassicComponentClass; - export default ReactCalendarTimeline; - + export interface ItemRendererGetItemPropsReturnType { + key: number; + ref: React.Ref; + className: string; + onMouseDown: React.MouseEventHandler; + onMouseUp: React.MouseEventHandler; + onTouchStart: React.TouchEventHandler; + onTouchEnd: React.TouchEventHandler; + onDoubleClick: React.MouseEventHandler; + onContextMenu: React.ReactEventHandler; + style: React.CSSProperties; + } + + export type GetItemsProps = Partial>; + + export interface ItemRendererGetResizePropsReturnType { + left?: { + key: number; + ref: React.Ref; + className: string; + }; + right?: { + key: number; + ref: React.Ref; + className: string; + }; + } + + export type GetResizeProps = { + leftStyle?: React.CSSProperties; + rightStyle?: React.CSSProperties; + leftClassName?: string; + rightClassName?: string; + }; + + export interface ReactCalendarItemRendererProps< + CustomItem extends TimelineItemBase = TimelineItemBase + > { + item: CustomItem; + itemContext: ItemContext; + getItemProps: ( + props: GetItemsProps, + ) => { + key: number; + ref: React.Ref; + className: string; + onMouseDown: React.MouseEventHandler; + onMouseUp: React.MouseEventHandler; + onTouchStart: React.TouchEventHandler; + onTouchEnd: React.TouchEventHandler; + onDoubleClick: React.MouseEventHandler; + onContextMenu: React.ReactEventHandler; + style: React.CSSProperties; + }; + getResizeProps: (propsOverride?: GetResizeProps) => ItemRendererGetResizePropsReturnType; + } + + export interface ReactCalendarGroupRendererProps { + group: CustomGroup; + isRightSidebar?: boolean; + } + + export interface OnItemDragObjectBase { + eventType: 'move' | 'resize'; + itemId: number; + time: number; + } + + export interface OnItemDragObjectMove extends OnItemDragObjectBase { + eventType: 'move'; + newGroupOrder: number; + } + + export interface OnItemDragObjectResize extends OnItemDragObjectBase { + eventType: 'resize'; + edge?: 'left' | 'right'; + } + + export interface TimelineKeys { + groupIdKey: string; + groupTitleKey: string; + groupRightTitleKey: string; + itemIdKey: string; + itemTitleKey: string; + itemDivTitleKey: string; + itemGroupKey: string; + itemTimeStartKey: string; + itemTimeEndKey: string; + } + + export interface ReactCalendarTimelineProps< + CustomItem extends TimelineItemBase = TimelineItemBase, + CustomGroup extends TimelineGroupBase = TimelineGroupBase + > { + groups: CustomGroup[]; + items: CustomItem[]; + keys?: TimelineKeys; + defaultTimeStart?: Date | Moment; + defaultTimeEnd?: Date | Moment; + visibleTimeStart?: Date | Moment; + visibleTimeEnd?: Date | Moment; + selected?: number[]; + sidebarWidth?: number; + sidebarContent?: React.ReactNode; + rightSidebarWidth?: number; + rightSidebarContent?: React.ReactNode; + dragSnap?: number; + minResizeWidth?: number; + lineHeight?: number; + itemHeightRatio?: number; + minZoom?: number; + maxZoom?: number; + clickTolerance?: number; + canMove?: boolean; + canChangeGroup?: boolean; + canResize?: false | true | 'left' | 'right' | 'both'; + useResizeHandle?: boolean; + stackItems?: boolean; + traditionalZoom?: boolean; + itemTouchSendsClick?: boolean; + timeSteps?: TimelineTimeSteps; + scrollRef?: React.Ref; + onItemDrag?(itemDragObject: OnItemDragObjectMove | OnItemDragObjectResize): void; + onItemMove?(itemId: number, dragTime: number, newGroupOrder: number): void; + onItemResize?(itemId: number, endTimeOrStartTime: number, edge: 'left' | 'right'): void; + onItemSelect?(itemId: number, e: any, time: number): void; + onItemDeselect?(e: React.SyntheticEvent): void; + onItemClick?(itemId: number, e: React.SyntheticEvent, time: number): void; + onItemDoubleClick?(itemId: number, e: React.SyntheticEvent, time: number): void; + onItemContextMenu?(itemId: number, e: React.SyntheticEvent, time: number): void; + onCanvasClick?(groupId: number, time: number, e: React.SyntheticEvent): void; + onCanvasDoubleClick?(group: CustomGroup, time: number, e: React.SyntheticEvent): void; + onCanvasContextMenu?(group: CustomGroup, time: number, e: React.SyntheticEvent): void; + onZoom?(timelineContext: TimelineContext): void; + moveResizeValidator?( + action: 'move' | 'resize', + itemId: number, + time: number, + resizeEdge: 'left' | 'right', + ): number; + onTimeChange?( + visibleTimeStart: number, + visibleTimeEnd: number, + updateScrollCanvas: (start: number, end: number) => void, + ): any; + onBoundsChange?(canvasTimeStart: number, canvasTimeEnd: number): any; + itemRenderer?: (props: ReactCalendarItemRendererProps) => React.ReactNode; + groupRenderer?: (props: ReactCalendarGroupRendererProps) => React.ReactNode; + resizeDetector?: (containerResizeDetector: any) => void; + verticalLineClassNamesForTime?: (start: number, end: number) => string[] | undefined; + horizontalLineClassNamesForGroup?: (group: CustomGroup) => string[]; + + // Fields that are in propTypes but not documented + headerRef?: React.Ref; + } + + export interface TimelineTimeSteps { + second: number; + minute: number; + hour: number; + day: number; + month: number; + year: number; + } + + export class TimelineMarkers extends React.Component {} + + export interface CustomMarkerChildrenProps { + styles: React.CSSProperties; + date: number; + } + export interface MarkerProps { + date: Date | number; + children?: (props: CustomMarkerChildrenProps) => React.ReactNode; + } + + export class CustomMarker extends React.Component {} + + export interface TodayMarkerProps extends MarkerProps { + interval?: number; + } + export class TodayMarker extends React.Component {} + + export type CursorMarkerProps = Omit; + export class CursorMarker extends React.Component {} + + export class TimelineHeaders extends React.Component> {} + + export interface TimelineHeaderProps { + style?: React.CSSProperties; + className?: string; + calendarHeaderStyle?: React.CSSProperties; + calendarHeaderClassName?: string; + headerRef?: React.Ref; + } + export class TimelineHeader extends React.Component {} + + export interface SidebarHeaderChildrenFnProps { + getRootProps: (propsToOverride?: { style: React.CSSProperties }) => { style: React.CSSProperties }; + data: Data; + } + + export interface SidebarHeaderProps { + variant?: 'left' | 'right'; + headerData?: Data; + children: (props: SidebarHeaderChildrenFnProps) => React.ReactNode; + } + export class SidebarHeader extends React.Component> {} + + export type Unit = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; + + export interface IntervalContext { + interval: { startTime: number; endTime: number; labelWidth: number; left: number }; + intervalText: string; + } + export interface GetIntervalProps { + interval?: Interval; + style?: React.CSSProperties; + onClick?: React.MouseEventHandler; + } + export interface IntervalRenderer { + intervalContext: IntervalContext; + getIntervalProps: (props?: GetIntervalProps) => Required & { key: string | number }; + data?: Data; + } + export interface DateHeaderProps { + style?: React.CSSProperties; + className?: string; + unit?: Unit | 'primaryHeader'; + labelFormat?: string | (([startTime, endTime]: [Moment, Moment], unit: Unit, labelWidth: number) => string); + intervalRenderer?: (props?: IntervalRenderer) => React.ReactNode; + headerData?: Data; + children?: (props: SidebarHeaderChildrenFnProps) => React.ReactNode; + height?: number; + } + export class DateHeader extends React.Component> {} + export interface Interval { + startTime: Moment; + endTime: Moment; + } + export interface HeaderContext { + intervals: { startTime: Moment; endTime: Moment }[]; + unit: string; + } + export interface CustomHeaderPropsChildrenFnProps { + timelineContext: TimelineContext; + headerContext: HeaderContext; + getIntervalProps: (props?: GetIntervalProps) => Required & { key: string | number }; + getRootProps: (propsToOverride?: { style: React.CSSProperties }) => { style: React.CSSProperties }; + showPeriod: (startDate: Moment | number, endDate: Moment | number) => void; + data: Data; + } + export interface CustomHeaderProps { + unit?: Unit; + headerData?: Data; + height?: number; + children: (props?: CustomHeaderPropsChildrenFnProps) => React.ReactNode; + } + export class CustomHeader extends React.Component> {} + + export const defaultKeys: TimelineKeys; + export const defaultTimeSteps: TimelineTimeSteps; + export const defaultHeaderFormats: LabelFormat; + + export default class ReactCalendarTimeline< + CustomItem extends TimelineItemBase = TimelineItemBase, + CustomGroup extends TimelineGroupBase = TimelineGroupBase + > extends React.Component> {} } diff --git a/types/react-calendar-timeline/package.json b/types/react-calendar-timeline/package.json new file mode 100644 index 0000000000..44a561e3d0 --- /dev/null +++ b/types/react-calendar-timeline/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "moment": "^2.0.0" + } +} diff --git a/types/react-calendar-timeline/react-calendar-timeline-tests.tsx b/types/react-calendar-timeline/react-calendar-timeline-tests.tsx index 0814bb3afe..3cb986fb35 100644 --- a/types/react-calendar-timeline/react-calendar-timeline-tests.tsx +++ b/types/react-calendar-timeline/react-calendar-timeline-tests.tsx @@ -1,30 +1,314 @@ -import * as React from "react"; -import ReactCalendarTimeline from 'react-calendar-timeline'; -// Don't want to add this as a dependency, because it is only used for tests. -declare const moment: any; +import * as React from 'react'; +import { useState } from 'react'; +import Timeline, { + TimelineGroupBase, + TimelineItemBase, + TimelineItem, + TimelineGroup, + TimelineHeaders, + SidebarHeader, + DateHeader, + CustomHeader, +} from 'react-calendar-timeline'; +import * as moment from 'moment'; +import { Moment } from 'moment'; -const groups = [ - {id: 1, title: 'group 1'}, - {id: 2, title: 'group 2'} -] +const groups1 = [{ id: 1, title: 'group 1' }, { id: 2, title: 'group 2' }] as TimelineGroupBase[]; -const items = [ - {id: 1, group: 1, title: 'item 1', start_time: moment(), end_time: moment().add(1, 'hour')}, - {id: 2, group: 2, title: 'item 2', start_time: moment().add(-0.5, 'hour'), end_time: moment().add(0.5, 'hour')}, - {id: 3, group: 1, title: 'item 3', start_time: moment().add(2, 'hour'), end_time: moment().add(3, 'hour')} -] +const items1 = [ + { id: 1, group: 1, title: 'item 1', start_time: moment(), end_time: moment().add(1, 'hour') }, + { id: 2, group: 2, title: 'item 2', start_time: moment().add(-0.5, 'hour'), end_time: moment().add(0.5, 'hour') }, + { id: 3, group: 1, title: 'item 3', start_time: moment().add(2, 'hour'), end_time: moment().add(3, 'hour') }, +] as TimelineItemBase[]; class ExampleOfUsingReactCalendarTimeline extends React.Component { - render(){ - return( -
+ render() { + return ( +
Rendered by react! - + > + groups={groups1} + items={items1} + defaultTimeStart={moment().add(-12, 'hour')} + defaultTimeEnd={moment().add(12, 'hour')} + />
- ); + ); } -}; \ No newline at end of file +} + +type TimelineGroupCustom = TimelineGroup<{ data: string }>; +type TimelineItemCustom = TimelineItem<{ data: string }, Moment>; + +const groups2 = [ + { id: 1, title: 'group 1', data: '1' }, + { id: 2, title: 'group 2', data: '1' }, +] as TimelineGroupCustom[]; + +const items2 = [ + { id: 1, group: 1, title: 'item 1', start_time: moment(), end_time: moment().add(1, 'hour'), data: '1' }, + { + id: 2, + group: 2, + title: 'item 2', + start_time: moment().add(-0.5, 'hour'), + end_time: moment().add(0.5, 'hour'), + data: '1', + }, + { + id: 3, + group: 1, + title: 'item 3', + start_time: moment().add(2, 'hour'), + end_time: moment().add(3, 'hour'), + data: '1', + }, +] as TimelineItemCustom[]; + +class ExampleOfUsingReactCalendarTimelineWithCustomGroupAndItemExtension extends React.Component { + render() { + return ( +
+ Rendered by react! + + groups={groups2} + items={items2} + defaultTimeStart={moment().add(-12, 'hour')} + defaultTimeEnd={moment().add(12, 'hour')} + /> +
+ ); + } +} + +const Example: React.FC = () => ( + + + + {({ getRootProps }) => { + return
Left
; + }} +
+ + + + {({ headerContext: { intervals }, getRootProps, getIntervalProps, showPeriod, data }) => { + return ( +
+ {intervals.map(interval => { + const intervalStyle = { + lineHeight: '30px', + textAlign: 'center', + borderLeft: '1px solid black', + cursor: 'pointer', + backgroundColor: 'Turquoise', + color: 'white', + } as React.CSSProperties; + return ( +
{ + showPeriod(interval.startTime, interval.endTime); + }} + {...getIntervalProps({ + interval, + style: intervalStyle, + })} + > +
{interval.startTime.format('YYYY')}
+
+ ); + })} +
+ ); + }} +
+
+
+); + +const groups = [{ id: 1, title: 'group 1' }, { id: 2, title: 'group 2' }] as TimelineGroupBase[]; + +const items = [ + { id: 1, group: 1, title: 'item 1', start_time: 1, end_time: 1 }, + { id: 2, group: 2, title: 'item 2', start_time: 1, end_time: 1 }, + { id: 3, group: 1, title: 'item 3', start_time: 1, end_time: 1 }, +] as TimelineItemBase[]; + +const defaultTimeStart = moment() + .startOf('day') + .toDate(); +const defaultTimeEnd = moment() + .startOf('day') + .add(1, 'day') + .toDate(); + +const Resize = () => { + const [itemsState, setItems] = useState(items); + + return ( + { + const group = groups[newGroupOrder]; + + setItems( + itemsState.map(item => + item.id === itemId + ? { + ...item, + ...{ + start_time: dragTime, + end_time: dragTime + (item.end_time - item.start_time), + group: group.id, + }, + } + : item, + ), + ); + + console.log('Moved', itemId, dragTime, newGroupOrder); + }} + onItemResize={(itemId, time, edge) => { + setItems( + itemsState.map(item => + item.id === itemId + ? { + ...item, + ...{ + start_time: edge === 'left' ? time : item.start_time, + end_time: edge === 'left' ? item.end_time : time, + }, + } + : item, + ), + ); + + console.log('Resized', itemId, time, edge); + }} + /> + ); +}; + +const TimelineDragTest = () => { + const [itemsState, setItems] = useState(items); + const [draggedItem, setDraggedItem] = useState< + { item: TimelineItemBase; group: TimelineGroupBase; time: number } | undefined + >(undefined); + + return ( + + { + const group = groups[newGroupOrder]; + + setItems( + itemsState.map(item => + item.id === itemId + ? { + ...item, + ...{ + start_time: dragTime, + end_time: dragTime + (item.end_time - item.start_time), + group: group.id, + }, + } + : item, + ), + ); + setDraggedItem(undefined); + console.log('Moved', itemId, dragTime, newGroupOrder); + }} + onItemResize={(itemId, time, edge) => { + setItems( + itemsState.map(item => + item.id === itemId + ? { + ...item, + ...{ + start_time: edge === 'left' ? time : item.start_time, + end_time: edge === 'left' ? item.end_time : time, + }, + } + : item, + ), + ); + setDraggedItem(undefined); + + console.log('Resized', itemId, time, edge); + }} + onItemDrag={itemDragObject => { + if (itemDragObject.eventType === 'move') { + const { itemId, newGroupOrder, time } = itemDragObject; + let item = draggedItem ? draggedItem.item : undefined; + if (!item) { + item = itemsState.find(i => i.id === itemId); + } + setDraggedItem({ item: item, group: groups[newGroupOrder], time }); + } + }} + /> + {draggedItem && ( +
+ {`${moment(draggedItem.time).format('LLL')}, ${draggedItem.group ? draggedItem.group.title : ''}`} +
+ )} +
+ ); +}; + +const Basic: React.FC = () => { + return ( + Above The Left
} + itemTouchSendsClick={false} + stackItems + itemHeightRatio={0.75} + canMove={false} + canResize={false} + defaultTimeStart={defaultTimeStart} + defaultTimeEnd={defaultTimeEnd} + > + + + {({ getRootProps }) => { + return
Left
; + }} +
+ + +
+ + ); +}; diff --git a/types/react-calendar-timeline/tsconfig.json b/types/react-calendar-timeline/tsconfig.json index 0338517fd1..6c69066da1 100644 --- a/types/react-calendar-timeline/tsconfig.json +++ b/types/react-calendar-timeline/tsconfig.json @@ -22,4 +22,4 @@ "index.d.ts", "react-calendar-timeline-tests.tsx" ] -} \ No newline at end of file +}