UNPKG

claritykit-svelte

Version:

A comprehensive Svelte component library focused on accessibility, ADHD-optimized design, developer experience, and full SSR compatibility

260 lines (259 loc) 9.54 kB
import { Node, mergeAttributes } from '@tiptap/core'; import { createSvelteNodeView } from '../utils/svelte-node-view'; import DatabaseViewBlock from '../components/DatabaseViewBlock.svelte'; /** * DatabaseView Extension for TipTap * * Provides inline database views with multiple display modes (table, kanban, gallery, etc.) * Integrates with PostgreSQL materialized views and event sourcing */ export const DatabaseViewExtension = Node.create({ name: 'databaseView', group: 'block', atom: true, draggable: true, addOptions() { return { HTMLAttributes: {}, dictionary: { insertDatabaseView: 'Insert database view', selectViewType: 'Select view type', configureView: 'Configure view', }, }; }, addAttributes() { return { id: { default: null, parseHTML: element => element.getAttribute('data-id'), renderHTML: attributes => { if (!attributes.id) { return {}; } return { 'data-id': attributes.id, }; }, }, source: { default: null, parseHTML: element => element.getAttribute('data-source'), renderHTML: attributes => { if (!attributes.source) { return {}; } return { 'data-source': attributes.source, }; }, }, viewType: { default: 'table', parseHTML: element => element.getAttribute('data-view-type') || 'table', renderHTML: attributes => ({ 'data-view-type': attributes.viewType, }), }, config: { default: {}, parseHTML: element => { const config = element.getAttribute('data-config'); if (config) { try { return JSON.parse(config); } catch { return {}; } } return {}; }, renderHTML: attributes => { if (!attributes.config || Object.keys(attributes.config).length === 0) { return {}; } return { 'data-config': JSON.stringify(attributes.config), }; }, }, title: { default: '', parseHTML: element => element.getAttribute('data-title') || '', renderHTML: attributes => { if (!attributes.title) { return {}; } return { 'data-title': attributes.title, }; }, }, filters: { default: [], parseHTML: element => { const filters = element.getAttribute('data-filters'); if (filters) { try { return JSON.parse(filters); } catch { return []; } } return []; }, renderHTML: attributes => { if (!attributes.filters || attributes.filters.length === 0) { return {}; } return { 'data-filters': JSON.stringify(attributes.filters), }; }, }, sorting: { default: null, parseHTML: element => { const sorting = element.getAttribute('data-sorting'); if (sorting) { try { return JSON.parse(sorting); } catch { return null; } } return null; }, renderHTML: attributes => { if (!attributes.sorting) { return {}; } return { 'data-sorting': JSON.stringify(attributes.sorting), }; }, }, editable: { default: true, parseHTML: element => element.getAttribute('data-editable') !== 'false', renderHTML: attributes => ({ 'data-editable': attributes.editable, }), }, realtime: { default: false, parseHTML: element => element.getAttribute('data-realtime') === 'true', renderHTML: attributes => ({ 'data-realtime': attributes.realtime, }), }, }; }, parseHTML() { return [ { tag: 'div[data-type="database-view"]', }, ]; }, renderHTML({ HTMLAttributes }) { return [ 'div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { 'data-type': 'database-view', class: 'database-view-block', }), ]; }, addCommands() { return { insertDatabaseView: (options = {}) => ({ commands, editor }) => { const { source = '', viewType = 'table', config = {} } = options; // Generate unique ID for the view const id = `dbview-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; return commands.insertContent({ type: this.name, attrs: { id, source, viewType, config: { ...config, defaultView: viewType, views: config.views || { table: { columns: [], allowAdd: true, allowEdit: true, allowDelete: true, }, kanban: { columns: [ { id: 'todo', title: 'To Do' }, { id: 'in_progress', title: 'In Progress' }, { id: 'done', title: 'Done' }, ], groupBy: 'status', }, gallery: { columns: 3, titleField: 'title', imageField: 'image', descriptionField: 'description', }, list: { columns: [], showCheckbox: true, expandable: true, }, calendar: { dateField: 'date', titleField: 'title', }, gantt: { startField: 'start_date', endField: 'end_date', titleField: 'title', progressField: 'progress', }, }, }, title: config.title || 'Database View', filters: [], sorting: null, editable: true, realtime: false, }, }); }, updateDatabaseView: (options) => ({ commands, editor, state }) => { const { selection } = state; const node = selection.$head.node(); if (node.type.name !== this.name) { return false; } return commands.updateAttributes(this.name, options); }, }; }, addNodeView() { return createSvelteNodeView({ component: DatabaseViewBlock, className: 'database-view-node-wrapper', tag: 'div', }); }, addKeyboardShortcuts() { return { // Mod-Shift-D to insert database view 'Mod-Shift-d': () => this.editor.commands.insertDatabaseView(), }; }, // Add to slash commands addProseMirrorPlugins() { return []; }, });