1+ import classes from './document.module.css'
2+
13type DocumentWithBlocks = {
24 title : string
35 description : string
@@ -11,23 +13,152 @@ type BlockData = {
1113 title ?: string
1214 content : string
1315 url ?: string
16+ id ?: string
17+ created_at ?: Date
18+ updated_at ?: Date
1419}
1520
1621const Document = ( { document } : { document : DocumentWithBlocks } ) => {
1722 return (
18- < div >
23+ < div className = { classes . document } >
1924 < h1 > { document . title } </ h1 >
2025 < p > { document . description } </ p >
2126 < ul >
22- { document . blocks . map ( ( block ) => (
23- < li key = { block . position } >
24- < h2 > { block . title } </ h2 >
25- < p > { block . content } </ p >
26- </ li >
27- ) ) }
27+ { document . blocks . map ( ( block ) => {
28+ switch ( block . type ) {
29+ case 'paragraph' :
30+ return (
31+ < DocumentParagraph key = { block . id } content = { block . content } />
32+ )
33+ case 'heading2' :
34+ return < DocumentHeading2 key = { block . id } content = { block . content } />
35+ case 'heading3' :
36+ return < DocumentHeading3 key = { block . id } content = { block . content } />
37+ case 'list' :
38+ return < DocumentList key = { block . id } content = { block . content } />
39+ case 'youtube' :
40+ return < DocumentYoutube key = { block . id } url = { block . url } />
41+ case 'image' :
42+ return < DocumentImage key = { block . id } url = { block . url } />
43+ case 'quote' :
44+ return (
45+ < DocumentQuote
46+ key = { block . id }
47+ content = { block . content }
48+ url = { block . url }
49+ />
50+ )
51+ default :
52+ return < p > Unknown block type: { block . type } </ p >
53+ }
54+ } ) }
2855 </ ul >
2956 </ div >
3057 )
3158}
3259
3360export default Document
61+
62+ function DocumentParagraph ( { content } : { content : string } ) {
63+ return < p > { content } </ p >
64+ }
65+
66+ function DocumentHeading2 ( { content } : { content : string } ) {
67+ return < h2 > { content } </ h2 >
68+ }
69+
70+ function DocumentHeading3 ( { content } : { content : string } ) {
71+ return < h3 > { content } </ h3 >
72+ }
73+
74+ function DocumentList ( { content } : { content : string } ) {
75+ // Split content by new lines and render each as a list item
76+ return (
77+ < ul >
78+ { content
79+ . split ( '\n' )
80+ . filter ( ( item ) => item . trim ( ) . length > 0 )
81+ . map ( ( item , idx ) => (
82+ < li key = { idx } > { item } </ li >
83+ ) ) }
84+ </ ul >
85+ )
86+ }
87+
88+ function DocumentYoutube ( { url } : { url : string } ) {
89+ // Convert YouTube URL to embed format
90+ const convertToEmbedUrl = ( youtubeUrl : string ) : string => {
91+ // Handle different YouTube URL formats
92+ const patterns = [
93+ // Patterns to extract the YouTube video ID from various common URL formats:
94+ // 1. Matches standard watch URLs, short youtu.be URLs, and embed URLs.
95+ // 2. Handles watch URLs with additional query parameters.
96+ / (?: y o u t u b e \. c o m \/ w a t c h \? v = | y o u t u \. b e \/ | y o u t u b e \. c o m \/ e m b e d \/ ) ( [ a - z A - Z 0 - 9 _ - ] + ) / ,
97+ / y o u t u b e \. c o m \/ w a t c h \? .* v = ( [ a - z A - Z 0 - 9 _ - ] + ) / ,
98+ ]
99+
100+ for ( const pattern of patterns ) {
101+ const match = youtubeUrl . match ( pattern )
102+ if ( match && match [ 1 ] ) {
103+ return `https://www.youtube.com/embed/${ match [ 1 ] } `
104+ }
105+ }
106+
107+ // If no pattern matches, return the original URL
108+ return youtubeUrl
109+ }
110+
111+ const embedUrl = convertToEmbedUrl ( url )
112+
113+ return (
114+ < div >
115+ < iframe
116+ width = "560"
117+ height = "315"
118+ src = { embedUrl }
119+ title = "YouTube video player"
120+ allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
121+ referrerPolicy = "strict-origin-when-cross-origin"
122+ allowFullScreen
123+ />
124+ </ div >
125+ )
126+ }
127+
128+ function DocumentImage ( { url } : { url : string } ) {
129+ return (
130+ < div >
131+ < img src = { url } width = { 300 } />
132+ </ div >
133+ )
134+ }
135+
136+ function DocumentQuote ( { content, url } : { content : string ; url : string } ) {
137+ return (
138+ < section >
139+ < blockquote >
140+ { content }
141+ { url && (
142+ < footer >
143+ < cite >
144+ < a
145+ href = { url }
146+ target = "_blank"
147+ rel = "noopener noreferrer"
148+ style = { {
149+ color : '#555' ,
150+ textDecoration : 'underline' ,
151+ fontSize : '0.95em' ,
152+ marginLeft : '0.5em' ,
153+ wordBreak : 'break-all' ,
154+ } }
155+ >
156+ { url }
157+ </ a >
158+ </ cite >
159+ </ footer >
160+ ) }
161+ </ blockquote >
162+ </ section >
163+ )
164+ }
0 commit comments