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

Commit

Permalink
feat: Spacer block
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyashkgupta committed Dec 12, 2024
1 parent d710430 commit e93528b
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 4 deletions.
59 changes: 59 additions & 0 deletions packages/akiradocs/src/components/blocks/SpacerBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { cn } from '@/lib/utils'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { useState } from 'react'

interface SpacerBlockProps {
size?: 'small' | 'medium' | 'large' | 'xlarge'
isEditing?: boolean
onUpdate?: (size: string) => void
}

const spacingSizes = {
small: 'h-4',
medium: 'h-8',
large: 'h-16',
xlarge: 'h-24'
}

export function SpacerBlock({ size = 'medium', isEditing, onUpdate }: SpacerBlockProps) {
const [isFocused, setIsFocused] = useState(false)

if (!isEditing) {
return <div className={cn('w-full', spacingSizes[size])} />
}

return (
<div
className="flex items-center gap-2 py-2"
tabIndex={0}
onFocus={() => setIsFocused(true)}
onBlur={(e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setIsFocused(false)
}
}}
>
<div className={cn(
'flex-grow border-2 border-dashed rounded transition-colors duration-200',
spacingSizes[size],
isFocused ? 'border-muted-foreground/20' : 'border-transparent'
)} />
{isFocused && (
<Select
value={size}
onValueChange={(value) => onUpdate?.(value)}
>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="Select size" />
</SelectTrigger>
<SelectContent>
<SelectItem value="small">Small</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="large">Large</SelectItem>
<SelectItem value="xlarge">Extra Large</SelectItem>
</SelectContent>
</Select>
)}
</div>
)
}
15 changes: 15 additions & 0 deletions packages/akiradocs/src/components/editor/AIRewriteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ const blockStyles = {
label: 'Descriptive',
prompt: 'Rewrite the API reference to be more descriptive and engaging.'
}
],
spacer: [
{
value: 'default',
label: 'Default',
prompt: 'No rewriting options available for spacer'
}
]
} as const;

Expand All @@ -213,6 +220,10 @@ export function AIRewriteButton({ onRewrite, blockType, isRewriting }: AIRewrite
};

