@trackunit/react-date-and-time-components 1.3.46 → 1.3.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -28,7 +28,8 @@ var defaultTranslations = {
28
28
  "shared.timePeriods.months_plural": "{{count}} months",
29
29
  "shared.timePeriods.today": "Today",
30
30
  "shared.timePeriods.weeks": "{{count}} week",
31
- "shared.timePeriods.weeks_plural": "{{count}} weeks"
31
+ "shared.timePeriods.weeks_plural": "{{count}} weeks",
32
+ "timeline.loadMore": "Click for more..."
32
33
  };
33
34
 
34
35
  /** The translation namespace for this library */
@@ -371,6 +372,8 @@ const cvaTimelineElementTime = cssClassVarianceUtilities.cvaMerge([
371
372
  },
372
373
  });
373
374
 
375
+ // Constants for pagination
376
+ const ITEMS_PER_PAGE = 20;
374
377
  /**
375
378
  * The Timeline component offers a visual representation of events or milestones in chronological order, helping users easily follow the progression of activities, tasks, or data points over time.
376
379
  *
@@ -380,19 +383,49 @@ const cvaTimelineElementTime = cssClassVarianceUtilities.cvaMerge([
380
383
  const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, toggleButton, }) => {
381
384
  const ref = react.useRef(null);
382
385
  const [isOpen, setIsOpen] = react.useState(false);
386
+ const [visibleCount, setVisibleCount] = react.useState(ITEMS_PER_PAGE);
387
+ const [isLoadingMore, setIsLoadingMore] = react.useState(false);
383
388
  const { formatDate, formatRange } = reactDateAndTimeHooks.useDateAndTime();
384
389
  const locale = reactDateAndTimeHooks.useLocale();
385
390
  const hourCycle = dateAndTimeUtils.getHourCycle(locale);
391
+ const { t } = useTranslation();
386
392
  const childrenArray = react.useMemo(() => react.Children.toArray(children), [children]);
387
393
  const [firstChild, ...remainingChildren] = childrenArray;
388
394
  const childProps = react.isValidElement(firstChild) ? firstChild.props : {};
389
395
  const lastChild = remainingChildren.pop();
390
396
  const middleChildren = remainingChildren;
397
+ // Slice middleChildren to only render the visible count
398
+ const visibleMiddleChildren = react.useMemo(() => middleChildren.slice(0, visibleCount), [middleChildren, visibleCount]);
391
399
  react.useEffect(() => {
392
400
  // Set maxHeight dynamically for smooth animation
393
401
  if (ref.current) {
394
402
  ref.current.style.maxHeight = isOpen ? `${ref.current.scrollHeight}px` : "0px";
395
403
  }
404
+ }, [isOpen, visibleCount]);
405
+ const loadMoreItems = react.useCallback(() => {
406
+ if (isLoadingMore || visibleCount >= middleChildren.length) {
407
+ return;
408
+ }
409
+ setIsLoadingMore(true);
410
+ // Use requestAnimationFrame to avoid layout thrashing
411
+ requestAnimationFrame(() => {
412
+ const scrollPos = ref.current?.scrollTop || 0;
413
+ setVisibleCount(prev => Math.min(prev + ITEMS_PER_PAGE, middleChildren.length));
414
+ // After state update, preserve scroll position
415
+ setTimeout(() => {
416
+ if (ref.current) {
417
+ ref.current.scrollTop = scrollPos;
418
+ }
419
+ setIsLoadingMore(false);
420
+ }, 50); // Small delay to ensure DOM has updated
421
+ });
422
+ }, [isLoadingMore, middleChildren.length, visibleCount]);
423
+ // Reset visible count when toggling open/closed
424
+ react.useEffect(() => {
425
+ if (isOpen) {
426
+ setVisibleCount(ITEMS_PER_PAGE);
427
+ setIsLoadingMore(false);
428
+ }
396
429
  }, [isOpen]);
397
430
  const renderToggleButton = () => (jsxRuntime.jsxs("div", { className: cvaTimelineElement(), children: [childProps.date ? (jsxRuntime.jsx("div", { className: cvaTimelineElementTime({
398
431
  width: hourCycle === "h12" || hourCycle === "h11" ? "large" : "small",
@@ -410,7 +443,10 @@ const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, t
410
443
  const { date, dateRange, showTime } = dateHeader;
411
444
  return (jsxRuntime.jsx("div", { className: "pb-4", "data-testid": "timeline-date-header", children: date ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.Text, { size: "medium", type: "span", weight: "thick", children: formatDate(date, { selectFormat: !showTime ? "dateOnly" : undefined }) }), jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-500", size: "medium", type: "span", weight: "thick", children: ` (${dateAndTimeUtils.timeSinceAuto(date, new Date(), undefined, locale)})` })] })) : (jsxRuntime.jsx(reactComponents.Text, { className: "pr-1", size: "medium", type: "span", weight: "thick", children: formatRange(dateRange, { dateStyle: !showTime ? "medium" : undefined }) })) }));
412
445
  }, [dateHeader, formatDate, formatRange, customHeader, locale]);
413
- return (jsxRuntime.jsxs("div", { className: className, "data-testid": dataTestId, children: [renderDateHeader(), customHeader ? jsxRuntime.jsx("div", { className: "text-sm font-semibold", children: customHeader }) : null, toggleButton && middleChildren.length > 0 ? (jsxRuntime.jsxs("div", { children: [firstChild, middleChildren.length > 0 ? renderToggleButton() : null, jsxRuntime.jsxs("div", { "aria-hidden": !isOpen, className: "overflow-hidden transition-all duration-300", ref: ref, children: [jsxRuntime.jsx("div", {}), middleChildren, jsxRuntime.jsx("div", {})] }), lastChild] })) : (jsxRuntime.jsx("div", { children: children }))] }));
446
+ return (jsxRuntime.jsxs("div", { className: className, "data-testid": dataTestId, children: [renderDateHeader(), customHeader ? jsxRuntime.jsx("div", { className: "text-sm font-semibold", children: customHeader }) : null, toggleButton && middleChildren.length > 0 ? (jsxRuntime.jsxs("div", { children: [firstChild, middleChildren.length > 0 ? renderToggleButton() : null, jsxRuntime.jsxs("div", { "aria-hidden": !isOpen, className: "overflow-hidden overflow-y-auto transition-all duration-300", ref: ref, children: [jsxRuntime.jsx("div", {}), visibleMiddleChildren, isOpen && visibleCount < middleChildren.length ? (jsxRuntime.jsx("div", { className: "flex justify-center py-2", onClick: e => {
447
+ e.stopPropagation(); // Prevent parent handlers from firing
448
+ loadMoreItems();
449
+ }, children: jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-500 hover:text-secondary-600 cursor-pointer", "data-testid": "load-more-text", size: "small", type: "span", children: t("timeline.loadMore") }) })) : null, jsxRuntime.jsx("div", {})] }), lastChild] })) : (jsxRuntime.jsx("div", { children: children }))] }));
414
450
  };
415
451
 
416
452
  /**
package/index.esm.js CHANGED
@@ -26,7 +26,8 @@ var defaultTranslations = {
26
26
  "shared.timePeriods.months_plural": "{{count}} months",
27
27
  "shared.timePeriods.today": "Today",
28
28
  "shared.timePeriods.weeks": "{{count}} week",
29
- "shared.timePeriods.weeks_plural": "{{count}} weeks"
29
+ "shared.timePeriods.weeks_plural": "{{count}} weeks",
30
+ "timeline.loadMore": "Click for more..."
30
31
  };
31
32
 
32
33
  /** The translation namespace for this library */
