@uob-web-and-digital/component-library 2.20.17 → 2.20.18
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/dist/components/organisms/Timeline/Timeline.d.ts +35 -0
- package/dist/components/organisms/Timeline/Timeline.stories.d.ts +24 -0
- package/dist/components/organisms/Timeline/TimelineItem.d.ts +41 -0
- package/dist/components/organisms/Timeline/defaultProps.d.ts +4 -0
- package/dist/components/organisms/Timeline/timelineLayout.d.ts +33 -0
- package/dist/components/organisms/Timeline/useTimelineEmbla.d.ts +26 -0
- package/dist/index.d.ts +3 -2
- package/dist/main.css +1 -1
- package/dist/main.js +3 -3
- package/dist/module.js +5 -5
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { ThemeProps } from '../../../themeProps';
|
|
3
|
+
import { HeadingProps } from '../../molecules/Heading/Heading';
|
|
4
|
+
import { HeadingProps as HeadingElementProps } from '../../atoms/Heading/Heading';
|
|
5
|
+
import { TimelinePoint } from './TimelineItem';
|
|
6
|
+
import './timeline.scss';
|
|
7
|
+
export interface TimelineProps {
|
|
8
|
+
/**
|
|
9
|
+
* The theme to use
|
|
10
|
+
*/
|
|
11
|
+
theme: ThemeProps;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to inverse the colours. Defaults to true (light background, dark
|
|
14
|
+
* text). Set to false for the dark background variant (dark background, light
|
|
15
|
+
* text). The gold labels are unchanged in both.
|
|
16
|
+
*/
|
|
17
|
+
inverse?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Optional component heading. Accepts the Heading molecule's props directly
|
|
20
|
+
* (title, description, linkProps, htmlTag). theme and inverse are taken from the
|
|
21
|
+
* Timeline and override any set here, so the heading always matches the component.
|
|
22
|
+
*/
|
|
23
|
+
heading?: HeadingProps;
|
|
24
|
+
/**
|
|
25
|
+
* Heading level for each timeline point heading. Not hardcoded so the CMS can
|
|
26
|
+
* set h2, h3 or h4 depending on the page context and whether a component
|
|
27
|
+
* heading is present.
|
|
28
|
+
*/
|
|
29
|
+
pointHeadingLevel?: Extract<HeadingElementProps['htmlTag'], 'h2' | 'h3' | 'h4'>;
|
|
30
|
+
/**
|
|
31
|
+
* Timeline points (3-12)
|
|
32
|
+
*/
|
|
33
|
+
points: TimelinePoint[];
|
|
34
|
+
}
|
|
35
|
+
export default function Timeline({ theme, inverse, heading, pointHeadingLevel, points }: TimelineProps): ReactElement;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StoryObj } from '@storybook/react';
|
|
2
|
+
import Timeline from './Timeline';
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof Timeline;
|
|
6
|
+
tags: string[];
|
|
7
|
+
argTypes: {
|
|
8
|
+
pointHeadingLevel: {
|
|
9
|
+
control: "select";
|
|
10
|
+
options: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
inverse: {
|
|
14
|
+
control: "boolean";
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export default meta;
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
export declare const Default: Story;
|
|
22
|
+
export declare const DarkBackground: Story;
|
|
23
|
+
export declare const WithoutComponentHeading: Story;
|
|
24
|
+
export declare const FewPoints: Story;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { ThemeProps } from '../../../themeProps';
|
|
3
|
+
import { HeadingProps } from '../../atoms/Heading/Heading';
|
|
4
|
+
import { ImageProps } from '../../atoms/Image/Image';
|
|
5
|
+
export interface TimelinePointLink {
|
|
6
|
+
url: string;
|
|
7
|
+
title: string;
|
|
8
|
+
externalLink?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface TimelinePoint {
|
|
11
|
+
/**
|
|
12
|
+
* Date or short label shown in the gold badge (required, max 20 characters)
|
|
13
|
+
*/
|
|
14
|
+
label: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional point heading (max 30 characters)
|
|
17
|
+
*/
|
|
18
|
+
heading?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Description text (required, max 100 characters)
|
|
21
|
+
*/
|
|
22
|
+
description: string;
|
|
23
|
+
/**
|
|
24
|
+
* Optional link rendered with a trailing arrow (max 25 characters)
|
|
25
|
+
*/
|
|
26
|
+
link?: TimelinePointLink;
|
|
27
|
+
/**
|
|
28
|
+
* Optional image, converted to webp and resized via URL params
|
|
29
|
+
*/
|
|
30
|
+
image?: ImageProps;
|
|
31
|
+
}
|
|
32
|
+
export interface TimelineItemProps extends TimelinePoint {
|
|
33
|
+
theme: ThemeProps;
|
|
34
|
+
inverse?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Heading level for the point heading. Not hardcoded so the CMS can set the
|
|
37
|
+
* correct level (h2/h3/h4) for the page context.
|
|
38
|
+
*/
|
|
39
|
+
headingTag: HeadingProps['htmlTag'];
|
|
40
|
+
}
|
|
41
|
+
export default function TimelineItem({ theme, inverse, label, heading, description, link, image, headingTag }: TimelineItemProps): ReactElement;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface TimelinePagePoint {
|
|
2
|
+
/** Index of the point in the source `points` array. */
|
|
3
|
+
index: number;
|
|
4
|
+
/** translateX (px) of the point within its page (desktop only; 0 on mobile). */
|
|
5
|
+
x: number;
|
|
6
|
+
/** 0 = above the line, 1 = below. */
|
|
7
|
+
row: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TimelinePage {
|
|
10
|
+
points: TimelinePagePoint[];
|
|
11
|
+
}
|
|
12
|
+
export interface TimelineLayout {
|
|
13
|
+
pages: TimelinePage[];
|
|
14
|
+
/** Width (px) of one grid column at this viewport - drives the --timeline-col CSS var. */
|
|
15
|
+
colWidth: number;
|
|
16
|
+
}
|
|
17
|
+
/** Column span for a point, by whether it has an image. */
|
|
18
|
+
export declare function pointSpan(hasImage: boolean): number;
|
|
19
|
+
/**
|
|
20
|
+
* Group points into viewport-wide pages.
|
|
21
|
+
*
|
|
22
|
+
* Mobile: one point per page (full width), so Embla snaps one point at a time.
|
|
23
|
+
*
|
|
24
|
+
* Desktop: pack points in source order, alternating above (even index) / below (odd)
|
|
25
|
+
* rows. A single left-to-right cursor drives placement: each point starts at least
|
|
26
|
+
* STAGGER columns right of the previous one (so points read in source order regardless
|
|
27
|
+
* of row) AND clear of the last point in its own row (so same-row points never overlap).
|
|
28
|
+
* When a point would overrun the 12 columns it starts a new page. So each page is a
|
|
29
|
+
* contiguous run of points, the top/bottom alternation stays in sync across pages (a
|
|
30
|
+
* page can begin with a below point), and a page only ever holds whole points (no
|
|
31
|
+
* partials) - leftover columns are quiet whitespace.
|
|
32
|
+
*/
|
|
33
|
+
export declare function computeTimelinePages(spans: number[], viewportWidth: number, isDesktop: boolean): TimelineLayout;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TimelinePoint } from './TimelineItem';
|
|
2
|
+
import { TimelinePage } from './timelineLayout';
|
|
3
|
+
export interface TimelineEmbla {
|
|
4
|
+
/** Ref for the Embla viewport (the overflow-hidden scroller). */
|
|
5
|
+
emblaRef: (node: HTMLElement | null) => void;
|
|
6
|
+
/** Pages to render; each page is one Embla slide (one viewport wide). */
|
|
7
|
+
pages: TimelinePage[];
|
|
8
|
+
/** Current grid column width (px) - set as the --timeline-col CSS var. */
|
|
9
|
+
colWidth: number;
|
|
10
|
+
/** true on desktop (>=768px), where points fan out into the staggered two-row layout. */
|
|
11
|
+
isDesktop: boolean;
|
|
12
|
+
/** Whether the track scrolls (controls whether the nav renders). */
|
|
13
|
+
canScroll: boolean;
|
|
14
|
+
canPrev: boolean;
|
|
15
|
+
canNext: boolean;
|
|
16
|
+
scrollPrev: () => void;
|
|
17
|
+
scrollNext: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wires embla-carousel to the timeline. Embla owns the scroll/drag/snap mechanics; the
|
|
21
|
+
* page composition and per-point positions still come from computeTimelinePages (the
|
|
22
|
+
* staggered, paged, no-partials packing Embla cannot express). Each page is one Embla
|
|
23
|
+
* slide, so Embla snaps page-by-page on desktop and point-by-point on mobile. Pages are
|
|
24
|
+
* recomputed from the live viewport width, and Embla is re-initialised when they change.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useTimelineEmbla(points: TimelinePoint[]): TimelineEmbla;
|
package/dist/index.d.ts
CHANGED
|
@@ -76,5 +76,6 @@ import LogoWall, { LogoWallProps } from './components/organisms/LogoWall/LogoWal
|
|
|
76
76
|
import TextOverMedia, { TextOverMediaProps } from './components/organisms/TextOverMedia/TextOverMedia';
|
|
77
77
|
import SocialFeed, { SocialFeedProps } from './components/organisms/SocialFeed/SocialFeed';
|
|
78
78
|
import { HeaderLanding, HeaderLandingProps } from './components/organisms/HeaderLanding/HeaderLanding';
|
|
79
|
-
|
|
80
|
-
export {
|
|
79
|
+
import Timeline, { TimelineProps } from './components/organisms/Timeline/Timeline';
|
|
80
|
+
export type { AccordionProps, AccreditationPromotionProps, AlertBannerProps, AnchorTargetProps, BreadcrumbsProps, CarouselProps, CardProfileProps, CardStaffProps, CenterWrapperProps, ClearingBannerProps, CourseDeliveryPillsProps, CTAComponentProps, EmbedWrapperProps, FactBoxesProps, FeaturedContentProps, FeaturedSignpostProps, FeaturedVideoProps, FiveArticlesGridProps, FooterProps, FormSelectProps, GenericDetailSectionProps, GeneralSearchFilterProps, HeaderProps, HeadingProps, HeroProps, HeroArticleProps, HeroCarouselProps, HeroCoursesProps, HeroProfileProps, HeroVideoProps, ImageBlockProps, ImageCarouselProps, ImageMosaicProps, InPageNavigationProps, DocumentNavigationProps, DocumentPageNavigationProps, IntroTextProps, LeadNewsArticlesProps, ListingFilterWrapperProps, ListingResultsProps, ListingResultsCountProps, ListTableProps, LocationSelectorProps, MainWrapperProps, ModuleCardGroupProps, NewsArticlesPromoBlockProps, PaginationProps, PromoBlockProps, QuoteBlockProps, RelatedCoursesProps, ResearchStoryCarouselProps, RichTextProps, ScholarshipCarouselProps, SearchCoursesProps, SelectOptionsProps, StaffListingCarouselProps, SidebarWrapperProps, SignPostProps, SingleButtonProps, SkipToContentProps, TabsBlockProps, TextWithBackgroundProps, TwoColumnWrapperProps, TopicSelectorProps, StandardListingFilterProps, VideoCardComponentProps, VideoEmbedProps, ScrollableTableProps, ScrollableTableCategory, ModalCTAProps, LocationSectionProps, CardPromoProfileProps, CardPromoSignpostProps, CardImageAndTextProps, LogoWallProps, TextOverMediaProps, SocialFeedProps, HeaderLandingProps, TimelineProps };
|
|
81
|
+
export { Accordion, AcreditationPromotion, AlertBanner, AnchorTarget, Breadcrumbs, Carousel, CardProfile, CardPromoSignpost, CardStaff, CentreWrapper, ClearingBanner, CourseDeliveryPills, CTAComponent, EmbedWrapper, FactBoxes, FeaturedContent, FeaturedSignpost, FeaturedVideo, FiveArticlesGrid, Footer, GeneralSearchFilter, GenericDetailSection, Header, Heading, Hero, HeroArticle, HeroCarousel, HeroCourses, HeroProfile, HeroVideo, ImageBlock, ImageCarousel, ImageMosaic, InPageNavigation, DocumentNavigation, DocumentPageNavigation, IntroText, LeadNewsArticles, ListingFilterWrapper, ListingResults, ListingResultsCount, ListTable, LocationSelector, MainWrapper, ModuleCardGroup, NewsArticlesPromoBlock, Pagination, PromoBlock, PromoBlockGrid, QuoteBlock, RelatedCourses, ResearchStoryCarousel, RichText, ScholarshipCarousel, SearchCourses, SidebarWrapper, SignPost, SingleButton, SkipToContent, StaffListingCarousel, StandardListingFilter, TabsBlock, TextWithBackground, TopicSelector, TwoColumnWrapper, VideoCardComponent, VideoEmbed, ScrollableTable, ModalCTA, LocationSection, CardPromoProfile, CardImageAndText, LogoWall, TextOverMedia, SocialFeed, HeaderLanding, Timeline };
|