wix / react-native-calendars

React Native Calendar Components 🗓️ 📆
MIT License
9.5k stars 2.94k forks source link

Timeline Hour Block Height #2295

Open pradipjs opened 1 year ago

pradipjs commented 1 year ago

Description

It seems trial had been done already to make timeline hour block height dynamic but couldn't achieve expected results so that line is currently commented and replaced by fixed height "100". Can we make this parameterized at least so that we can pass it and accordingly height of timeline hour block will be set? Also, currently line half always show, we can make that as well conditional. i.e. just simple condition based on parameter.

This can be feature to the calendar timeline component.

pradipjs commented 1 year ago

Reference image

Bengejd commented 1 year ago

IDK if you're still looking for this functionality, but likely the devs won't respond, so here's a patch-package patch for it.

react-native-calendars+1.13000.0.patch

diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts b/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
index e6e2bff..ff963ed 100644
--- a/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
+++ b/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
@@ -28,6 +28,12 @@ export interface ExpandableCalendarProps extends CalendarListProps {
     closeThreshold?: number;
     /** Whether to close the calendar on day press. Default = true */
     closeOnDayPress?: boolean;
+
+    calendarConstants?: {
+        week_height?: number;
+        closed_height?: number;
+        day_names_padding?: number;
+    }
 }
 /**
  * @description: Expandable calendar component
diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.js b/node_modules/react-native-calendars/src/expandableCalendar/index.js
index 4fb12b0..23ff466 100644
--- a/node_modules/react-native-calendars/src/expandableCalendar/index.js
+++ b/node_modules/react-native-calendars/src/expandableCalendar/index.js
@@ -57,8 +57,17 @@ const ExpandableCalendar = (props) => {
     /** ExpandableCalendar props */
     initialPosition = Positions.CLOSED, onCalendarToggled, disablePan, hideKnob = numberOfDays && numberOfDays > 1, leftArrowImageSource = LEFT_ARROW, rightArrowImageSource = RIGHT_ARROW, allowShadow = true, disableWeekScroll, openThreshold = PAN_GESTURE_THRESHOLD, closeThreshold = PAN_GESTURE_THRESHOLD, closeOnDayPress = true, 
     /** CalendarList props */
-    horizontal = true, calendarStyle, theme, style: propsStyle, firstDay = 0, onDayPress, hideArrows, onPressArrowLeft, onPressArrowRight, renderArrow, testID, ...others } = props;
+    horizontal = true, calendarStyle, theme, style: propsStyle, firstDay = 0, onDayPress, hideArrows, onPressArrowLeft, onPressArrowRight, renderArrow,
+      calendarConstants,
+      testID, ...others } = props;
     const [screenReaderEnabled, setScreenReaderEnabled] = useState(false);
+
+    const {
+        week_height = WEEK_HEIGHT,
+        closed_height = CLOSED_HEIGHT,
+        day_names_padding = DAY_NAMES_PADDING,
+    } = calendarConstants;
+
     /** Date */
     const getYear = (date) => {
         const d = new XDate(date);
@@ -100,10 +109,10 @@ const ExpandableCalendar = (props) => {
         if (!horizontal) {
             return Math.max(constants.screenHeight, constants.screenWidth);
         }
-        return CLOSED_HEIGHT + (WEEK_HEIGHT * (numberOfWeeks.current - 1)) + (hideKnob ? 12 : KNOB_CONTAINER_HEIGHT) + (constants.isAndroid ? 3 : 0);
+        return closed_height + (week_height * (numberOfWeeks.current - 1)) + (hideKnob ? 12 : KNOB_CONTAINER_HEIGHT) + (constants.isAndroid ? 3 : 0);
     };
     const openHeight = useRef(getOpenHeight());
-    const closedHeight = useMemo(() => CLOSED_HEIGHT + (hideKnob || Number(numberOfDays) > 1 ? 0 : KNOB_CONTAINER_HEIGHT), [numberOfDays, hideKnob]);
+    const closedHeight = useMemo(() => closed_height + (hideKnob || Number(numberOfDays) > 1 ? 0 : KNOB_CONTAINER_HEIGHT), [numberOfDays, hideKnob]);
     const startHeight = useMemo(() => isOpen ? openHeight.current : closedHeight, [closedHeight, isOpen]);
     const _height = useRef(startHeight);
     const deltaY = useMemo(() => new Animated.Value(startHeight), [startHeight]);
@@ -144,8 +153,8 @@ const ExpandableCalendar = (props) => {
         return [
             style.current.weekDayNames,
             {
-                paddingLeft: isNumber(leftPaddings) ? leftPaddings + 6 : DAY_NAMES_PADDING,
-                paddingRight: isNumber(rightPaddings) ? rightPaddings + 6 : DAY_NAMES_PADDING
+                paddingLeft: isNumber(leftPaddings) ? leftPaddings + 6 : day_names_padding,
+                paddingRight: isNumber(rightPaddings) ? rightPaddings + 6 : day_names_padding
             }
         ];
     }, [calendarStyle]);