@@ -369,6 +370,8 @@ const cvaTimelineElementTime = cvaMerge([
369
370
  },
370
371
  });
371
372
 
373
+ // Constants for pagination
374
+ const ITEMS_PER_PAGE = 20;
372
375
  /**
373
376
  * The Timeline component offers a visual representation of events or milestones in chronological order, helping users easily follow the progression of activities, tasks, or data points over time.
374
377
  *
@@ -378,19 +381,49 @@ const cvaTimelineElementTime = cvaMerge([
378
381
  const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, toggleButton, }) => {
379
382
  const ref = useRef(null);
380
383
  const [isOpen, setIsOpen] = useState(false);
384
+ const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
385
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
381
386
  const { formatDate, formatRange } = useDateAndTime();
382
387
  const locale = useLocale$1();
383
388
  const hourCycle = getHourCycle(locale);
389
+ const { t } = useTranslation();
384
390
  const childrenArray = useMemo(() => Children.toArray(children), [children]);
385
391
  const [firstChild, ...remainingChildren] = childrenArray;
386
392
  const childProps = isValidElement(firstChild) ? firstChild.props : {};
387
393
  const lastChild = remainingChildren.pop();
388
394
  const middleChildren = remainingChildren;
395
+ // Slice middleChildren to only render the visible count
396
+ const visibleMiddleChildren = useMemo(() => middleChildren.slice(0, visibleCount), [middleChildren, visibleCount]);
389
397
  useEffect(() => {
390
398
  // Set maxHeight dynamically for smooth animation
391
399
  if (ref.current) {
392
400
  ref.current.style.maxHeight = isOpen ? `${ref.current.scrollHeight}px` : "0px";
393
401
  }
402
+ }, [isOpen, visibleCount]);
403
+ const loadMoreItems = useCallback(() => {
404
+ if (isLoadingMore || visibleCount >= middleChildren.length) {
405
+ return;
406
+ }
407
+ setIsLoadingMore(true);
408
+ // Use requestAnimationFrame to avoid layout thrashing
409
+ requestAnimationFrame(() => {
410
+ const scrollPos = ref.current?.scrollTop || 0;
411
+ setVisibleCount(prev => Math.min(prev + ITEMS_PER_PAGE, middleChildren.length));
412
+ // After state update, preserve scroll position
413
+ setTimeout(() => {
414
+ if (ref.current) {
415
+ ref.current.scrollTop = scrollPos;
416
+ }
417
+ setIsLoadingMore(false);
418
+ }, 50); // Small delay to ensure DOM has updated
419
+ });
420
+ }, [isLoadingMore, middleChildren.length, visibleCount]);
421
+ // Reset visible count when toggling open/closed
422
+ useEffect(() => {
423
+ if (isOpen) {
424
+ setVisibleCount(ITEMS_PER_PAGE);
425
+ setIsLoadingMore(false);
426
+ }
394
427
  }, [isOpen]);
395
428
  const renderToggleButton = () => (jsxs("div", { className: cvaTimelineElement(), children: [childProps.date ? (jsx("div", { className: cvaTimelineElementTime({
396
429
  width: hourCycle === "h12" || hourCycle === "h11" ? "large" : "small",
@@ -408,7 +441,10 @@ const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, t
408
441
  const { date, dateRange, showTime } = dateHeader;
409
442
  return (jsx("div", { className: "pb-4", "data-testid": "timeline-date-header", children: date ? (jsxs(Fragment, { children: [jsx(Text, { size: "medium", type: "span", weight: "thick", children: formatDate(date, { selectFormat: !showTime ? "dateOnly" : undefined }) }), jsx(Text, { className: "text-secondary-500", size: "medium", type: "span", weight: "thick", children: ` (${timeSinceAuto(date, new Date(), undefined, locale)})` })] })) : (jsx(Text, { className: "pr-1", size: "medium", type: "span", weight: "thick", children: formatRange(dateRange, { dateStyle: !showTime ? "medium" : undefined }) })) }));
410
443
  }, [dateHeader, formatDate, formatRange, customHeader, locale]);
411
- return (jsxs("div", { className: className, "data-testid": dataTestId, children: [renderDateHeader(), customHeader ? jsx("div", { className: "text-sm font-semibold", children: customHeader }) : null, toggleButton && middleChildren.length > 0 ? (jsxs("div", { children: [firstChild, middleChildren.length > 0 ? renderToggleButton() : null, jsxs("div", { "aria-hidden": !isOpen, className: "overflow-hidden transition-all duration-300", ref: ref, children: [jsx("div", {}), middleChildren, jsx("div", {})] }), lastChild] })) : (jsx("div", { children: children }))] }));
444
+ return (jsxs("div", { className: className, "data-testid": dataTestId, children: [renderDateHeader(), customHeader ? jsx("div", { className: "text-sm font-semibold", children: customHeader }) : null, toggleButton && middleChildren.length > 0 ? (jsxs("div", { children: [firstChild, middleChildren.length > 0 ? renderToggleButton() : null, jsxs("div", { "aria-hidden": !isOpen, className: "overflow-hidden overflow-y-auto transition-all duration-300", ref: ref, children: [jsx("div", {}), visibleMiddleChildren, isOpen && visibleCount < middleChildren.length ? (jsx("div", { className: "flex justify-center py-2", onClick: e => {
445
+ e.stopPropagation(); // Prevent parent handlers from firing
446
+ loadMoreItems();
447
+ }, children: jsx(Text, { className: "text-secondary-500 hover:text-secondary-600 cursor-pointer", "data-testid": "load-more-text", size: "small", type: "span", children: t("timeline.loadMore") }) })) : null, jsx("div", {})] }), lastChild] })) : (jsx("div", { children: children }))] }));
412
448
  };
413
449
 
414
450
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-date-and-time-components",
3
- "version": "1.3.46",
3
+ "version": "1.3.48",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -11,15 +11,15 @@
11
11
  "tailwind-merge": "^2.0.0",
12
12
  "react-day-picker": "9.5.1",
13
13
  "react": "19.0.0",
14
- "@trackunit/react-components": "1.4.42",
15
- "@trackunit/react-date-and-time-hooks": "1.3.41",
16
- "@trackunit/react-core-hooks": "1.3.40",
17
- "@trackunit/date-and-time-utils": "1.3.38",
18
- "@trackunit/css-class-variance-utilities": "1.3.38",
19
- "@trackunit/ui-icons": "1.3.38",
20
- "@trackunit/ui-design-tokens": "1.3.38",
21
- "@trackunit/shared-utils": "1.5.38",
22
- "@trackunit/i18n-library-translation": "1.3.41"
14
+ "@trackunit/react-components": "1.4.43",
15
+ "@trackunit/react-date-and-time-hooks": "1.3.42",
16
+ "@trackunit/react-core-hooks": "1.3.41",
17
+ "@trackunit/date-and-time-utils": "1.3.39",
18
+ "@trackunit/css-class-variance-utilities": "1.3.39",
19
+ "@trackunit/ui-icons": "1.3.39",
20
+ "@trackunit/ui-design-tokens": "1.3.39",
21
+ "@trackunit/shared-utils": "1.5.39",
22
+ "@trackunit/i18n-library-translation": "1.3.42"
23
23
  },
24
24
  "module": "./index.esm.js",
25
25
  "main": "./index.cjs.js",
@@ -11,3 +11,4 @@ export declare const CustomTimestamp: () => import("react/jsx-runtime").JSX.Elem
11
11
  export declare const CustomDot: () => import("react/jsx-runtime").JSX.Element;
12
12
  export declare const IncorrectUse: () => import("react/jsx-runtime").JSX.Element;
13
13
  export declare const HoverAndSelectBehavior: () => import("react/jsx-runtime").JSX.Element;
14
+ export declare const PaginatedTimeline: () => import("react/jsx-runtime").JSX.Element;
@@ -14,8 +14,8 @@ export declare const translations: TranslationResource<TranslationKeys>;
14
14
  /**
15
15
  * Local useTranslation for this specific library
16
16
  */
