Skip to content

Commit

Permalink
added example with carousel plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Darginec05 committed Jun 5, 2024
1 parent 28c70e7 commit 0b5539a
Show file tree
Hide file tree
Showing 18 changed files with 864 additions and 93 deletions.
2 changes: 2 additions & 0 deletions packages/core/editor/src/editor/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ export const Blocks = {
updateBlock,
toggleBlock,
insertBlocks,
// [TODO]
// updateBlocks
};
1 change: 1 addition & 0 deletions packages/plugins/accordion/src/plugin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const Accordion = new YooptaPlugin<AccordionElementKeys, AccordionListItemProps>
description: 'Create collapses',
icon: <ListCollapse size={24} />,
},
shortcuts: ['accordion'],
},
});

Expand Down
34 changes: 18 additions & 16 deletions web/next-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,28 @@
"@floating-ui/react": "^0.26.11",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@yoopta/accordion": "^4.0.1-alpha.1",
"@yoopta/action-menu-list": "^4.5.0-alpha.2",
"@yoopta/blockquote": "^4.5.0-alpha.2",
"@yoopta/callout": "^4.5.0-alpha.2",
"@yoopta/code": "^4.5.0-alpha.2",
"@yoopta/editor": "^4.5.0-alpha.2",
"@yoopta/embed": "^4.5.0-alpha.2",
"@yoopta/file": "^4.5.0-alpha.2",
"@yoopta/headings": "^4.5.0-alpha.2",
"@yoopta/image": "^4.5.0-alpha.2",
"@yoopta/link": "^4.5.0-alpha.2",
"@yoopta/link-tool": "^4.5.0-alpha.2",
"@yoopta/lists": "^4.5.0-alpha.2",
"@radix-ui/react-slot": "^1.0.2",
"@yoopta/accordion": "^4.0.1-alpha.2",
"@yoopta/action-menu-list": "^4.5.0-alpha.3",
"@yoopta/blockquote": "^4.5.0-alpha.3",
"@yoopta/callout": "^4.5.0-alpha.3",
"@yoopta/code": "^4.5.0-alpha.3",
"@yoopta/editor": "^4.5.0-alpha.3",
"@yoopta/embed": "^4.5.0-alpha.3",
"@yoopta/file": "^4.5.0-alpha.3",
"@yoopta/headings": "^4.5.0-alpha.3",
"@yoopta/image": "^4.5.0-alpha.3",
"@yoopta/link": "^4.5.0-alpha.3",
"@yoopta/link-tool": "^4.5.0-alpha.3",
"@yoopta/lists": "^4.5.0-alpha.3",
"@yoopta/marks": "^4.3.0",
"@yoopta/paragraph": "^4.5.0-alpha.2",
"@yoopta/toolbar": "^4.5.0-alpha.2",
"@yoopta/video": "^4.5.0-alpha.2",
"@yoopta/paragraph": "^4.5.0-alpha.3",
"@yoopta/toolbar": "^4.5.0-alpha.3",
"@yoopta/video": "^4.5.0-alpha.3",
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.0",
"embla-carousel-react": "^8.1.3",
"lucide-react": "^0.366.0",
"next": "14.1.4",
"next-themes": "^0.3.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { YooptaPlugin } from '@yoopta/editor';
import { BarcodeIcon } from 'lucide-react';
import { Carousel } from './renders/Carousel';
import { CarouselItem } from './renders/CarouselItem';
import { CarouselItemDescription } from './renders/CarouselItemDescription';
import { CarouselItemImage } from './renders/CarouselItemImage';
import { CarouselItemTitle } from './renders/CarouselItemTitle';

const CarouselPlugin = new YooptaPlugin({
type: 'Carousel',
elements: {
carousel: {
render: Carousel,
children: ['carousel-item'],
asRoot: true,
},
'carousel-item': {
render: CarouselItem,
children: ['carousel-item-title', 'carousel-item-description', 'carousel-item-image'],
},
'carousel-item-image': {
render: CarouselItemImage,
props: {
nodeType: 'void',
src: null,
},
},
'carousel-item-title': {
render: CarouselItemTitle,
},
'carousel-item-description': {
render: CarouselItemDescription,
},
},
options: {
display: {
title: 'Carousel',
description: 'Create a carousel',
icon: <BarcodeIcon size={24} />,
},
shortcuts: ['carousel'],
},
parsers: {
html: {
deserialize: {
nodeNames: ['CAROUSEL'],
},
},
},
});

export { CarouselPlugin };
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Carousel as CarouselRoot, CarouselContent, CarouselPrevious, CarouselNext } from '@/components/ui/carousel';
import { Elements, PluginElementRenderProps, useYooptaEditor, useYooptaReadOnly } from '@yoopta/editor';
import { PlusCircle } from 'lucide-react';

const Carousel = ({ children, element, blockId, attributes }: PluginElementRenderProps) => {
const editor = useYooptaEditor();

const onAddCarouselItem = () => {
Elements.createElement(editor, blockId, { type: 'carousel-item' }, { path: 'next', focus: true, split: false });
};

return (
<CarouselRoot {...attributes}>
<CarouselContent>{children}</CarouselContent>
<CarouselPrevious contentEditable={false} />
<CarouselNext contentEditable={false} />
<button type="button" onClick={onAddCarouselItem}>
<PlusCircle />
</button>
</CarouselRoot>
);
};

