1
- import { CallToAction } from 'components/CallToAction'
2
1
import { graphql , useStaticQuery } from 'gatsby'
3
- import Link from 'components/Link'
4
- import { heading , section } from './classes'
2
+ import { heading } from './classes'
5
3
import groupBy from 'lodash.groupby'
6
- import React from 'react'
4
+ import React , { useEffect , useRef , useState } from 'react'
5
+ import { ArrowLeft , ArrowRight } from '@posthog/icons'
6
+ import { useBreakpoint } from 'gatsby-plugin-breakpoints'
7
7
8
8
const months = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ]
9
9
const categories = {
@@ -12,7 +12,42 @@ const categories = {
12
12
'Something cool happened' : 'milestone' ,
13
13
}
14
14
15
+ export const Items = ( { items } ) => {
16
+ const ref = useRef ( null )
17
+ const [ isOverflowing , setIsOverflowing ] = useState ( false )
18
+
19
+ useEffect ( ( ) => {
20
+ setIsOverflowing ( ref ?. current ?. scrollHeight > ref ?. current ?. getBoundingClientRect ( ) ?. height )
21
+ } , [ ] )
22
+
23
+ return (
24
+ < div
25
+ className = { `relative ${
26
+ isOverflowing
27
+ ? 'after:absolute after:h-[30px] after:w-full after:bottom-0 after:left-0 after:bg-gradient-to-t after:from-accent dark:after:from-accent-dark after:to-transparent'
28
+ : ''
29
+ } `}
30
+ >
31
+ < ul ref = { ref } className = { `m-0 p-0 h-[110px] overflow-auto ${ isOverflowing ? 'pb-[30px]' : '' } ` } >
32
+ { items ?. map ( ( { title, category } ) => {
33
+ return (
34
+ < li
35
+ key = { title }
36
+ className = "relative list-none text-sm text-left pl-4 content-none before:inline-block before:absolute before:w-[10px] before:h-[10px] before:left-0 before:top-[5px] before:rounded-full before:mr-2 mt-1 first:mt-0"
37
+ data-type = { categories [ category ] }
38
+ >
39
+ { title }
40
+ </ li >
41
+ )
42
+ } ) }
43
+ </ ul >
44
+ </ div >
45
+ )
46
+ }
47
+
15
48
export default function Timeline ( ) {
49
+ const breakpoints = useBreakpoint ( )
50
+ const listRef = useRef ( null )
16
51
const {
17
52
allSqueakRoadmap : { nodes } ,
18
53
} = useStaticQuery ( graphql `
@@ -49,8 +84,12 @@ export default function Timeline() {
49
84
}
50
85
)
51
86
87
+ useEffect ( ( ) => {
88
+ listRef ?. current ?. scrollBy ( { left : listRef ?. current ?. scrollWidth } )
89
+ } , [ ] )
90
+
52
91
return (
53
- < section className = "px-4 mb-12 md:mb-20" >
92
+ < section className = "px-4 mb-12 md:mb-20 overflow-hidden " >
54
93
< h2 className = { heading ( ) } >
55
94
We ship < br className = "sm:hidden" />
56
95
< span className = "text-orange" > weirdly fast</ span >
@@ -73,82 +112,89 @@ export default function Timeline() {
73
112
</ div >
74
113
</ div >
75
114
</ div >
76
-
77
- < div className = "max-w-screen-2xl mx-auto mdlg:grid grid-cols-2 xl:grid-cols-4 border border-light dark:border-dark divide-x divide-light dark:divide-dark" >
78
- { Object . keys ( pastEvents ) . map ( ( year ) => {
79
- const pastMonths = groupBy ( pastEvents [ year ] , ( node ) => {
80
- const date = new Date ( node . dateCompleted || node . projectedCompletion )
81
- return months [ date . getUTCMonth ( ) ]
82
- } )
83
- const futureQuarters = groupBy ( futureEvents [ year ] , ( node ) => {
84
- const date = node . dateCompleted || node . projectedCompletion
85
- return Math . floor ( new Date ( date ) . getUTCMonth ( ) / 3 + 1 )
86
- } )
87
- return (
88
- < div
89
- key = { year }
90
- className = "bg-accent dark:bg-accent-dark w-full mb-4 lg:mb-0 last:border-r border-light dark:border-dark"
91
- >
92
- < h4 className = "text-base py-1 font-bold text-center bg-light dark:bg-dark border-y xl:border-t-0 border-light dark:border-dark" >
93
- { year }
94
- </ h4 >
95
- < div className = "px-8" >
96
- < ul role = "list" className = "py-1 px-0" >
97
- { Object . keys ( pastMonths ) . map ( ( month ) => {
98
- return (
99
- < li
100
- key = { month }
101
- className = "timeline-entry list-none border-light dark:border-dark border-b last:border-none flex py-2 gap-3 items-start"
102
- >
103
- < p className = "text-[14px] text-gray capitalize w-[30px] flex-shrink-0 m-0" >
104
- { month }
105
- </ p >
106
- < ul className = "list-none m-0 p-0" >
107
- { pastMonths [ month ] . map ( ( { title, category } ) => {
108
- return (
109
- < li
110
- key = { title }
111
- className = "flex-auto relative text-[14px] text-left pl-4 content-none before:inline-block before:absolute before:w-[10px] before:h-[10px] before:left-0 before:top-[5px] before:rounded-full before:mr-2 mt-1 first:mt-0"
112
- data-type = { categories [ category ] }
113
- >
114
- { title }
115
- </ li >
116
- )
117
- } ) }
118
- </ ul >
119
- </ li >
120
- )
121
- } ) }
122
- { Object . keys ( futureQuarters ) . map ( ( quarter ) => {
123
- return (
124
- < li
125
- key = { quarter }
126
- className = "timeline-entry list-none flex py-2 gap-3 items-start"
127
- >
128
- < p className = "text-[14px] text-gray capitalize w-[30px] flex-shrink-0 m-0" >
129
- Q{ quarter }
130
- </ p >
131
- < ul className = "list-none m-0 p-0" >
132
- { futureQuarters [ quarter ] . map ( ( { title, category } ) => {
133
- return (
134
- < li
135
- key = { title }
136
- className = "flex-auto relative text-[14px] text-left pl-4 text-gray content-none before:inline-block before:absolute before:w-[10px] before:h-[10px] before:left-0 before:top-[5px] before:rounded-full before:mr-2 mt-1 first:mt-0"
137
- data-type = { categories [ category ] }
138
- >
139
- { title }
140
- </ li >
141
- )
142
- } ) }
143
- </ ul >
144
- </ li >
145
- )
146
- } ) }
147
- </ ul >
115
+ < div className = "relative -mx-4 pr-4" >
116
+ < div className = "md:block hidden absolute z-20 top-1/2 left-0 -translate-y-1/2" >
117
+ < button
118
+ onClick = { ( ) => listRef ?. current ?. scrollBy ( { left : - 50 , behavior : 'smooth' } ) }
119
+ className = "relative hover:scale-[1.01] hover:top-[-1px] active:top-[.5px] active:scale-[.99] md:z-30 px-2 py-8"
120
+ >
121
+ < ArrowLeft className = "w-10" />
122
+ </ button >
123
+ </ div >
124
+ < div
125
+ ref = { listRef }
126
+ className = "-mr-4 px-4 md:px-16 snap-x snap-mandatory flex flex-nowrap gap-4 overflow-auto relative"
127
+ >
128
+ { Object . keys ( pastEvents ) . map ( ( year , index ) => {
129
+ const pastMonths = groupBy ( pastEvents [ year ] , ( node ) => {
130
+ const date = new Date ( node . dateCompleted || node . projectedCompletion )
131
+ return months [ date . getUTCMonth ( ) ]
132
+ } )
133
+ const futureQuarters = groupBy ( futureEvents [ year ] , ( node ) => {
134
+ const date = node . dateCompleted || node . projectedCompletion
135
+ return Math . floor ( new Date ( date ) . getUTCMonth ( ) / 3 + 1 )
136
+ } )
137
+ return (
138
+ < div key = { year } className = "w-[80vw] md:w-[90vw] max-w-5xl shrink-0 mb-4 snap-center" >
139
+ < h4 className = "text-2xl py-1 font-bold text-center" > { year } </ h4 >
140
+ < div className = "p-4 bg-white dark:bg-dark border border-light dark:border-dark sm:h-auto h-96 overflow-auto snap-x" >
141
+ < ul role = "list" className = "py-1 px-0 grid sm:grid-cols-2 md:grid-cols-4 gap-4" >
142
+ { months . map ( ( month ) => {
143
+ const items = pastMonths [ month ]
144
+ if ( breakpoints . sm && ! items ) return null
145
+ return (
146
+ < li
147
+ key = { month }
148
+ className = "timeline-entry bg-accent dark:bg-accent-dark list-none px-4 pb-4 text-center min-h-[6rem] md:min-h-[8rem] lg:min-h-[10rem]"
149
+ >
150
+ < p
151
+ className = { `text-lg font-bold border-b border-light dark:border-dark capitalize py-2 mb-2` }
152
+ >
153
+ { month }
154
+ </ p >
155
+ < Items items = { items } />
156
+ </ li >
157
+ )
158
+ } ) }
159
+ { Object . keys ( futureQuarters ) . map ( ( quarter ) => {
160
+ return (
161
+ < li
162
+ key = { quarter }
163
+ className = "timeline-entry list-none bg-accent dark:bg-accent-dark px-4 pb-4"
164
+ >
165
+ < p className = "text-lg font-bold border-b border-light dark:border-dark capitalize py-2 mb-2 text-center" >
166
+ Q{ quarter }
167
+ </ p >
168
+ < ul className = "list-none m-0 p-0" >
169
+ { futureQuarters [ quarter ] . map ( ( { title, category } ) => {
170
+ return (
171
+ < li
172
+ key = { title }
173
+ className = "flex-auto relative text-[14px] text-left pl-4 content-none before:inline-block before:absolute before:w-[10px] before:h-[10px] before:left-0 before:top-[5px] before:rounded-full before:mr-2 mt-1 first:mt-0"
174
+ data-type = { categories [ category ] }
175
+ >
176
+ { title }
177
+ </ li >
178
+ )
179
+ } ) }
180
+ </ ul >
181
+ </ li >
182
+ )
183
+ } ) }
184
+ </ ul >
185
+ </ div >
148
186
</ div >
149
- </ div >
150
- )
151
- } ) }
187
+ )
188
+ } ) }
189
+ </ div >
190
+ < div className = "absolute top-1/2 right-0 -translate-y-1/2" >
191
+ < button
192
+ onClick = { ( ) => listRef ?. current ?. scrollBy ( { left : 50 , behavior : 'smooth' } ) }
193
+ className = "md:block hidden relative hover:scale-[1.01] hover:top-[-1px] active:top-[.5px] active:scale-[.99] md:z-30 px-2 py-8"
194
+ >
195
+ < ArrowRight className = "w-10" />
196
+ </ button >
197
+ </ div >
152
198
</ div >
153
199
</ section >
154
200
)
0 commit comments