@@ -280,9 +289,10 @@ const ExpandableCalendar = (props) => {
                 speed: SPEED,
                 bounciness: BOUNCINESS,
                 useNativeDriver: false
-            }).start();
+            }).start(() => {
+                setPosition(() => _height.current === closedHeight ? Positions.CLOSED : Positions.OPEN);
+            });
             onCalendarToggled?.(_isOpen);
-            setPosition(() => _height.current === closedHeight ? Positions.CLOSED : Positions.OPEN);
             closeHeader(_isOpen);
             resetWeekCalendarOpacity(_isOpen);
         }
diff --git a/node_modules/react-native-calendars/src/infinite-list/index.js b/node_modules/react-native-calendars/src/infinite-list/index.js
index d98374b..b0b851b 100644
--- a/node_modules/react-native-calendars/src/infinite-list/index.js
+++ b/node_modules/react-native-calendars/src/infinite-list/index.js
@@ -86,7 +86,7 @@ const InfiniteList = (props, ref) => {
         };
     }, [onScrollBeginDrag, onMomentumScrollEnd, scrollViewProps, isHorizontal]);
     const _style = useMemo(() => {
-        return [{ height: pageHeight }, style];
+        return {...style, height: pageHeight};
     }, [pageHeight, style]);
     return (<RecyclerListView 
     // @ts-expect-error
diff --git a/node_modules/react-native-calendars/src/timeline/Timeline.d.ts b/node_modules/react-native-calendars/src/timeline/Timeline.d.ts
index 8821fd8..1b86b92 100644
--- a/node_modules/react-native-calendars/src/timeline/Timeline.d.ts
+++ b/node_modules/react-native-calendars/src/timeline/Timeline.d.ts
@@ -101,6 +101,14 @@ export interface TimelineProps {
      * The left inset of the timeline calendar (sidebar width), default is 72
      */
     timelineLeftInset?: number;
+    /**
+     * A replacement for the constant HOUR_BLOCK_HEIGHT variable, default is 100
+     */
+    hour_block_height?: number;
+    /**
+     * Controls whether half hour tick lines should show up or not
+     */
+    halfHourLines?: boolean;
 }
 export { Event as TimelineEventProps, PackedEvent as TimelinePackedEventProps };
 declare const _default: React.MemoExoticComponent<(props: TimelineProps) => React.JSX.Element>;
diff --git a/node_modules/react-native-calendars/src/timeline/Timeline.js b/node_modules/react-native-calendars/src/timeline/Timeline.js
index 12c7851..851ce1c 100644
--- a/node_modules/react-native-calendars/src/timeline/Timeline.js
+++ b/node_modules/react-native-calendars/src/timeline/Timeline.js
@@ -15,10 +15,14 @@ import EventBlock from './EventBlock';
 import NowIndicator from './NowIndicator';
 import useTimelineOffset from './useTimelineOffset';
 const Timeline = (props) => {
-    const { format24h = true, start = 0, end = 24, date = '', events, onEventPress, onBackgroundLongPress, onBackgroundLongPressOut, renderEvent, theme, scrollToFirst, scrollToNow, initialTime, showNowIndicator, scrollOffset, onChangeOffset, overlapEventsSpacing = 0, rightEdgeSpacing = 0, unavailableHours, unavailableHoursColor, eventTapped, numberOfDays = 1, timelineLeftInset = 0 } = props;
+    const {
+        hour_block_height = HOUR_BLOCK_HEIGHT,
+        halfHourLines = true,
+        format24h = true, start = 0, end = 24, date = '', events, onEventPress, onBackgroundLongPress, onBackgroundLongPressOut, renderEvent, theme, scrollToFirst, scrollToNow, initialTime, showNowIndicator, scrollOffset, onChangeOffset, overlapEventsSpacing = 0, rightEdgeSpacing = 0, unavailableHours, unavailableHoursColor, eventTapped, numberOfDays = 1, timelineLeftInset = 0 } = props;
     const pageDates = useMemo(() => {
         return typeof date === 'string' ? [date] : date;
     }, [date]);
+
     const groupedEvents = useMemo(() => {
         return groupBy(events, e => getCalendarDateString(e.start));
     }, [events]);
@@ -26,7 +30,7 @@ const Timeline = (props) => {
         return map(pageDates, d => groupedEvents[d] || []);
     }, [pageDates, groupedEvents]);
     const scrollView = useRef();
-    const calendarHeight = useRef((end - start) * HOUR_BLOCK_HEIGHT);
+    const calendarHeight = useRef((end - start) * hour_block_height);
     const styles = useRef(styleConstructor(theme || props.styles, calendarHeight.current));
     const { scrollEvents } = useTimelineOffset({ onChangeOffset, scrollOffset, scrollViewRef: scrollView });
     const width = useMemo(() => {
@@ -45,18 +49,18 @@ const Timeline = (props) => {
     useEffect(() => {
         let initialPosition = 0;
         if (scrollToNow) {
-            initialPosition = calcTimeOffset(HOUR_BLOCK_HEIGHT);
+            initialPosition = calcTimeOffset(hour_block_height);
         }
         else if (scrollToFirst && packedEvents[0].length > 0) {
             initialPosition = min(map(packedEvents[0], 'top')) ?? 0;
         }
         else if (initialTime) {
-            initialPosition = calcTimeOffset(HOUR_BLOCK_HEIGHT, initialTime.hour, initialTime.minutes);
+            initialPosition = calcTimeOffset(hour_block_height, initialTime.hour, initialTime.minutes);
         }
         if (initialPosition) {
             setTimeout(() => {
                 scrollView?.current?.scrollTo({
-                    y: Math.max(0, initialPosition - HOUR_BLOCK_HEIGHT),
+                    y: Math.max(0, initialPosition - hour_block_height),
                     animated: true
                 });
             }, 0);
@@ -92,7 +96,7 @@ const Timeline = (props) => {
     return (<ScrollView 
     // @ts-expect-error
     ref={scrollView} style={styles.current.container} contentContainerStyle={[styles.current.contentStyle, { width: constants.screenWidth }]} showsVerticalScrollIndicator={false} {...scrollEvents}>
-      <TimelineHours start={start} end={end} date={pageDates[0]} format24h={format24h} styles={styles.current} unavailableHours={unavailableHours} unavailableHoursColor={unavailableHoursColor} onBackgroundLongPress={onBackgroundLongPress} onBackgroundLongPressOut={onBackgroundLongPressOut} width={width} numberOfDays={numberOfDays} timelineLeftInset={timelineLeftInset}/>
+      <TimelineHours halfHourLines={halfHourLines} hour_block_height={hour_block_height} start={start} end={end} date={pageDates[0]} format24h={format24h} styles={styles.current} unavailableHours={unavailableHours} unavailableHoursColor={unavailableHoursColor} onBackgroundLongPress={onBackgroundLongPress} onBackgroundLongPressOut={onBackgroundLongPressOut} width={width} numberOfDays={numberOfDays} timelineLeftInset={timelineLeftInset}/>
       {times(numberOfDays, renderTimelineDay)}
     </ScrollView>);
 };
diff --git a/node_modules/react-native-calendars/src/timeline/TimelineHours.d.ts b/node_modules/react-native-calendars/src/timeline/TimelineHours.d.ts
index 4d50679..87fad7a 100644
--- a/node_modules/react-native-calendars/src/timeline/TimelineHours.d.ts
+++ b/node_modules/react-native-calendars/src/timeline/TimelineHours.d.ts
@@ -21,6 +21,8 @@ export interface TimelineHoursProps {
     width: number;
     numberOfDays: number;
     timelineLeftInset?: number;
+    hour_block_height?: number;
+    halfHourLines?: boolean;
 }
 declare const _default: React.MemoExoticComponent<(props: TimelineHoursProps) => React.JSX.Element>;
 export default _default;
diff --git a/node_modules/react-native-calendars/src/timeline/TimelineHours.js b/node_modules/react-native-calendars/src/timeline/TimelineHours.js
index 5ee9c11..e290641 100644
--- a/node_modules/react-native-calendars/src/timeline/TimelineHours.js
+++ b/node_modules/react-native-calendars/src/timeline/TimelineHours.js
@@ -8,10 +8,12 @@ import { buildUnavailableHoursBlocks, HOUR_BLOCK_HEIGHT } from './Packer';
 const dimensionWidth = constants.screenWidth;
 const EVENT_DIFF = 20;
 const TimelineHours = (props) => {
-    const { format24h, start = 0, end = 24, date, unavailableHours, unavailableHoursColor, styles, onBackgroundLongPress, onBackgroundLongPressOut, width, numberOfDays = 1, timelineLeftInset = 0 } = props;
+    const {
+        halfHourLines = true,
+        hour_block_height = HOUR_BLOCK_HEIGHT, format24h, start = 0, end = 24, date, unavailableHours, unavailableHoursColor, styles, onBackgroundLongPress, onBackgroundLongPressOut, width, numberOfDays = 1, timelineLeftInset = 0 } = props;
     const lastLongPressEventTime = useRef();
     // const offset = this.calendarHeight / (end - start);
-    const offset = HOUR_BLOCK_HEIGHT;
+    const offset = hour_block_height;
     const unavailableHoursBlocks = buildUnavailableHoursBlocks(unavailableHours, { dayStart: start, dayEnd: end });
     const hours = useMemo(() => {
         return range(start, end + 1).map(i => {
@@ -37,7 +39,7 @@ const TimelineHours = (props) => {
     const handleBackgroundPress = useCallback(event => {
         const yPosition = event.nativeEvent.locationY;
         const xPosition = event.nativeEvent.locationX;
-        const { hour, minutes } = calcTimeByPosition(yPosition, HOUR_BLOCK_HEIGHT);
+        const { hour, minutes } = calcTimeByPosition(yPosition, hour_block_height);
         const dateByPosition = calcDateByPosition(xPosition, timelineLeftInset, numberOfDays, date);
         lastLongPressEventTime.current = { hour, minutes, date: dateByPosition };
         const timeString = buildTimeString(hour, minutes, dateByPosition);
@@ -68,7 +70,7 @@ const TimelineHours = (props) => {
               {timeText}
             </Text>
             {time === start ? null : (<View key={`line${time}`} style={[styles.line, { top: offset * index, width: dimensionWidth - EVENT_DIFF, left: timelineLeftInset - 16 }]}/>)}
-            {<View key={`lineHalf${time}`} style={[styles.line, { top: offset * (index + 0.5), width: dimensionWidth - EVENT_DIFF, left: timelineLeftInset - 16 }]}/>}
+            { halfHourLines ? <View key={`lineHalf${time}`} style={[styles.line, { top: offset * (index + 0.5), width: dimensionWidth - EVENT_DIFF, left: timelineLeftInset - 16 }]}/> : null}
           </React.Fragment>);
         })}
       {times(numberOfDays, (index) => <View key={index} style={[styles.verticalLine, { right: (index + 1) * width / numberOfDays }]}/>)}

Once your patch has installed, simply use it like so:

Just pass the props to your Timeline component

        halfHourLines={false}
        hour_block_height={50}

For reference, I kept the naming convention the same to indicate that it's not a normal variable, it's a constant that we are messing with, hence the hour_block_height instead of calling it hourBlockHeight. I have no idea if this would have any side effects outside of the Timeline component, since that's the only place that I tested this in.

BToriola commented 5 months ago

How can I change the default white background color of the timeline to dark

borkopp commented 5 months ago

You can change the background in the timelineProps, adding theme and calendarBackground like in the example below

   <TimelineList
          events={EVENTS}
          timelineProps={{
            format24h: true,
            start: 8,
            end: 20,
            overlapEventsSpacing: 8,
            rightEdgeSpacing: 24,
            theme: {
              calendarBackground: "gray",
              }