Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
Merge pull request #184 from Cloud-Code-AI/159-add-more-block-types
Browse files Browse the repository at this point in the history
159 add more block types
  • Loading branch information
shreyashkgupta authored Dec 7, 2024
2 parents 5112db4 + 35e61ea commit 3ae8cc8
Show file tree
Hide file tree
Showing 46 changed files with 3,142 additions and 503 deletions.
14 changes: 8 additions & 6 deletions packages/akiradocs/src/app/[locale]/[type]/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,19 @@ export default async function ContentPage({ params }: Props) {
</div>
<MainTitle>{t(post.title)}</MainTitle>
<SubTitle>{t(post.description)}</SubTitle>
{post.blocks.map((block) => (
block.content !== post.title && (
<div className="mt-6">
{post.blocks.map((block) => (
block.content !== post.title && (
<BlockRenderer
key={block.id}
block={{
...block,
content: block.content
}}
/>
)
))}
}}
/>
)
))}
</div>
<PageNavigation prev={prev} next={next} locale={locale} />
</div>
</PostContainer>
Expand Down
4 changes: 2 additions & 2 deletions packages/akiradocs/src/components/blocks/ArticleHeaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export function ArticleHeaders({ title, setTitle, subtitle, setSubtitle, showPre
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Article title"
className="text-4xl font-bold mb-4 border-none px-0"
className="text-4xl font-bold mb-4 border-none px-0 max-w-4xl break-words"
/>
<Input
value={subtitle}
onChange={(e) => setSubtitle(e.target.value)}
placeholder="Add a subtitle"
className="text-xl mb-4 border-none px-0"
className="text-xl mb-4 border-none px-0 max-w-4xl break-words"
/>
</>
)
Expand Down
224 changes: 195 additions & 29 deletions packages/akiradocs/src/components/blocks/AudioBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,198 @@
import React from 'react';
import { cn } from "@/lib/utils";

interface AudioProps {
id?: string;
src: string;
caption?: string;
align?: 'left' | 'center' | 'right';
styles?: {
bold?: boolean;
italic?: boolean;
underline?: boolean;
};
"use client"

import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Music, Pause, Play } from 'lucide-react'
import { useState, useRef, useEffect } from 'react'

interface AudioBlockProps {
content: string
id: string
onUpdate?: (content: string) => void
isEditing?: boolean
metadata?: {
caption?: string
alignment?: 'left' | 'center' | 'right'
styles?: {
bold?: boolean
italic?: boolean
underline?: boolean
}
}
}

export function Audio({ id, src, caption, align = 'left', styles }: AudioProps) {
const alignClass = align === 'center' ? 'mx-auto' : align === 'right' ? 'ml-auto' : '';
return (
<div id={id} className={`mb-6 py-1 ${alignClass}`}>
<audio controls className="w-full bg-background border border-border rounded-md">
<source src={src} type="audio/mpeg" />
<span className="text-muted-foreground">Your browser does not support the audio element.</span>
</audio>
{caption && <p className={cn(
"mt-2 text-sm text-muted-foreground",
styles?.bold && 'font-bold',
styles?.italic && 'italic',
styles?.underline && 'underline'
)}>{caption}</p>}
</div>
);
interface AudioBlockContent {
url: string
caption?: string
alignment?: 'left' | 'center' | 'right'
}

export function AudioBlock({ content, id, onUpdate, isEditing, metadata }: AudioBlockProps) {
const [isHovered, setIsHovered] = useState(false)
const [isPlaying, setIsPlaying] = useState(false)
const audioRef = useRef<HTMLAudioElement>(null)

const handlePlayPause = (e: React.MouseEvent) => {
e.stopPropagation()
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause()
} else {
audioRef.current.play()
}
setIsPlaying(!isPlaying)
}
}

// Add event listeners to sync the isPlaying state with audio events
useEffect(() => {
const audio = audioRef.current
if (!audio) return

const handlePlay = () => setIsPlaying(true)
const handlePause = () => setIsPlaying(false)
const handleEnded = () => setIsPlaying(false)

audio.addEventListener('play', handlePlay)
audio.addEventListener('pause', handlePause)
audio.addEventListener('ended', handleEnded)

return () => {
audio.removeEventListener('play', handlePlay)
audio.removeEventListener('pause', handlePause)
audio.removeEventListener('ended', handleEnded)
}
}, [])

const parseAudioContent = (content: string): AudioBlockContent => {
try {
return typeof content === 'string' ? JSON.parse(content) : content
} catch {
return {
url: content,
caption: metadata?.caption || '',
alignment: metadata?.alignment || 'center'
}
}
}

const UploadButton = () => (
<div className="text-center">
<input
type="file"
id={`audio-upload-${id}`}
className="hidden"
accept="audio/*"
/>
<div className="flex flex-col items-center gap-2">
<Button
variant="ghost"
className="w-full justify-start h-auto py-6 px-8 hover:bg-accent flex flex-col items-center gap-2"
onClick={(e) => {
e.stopPropagation()
document.getElementById(`audio-upload-${id}`)?.click()
}}
>
<Music className="h-8 w-8 text-muted-foreground" />
<span className="font-medium">Upload Audio</span>
<p className="text-sm text-muted-foreground">
Click to upload or drag and drop
</p>
</Button>
</div>
</div>
)

if (!content && isEditing) {
return (
<div className="flex items-center justify-center border-2 border-dashed border-muted-foreground/25 rounded-lg my-4">
<UploadButton />
</div>
)
}

const audioContent = parseAudioContent(content)
const alignment = audioContent.alignment || metadata?.alignment || 'center'

return (
<figure
className={cn(
"py-1 mb-6 relative group",
"flex flex-col",
alignment === 'left' && "items-start",
alignment === 'center' && "items-center",
alignment === 'right' && "items-end",
)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="relative w-full max-w-[500px]">
<div className="bg-muted rounded-lg p-4 flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center">
<Music className="h-6 w-6 text-primary" />
</div>
<audio
ref={audioRef}
src={audioContent.url}
controls
className="w-full"
/>
</div>
{isEditing && isHovered && (
<div
className="absolute inset-0 bg-black/50 flex items-center justify-center gap-2 rounded-lg transition-opacity"
onClick={(e) => e.stopPropagation()}
>
<input
type="file"
id={`audio-change-${id}`}
className="hidden"
accept="audio/*"
/>
<Button
variant="secondary"
className="gap-2"
onClick={(e) => {
e.stopPropagation()
document.getElementById(`audio-change-${id}`)?.click()
}}
>
<Music className="h-4 w-4" />
Change Audio
</Button>
<Button
variant="secondary"
className="gap-2"
onClick={handlePlayPause}
>
{isPlaying ? (
<>
<Pause className="h-4 w-4" />
Pause
</>
) : (
<>
<Play className="h-4 w-4" />
Play
</>
)}
</Button>
</div>
)}
</div>
{audioContent.caption && (
<figcaption className={cn(
"mt-2 text-sm text-muted-foreground italic",
alignment === 'left' && "text-left",
alignment === 'center' && "text-center",
alignment === 'right' && "text-right",
metadata?.styles?.bold && "font-bold",
metadata?.styles?.italic && "italic",
metadata?.styles?.underline && "underline"
)}>
{audioContent.caption}
</figcaption>
)}
</figure>
)
}
4 changes: 2 additions & 2 deletions packages/akiradocs/src/components/blocks/BlockquoteBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function Blockquote({ id, children, align = 'left', styles, isEditing, on

if (isEditing) {
return (
<div className={cn("border-l-4 border-border pl-4 text-muted-foreground", alignClass)}>
<div className={cn("border-l-4 border-border pl-4 py-1 mb-6 text-muted-foreground", alignClass)}>
<textarea
ref={textareaRef}
value={children as string}
Expand All @@ -63,7 +63,7 @@ export function Blockquote({ id, children, align = 'left', styles, isEditing, on
<blockquote
id={id}
className={cn(
"border-l-4 border-border pl-4 text-muted-foreground",
"border-l-4 border-border pl-4 py-1 mb-6 text-muted-foreground",
alignClass,
commonStyles
)}
Expand Down
2 changes: 1 addition & 1 deletion packages/akiradocs/src/components/blocks/CalloutBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function Callout({ id, type, title, children, align = 'left', styles, isE

return (
<Alert className={cn(
'flex flex-col sm:flex-row items-start gap-4 p-4 my-4 rounded-lg border-2',
'flex flex-col sm:flex-row items-start gap-4 p-4 mb-7 mt-1 rounded-lg border-2',
className,
{
'text-left': align === 'left',
Expand Down
Loading

0 comments on commit 3ae8cc8

Please sign in to comment.