Skip to content

Commit f2a146e

Browse files
committed
✨ Add Timeline component
1 parent 1bc285f commit f2a146e

File tree

14 files changed

+504
-0
lines changed

14 files changed

+504
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,6 @@ import { Accordion } from 'webcoreui/react'
149149
- [Spinner](https://github.com/Frontendland/webcoreui/tree/main/src/components/Spinner)
150150
- [Switch](https://github.com/Frontendland/webcoreui/tree/main/src/components/Switch)
151151
- [Tabs](https://github.com/Frontendland/webcoreui/tree/main/src/components/Tabs)
152+
- [Timeline](https://github.com/Frontendland/webcoreui/blob/main/src/pages/timeline.astro)
152153
- [Toast](https://github.com/Frontendland/webcoreui/tree/main/src/components/Toast)
153154
- [Tooltip](https://github.com/Frontendland/webcoreui/blob/main/src/pages/tooltip.astro)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
import type { TimelineProps } from './timeline'
3+
import './timeline.scss'
4+
5+
interface Props extends TimelineProps {}
6+
7+
const {
8+
theme,
9+
counter,
10+
alternate,
11+
centered,
12+
color,
13+
textColor,
14+
className
15+
} = Astro.props
16+
17+
const classes = [
18+
'w-timeline',
19+
theme,
20+
alternate && 'alternate',
21+
centered && 'centered',
22+
className
23+
]
24+
25+
const styles = [
26+
color && `--w-timeline-color: ${color};`,
27+
textColor && `--w-timeline-text-color: ${textColor};`,
28+
counter && `--w-timeline-counter: ${counter};`,
29+
].join('')
30+
---
31+
32+
<ul class:list={classes} style={styles}>
33+
<slot />
34+
</ul>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script lang="ts">
2+
import type { TimelineProps } from './timeline'
3+
import './timeline.scss'
4+
5+
export let theme: TimelineProps['theme'] = null
6+
export let counter: TimelineProps['counter'] = null
7+
export let alternate: TimelineProps['alternate'] = false
8+
export let centered: TimelineProps['centered'] = false
9+
export let color: TimelineProps['color'] = ''
10+
export let textColor: TimelineProps['textColor'] = ''
11+
export let className: TimelineProps['className'] = ''
12+
13+
const classes = [
14+
'w-timeline',
15+
theme,
16+
alternate && 'alternate',
17+
centered && 'centered',
18+
className
19+
].filter(Boolean).join(' ')
20+
21+
const styles = [
22+
color && `--w-timeline-color: ${color};`,
23+
textColor && `--w-timeline-text-color: ${textColor};`,
24+
counter && `--w-timeline-counter: ${counter};`,
25+
].join('')
26+
</script>
27+
28+
<ul class={classes} style={styles}>
29+
<slot />
30+
</ul>

src/components/Timeline/Timeline.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react'
2+
import type { TimelineProps } from './timeline'
3+
4+
import './timeline.scss'
5+
6+
type ReactTimelineProps = {
7+
children: React.ReactNode
8+
} & TimelineProps
9+
10+
const Timeline = ({
11+
theme,
12+
counter,
13+
alternate,
14+
centered,
15+
color,
16+
textColor,
17+
className,
18+
children
19+
}: ReactTimelineProps) => {
20+
const classes = [
21+
'w-timeline',
22+
theme,
23+
alternate && 'alternate',
24+
centered && 'centered',
25+
className
26+
].filter(Boolean).join(' ')
27+
28+
const styles = {
29+
...(color && { '--w-timeline-color': color }),
30+
...(textColor && { '--w-timeline-text-color': textColor }),
31+
...(counter && { '--w-timeline-counter': counter }),
32+
} as React.CSSProperties
33+
34+
return (
35+
<ul className={classes} style={styles}>
36+
{children}
37+
</ul>
38+
)
39+
}
40+
41+
export default Timeline

src/components/Timeline/timeline.scss

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
@import '../../scss/config.scss';
2+
3+
// (circle width + border * 2) / 2 - line width
4+
$leftOffset: calc(((25px + 4px) / 2) - 1px);
5+
$rightOffset: calc((((25px + 4px) / 2) - 1px) * -1);
6+
7+
.w-timeline {
8+
display: flex;
9+
gap: 20px;
10+
flex-direction: column;
11+
padding: 0 0 0 40px;
12+
margin: 0;
13+
list-style-type: none;
14+
counter-reset: item;
15+
position: relative;
16+
17+
&::before {
18+
position: absolute;
19+
content: '';
20+
width: 1px;
21+
background: var(--w-timeline-color);
22+
height: 100%;
23+
left: $leftOffset
24+
}
25+
26+
&.fill li::before {
27+
content: '';
28+
}
29+
30+
&.stroke li::before {
31+
background: #000;
32+
border: 2px solid var(--w-timeline-color);
33+
}
34+
}
35+
36+
@include Media('xs') {
37+
.w-timeline {
38+
&.alternate {
39+
padding: 0;
40+
41+
&::before {
42+
left: calc(50% - 1px);
43+
}
44+
45+
li {
46+
width: 50%;
47+
48+
&:nth-child(odd) {
49+
padding-right: 40px;
50+
}
51+
52+
&:nth-child(even) {
53+
align-self: flex-end;
54+
padding-left: 40px;
55+
56+
&::before {
57+
left: 25px;
58+
}
59+
}
60+
61+
&::before {
62+
right: $rightOffset;
63+
}
64+
}
65+
}
66+
67+
&.centered li:nth-child(odd) {
68+
text-align: right;
69+
}
70+
}
71+
}

src/components/Timeline/timeline.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export type TimelineProps = {
2+
theme?: 'fill' | 'stroke' | 'stroke fill' | null
3+
alternate?: boolean
4+
centered?: boolean
5+
color?: string
6+
textColor?: string
7+
className?: string
8+
counter?: 'arabic-indic'
9+
| 'armenian'
10+
| 'bengali'
11+
| 'cambodian'
12+
| 'circle'
13+
| 'cjk-decimal'
14+
| 'cjk-earthly-branch'
15+
| 'cjk-heavenly-stem'
16+
| 'decimal-leading-zero'
17+
| 'devanagari'
18+
| 'disc'
19+
| 'disclosure-closed'
20+
| 'disclosure-open'
21+
| 'ethiopic-numeric'
22+
| 'georgian'
23+
| 'gujarati'
24+
| 'gurmukhi'
25+
| 'hebrew'
26+
| 'hiragana'
27+
| 'hiragana-iroha'
28+
| 'japanese-formal'
29+
| 'kannada'
30+
| 'katakana'
31+
| 'katakana-iroha'
32+
| 'khmer'
33+
| 'korean-hangul-formal'
34+
| 'korean-hanja-formal'
35+
| 'lao'
36+
| 'lower-alpha'
37+
| 'lower-alpha'
38+
| 'lower-armenian'
39+
| 'lower-greek'
40+
| 'lower-roman'
41+
| 'malayalam'
42+
| 'mongolian'
43+
| 'myanmar'
44+
| 'oriya'
45+
| 'persian'
46+
| 'simp-chinese-formal'
47+
| 'square'
48+
| 'tamil'
49+
| 'telugu'
50+
| 'thai'
51+
| 'tibetan'
52+
| 'trad-chinese-formal'
53+
| 'upper-alpha'
54+
| 'upper-armenian'
55+
| 'upper-roman'
56+
| null
57+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
import type { TimelineItemProps } from './timelineitem'
3+
import './timelineitem.scss'
4+
5+
interface Props extends TimelineItemProps {}
6+
7+
const {
8+
title,
9+
titleTag = 'span',
10+
className
11+
} = Astro.props
12+
13+
const classes = [
14+
'w-timeline-item',
15+
className
16+
]
17+
18+
const Title = titleTag
19+
---
20+
21+
<li class:list={classes}>
22+
{title && (
23+
<Title class:list="timeline-title">{title}</Title>
24+
)}
25+
<slot />
26+
</li>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts">
2+
import type { TimelineItemProps } from './timelineitem'
3+
import './timelineitem.scss'
4+
5+
export let title: TimelineItemProps['title'] = ''
6+
export let titleTag: TimelineItemProps['titleTag'] = 'span'
7+
export let className: TimelineItemProps['className'] = ''
8+
9+
const classes = [
10+
'w-timeline-item',
11+
className
12+
].filter(Boolean).join(' ')
13+
</script>
14+
15+
<li class={classes}>
16+
{#if title}
17+
<svelte:element this={titleTag} class="timeline-title">
18+
{title}
19+
</svelte:element>
20+
{/if}
21+
<slot />
22+
</li>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react'
2+
import type { TimelineItemProps } from './timelineitem'
3+
4+
import './timelineitem.scss'
5+
6+
type ReactTimelineItemProps = {
7+
TitleTag?: keyof JSX.IntrinsicElements
8+
children: React.ReactNode
9+
} & Omit<TimelineItemProps, 'titleTag'>
10+
11+
const TimelineItem = ({
12+
title,
13+
TitleTag = 'span',
14+
className,
15+
children
16+
}: ReactTimelineItemProps) => {
17+
const classes = [
18+
'w-timeline-item',
19+
className
20+
].filter(Boolean).join(' ')
21+
22+
return (
23+
<li className={classes}>
24+
{title && (
25+
<TitleTag className="timeline-title">{title}</TitleTag>
26+
)}
27+
{children}
28+
</li>
29+
)
30+
}
31+
32+
export default TimelineItem
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@import '../../scss/config.scss';
2+
3+
.w-timeline-item {
4+
position: relative;
5+
font-size: 16px;
6+
7+
&::before {
8+
content: counter(item, var(--w-timeline-counter));
9+
counter-increment: item;
10+
position: absolute;
11+
top: -5px;
12+
width: 25px;
13+
height: 25px;
14+
border-radius: 100%;
15+
background: var(--w-timeline-color);
16+
color: var(--w-timeline-text-color);
17+
font-size: 14px;
18+
display: inline-flex;
19+
align-items: center;
20+
justify-content: center;
21+
border: 2px solid #000;
22+
margin-left: -40px;
23+
}
24+
25+
.timeline-title {
26+
display: block;
27+
margin-bottom: 10px;
28+
font-family: Bold;
29+
font-size: 18px;
30+
}
31+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type TimelineItemProps = {
2+
title?: string
3+
titleTag?: string
4+
className?: string
5+
}

src/pages/index.astro

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import Rating from '@components/Rating/Rating.astro'
1414
import Spinner from '@components/Spinner/Spinner.astro'
1515
import Switch from '@components/Switch/Switch.astro'
1616
import Tabs from '@components/Tabs/Tabs.astro'
17+
import Timeline from '@components/Timeline/Timeline.astro'
18+
import TimelineItem from '@components/TimelineItem/TimelineItem.astro'
1719
1820
import infoIcon from '../icons/info.svg?raw'
1921
import gitHubIcon from '../icons/github.svg?raw'
@@ -126,6 +128,12 @@ const tabItems = [{
126128
<CardWrapper title="Tabs" href="/tabs">
127129
<Tabs items={tabItems} theme="boxed" />
128130
</CardWrapper>
131+
<CardWrapper title="Timeline" href="/timeline">
132+
<Timeline>
133+
<TimelineItem>Getting Started</TimelineItem>
134+
<TimelineItem>Setup</TimelineItem>
135+
</Timeline>
136+
</CardWrapper>
129137
<CardWrapper title="Toast" href="/toast">
130138
<Alert theme="success">Post saved</Alert>
131139
</CardWrapper>

0 commit comments

Comments
 (0)