163 lines
4.6 KiB
TypeScript
163 lines
4.6 KiB
TypeScript
import {
|
|
IconBlockquote,
|
|
IconCheckbox, IconCode,
|
|
IconH1,
|
|
IconH2,
|
|
IconH3,
|
|
IconList,
|
|
IconListNumbers, IconPhoto,
|
|
IconTypography,
|
|
} from '@tabler/icons-react';
|
|
import { CommandProps, SlashMenuGroupedItemsType } from '@/features/editor/components/slash-menu/types';
|
|
|
|
const CommandGroups: SlashMenuGroupedItemsType = {
|
|
basic: [
|
|
{
|
|
title: 'Text',
|
|
description: 'Just start typing with plain text.',
|
|
searchTerms: ['p', 'paragraph'],
|
|
icon: IconTypography,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.toggleNode('paragraph', 'paragraph')
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: 'To-do List',
|
|
description: 'Track tasks with a to-do list.',
|
|
searchTerms: ['todo', 'task', 'list', 'check', 'checkbox'],
|
|
icon: IconCheckbox,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor.chain().focus().deleteRange(range).toggleTaskList().run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Heading 1',
|
|
description: 'Big section heading.',
|
|
searchTerms: ['title', 'big', 'large'],
|
|
icon: IconH1,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode('heading', { level: 1 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Heading 2',
|
|
description: 'Medium section heading.',
|
|
searchTerms: ['subtitle', 'medium'],
|
|
icon: IconH2,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode('heading', { level: 2 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Heading 3',
|
|
description: 'Small section heading.',
|
|
searchTerms: ['subtitle', 'small'],
|
|
icon: IconH3,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode('heading', { level: 3 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Bullet List',
|
|
description: 'Create a simple bullet list.',
|
|
searchTerms: ['unordered', 'point'],
|
|
icon: IconList,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Numbered List',
|
|
description: 'Create a list with numbering.',
|
|
searchTerms: ['ordered'],
|
|
icon: IconListNumbers,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
},
|
|
},
|
|
{
|
|
title: 'Quote',
|
|
description: 'Capture a quote.',
|
|
searchTerms: ['blockquote', 'quotes'],
|
|
icon: IconBlockquote,
|
|
command: ({ editor, range }: CommandProps) =>
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.toggleNode('paragraph', 'paragraph')
|
|
.toggleBlockquote()
|
|
.run(),
|
|
},
|
|
{
|
|
title: 'Code',
|
|
description: 'Capture a code snippet.',
|
|
searchTerms: ['codeblock'],
|
|
icon: IconCode,
|
|
command: ({ editor, range }: CommandProps) =>
|
|
editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
|
|
},
|
|
{
|
|
title: 'Image',
|
|
description: 'Upload an image from your computer.',
|
|
searchTerms: ['photo', 'picture', 'media'],
|
|
icon: IconPhoto,
|
|
command: ({ editor, range }: CommandProps) => {
|
|
editor.chain().focus().deleteRange(range).run();
|
|
// upload image
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = 'image/*';
|
|
input.onchange = async () => {
|
|
if (input.files?.length) {
|
|
const file = input.files[0];
|
|
const pos = editor.view.state.selection.from;
|
|
//startImageUpload(file, editor.view, pos);
|
|
}
|
|
};
|
|
input.click();
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
export const getSuggestionItems = ({ query }: { query: string }): SlashMenuGroupedItemsType => {
|
|
const search = query.toLowerCase();
|
|
const filteredGroups: SlashMenuGroupedItemsType = {};
|
|
|
|
for (const [group, items] of Object.entries(CommandGroups)) {
|
|
const filteredItems = items.filter((item) => {
|
|
return item.title.toLowerCase().includes(search)
|
|
|| item.description.toLowerCase().includes(search)
|
|
|| (item.searchTerms && item.searchTerms.some((term: string) => term.includes(search)));
|
|
});
|
|
|
|
if (filteredItems.length) {
|
|
filteredGroups[group] = filteredItems;
|
|
}
|
|
}
|
|
|
|
return filteredGroups;
|
|
};
|
|
|
|
export default getSuggestionItems;
|