'use client' import { Node, mergeAttributes } from '@tiptap/core' import { ReactNodeViewRenderer, NodeViewWrapper, type NodeViewProps } from '@tiptap/react' import type { Editor } from '@tiptap/core' import type { Node as PMNode } from '@tiptap/pm/model' import { StructuredViewBlockEmbed } from '@/components/structured-view-block-embed' function StructuredViewBlockView(props: NodeViewProps) { return ( ) } const defaultColumns = [ { id: 'col-title', name: 'Nom', type: 'text' }, { id: 'col-done', name: 'Fait', type: 'checkbox' }, { id: 'col-status', name: 'Statut', type: 'select', options: ['À faire', 'En cours', 'Terminé'] } ] const defaultRows = [ { id: 'row-1', values: { 'col-title': '', 'col-done': false, 'col-status': 'À faire' } }, { id: 'row-2', values: { 'col-title': '', 'col-done': false, 'col-status': 'À faire' } } ] export const StructuredViewBlockExtension = Node.create({ name: 'structuredViewBlock', group: 'block', atom: true, draggable: true, selectable: true, addAttributes() { return { notebookId: { default: null, parseHTML: (el) => el.getAttribute('data-sv-notebook-id'), renderHTML: (attrs) => attrs.notebookId ? { 'data-sv-notebook-id': attrs.notebookId } : {}, }, displayMode: { default: 'table', parseHTML: (el) => el.getAttribute('data-sv-mode') || 'table', renderHTML: (attrs) => ({ 'data-sv-mode': attrs.displayMode || 'table' }), }, filterJson: { default: '{}', parseHTML: (el) => el.getAttribute('data-sv-filter') || '{}', renderHTML: (attrs) => ({ 'data-sv-filter': attrs.filterJson || '{}' }), }, isLocal: { default: true, // Par défaut, on crée une base de données locale autonome parseHTML: (el) => el.getAttribute('data-sv-is-local') === 'true', renderHTML: (attrs) => attrs.isLocal ? { 'data-sv-is-local': 'true' } : { 'data-sv-is-local': 'false' }, }, localColumnsJson: { default: JSON.stringify(defaultColumns), parseHTML: (el) => el.getAttribute('data-sv-local-cols') || '[]', renderHTML: (attrs) => ({ 'data-sv-local-cols': attrs.localColumnsJson || '[]' }), }, localRowsJson: { default: JSON.stringify(defaultRows), parseHTML: (el) => el.getAttribute('data-sv-local-rows') || '[]', renderHTML: (attrs) => ({ 'data-sv-local-rows': attrs.localRowsJson || '[]' }), }, } }, parseHTML() { return [ { tag: 'div[data-structured-view-block]' }, { tag: 'div[data-database-block]', getAttrs: () => ({ isLocal: true, localColumnsJson: JSON.stringify(defaultColumns), localRowsJson: JSON.stringify(defaultRows), }) } ] }, renderHTML({ HTMLAttributes }) { return ['div', mergeAttributes(HTMLAttributes, { 'data-structured-view-block': 'true' })] }, addNodeView() { return ReactNodeViewRenderer(StructuredViewBlockView) }, }) export function insertStructuredViewBlockAtSelection(editor: Editor, notebookId?: string | null): boolean { const type = editor.schema.nodes.structuredViewBlock if (!type) return false // Par défaut, si la note a un carnet et qu'on n'est pas dans l'inbox, on peut l'insérer liée au carnet. // Mais si aucun notebookId n'est passé ou si on veut favoriser la base locale autonome Notion-like par défaut : // On l'insère en base locale autonome par défaut ! const attrs = { notebookId: null, displayMode: 'table', filterJson: '{}', isLocal: true, localColumnsJson: JSON.stringify(defaultColumns), localRowsJson: JSON.stringify(defaultRows) } const { empty, $from } = editor.state.selection const parent = $from.parent if (empty && parent.type.name === 'paragraph' && parent.content.size === 0) { const pos = $from.before() return editor .chain() .focus() .command(({ tr, dispatch }) => { tr.replaceWith(pos, pos + parent.nodeSize, type.create(attrs)) if (dispatch) dispatch(tr) return true }) .run() } return editor.chain().focus().insertContent({ type: 'structuredViewBlock', attrs }).run() } export function replaceBlockWithStructuredView( editor: Editor, blockPos: number, blockNode: PMNode, notebookId?: string | null ): void { const type = editor.schema.nodes.structuredViewBlock if (!type || blockPos < 0 || !blockNode) return const attrs = { notebookId: null, displayMode: 'table', filterJson: '{}', isLocal: true, localColumnsJson: JSON.stringify(defaultColumns), localRowsJson: JSON.stringify(defaultRows) } const svNode = type.create(attrs) editor.view.dispatch(editor.state.tr.replaceWith(blockPos, blockPos + blockNode.nodeSize, svNode)) editor.commands.focus() }