17
- export declare const useTranslation: () => [TransForLibs<"dateTime.instant.now" | "dayRangePickerPopover.icon.tooltip.calendar" | "layout.actions.apply" | "layout.actions.cancel" | "layout.actions.clear" | "shared.timePeriods.days" | "shared.timePeriods.days_plural" | "shared.timePeriods.hours" | "shared.timePeriods.hours_plural" | "shared.timePeriods.months" | "shared.timePeriods.months_plural" | "shared.timePeriods.today" | "shared.timePeriods.weeks" | "shared.timePeriods.weeks_plural">, import("i18next").i18n, boolean] & {
18
- t: TransForLibs<"dateTime.instant.now" | "dayRangePickerPopover.icon.tooltip.calendar" | "layout.actions.apply" | "layout.actions.cancel" | "layout.actions.clear" | "shared.timePeriods.days" | "shared.timePeriods.days_plural" | "shared.timePeriods.hours" | "shared.timePeriods.hours_plural" | "shared.timePeriods.months" | "shared.timePeriods.months_plural" | "shared.timePeriods.today" | "shared.timePeriods.weeks" | "shared.timePeriods.weeks_plural">;
17
+ export declare const useTranslation: () => [TransForLibs<"dateTime.instant.now" | "dayRangePickerPopover.icon.tooltip.calendar" | "layout.actions.apply" | "layout.actions.cancel" | "layout.actions.clear" | "shared.timePeriods.days" | "shared.timePeriods.days_plural" | "shared.timePeriods.hours" | "shared.timePeriods.hours_plural" | "shared.timePeriods.months" | "shared.timePeriods.months_plural" | "shared.timePeriods.today" | "shared.timePeriods.weeks" | "shared.timePeriods.weeks_plural" | "timeline.loadMore">, import("i18next").i18n, boolean] & {
18
+ t: TransForLibs<"dateTime.instant.now" | "dayRangePickerPopover.icon.tooltip.calendar" | "layout.actions.apply" | "layout.actions.cancel" | "layout.actions.clear" | "shared.timePeriods.days" | "shared.timePeriods.days_plural" | "shared.timePeriods.hours" | "shared.timePeriods.hours_plural" | "shared.timePeriods.months" | "shared.timePeriods.months_plural" | "shared.timePeriods.today" | "shared.timePeriods.weeks" | "shared.timePeriods.weeks_plural" | "timeline.loadMore">;
19
19
  i18n: import("i18next").i18n;
20
20
  ready: boolean;
21
21
  };