@scrider/formatter 1.3.3 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/index.cjs +159 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -1
- package/dist/index.d.ts +70 -1
- package/dist/index.js +155 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,23 @@ deltaToHtml(delta, { registry }) // Delta → HTML string
|
|
|
77
77
|
htmlToDelta(html, { registry }) // HTML string → Delta
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
**Simple Table presentation (v1.3.4+)** — inline borders/shades for clipboard and Office paste (Delta structure unchanged):
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { deltaToHtml, type TablePresentation } from '@scrider/formatter';
|
|
84
|
+
|
|
85
|
+
const tablePresentation: TablePresentation = {
|
|
86
|
+
grid: true,
|
|
87
|
+
borderColor: '#e7e7e7',
|
|
88
|
+
headerBold: true,
|
|
89
|
+
headerCenter: true,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
deltaToHtml(delta, { tablePresentation });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
See scrider-editor `docs/simple-tables.md` §8 for the full contract (`grid` / `line`, `headerShade`, `zebraRows`, `defaultCellAlign`, …).
|
|
96
|
+
|
|
80
97
|
### Markdown Conversion
|
|
81
98
|
|
|
82
99
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,7 @@ __export(index_exports, {
|
|
|
61
61
|
deltaToHtml: () => deltaToHtml,
|
|
62
62
|
deltaToMarkdown: () => deltaToMarkdown,
|
|
63
63
|
dividerFormat: () => dividerFormat,
|
|
64
|
+
documentPresentationStyleParts: () => documentPresentationStyleParts,
|
|
64
65
|
escapeHtml: () => escapeHtml,
|
|
65
66
|
extractBoxOpAttributes: () => extractBoxOpAttributes,
|
|
66
67
|
extractTableRegion: () => extractTableRegion,
|
|
@@ -82,6 +83,7 @@ __export(index_exports, {
|
|
|
82
83
|
isTextNode: () => isTextNode,
|
|
83
84
|
isValidColor: () => isValidColor,
|
|
84
85
|
isValidHexColor: () => isValidHexColor,
|
|
86
|
+
isZebraBodyRow: () => isZebraBodyRow,
|
|
85
87
|
italicFormat: () => italicFormat,
|
|
86
88
|
kbdFormat: () => kbdFormat,
|
|
87
89
|
linkFormat: () => linkFormat,
|
|
@@ -92,6 +94,8 @@ __export(index_exports, {
|
|
|
92
94
|
nodeAdapter: () => nodeAdapter,
|
|
93
95
|
normalizeDelta: () => normalizeDelta,
|
|
94
96
|
preloadRemark: () => preloadRemark,
|
|
97
|
+
resolveDocumentPresentation: () => resolveDocumentPresentation,
|
|
98
|
+
resolveTablePresentation: () => resolveTablePresentation,
|
|
95
99
|
sanitizeDelta: () => sanitizeDelta,
|
|
96
100
|
sizeFormat: () => sizeFormat,
|
|
97
101
|
slugify: () => slugify,
|
|
@@ -2599,6 +2603,94 @@ function slugifyWithDedup(text, usedSlugs) {
|
|
|
2599
2603
|
return `${base}-${count}`;
|
|
2600
2604
|
}
|
|
2601
2605
|
|
|
2606
|
+
// src/conversion/html/document-presentation.ts
|
|
2607
|
+
function resolveDocumentPresentation(presentation) {
|
|
2608
|
+
if (!presentation) return void 0;
|
|
2609
|
+
const lineSpacing = typeof presentation.lineSpacing === "number" && presentation.lineSpacing > 0 ? presentation.lineSpacing : void 0;
|
|
2610
|
+
const textIndentCm = typeof presentation.textIndentCm === "number" && presentation.textIndentCm > 0 ? presentation.textIndentCm : void 0;
|
|
2611
|
+
if (lineSpacing === void 0 && textIndentCm === void 0) return void 0;
|
|
2612
|
+
return { lineSpacing, textIndentCm };
|
|
2613
|
+
}
|
|
2614
|
+
var LINE_HEIGHT_TAGS = /* @__PURE__ */ new Set(["p", "li", "blockquote"]);
|
|
2615
|
+
function documentPresentationStyleParts(tag, resolved) {
|
|
2616
|
+
if (!resolved) return [];
|
|
2617
|
+
const parts = [];
|
|
2618
|
+
if (resolved.lineSpacing !== void 0 && LINE_HEIGHT_TAGS.has(tag)) {
|
|
2619
|
+
const pct = Math.round(resolved.lineSpacing * 100);
|
|
2620
|
+
parts.push(`line-height:${resolved.lineSpacing}`);
|
|
2621
|
+
parts.push(`mso-line-height-alt:${pct}%`);
|
|
2622
|
+
}
|
|
2623
|
+
if (resolved.textIndentCm !== void 0 && tag === "p") {
|
|
2624
|
+
parts.push(`text-indent:${resolved.textIndentCm}cm`);
|
|
2625
|
+
}
|
|
2626
|
+
return parts;
|
|
2627
|
+
}
|
|
2628
|
+
function joinStyleParts(parts) {
|
|
2629
|
+
return parts.length > 0 ? ` style="${parts.join("; ")}"` : "";
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
// src/conversion/html/table-presentation.ts
|
|
2633
|
+
var DEFAULT_BORDER_COLOR = "#e7e7e7";
|
|
2634
|
+
var DEFAULT_HEADER_BG = "#f5f5f5";
|
|
2635
|
+
var DEFAULT_ZEBRA_BG = "#fafafa";
|
|
2636
|
+
var CELL_PADDING = "6px 13px";
|
|
2637
|
+
function resolveTablePresentation(presentation) {
|
|
2638
|
+
return {
|
|
2639
|
+
grid: presentation?.grid === true,
|
|
2640
|
+
line: presentation?.line === true && presentation?.grid !== true,
|
|
2641
|
+
borderColor: presentation?.borderColor ?? DEFAULT_BORDER_COLOR,
|
|
2642
|
+
headerShade: presentation?.headerShade === true,
|
|
2643
|
+
zebraRows: presentation?.zebraRows === true,
|
|
2644
|
+
headerBold: presentation?.headerBold === true,
|
|
2645
|
+
headerCenter: presentation?.headerCenter === true,
|
|
2646
|
+
defaultCellAlign: presentation?.defaultCellAlign ?? "left"
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function isZebraBodyRow(headerRowCount, bodyRowIndex) {
|
|
2650
|
+
return (headerRowCount + bodyRowIndex + 1) % 2 === 0;
|
|
2651
|
+
}
|
|
2652
|
+
function isTableCellAlign(value) {
|
|
2653
|
+
return value === "left" || value === "center" || value === "right";
|
|
2654
|
+
}
|
|
2655
|
+
function tableOpenTag(presentation) {
|
|
2656
|
+
if (!presentation.grid && !presentation.line) {
|
|
2657
|
+
return "<table>";
|
|
2658
|
+
}
|
|
2659
|
+
return `<table style="border-collapse: collapse">`;
|
|
2660
|
+
}
|
|
2661
|
+
function buildTableCellStyleAttr(params) {
|
|
2662
|
+
const { presentation, cellTag, colAlign, headerRowCount, bodyRowIndex } = params;
|
|
2663
|
+
const parts = [];
|
|
2664
|
+
parts.push(`padding: ${CELL_PADDING}`);
|
|
2665
|
+
const color = presentation.borderColor;
|
|
2666
|
+
if (presentation.grid) {
|
|
2667
|
+
parts.push(`border: 1px solid ${color}`);
|
|
2668
|
+
} else if (presentation.line) {
|
|
2669
|
+
const width = cellTag === "th" ? "1px" : "0.5px";
|
|
2670
|
+
parts.push(`border-bottom: ${width} solid ${color}`);
|
|
2671
|
+
}
|
|
2672
|
+
let textAlign;
|
|
2673
|
+
if (cellTag === "th" && presentation.headerCenter) {
|
|
2674
|
+
textAlign = "center";
|
|
2675
|
+
} else if (isTableCellAlign(colAlign)) {
|
|
2676
|
+
textAlign = colAlign;
|
|
2677
|
+
} else if (colAlign == null || colAlign === "left") {
|
|
2678
|
+
textAlign = presentation.defaultCellAlign;
|
|
2679
|
+
}
|
|
2680
|
+
if (textAlign && textAlign !== "left") {
|
|
2681
|
+
parts.push(`text-align: ${textAlign}`);
|
|
2682
|
+
}
|
|
2683
|
+
if (cellTag === "th" && presentation.headerBold) {
|
|
2684
|
+
parts.push("font-weight: bold");
|
|
2685
|
+
}
|
|
2686
|
+
if (cellTag === "th" && presentation.headerShade) {
|
|
2687
|
+
parts.push(`background-color: ${DEFAULT_HEADER_BG}`);
|
|
2688
|
+
} else if (cellTag === "td" && presentation.zebraRows && bodyRowIndex !== void 0 && isZebraBodyRow(headerRowCount, bodyRowIndex)) {
|
|
2689
|
+
parts.push(`background-color: ${DEFAULT_ZEBRA_BG}`);
|
|
2690
|
+
}
|
|
2691
|
+
return ` style="${parts.join("; ")}"`;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2602
2694
|
// src/conversion/html/delta-to-html.ts
|
|
2603
2695
|
function deltaToHtml(delta, options = {}) {
|
|
2604
2696
|
const lines = splitIntoLines(delta);
|
|
@@ -2607,6 +2699,7 @@ function deltaToHtml(delta, options = {}) {
|
|
|
2607
2699
|
const hierarchicalNumbers = options.hierarchicalNumbers ?? false;
|
|
2608
2700
|
const blockHandlers = options.blockHandlers;
|
|
2609
2701
|
const anchorLinks = options.anchorLinks ?? false;
|
|
2702
|
+
const resolvedDocumentPresentation = resolveDocumentPresentation(options.documentPresentation);
|
|
2610
2703
|
let html = "";
|
|
2611
2704
|
let listStack = [];
|
|
2612
2705
|
let counters = [];
|
|
@@ -2669,7 +2762,14 @@ function deltaToHtml(delta, options = {}) {
|
|
|
2669
2762
|
if (hierarchicalNumbers && listType === "ordered") {
|
|
2670
2763
|
hierarchicalNumber = counters.slice(0, indent + 1).join(".");
|
|
2671
2764
|
}
|
|
2672
|
-
html += renderListItem(
|
|
2765
|
+
html += renderListItem(
|
|
2766
|
+
content,
|
|
2767
|
+
listItemAttrs,
|
|
2768
|
+
pretty,
|
|
2769
|
+
indentLevel,
|
|
2770
|
+
hierarchicalNumber,
|
|
2771
|
+
resolvedDocumentPresentation
|
|
2772
|
+
);
|
|
2673
2773
|
} else {
|
|
2674
2774
|
let headingId;
|
|
2675
2775
|
if (line.attributes?.header) {
|
|
@@ -2681,7 +2781,14 @@ function deltaToHtml(delta, options = {}) {
|
|
|
2681
2781
|
headingId = slugifyWithDedup(plainText, slugUsageMap);
|
|
2682
2782
|
}
|
|
2683
2783
|
}
|
|
2684
|
-
html += renderBlock(
|
|
2784
|
+
html += renderBlock(
|
|
2785
|
+
content,
|
|
2786
|
+
tag,
|
|
2787
|
+
line.attributes,
|
|
2788
|
+
pretty,
|
|
2789
|
+
headingId,
|
|
2790
|
+
resolvedDocumentPresentation
|
|
2791
|
+
);
|
|
2685
2792
|
}
|
|
2686
2793
|
}
|
|
2687
2794
|
html += closeAllLists(listStack, pretty);
|
|
@@ -2782,7 +2889,10 @@ function renderTable(tableLines, embedRenderers, pretty, blockHandlers, options)
|
|
|
2782
2889
|
const bodyRows = sortedRows.filter(([, r]) => !r.isHeader);
|
|
2783
2890
|
const indent = pretty ? " " : "";
|
|
2784
2891
|
const nl = pretty ? "\n" : "";
|
|
2785
|
-
|
|
2892
|
+
const usePresentation = options?.tablePresentation !== void 0;
|
|
2893
|
+
const presentation = usePresentation ? resolveTablePresentation(options.tablePresentation) : null;
|
|
2894
|
+
const headerRowCount = headerRows.length;
|
|
2895
|
+
let html = usePresentation && presentation ? `${tableOpenTag(presentation)}${nl}` : `<table>${nl}`;
|
|
2786
2896
|
if (headerRows.length > 0) {
|
|
2787
2897
|
html += `${indent}<thead>${nl}`;
|
|
2788
2898
|
for (const [, row] of headerRows) {
|
|
@@ -2794,13 +2904,16 @@ function renderTable(tableLines, embedRenderers, pretty, blockHandlers, options)
|
|
|
2794
2904
|
pretty,
|
|
2795
2905
|
2,
|
|
2796
2906
|
blockHandlers,
|
|
2797
|
-
options
|
|
2907
|
+
options,
|
|
2908
|
+
presentation,
|
|
2909
|
+
headerRowCount
|
|
2798
2910
|
);
|
|
2799
2911
|
}
|
|
2800
2912
|
html += `${indent}</thead>${nl}`;
|
|
2801
2913
|
}
|
|
2802
2914
|
if (bodyRows.length > 0) {
|
|
2803
2915
|
html += `${indent}<tbody>${nl}`;
|
|
2916
|
+
let bodyRowIndex = 0;
|
|
2804
2917
|
for (const [, row] of bodyRows) {
|
|
2805
2918
|
html += renderTableRow(
|
|
2806
2919
|
row.cells,
|
|
@@ -2810,8 +2923,12 @@ function renderTable(tableLines, embedRenderers, pretty, blockHandlers, options)
|
|
|
2810
2923
|
pretty,
|
|
2811
2924
|
2,
|
|
2812
2925
|
blockHandlers,
|
|
2813
|
-
options
|
|
2926
|
+
options,
|
|
2927
|
+
presentation,
|
|
2928
|
+
headerRowCount,
|
|
2929
|
+
bodyRowIndex
|
|
2814
2930
|
);
|
|
2931
|
+
bodyRowIndex += 1;
|
|
2815
2932
|
}
|
|
2816
2933
|
html += `${indent}</tbody>${nl}`;
|
|
2817
2934
|
}
|
|
@@ -2819,7 +2936,7 @@ function renderTable(tableLines, embedRenderers, pretty, blockHandlers, options)
|
|
|
2819
2936
|
if (pretty) html += "\n";
|
|
2820
2937
|
return html;
|
|
2821
2938
|
}
|
|
2822
|
-
function renderTableRow(cells, maxCol, cellTag, embedRenderers, pretty, depth, blockHandlers, options) {
|
|
2939
|
+
function renderTableRow(cells, maxCol, cellTag, embedRenderers, pretty, depth, blockHandlers, options, presentation, headerRowCount = 0, bodyRowIndex) {
|
|
2823
2940
|
const indent = pretty ? " ".repeat(depth) : "";
|
|
2824
2941
|
const cellIndent = pretty ? " ".repeat(depth + 1) : "";
|
|
2825
2942
|
const nl = pretty ? "\n" : "";
|
|
@@ -2827,8 +2944,19 @@ function renderTableRow(cells, maxCol, cellTag, embedRenderers, pretty, depth, b
|
|
|
2827
2944
|
for (let col = 0; col <= maxCol; col++) {
|
|
2828
2945
|
const cell = cells.get(col);
|
|
2829
2946
|
const content = cell ? renderLineContent(cell.ops, embedRenderers, blockHandlers, options) : "";
|
|
2830
|
-
|
|
2831
|
-
|
|
2947
|
+
let styleAttr = "";
|
|
2948
|
+
if (presentation) {
|
|
2949
|
+
styleAttr = buildTableCellStyleAttr({
|
|
2950
|
+
presentation,
|
|
2951
|
+
cellTag,
|
|
2952
|
+
colAlign: cell?.colAlign,
|
|
2953
|
+
headerRowCount,
|
|
2954
|
+
bodyRowIndex: cellTag === "td" ? bodyRowIndex : void 0
|
|
2955
|
+
});
|
|
2956
|
+
} else {
|
|
2957
|
+
styleAttr = cell?.colAlign && cell.colAlign !== "left" ? ` style="text-align: ${cell.colAlign}"` : "";
|
|
2958
|
+
}
|
|
2959
|
+
html += `${cellIndent}<${cellTag}${styleAttr}>${content}</${cellTag}>${nl}`;
|
|
2832
2960
|
}
|
|
2833
2961
|
html += `${indent}</tr>${nl}`;
|
|
2834
2962
|
return html;
|
|
@@ -2952,36 +3080,41 @@ function getListItemAttributes(attributes) {
|
|
|
2952
3080
|
}
|
|
2953
3081
|
return "";
|
|
2954
3082
|
}
|
|
2955
|
-
function renderListItem(content, attrs, pretty, indentLevel, hierarchicalNumber) {
|
|
3083
|
+
function renderListItem(content, attrs, pretty, indentLevel, hierarchicalNumber, resolvedDocumentPresentation) {
|
|
2956
3084
|
const indent = pretty ? getIndent(indentLevel) : "";
|
|
2957
3085
|
const innerContent = content || "<br>";
|
|
2958
3086
|
let fullAttrs = attrs;
|
|
2959
3087
|
if (hierarchicalNumber) {
|
|
2960
3088
|
fullAttrs += ` data-number="${hierarchicalNumber}"`;
|
|
2961
3089
|
}
|
|
3090
|
+
const styleAttr = joinStyleParts(
|
|
3091
|
+
documentPresentationStyleParts("li", resolvedDocumentPresentation)
|
|
3092
|
+
);
|
|
3093
|
+
fullAttrs += styleAttr;
|
|
2962
3094
|
const html = `${indent}<li${fullAttrs}>${innerContent}</li>`;
|
|
2963
3095
|
return pretty ? html + "\n" : html;
|
|
2964
3096
|
}
|
|
2965
|
-
function renderBlock(content, tag, attributes, pretty, id) {
|
|
3097
|
+
function renderBlock(content, tag, attributes, pretty, id, resolvedDocumentPresentation) {
|
|
2966
3098
|
const idAttr = id ? ` id="${escapeHtml(id)}"` : "";
|
|
2967
|
-
const styleAttr = getBlockStyleAttribute(attributes);
|
|
3099
|
+
const styleAttr = getBlockStyleAttribute(tag, attributes, resolvedDocumentPresentation);
|
|
2968
3100
|
const innerContent = content || "<br>";
|
|
2969
3101
|
const html = `<${tag}${idAttr}${styleAttr}>${innerContent}</${tag}>`;
|
|
2970
3102
|
return pretty ? html + "\n" : html;
|
|
2971
3103
|
}
|
|
2972
|
-
function getBlockStyleAttribute(attributes) {
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3104
|
+
function getBlockStyleAttribute(tag, attributes, resolvedDocumentPresentation) {
|
|
3105
|
+
const styles = documentPresentationStyleParts(tag, resolvedDocumentPresentation);
|
|
3106
|
+
if (attributes) {
|
|
3107
|
+
const alignVal = attributes.align;
|
|
3108
|
+
if (alignVal && typeof alignVal === "string" && alignVal !== "left") {
|
|
3109
|
+
styles.push(`text-align: ${alignVal}`);
|
|
3110
|
+
}
|
|
3111
|
+
if (attributes.indent && typeof attributes.indent === "number") {
|
|
3112
|
+
if (!attributes.list) {
|
|
3113
|
+
styles.push(`margin-left: ${attributes.indent * 2}em`);
|
|
3114
|
+
}
|
|
2982
3115
|
}
|
|
2983
3116
|
}
|
|
2984
|
-
return styles
|
|
3117
|
+
return joinStyleParts(styles);
|
|
2985
3118
|
}
|
|
2986
3119
|
function extractPlainText(ops) {
|
|
2987
3120
|
let text = "";
|
|
@@ -5134,6 +5267,7 @@ function extractTableRegion(ops, hintOpIdx) {
|
|
|
5134
5267
|
deltaToHtml,
|
|
5135
5268
|
deltaToMarkdown,
|
|
5136
5269
|
dividerFormat,
|
|
5270
|
+
documentPresentationStyleParts,
|
|
5137
5271
|
escapeHtml,
|
|
5138
5272
|
extractBoxOpAttributes,
|
|
5139
5273
|
extractTableRegion,
|
|
@@ -5155,6 +5289,7 @@ function extractTableRegion(ops, hintOpIdx) {
|
|
|
5155
5289
|
isTextNode,
|
|
5156
5290
|
isValidColor,
|
|
5157
5291
|
isValidHexColor,
|
|
5292
|
+
isZebraBodyRow,
|
|
5158
5293
|
italicFormat,
|
|
5159
5294
|
kbdFormat,
|
|
5160
5295
|
linkFormat,
|
|
@@ -5165,6 +5300,8 @@ function extractTableRegion(ops, hintOpIdx) {
|
|
|
5165
5300
|
nodeAdapter,
|
|
5166
5301
|
normalizeDelta,
|
|
5167
5302
|
preloadRemark,
|
|
5303
|
+
resolveDocumentPresentation,
|
|
5304
|
+
resolveTablePresentation,
|
|
5168
5305
|
sanitizeDelta,
|
|
5169
5306
|
sizeFormat,
|
|
5170
5307
|
slugify,
|