diff --git a/package.json b/package.json index e1b880e180..57f0640d08 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "pack": "npm run build:super-editor && npm --prefix ./packages/superdoc run pack", "prepare": "husky", "lint:staged": "lint-staged", - "watch": "npm run watch:es --workspace=packages/superdoc" + "watch": "npm run watch:es --workspace=packages/superdoc", + "check:all": "npm run format && npm run lint:fix && npm --workspace=packages/super-editor run types:build" }, "lint-staged": { "*.{js,jsx}": [ diff --git a/packages/super-editor/src/core/helpers/isInTable.js b/packages/super-editor/src/core/helpers/isInTable.js index 2d48ca1f40..bd28d58743 100644 --- a/packages/super-editor/src/core/helpers/isInTable.js +++ b/packages/super-editor/src/core/helpers/isInTable.js @@ -1,3 +1,15 @@ +// @ts-check +/** + * Check if cursor is inside a table + * @private + * @category Helper + * @param {Object} state - Editor state + * @returns {boolean} True if cursor is in table + * @example + * if (isInTable(state)) { + * // Enable table-specific commands + * } + */ export const isInTable = (state) => { const { $head } = state.selection; diff --git a/packages/super-editor/src/extensions/bold/bold.js b/packages/super-editor/src/extensions/bold/bold.js index ad72c6d7be..60dfabf504 100644 --- a/packages/super-editor/src/extensions/bold/bold.js +++ b/packages/super-editor/src/extensions/bold/bold.js @@ -5,6 +5,8 @@ import { Mark, Attribute } from '@core/index.js'; * @module Bold * @sidebarTitle Bold * @snippetPath /snippets/extensions/bold.mdx + * @shortcut Mod-b | toggleBold | Toggle bold formatting + * @shortcut Mod-B | toggleBold | Toggle bold formatting (uppercase) */ export const Bold = Mark.create({ name: 'bold', @@ -17,11 +19,14 @@ export const Bold = Mark.create({ addAttributes() { return { + /** + * @category Attribute + * @param {string} [value] - Bold weight value ('0' renders as normal) + */ value: { default: null, renderDOM: (attrs) => { if (!attrs.value) return {}; - if (attrs.value === '0') { return { style: 'font-weight: normal' }; } diff --git a/packages/super-editor/src/extensions/highlight/highlight.js b/packages/super-editor/src/extensions/highlight/highlight.js index ad00d37277..3acd1d5718 100644 --- a/packages/super-editor/src/extensions/highlight/highlight.js +++ b/packages/super-editor/src/extensions/highlight/highlight.js @@ -5,6 +5,7 @@ import { Mark, Attribute } from '@core/index.js'; * @module Highlight * @sidebarTitle Highlight * @snippetPath /snippets/extensions/highlight.mdx + * @shortcut Mod-Shift-h | toggleHighlight | Toggle highlighted formatting */ export const Highlight = Mark.create({ name: 'highlight', @@ -17,6 +18,10 @@ export const Highlight = Mark.create({ addAttributes() { return { + /** + * @category Attribute + * @param {string} [color] - Background color (CSS color value) + */ color: { default: null, parseDOM: (element) => element.getAttribute('data-color') || element.style.backgroundColor, diff --git a/packages/super-editor/src/extensions/italic/italic.js b/packages/super-editor/src/extensions/italic/italic.js index beedb6652f..46a131cf2d 100644 --- a/packages/super-editor/src/extensions/italic/italic.js +++ b/packages/super-editor/src/extensions/italic/italic.js @@ -5,6 +5,8 @@ import { Mark, Attribute } from '@core/index.js'; * @module Italic * @sidebarTitle Italic * @snippetPath /snippets/extensions/italic.mdx + * @shortcut Mod-i | toggleItalic | Toggle italic formatting + * @shortcut Mod-I | toggleItalic | Toggle italic formatting (uppercase) */ export const Italic = Mark.create({ name: 'italic', diff --git a/packages/super-editor/src/extensions/link/link.js b/packages/super-editor/src/extensions/link/link.js index 828d53e367..1af81e5528 100644 --- a/packages/super-editor/src/extensions/link/link.js +++ b/packages/super-editor/src/extensions/link/link.js @@ -55,6 +55,10 @@ export const Link = Mark.create({ addAttributes() { return { + /** + * @category Attribute + * @param {string} [href] - URL or anchor reference + */ href: { default: null, renderDOM: ({ href, name }) => { @@ -63,10 +67,31 @@ export const Link = Mark.create({ return {}; }, }, + /** + * @category Attribute + * @param {string} [target='_blank'] - Link target window + */ target: { default: this.options.htmlAttributes.target }, + /** + * @category Attribute + * @param {string} [rel='noopener noreferrer nofollow'] - Relationship attributes + */ rel: { default: this.options.htmlAttributes.rel }, + /** + * @private + * @category Attribute + * @param {string} [rId] - Word relationship ID for internal links + */ rId: { default: this.options.htmlAttributes.rId || null }, + /** + * @category Attribute + * @param {string} [text] - Display text for the link + */ text: { default: null }, + /** + * @category Attribute + * @param {string} [name] - Anchor name for internal references + */ name: { default: null }, }; }, diff --git a/packages/super-editor/src/extensions/strike/strike.js b/packages/super-editor/src/extensions/strike/strike.js index 20727e15b1..43b0b8d259 100644 --- a/packages/super-editor/src/extensions/strike/strike.js +++ b/packages/super-editor/src/extensions/strike/strike.js @@ -5,6 +5,7 @@ import { Mark, Attribute } from '@core/index.js'; * @module Strike * @sidebarTitle Strike * @snippetPath /snippets/extensions/strike.mdx + * @shortcut Mod-Shift-s | toggleStrike | Toggle strikethrough formatting */ export const Strike = Mark.create({ name: 'strike', diff --git a/packages/super-editor/src/extensions/structured-content/document-section.js b/packages/super-editor/src/extensions/structured-content/document-section.js index 64c069873f..dc641e4bf2 100644 --- a/packages/super-editor/src/extensions/structured-content/document-section.js +++ b/packages/super-editor/src/extensions/structured-content/document-section.js @@ -75,7 +75,16 @@ export const DocumentSection = Node.create({ addAttributes() { return { + /** + * @category Attribute + * @param {number} [id] - Unique section identifier + */ id: {}, + /** + * @private + * @category Attribute + * @param {string} [sdBlockId] - Internal block tracking + */ sdBlockId: { default: null, keepOnSplit: false, @@ -84,9 +93,25 @@ export const DocumentSection = Node.create({ return attrs.sdBlockId ? { 'data-sd-block-id': attrs.sdBlockId } : {}; }, }, + /** + * @category Attribute + * @param {string} [title] - Section display label + */ title: {}, + /** + * @category Attribute + * @param {string} [description] - Section metadata + */ description: {}, + /** + * @category Attribute + * @param {string} [sectionType] - Business classification (e.g., 'legal', 'pricing') + */ sectionType: {}, + /** + * @category Attribute + * @param {boolean} [isLocked=false] - Lock state preventing edits + */ isLocked: { default: false }, }; }, diff --git a/packages/super-editor/src/extensions/table-cell/helpers/createCellBorders.js b/packages/super-editor/src/extensions/table-cell/helpers/createCellBorders.js index 1ec728580f..98f1556a7f 100644 --- a/packages/super-editor/src/extensions/table-cell/helpers/createCellBorders.js +++ b/packages/super-editor/src/extensions/table-cell/helpers/createCellBorders.js @@ -1,3 +1,30 @@ +// @ts-check +/** + * Cell border configuration + * @typedef {Object} CellBorder + * @property {number} [size=1] - Border width in pixels + * @property {string} [color='#000000'] - Border color + * @property {string} [style='solid'] - Border style + */ + +/** + * Cell borders object + * @typedef {Object} CellBorders + * @property {CellBorder} [top] - Top border + * @property {CellBorder} [right] - Right border + * @property {CellBorder} [bottom] - Bottom border + * @property {CellBorder} [left] - Left border + */ + +/** + * Create cell border configuration object + * @private + * @category Helper + * @param {Object} [options] - Border options + * @param {number} [options.size=0.66665] - Border width in pixels + * @param {string} [options.color='#000000'] - Border color (hex) + * @returns {CellBorders} Complete borders object for all cell sides + */ export const createCellBorders = ({ size = 0.66665, color = '#000000' } = {}) => { return { top: { size, color }, diff --git a/packages/super-editor/src/extensions/table-cell/table-cell.js b/packages/super-editor/src/extensions/table-cell/table-cell.js index 6263a3aa44..5eddebb5f8 100644 --- a/packages/super-editor/src/extensions/table-cell/table-cell.js +++ b/packages/super-editor/src/extensions/table-cell/table-cell.js @@ -1,6 +1,27 @@ +// @ts-check +/** + * Cell margins configuration + * @typedef {Object} CellMargins + * @property {number} [top] - Top margin in pixels + * @property {number} [right] - Right margin in pixels + * @property {number} [bottom] - Bottom margin in pixels + * @property {number} [left] - Left margin in pixels + */ + +/** + * Cell background configuration + * @typedef {Object} CellBackground + * @property {string} color - Background color (hex without #) + */ + import { Node, Attribute } from '@core/index.js'; import { createCellBorders } from './helpers/createCellBorders.js'; +/** + * @module TableCell + * @sidebarTitle Table Cell + * @snippetPath /snippets/extensions/table-cell.mdx + */ export const TableCell = Node.create({ name: 'tableCell', @@ -20,14 +41,26 @@ export const TableCell = Node.create({ addAttributes() { return { + /** + * @category Attribute + * @param {number} [colspan=1] - Number of columns this cell spans + */ colspan: { default: 1, }, + /** + * @category Attribute + * @param {number} [rowspan=1] - Number of rows this cell spans + */ rowspan: { default: 1, }, + /** + * @category Attribute + * @param {number[]} [colwidth=[100]] - Column widths array in pixels + */ colwidth: { default: [100], parseDOM: (elem) => { @@ -43,16 +76,10 @@ export const TableCell = Node.create({ }, }, - /* width: { - renderDOM: ({ width, widthType, widthUnit }) => { - if (!width) return {}; - let unit = widthUnit === 'px' ? widthUnit : 'in'; - if (widthType === 'pct') unit = '%'; - const style = `width: ${width}${unit}`; - return { style }; - }, - }, */ - + /** + * @category Attribute + * @param {CellBackground} [background] - Cell background color configuration + */ background: { renderDOM({ background }) { if (!background) return {}; @@ -62,6 +89,10 @@ export const TableCell = Node.create({ }, }, + /** + * @category Attribute + * @param {string} [verticalAlign] - Vertical content alignment (top, middle, bottom) + */ verticalAlign: { renderDOM({ verticalAlign }) { if (!verticalAlign) return {}; @@ -70,6 +101,10 @@ export const TableCell = Node.create({ }, }, + /** + * @category Attribute + * @param {CellMargins} [cellMargins] - Internal cell padding + */ cellMargins: { renderDOM({ cellMargins }) { if (!cellMargins) return {}; @@ -85,6 +120,10 @@ export const TableCell = Node.create({ }, }, + /** + * @category Attribute + * @param {CellBorders} [borders] - Cell border configuration + */ borders: { default: () => createCellBorders(), renderDOM({ borders }) { @@ -102,11 +141,21 @@ export const TableCell = Node.create({ }, }, + /** + * @private + * @category Attribute + * @param {string} [widthType='auto'] - Internal width type + */ widthType: { default: 'auto', rendered: false, }, + /** + * @private + * @category Attribute + * @param {string} [widthUnit='px'] - Internal width unit + */ widthUnit: { default: 'px', rendered: false, diff --git a/packages/super-editor/src/extensions/table-header/table-header.js b/packages/super-editor/src/extensions/table-header/table-header.js index c3375f6331..c7420de35b 100644 --- a/packages/super-editor/src/extensions/table-header/table-header.js +++ b/packages/super-editor/src/extensions/table-header/table-header.js @@ -1,5 +1,11 @@ +// @ts-check import { Node, Attribute } from '@core/index.js'; +/** + * @module TableHeader + * @sidebarTitle Table Header + * @snippetPath /snippets/extensions/table-header.mdx + */ export const TableHeader = Node.create({ name: 'tableHeader', @@ -19,12 +25,26 @@ export const TableHeader = Node.create({ addAttributes() { return { + /** + * @category Attribute + * @param {number} [colspan=1] - Number of columns this header spans + */ colspan: { default: 1, }, + + /** + * @category Attribute + * @param {number} [rowspan=1] - Number of rows this header spans + */ rowspan: { default: 1, }, + + /** + * @category Attribute + * @param {number[]} [colwidth] - Column widths array in pixels + */ colwidth: { default: null, parseDOM: (element) => { diff --git a/packages/super-editor/src/extensions/table-row/table-row.js b/packages/super-editor/src/extensions/table-row/table-row.js index 36795d0b54..b451e4ce44 100644 --- a/packages/super-editor/src/extensions/table-row/table-row.js +++ b/packages/super-editor/src/extensions/table-row/table-row.js @@ -1,5 +1,11 @@ +// @ts-check import { Node, Attribute } from '@core/index.js'; +/** + * @module TableRow + * @sidebarTitle Table Row + * @snippetPath /snippets/extensions/table-row.mdx + */ export const TableRow = Node.create({ name: 'tableRow', @@ -17,6 +23,10 @@ export const TableRow = Node.create({ addAttributes() { return { + /** + * @category Attribute + * @param {number} [rowHeight] - Fixed row height in pixels + */ rowHeight: { renderDOM({ rowHeight }) { if (!rowHeight) return {}; diff --git a/packages/super-editor/src/extensions/table/table.js b/packages/super-editor/src/extensions/table/table.js index cd529a346a..57e4b8b5ab 100644 --- a/packages/super-editor/src/extensions/table/table.js +++ b/packages/super-editor/src/extensions/table/table.js @@ -1,3 +1,60 @@ +// @ts-check + +/** + * Table configuration options + * @typedef {Object} TableConfig + * @property {number} [rows=3] - Number of rows to create + * @property {number} [cols=3] - Number of columns to create + * @property {boolean} [withHeaderRow=false] - Create first row as header row + */ + +/** + * Table attributes + * @typedef {Object} TableAttributes + * @property {Object} [tableIndent] - Table indentation + * @property {number} tableIndent.width - Indent width in pixels + * @property {string} [tableIndent.type='dxa'] - Indent type + * @property {import("./tableHelpers/createTableBorders.js").TableBorders} [borders] - Table border configuration + * @property {string} [borderCollapse='collapse'] - CSS border-collapse value + * @property {string} [tableStyleId] - Reference to table style ID + * @property {string} [tableLayout] - Table layout algorithm + * @property {number} [tableCellSpacing] - Cell spacing in pixels + */ + +/** + * Cell selection position + * @typedef {Object} CellSelectionPosition + * @property {number} anchorCell - Starting cell position + * @property {number} headCell - Ending cell position + */ + +/** + * Column group information + * @typedef {Object} ColGroupInfo + * @property {Array} [colgroup] - Column group DOM structure + * @property {string} [tableWidth] - Fixed table width or empty string + * @property {string} [tableMinWidth] - Minimum table width or empty string + * @property {number[]} [colgroupValues] - Array of column width values + */ + +/** + * Position resolution result + * @private + * @typedef {Object} CellPosition + * @property {Object} $pos - Resolved position + * @property {number} pos - Absolute position + * @property {number} depth - Depth in document tree + */ + +/** + * Current cell information + * @private + * @typedef {Object} CurrentCellInfo + * @property {Object} rect - Selected rectangle from ProseMirror + * @property {Object} cell - Current cell node + * @property {Object} attrs - Cell attributes without span properties + */ + import { Node, Attribute } from '@core/index.js'; import { callOrGet } from '@core/utilities/callOrGet.js'; import { getExtensionConfigField } from '@core/helpers/getExtensionConfigField.js'; @@ -23,7 +80,7 @@ import { deleteTable, fixTables, goToNextCell, - mergeCells, + mergeCells as originalMergeCells, setCellAttr, splitCell as originalSplitCell, tableEditing, @@ -37,6 +94,15 @@ import { import { cellAround } from './tableHelpers/cellAround.js'; import { cellWrapping } from './tableHelpers/cellWrapping.js'; +/** + * @module Table + * @sidebarTitle Table + * @snippetPath /snippets/extensions/table.mdx + * @shortcut Tab | goToNextCell/addRowAfter | Navigate to next cell or add row + * @shortcut Shift-Tab | goToPreviousCell | Navigate to previous cell + * @shortcut Backspace | deleteTableWhenSelected | Delete table when all cells selected + * @shortcut Delete | deleteTableWhenSelected | Delete table when all cells selected + */ export const Table = Node.create({ name: 'table', @@ -48,6 +114,17 @@ export const Table = Node.create({ tableRole: 'table', + /** + * Table extension options + * @category Options + * @typedef {Object} TableOptions + * @property {Object} [htmlAttributes={'aria-label': 'Table node'}] - Default HTML attributes for all tables + * @property {boolean} [resizable=true] - Enable column resizing functionality + * @property {number} [handleWidth=5] - Width of resize handles in pixels + * @property {number} [cellMinWidth=10] - Minimum cell width constraint in pixels + * @property {boolean} [lastColumnResizable=true] - Allow resizing of the last column + * @property {boolean} [allowTableNodeSelection=false] - Enable selecting the entire table node + */ addOptions() { return { htmlAttributes: { @@ -72,6 +149,12 @@ export const Table = Node.create({ }; }, }, */ + + /** + * @private + * @category Attribute + * @param {string} [sdBlockId] - Internal block tracking ID (not user-configurable) + */ sdBlockId: { default: null, keepOnSplit: false, @@ -81,6 +164,10 @@ export const Table = Node.create({ }, }, + /** + * @category Attribute + * @param {TableIndent} [tableIndent] - Table indentation configuration + */ tableIndent: { renderDOM: ({ tableIndent }) => { if (!tableIndent) return {}; @@ -93,6 +180,10 @@ export const Table = Node.create({ }, }, + /** + * @category Attribute + * @param {import("./tableHelpers/createTableBorders.js").TableBorders} [borders] - Border styling for this table + */ borders: { default: {}, renderDOM({ borders }) { @@ -107,6 +198,10 @@ export const Table = Node.create({ }, }, + /** + * @category Attribute + * @param {string} [borderCollapse='collapse'] - CSS border-collapse property + */ borderCollapse: { default: null, renderDOM({ borderCollapse }) { @@ -116,6 +211,10 @@ export const Table = Node.create({ }, }, + /** + * @category Attribute + * @param {string} [justification] - Table alignment ('left', 'center', 'right') + */ justification: { default: null, renderDOM: (attrs) => { @@ -132,14 +231,28 @@ export const Table = Node.create({ }, }, + /** + * @private + * @category Attribute + * @param {string} [tableStyleId] - Internal reference to table style (not user-configurable) + */ tableStyleId: { rendered: false, }, + /** + * @private + * @category Attribute + * @param {string} [tableLayout] - CSS table-layout property (advanced usage) + */ tableLayout: { rendered: false, }, + /** + * @category Attribute + * @param {number} [tableCellSpacing] - Cell spacing in pixels for this table + */ tableCellSpacing: { default: null, rendered: false, @@ -165,6 +278,19 @@ export const Table = Node.create({ addCommands() { return { + /** + * Insert a new table into the document + * @category Command + * @param {TableConfig} [config] - Table configuration options + * @returns {Function} Command + * @example + * // Using default values + * insertTable() // Creates 3x3 table without header + * + * // Using custom values + * insertTable({ rows: 3, cols: 3, withHeaderRow: true }) + * + */ insertTable: ({ rows = 3, cols = 3, withHeaderRow = false } = {}) => ({ tr, dispatch, editor }) => { @@ -180,12 +306,27 @@ export const Table = Node.create({ return true; }, + /** + * Delete the entire table containing the cursor + * @category Command + * @returns {Function} Command + * @example + * deleteTable() + */ deleteTable: () => ({ state, dispatch }) => { return deleteTable(state, dispatch); }, + /** + * Add a column before the current column + * @category Command + * @returns {Function} Command + * @example + * addColumnBefore() + * @note Preserves cell attributes from current column + */ addColumnBefore: () => ({ state, dispatch, chain }) => { @@ -226,6 +367,14 @@ export const Table = Node.create({ .run(); }, + /** + * Add a column after the current column + * @category Command + * @returns {Function} Command + * @example + * addColumnAfter() + * @note Preserves cell attributes from current column + */ addColumnAfter: () => ({ state, dispatch, chain }) => { @@ -266,12 +415,27 @@ export const Table = Node.create({ .run(); }, + /** + * Delete the column containing the cursor + * @category Command + * @returns {Function} Command + * @example + * deleteColumn() + */ deleteColumn: () => ({ state, dispatch }) => { return deleteColumn(state, dispatch); }, + /** + * Add a row before the current row + * @category Command + * @returns {Function} Command + * @example + * addRowBefore() + * @note Preserves cell attributes from current row + */ addRowBefore: () => ({ state, dispatch, chain }) => { @@ -312,6 +476,14 @@ export const Table = Node.create({ .run(); }, + /** + * Add a row after the current row + * @category Command + * @returns {Function} Command + * @example + * addRowAfter() + * @note Preserves cell attributes from current row + */ addRowAfter: () => ({ state, dispatch, chain }) => { @@ -350,18 +522,40 @@ export const Table = Node.create({ .run(); }, + /** + * Delete the row containing the cursor + * @category Command + * @returns {Function} Command + * @example + * deleteRow() + */ deleteRow: () => ({ state, dispatch }) => { return deleteRow(state, dispatch); }, + /** + * Merge selected cells into one + * @category Command + * @returns {Function} Command + * @example + * mergeCells() + * @note Content from all cells is preserved + */ mergeCells: () => ({ state, dispatch }) => { - return mergeCells(state, dispatch); + return originalMergeCells(state, dispatch); }, + /** + * Split a merged cell back into individual cells + * @category Command + * @returns {Function} Command - true if split, false if position invalid + * @example + * splitCell() + */ splitCell: () => ({ state, dispatch, commands }) => { @@ -372,6 +566,19 @@ export const Table = Node.create({ return commands.splitSingleCell(); }, + /** + * Split a single unmerged cell into two cells horizontally + * @category Command + * @returns {Function} Command - true if split, false if position invalid + * @example + * splitSingleCell() + * @note This command splits a single cell (not merged) into two cells by: + * - Dividing the cell width in half + * - Inserting a new cell to the right + * - Adjusting colspan for cells in other rows that span this column + * - Only works on cells with colspan=1 and rowspan=1 + * @note Different from splitCell which splits merged cells back to original cells + */ splitSingleCell: () => ({ state, dispatch, tr }) => { @@ -461,52 +668,113 @@ export const Table = Node.create({ return true; }, + /** + * Toggle between merge and split cells based on selection + * @category Command + * @returns {Function} Command + * @example + * mergeOrSplit() + * @note Merges if multiple cells selected, splits if merged cell selected + */ mergeOrSplit: () => - ({ state, dispatch }) => { - if (mergeCells(state, dispatch)) { + ({ state, dispatch, commands }) => { + if (originalMergeCells(state, dispatch)) { return true; } - return splitCell(state, dispatch); + return commands.splitCell(); }, + /** + * Toggle the first column as header column + * @category Command + * @returns {Function} Command + * @example + * toggleHeaderColumn() + */ toggleHeaderColumn: () => ({ state, dispatch }) => { return toggleHeader('column')(state, dispatch); }, + /** + * Toggle the first row as header row + * @category Command + * @returns {Function} Command + * @example + * toggleHeaderRow() + */ toggleHeaderRow: () => ({ state, dispatch }) => { return toggleHeader('row')(state, dispatch); }, + /** + * Toggle current cell as header cell + * @category Command + * @returns {Function} Command + * @example + * toggleHeaderCell() + */ toggleHeaderCell: () => ({ state, dispatch }) => { return toggleHeaderCell(state, dispatch); }, + /** + * Set an attribute on selected cells + * @category Command + * @param {string} name - Attribute name + * @param {*} value - Attribute value + * @returns {Function} Command + * @example + * setCellAttr('background', { color: 'ff0000' }) + * setCellAttr('verticalAlign', 'middle') + */ setCellAttr: (name, value) => ({ state, dispatch }) => { return setCellAttr(name, value)(state, dispatch); }, + /** + * Navigate to the next cell (Tab behavior) + * @category Command + * @returns {Function} Command + * @example + * goToNextCell() + */ goToNextCell: () => ({ state, dispatch }) => { return goToNextCell(1)(state, dispatch); }, + /** + * Navigate to the previous cell (Shift+Tab behavior) + * @category Command + * @returns {Function} Command + * @example + * goToPreviousCell() + */ goToPreviousCell: () => ({ state, dispatch }) => { return goToNextCell(-1)(state, dispatch); }, + /** + * Fix table structure inconsistencies + * @category Command + * @returns {Function} Command + * @example + * fixTables() + * @note Repairs malformed tables and normalizes structure + */ fixTables: () => ({ state, dispatch }) => { @@ -517,6 +785,14 @@ export const Table = Node.create({ return true; }, + /** + * Set cell selection programmatically + * @category Command + * @param {CellSelectionPosition} pos - Cell selection coordinates + * @returns {Function} Command + * @example + * setCellSelection({ anchorCell: 10, headCell: 15 }) + */ setCellSelection: (pos) => ({ tr, dispatch }) => { @@ -527,6 +803,15 @@ export const Table = Node.create({ return true; }, + /** + * Set background color for selected cells + * @category Command + * @param {string} value - Color value (hex with or without #) + * @returns {Function} Command + * @example + * setCellBackground('#ff0000') + * setCellBackground('ff0000') + */ setCellBackground: (value) => ({ editor, commands, dispatch }) => { @@ -545,6 +830,14 @@ export const Table = Node.create({ return true; }, + /** + * Remove all borders from table and its cells + * @category Command + * @returns {Function} Command + * @example + * deleteCellAndTableBorders() + * @note Sets all border sizes to 0 + */ deleteCellAndTableBorders: () => ({ state, tr }) => { @@ -638,11 +931,26 @@ export const Table = Node.create({ }, }); +/** + * Get the cell type based on table role + * @private + * @param {Object} params - Parameters + * @param {Object} params.node - Cell node + * @param {Object} params.state - Editor state + * @returns {Object} Cell node type + */ function getCellType({ node, state }) { const nodeTypes = tableNodeTypes(state.schema); return nodeTypes[node.type.spec.tableRole]; } +/** + * Copy cell attributes excluding span properties + * @private + * @param {Object} node - Cell node + * @returns {Object} Filtered attributes without colspan, rowspan, colwidth + * @note Used when creating new cells to preserve styling but not structure + */ function copyCellAttrs(node) { // Exclude colspan, rowspan and colwidth attrs. // eslint-disable-next-line no-unused-vars @@ -650,6 +958,12 @@ function copyCellAttrs(node) { return attrs; } +/** + * Get current cell attributes from selection + * @private + * @param {Object} state - Editor state + * @returns {CurrentCellInfo} Current cell information + */ function getCurrentCellAttrs(state) { let rect = selectedRect(state); let index = rect.top * rect.map.width + rect.left; diff --git a/packages/super-editor/src/extensions/table/tableHelpers/cellAround.js b/packages/super-editor/src/extensions/table/tableHelpers/cellAround.js index f082cfe47e..f2e6ee362c 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/cellAround.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/cellAround.js @@ -1,3 +1,4 @@ +// @ts-check // https://github.com/ProseMirror/prosemirror-tables/blob/a99f70855f2b3e2433bc77451fedd884305fda5b/src/util.ts export function cellAround($pos) { for (let d = $pos.depth - 1; d > 0; d--) diff --git a/packages/super-editor/src/extensions/table/tableHelpers/cellWrapping.js b/packages/super-editor/src/extensions/table/tableHelpers/cellWrapping.js index 294bb2e70d..a9df014313 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/cellWrapping.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/cellWrapping.js @@ -1,3 +1,4 @@ +// @ts-check // https://github.com/ProseMirror/prosemirror-tables/blob/a99f70855f2b3e2433bc77451fedd884305fda5b/src/util.ts export function cellWrapping($pos) { for (let d = $pos.depth; d > 0; d--) { diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createCell.js b/packages/super-editor/src/extensions/table/tableHelpers/createCell.js index 867ecc5bb3..81a4dce9cc 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createCell.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createCell.js @@ -1,3 +1,4 @@ +// @ts-check export const createCell = (cellType, cellContent = null) => { if (cellContent) { return cellType.createChecked(null, cellContent); diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js b/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js index 0c95c04a49..7eed030cac 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js @@ -1,3 +1,4 @@ +// @ts-check import { getColStyleDeclaration } from './getColStyleDeclaration.js'; export const createColGroup = (node, cellMinWidth, overrideCol, overrideValue) => { diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createTable.js b/packages/super-editor/src/extensions/table/tableHelpers/createTable.js index f2c28fd85b..749c9f38fe 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createTable.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createTable.js @@ -1,7 +1,23 @@ +// @ts-check import { getNodeType } from '@core/helpers/getNodeType.js'; import { createCell } from './createCell.js'; import { createTableBorders } from './createTableBorders.js'; +/** + * Create a new table with specified dimensions + * @private + * @category Helper + * @param {Object} schema - Editor schema + * @param {number} rowsCount - Number of rows + * @param {number} colsCount - Number of columns + * @param {boolean} withHeaderRow - Create first row as header + * @param {Object} [cellContent=null] - Initial cell content + * @returns {Object} Complete table node with borders + * @example + * const table = createTable(schema, 3, 3, true) + * @example + * const table = createTable(schema, 2, 4, false, paragraphNode) + */ export const createTable = (schema, rowsCount, colsCount, withHeaderRow, cellContent = null) => { const types = { table: getNodeType('table', schema), diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createTableBorders.js b/packages/super-editor/src/extensions/table/tableHelpers/createTableBorders.js index 94eda734c1..c8b6c9710d 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createTableBorders.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createTableBorders.js @@ -1,3 +1,43 @@ +// @ts-check +/** + * Table border configuration + * @typedef {Object} TableBorder + * @property {number} [size=1] - Border width in pixels + * @property {string} [color='#000000'] - Border color (hex or CSS color) + * @property {string} [style='solid'] - Border style (solid, dashed, dotted) + */ + +/** + * Table borders object + * @typedef {Object} TableBorders + * @property {TableBorder} [top] - Top border configuration + * @property {TableBorder} [right] - Right border configuration + * @property {TableBorder} [bottom] - Bottom border configuration + * @property {TableBorder} [left] - Left border configuration + * @property {TableBorder} [insideH] - Inside horizontal borders + * @property {TableBorder} [insideV] - Inside vertical borders + */ +/** + * Border creation options + * @typedef {Object} BorderOptions + * @property {number} [size=0.66665] - Border width in pixels + * @property {string} [color='#000000'] - Border color (hex) + */ + +/** + * Create table border configuration object + * @private + * @category Helper + * @param {BorderOptions} [options] - Border options + * @returns {TableBorders} Complete borders object for all sides + * @example + * // Using default values + * const borders = createTableBorders() + * + * // Using custom values + * const borders = createTableBorders({ size: 1, color: '#cccccc' }) + * @note Creates uniform borders for all sides including inside borders + */ export const createTableBorders = ({ size = 0.66665, color = '#000000' } = {}) => { return { top: { size, color }, diff --git a/packages/super-editor/src/extensions/table/tableHelpers/deleteTableWhenSelected.js b/packages/super-editor/src/extensions/table/tableHelpers/deleteTableWhenSelected.js index c4f513409d..25e027956b 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/deleteTableWhenSelected.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/deleteTableWhenSelected.js @@ -1,3 +1,4 @@ +// @ts-check import { findParentNodeClosestToPos } from '@core/helpers/findParentNodeClosestToPos.js'; import { isCellSelection } from './isCellSelection.js'; diff --git a/packages/super-editor/src/extensions/table/tableHelpers/getColStyleDeclaration.js b/packages/super-editor/src/extensions/table/tableHelpers/getColStyleDeclaration.js index c0938194d4..4b9760e26e 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/getColStyleDeclaration.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/getColStyleDeclaration.js @@ -1,3 +1,4 @@ +// @ts-check export const getColStyleDeclaration = (minWidth, width) => { if (width) { // Apply the stored width unless it is below the configured minimum cell width. diff --git a/packages/super-editor/src/extensions/table/tableHelpers/isCellSelection.js b/packages/super-editor/src/extensions/table/tableHelpers/isCellSelection.js index 3ab2b9820e..9c51d9f086 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/isCellSelection.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/isCellSelection.js @@ -1,3 +1,15 @@ +// @ts-check import { CellSelection } from 'prosemirror-tables'; +/** + * Check if selection is a cell selection + * @private + * @category Helper + * @param {*} value - Selection to check + * @returns {boolean} True if cell selection + * @example + * if (isCellSelection(editor.state.selection)) { + * // Handle cell selection + * } + */ export const isCellSelection = (value) => value instanceof CellSelection; diff --git a/packages/super-editor/src/extensions/underline/underline.js b/packages/super-editor/src/extensions/underline/underline.js index 91ffa3df00..ed0529ed89 100644 --- a/packages/super-editor/src/extensions/underline/underline.js +++ b/packages/super-editor/src/extensions/underline/underline.js @@ -1,7 +1,8 @@ // @ts-check /** - * Underline style types - * @typedef {'single'|'double'|'thick'|'dotted'|'dashed'|'wavy'} UnderlineType + * Underline style configuration + * @typedef {Object} UnderlineConfig + * @property {'single'|'double'|'thick'|'dotted'|'dashed'|'wavy'} value - Style variant */ import { Mark, Attribute } from '@core/index.js'; @@ -10,6 +11,8 @@ import { Mark, Attribute } from '@core/index.js'; * @module Underline * @sidebarTitle Underline * @snippetPath /snippets/extensions/underline.mdx + * @shortcut Mod-u | toggleUnderline | Toggle underline formatting + * @shortcut Mod-U | toggleUnderline | Toggle underline formatting (uppercase) */ export const Underline = Mark.create({ name: 'underline', @@ -34,6 +37,10 @@ export const Underline = Mark.create({ addAttributes() { return { + /** + * @category Attribute + * @param {UnderlineConfig} [underlineType='single'] - Style of underline + */ underlineType: { default: 'single', },