export { Carousel };
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Card, CardContent } from '@/components/ui/card';
import { CarouselItem as CarouselRootItem } from '@/components/ui/carousel';
import { Elements, PluginElementRenderProps, useYooptaEditor } from '@yoopta/editor';
import { Trash } from 'lucide-react';

const CarouselItem = ({ element, blockId, attributes, children }: PluginElementRenderProps) => {
const editor = useYooptaEditor();

const onDeleteItem = () => {
const items = Elements.getElementChildren(editor, blockId, { type: 'carousel' });
if (items?.length === 1) {
return editor.deleteBlock({ blockId });
}

const elementPath = Elements.getElementPath(editor, blockId, element);
if (elementPath) {
Elements.deleteElement(editor, blockId, { path: elementPath, type: 'carousel-item' });
}
};

return (
<CarouselRootItem key={element.id} {...attributes} className="md:basis-1/2 lg:basis-1/3 relative group">
<div className="p-1">
<Card>
<CardContent className="flex flex-col aspect-square items-center p-4">{children}</CardContent>
</Card>
</div>
{!editor.readOnly && (
<button
type="button"
onClick={onDeleteItem}
className="absolute top-3 right-3 opacity-0 group-hover:opacity-100"
>
<Trash size={16} color="gray" />
</button>
)}
</CarouselRootItem>
);
};

export { CarouselItem };
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PluginElementRenderProps } from '@yoopta/editor';

const CarouselItemDescription = (props: PluginElementRenderProps) => {
return (
<p {...props.attributes} className="w-full text-sm text-muted-foreground mb-[6px]">
{props.children}
</p>
);
};

export { CarouselItemDescription };
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Elements, PluginElementRenderProps, useYooptaEditor, useYooptaReadOnly } from '@yoopta/editor';
import { ImageIcon, DeleteIcon, Delete, Trash2 } from 'lucide-react';
import { ChangeEvent, MouseEvent } from 'react';

const CarouselItemImage = ({ element, children, attributes, blockId }: PluginElementRenderProps) => {
const editor = useYooptaEditor();
const readOnly = useYooptaReadOnly();

const onUploadFile = (event: ChangeEvent) => {
if (readOnly) return;

const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (!file) return;

const reader = new FileReader();
reader.onload = (e) => {
const src = e.target?.result as string;

const elementPath = Elements.getElementPath(editor, blockId, element);

Elements.updateElement(
editor,
blockId,
{ type: 'carousel-item-image', props: { ...element.props, src } },
{ path: elementPath },
);
};
reader.readAsDataURL(file);
};

const onDeleteImage = () => {
if (readOnly) return;

const elementPath = Elements.getElementPath(editor, blockId, element);

Elements.updateElement(
editor,
blockId,
{ type: 'carousel-item-image', props: { ...element.props, src: null } },
{ path: elementPath },
);
};

return (
<div className="p-0 py-1 h-full w-full" {...attributes} contentEditable={false}>
<div className="grid gap-2">
<label
htmlFor={`${element.id}-uploader`}
className="aspect-square w-full rounded-md object-cover cursor-pointer relative"
onClick={(e) => e.stopPropagation()}
>
<input
id={`${element.id}-uploader`}
onChange={onUploadFile}
type="file"
className="hidden opacity-0"
multiple={false}
disabled={readOnly}
accept="image/*"
/>
{element.props.src ? (
<>
<img
alt={`${element.id} image`}
loading="lazy"
width="100%"
height="100%"
decoding="async"
data-nimg="1"
className="aspect-square w-full rounded-md object-cover"
src={element.props.src}
/>
{!readOnly && (
<button onClick={onDeleteImage} className="absolute top-1 right-1 z-10" type="button">
<Trash2 size={20} color="red" />
</button>
)}
</>
) : (
<div className="aspect-square w-full h-full rounded-md bg-gray-200 relative">
<ImageIcon size={30} className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" />
</div>
)}
</label>
</div>
{children}
</div>
);
};

export { CarouselItemImage };
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PluginElementRenderProps } from '@yoopta/editor';

const CarouselItemTitle = (props: PluginElementRenderProps) => {
return (
<h4 {...props.attributes} className="w-full font-semibold leading-none tracking-tight my-[8px]">
{props.children}
</h4>
);
};

export { CarouselItemTitle };
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { YooptaPlugin } from '@yoopta/editor';
import { SeparatorHorizontal } from 'lucide-react';
import { DividerRenderElement } from './renders/Divider';

const DividerPlugin = new YooptaPlugin({
type: 'Divider',
elements: {
divider: {
render: DividerRenderElement,
props: {
nodeType: 'void',
},
},
},
options: {
shortcuts: ['--', '---'],
display: {
title: 'New Divider Plugin',
description: 'Separate',
icon: <SeparatorHorizontal />,
},
},
});

export { DividerPlugin };
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PluginElementRenderProps } from '@yoopta/editor';

const DividerRenderElement = ({ attributes, children, element, blockId }: PluginElementRenderProps) => {
return (
<div {...attributes} className="w-full flex relative items-center h-[14px] my-1" contentEditable={false}>
<hr className="absolute w-full" />
{children}
</div>
);
};

export { DividerRenderElement };
Loading

0 comments on commit 0b5539a

Please sign in to comment.