const handleRewrite = async () => {
if (blockType === 'spacer') {
return;
}

try {
await onRewrite(style)
} catch (error) {
Expand All @@ -224,6 +235,10 @@ export function AIRewriteButton({ onRewrite, blockType, isRewriting }: AIRewrite
}
}

if (blockType === 'spacer') {
return null;
}

return (
<Popover>
<PopoverTrigger asChild>
Expand Down
2 changes: 2 additions & 0 deletions packages/akiradocs/src/components/editor/AddBlockButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
Music,
File,
CheckSquare,
ArrowUpDown,
} from 'lucide-react'

interface AddBlockButtonProps {
Expand Down Expand Up @@ -83,6 +84,7 @@ export const AddBlockButton = forwardRef<
{ type: 'audio', icon: <Music size={18} />, label: 'Audio', description: 'Embed audio content.', group: 'Media' },
{ type: 'file', icon: <File size={18} />, label: 'File', description: 'Upload or link to a file.', group: 'Media' },
{ type: 'checkList', icon: <CheckSquare size={18} />, label: 'To-do list', description: 'Track tasks with a to-do list.', group: 'Basic' },
{ type: 'spacer', icon: <ArrowUpDown size={18} />, label: 'Spacing', description: 'Add vertical space between blocks.', group: 'Basic' },
]

const filteredOptions = blockOptions.filter((option) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function BlockFormatToolbar({
audioContent,
onAudioMetadataChange,
}: BlockFormatToolbarProps) {
if (blockType === 'file') {
if (blockType === 'file' || blockType === 'spacer') {
return null;
}

Expand Down
9 changes: 9 additions & 0 deletions packages/akiradocs/src/lib/renderers/BlockRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Table } from '@/components/blocks/TableBlock'
import { VideoBlock } from "@/components/blocks/VideoBlock"
import { AudioBlock } from "@/components/blocks/AudioBlock"
import { FileBlock } from "@/components/blocks/FileBlock"
import { SpacerBlock } from "@/components/blocks/SpacerBlock"

interface ImageBlockContent {
url: string;
Expand Down Expand Up @@ -234,6 +235,14 @@ export function BlockRenderer({ block, isEditing, onUpdate }: BlockRendererProps
onUpdate={(content) => onUpdate?.(block.id, content)}
/>
);
case 'spacer':
return (
<SpacerBlock
size={block.content as 'small' | 'medium' | 'large' | 'xlarge'}
isEditing={isEditing}
onUpdate={(size) => onUpdate?.(block.id, size)}
/>
);
default:
return null
}
Expand Down
2 changes: 1 addition & 1 deletion packages/akiradocs/src/types/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export type BlockType =
| 'video'
| 'audio'
| 'file'
// | 'emoji'
| 'callout'
| 'spacer'
| 'apiReference';

export interface Block {
Expand Down
5 changes: 5 additions & 0 deletions packages/editor/src/app/editMode/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ export default function ArticleEditorContent({ params }: { params: Promise<{ slu
metadata: {}
}

// Add default content for spacer blocks
if (newBlock.type === 'spacer') {
newBlock.content = 'medium'
}

if (newBlock.type === 'table') {
newBlock.metadata = {
headers: ['Column 1', 'Column 2'],
Expand Down
59 changes: 59 additions & 0 deletions packages/editor/src/components/blocks/SpacerBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { cn } from '@/lib/utils'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { useState } from 'react'

interface SpacerBlockProps {
size?: 'small' | 'medium' | 'large' | 'xlarge'
isEditing?: boolean
onUpdate?: (size: string) => void
}

const spacingSizes = {
small: 'h-4',
medium: 'h-8',
large: 'h-16',
xlarge: 'h-24'
}

export function SpacerBlock({ size = 'medium', isEditing, onUpdate }: SpacerBlockProps) {
const [isFocused, setIsFocused] = useState(false)

if (!isEditing) {
return <div className={cn('w-full', spacingSizes[size])} />
}

return (
<div
className="flex items-center gap-2 py-2"
tabIndex={0}
onFocus={() => setIsFocused(true)}
onBlur={(e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setIsFocused(false)
}
}}
>
<div className={cn(
'flex-grow border-2 border-dashed rounded transition-colors duration-200',
spacingSizes[size],
isFocused ? 'border-muted-foreground/20' : 'border-transparent'
)} />
{isFocused && (
<Select
value={size}
onValueChange={(value) => onUpdate?.(value)}
>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="Select size" />
</SelectTrigger>
<SelectContent>
<SelectItem value="small">Small</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="large">Large</SelectItem>
<SelectItem value="xlarge">Extra Large</SelectItem>
</SelectContent>
</Select>
)}
</div>
)
}
15 changes: 15 additions & 0 deletions packages/editor/src/components/editor/AIRewriteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ const blockStyles = {
label: 'Descriptive',
prompt: 'Rewrite the API reference to be more descriptive and engaging.'
}
],
spacer: [
{
value: 'default',
label: 'Default',
prompt: 'No rewriting options available for spacer'
}
]
} as const;

Expand All @@ -213,6 +220,10 @@ export function AIRewriteButton({ onRewrite, blockType, isRewriting }: AIRewrite
};

const handleRewrite = async () => {
if (blockType === 'spacer') {
return;
}

try {
await onRewrite(style)
} catch (error) {
Expand All @@ -224,6 +235,10 @@ export function AIRewriteButton({ onRewrite, blockType, isRewriting }: AIRewrite
}
}

if (blockType === 'spacer') {
return null;
}

return (
<Popover>
<PopoverTrigger asChild>
Expand Down
2 changes: 2 additions & 0 deletions packages/editor/src/components/editor/AddBlockButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
Music,
File,
CheckSquare,
ArrowUpDown,
} from 'lucide-react'

interface AddBlockButtonProps {
Expand Down Expand Up @@ -83,6 +84,7 @@ export const AddBlockButton = forwardRef<
{ type: 'audio', icon: <Music size={18} />, label: 'Audio', description: 'Embed audio content.', group: 'Media' },
{ type: 'file', icon: <File size={18} />, label: 'File', description: 'Upload or link to a file.', group: 'Media' },
{ type: 'checkList', icon: <CheckSquare size={18} />, label: 'To-do list', description: 'Track tasks with a to-do list.', group: 'Basic' },
{ type: 'spacer', icon: <ArrowUpDown size={18} />, label: 'Spacing', description: 'Add vertical space between blocks.', group: 'Basic' },
]

const filteredOptions = blockOptions.filter((option) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function BlockFormatToolbar({
audioContent,
onAudioMetadataChange,
}: BlockFormatToolbarProps) {
if (blockType === 'file') {
if (blockType === 'file' || blockType === 'spacer') {
return null;
}

Expand Down
9 changes: 9 additions & 0 deletions packages/editor/src/lib/renderers/BlockRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Table } from '@/components/blocks/TableBlock'
import { VideoBlock } from "@/components/blocks/VideoBlock"
import { AudioBlock } from "@/components/blocks/AudioBlock"
import { FileBlock } from "@/components/blocks/FileBlock"
import { SpacerBlock } from "@/components/blocks/SpacerBlock"

interface ImageBlockContent {
url: string;
Expand Down Expand Up @@ -230,6 +231,14 @@ export function BlockRenderer({ block, isEditing, onUpdate }: BlockRendererProps
onUpdate={(content) => onUpdate?.(block.id, content)}
/>
);
case 'spacer':
return (
<SpacerBlock
size={block.content as 'small' | 'medium' | 'large' | 'xlarge'}
isEditing={isEditing}
onUpdate={(size) => onUpdate?.(block.id, size)}
/>
);
default:
return null
}
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/types/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export type BlockType =
| 'video'
| 'audio'
| 'file'
// | 'emoji'
| 'callout'
| 'spacer'
| 'apiReference';

export interface Block {
Expand Down

0 comments on commit e93528b

Please sign in to comment.