@rowsncolumns/spreadsheet 10.6.0 → 10.6.4
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/dist/cjs/api/cf-data-bar.d.ts +73 -0
- package/dist/cjs/api/cf-data-bar.d.ts.map +1 -0
- package/dist/cjs/api/cf-data-bar.js +182 -0
- package/dist/cjs/api/cf-icons.d.ts +73 -0
- package/dist/cjs/api/cf-icons.d.ts.map +1 -0
- package/dist/cjs/api/cf-icons.js +322 -0
- package/dist/cjs/api/cf-overlays.d.ts +89 -3
- package/dist/cjs/api/cf-overlays.d.ts.map +1 -1
- package/dist/cjs/api/cf-overlays.js +256 -80
- package/dist/cjs/api/cf-priority.d.ts +34 -0
- package/dist/cjs/api/cf-priority.d.ts.map +1 -0
- package/dist/cjs/api/cf-priority.js +94 -0
- package/dist/cjs/api/heatmap.d.ts +7 -1
- package/dist/cjs/api/heatmap.d.ts.map +1 -1
- package/dist/cjs/api/heatmap.js +27 -11
- package/dist/cjs/api/selections.d.ts +2 -2
- package/dist/cjs/api/validation.d.ts +6 -0
- package/dist/cjs/api/validation.d.ts.map +1 -1
- package/dist/cjs/api/validation.js +10 -1
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +194 -51
- package/dist/cjs/components/canvas-grid/overflow.d.ts +37 -0
- package/dist/cjs/components/canvas-grid/overflow.d.ts.map +1 -0
- package/dist/cjs/components/canvas-grid/overflow.js +49 -0
- package/dist/cjs/components/canvas-grid/page-breaks.d.ts +15 -0
- package/dist/cjs/components/canvas-grid/page-breaks.d.ts.map +1 -0
- package/dist/cjs/components/canvas-grid/page-breaks.js +77 -0
- package/dist/cjs/components/canvas-grid/typings.d.ts +25 -0
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.d.ts +58 -1
- package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.js +144 -1
- package/dist/cjs/components/cell/cell-background.d.ts +2 -0
- package/dist/cjs/components/cell/cell-background.d.ts.map +1 -1
- package/dist/cjs/components/cell/cell-background.js +5 -6
- package/dist/cjs/components/cell/cell-backgrounds.d.ts.map +1 -1
- package/dist/cjs/components/cell/cell-backgrounds.js +5 -1
- package/dist/cjs/components/cell/cell-text-runs.js +1 -1
- package/dist/cjs/components/cell/index.d.ts.map +1 -1
- package/dist/cjs/components/cell/index.js +378 -37
- package/dist/cjs/components/cell/number-format-layout.d.ts +35 -0
- package/dist/cjs/components/cell/number-format-layout.d.ts.map +1 -0
- package/dist/cjs/components/cell/number-format-layout.js +106 -0
- package/dist/cjs/components/cell/typings.d.ts +22 -0
- package/dist/cjs/components/cell/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.js +2 -2
- package/dist/cjs/components/cell-filter/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-filter/index.js +3 -3
- package/dist/cjs/components/cell-overlay/index.d.ts +20 -0
- package/dist/cjs/components/cell-overlay/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-overlay/index.js +155 -28
- package/dist/cjs/components/cf-icon-preview/index.d.ts +16 -0
- package/dist/cjs/components/cf-icon-preview/index.d.ts.map +1 -0
- package/dist/cjs/components/cf-icon-preview/index.js +48 -0
- package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/cjs/components/embeds/embed-component.js +3 -2
- package/dist/cjs/components/hidden-markers/index.d.ts.map +1 -1
- package/dist/cjs/components/hidden-markers/index.js +8 -7
- package/dist/cjs/components/hidden-markers/style.d.ts +7 -0
- package/dist/cjs/components/hidden-markers/style.d.ts.map +1 -1
- package/dist/cjs/components/hidden-markers/style.js +13 -2
- package/dist/cjs/components/protect-sheet-dialog/index.d.ts +67 -0
- package/dist/cjs/components/protect-sheet-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/protect-sheet-dialog/index.js +196 -0
- package/dist/cjs/components/toolbar/types.d.ts +1 -1
- package/dist/cjs/components/toolbar/types.d.ts.map +1 -1
- package/dist/cjs/config.d.ts +13 -1
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +15 -3
- package/dist/cjs/helpers.d.ts +10 -5
- package/dist/cjs/helpers.d.ts.map +1 -1
- package/dist/cjs/helpers.js +43 -31
- package/dist/cjs/hooks/use-border-styles.d.ts.map +1 -1
- package/dist/cjs/hooks/use-border-styles.js +5 -28
- package/dist/cjs/hooks/use-cell-renderer.d.ts +35 -3
- package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-cell-renderer.js +85 -47
- package/dist/cjs/hooks/use-conditional-formatting.d.ts +13 -1
- package/dist/cjs/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/cjs/hooks/use-conditional-formatting.js +281 -230
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/release-date.js +1 -1
- package/dist/cjs/themes.d.ts +8 -0
- package/dist/cjs/themes.d.ts.map +1 -1
- package/dist/cjs/themes.js +21 -3
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +81 -4
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/api/cf-data-bar.d.ts +73 -0
- package/dist/esm/api/cf-data-bar.d.ts.map +1 -0
- package/dist/esm/api/cf-data-bar.js +178 -0
- package/dist/esm/api/cf-icons.d.ts +73 -0
- package/dist/esm/api/cf-icons.d.ts.map +1 -0
- package/dist/esm/api/cf-icons.js +306 -0
- package/dist/esm/api/cf-overlays.d.ts +89 -3
- package/dist/esm/api/cf-overlays.d.ts.map +1 -1
- package/dist/esm/api/cf-overlays.js +223 -67
- package/dist/esm/api/cf-priority.d.ts +34 -0
- package/dist/esm/api/cf-priority.d.ts.map +1 -0
- package/dist/esm/api/cf-priority.js +61 -0
- package/dist/esm/api/heatmap.d.ts +7 -1
- package/dist/esm/api/heatmap.d.ts.map +1 -1
- package/dist/esm/api/heatmap.js +24 -9
- package/dist/esm/api/selections.d.ts +2 -2
- package/dist/esm/api/validation.d.ts +6 -0
- package/dist/esm/api/validation.d.ts.map +1 -1
- package/dist/esm/api/validation.js +6 -0
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +163 -47
- package/dist/esm/components/canvas-grid/overflow.d.ts +37 -0
- package/dist/esm/components/canvas-grid/overflow.d.ts.map +1 -0
- package/dist/esm/components/canvas-grid/overflow.js +39 -0
- package/dist/esm/components/canvas-grid/page-breaks.d.ts +15 -0
- package/dist/esm/components/canvas-grid/page-breaks.d.ts.map +1 -0
- package/dist/esm/components/canvas-grid/page-breaks.js +50 -0
- package/dist/esm/components/canvas-grid/typings.d.ts +25 -0
- package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.d.ts +58 -1
- package/dist/esm/components/cell/alignment.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.js +110 -1
- package/dist/esm/components/cell/cell-background.d.ts +2 -0
- package/dist/esm/components/cell/cell-background.d.ts.map +1 -1
- package/dist/esm/components/cell/cell-background.js +4 -5
- package/dist/esm/components/cell/cell-backgrounds.d.ts.map +1 -1
- package/dist/esm/components/cell/cell-backgrounds.js +5 -1
- package/dist/esm/components/cell/cell-text-runs.js +2 -2
- package/dist/esm/components/cell/index.d.ts.map +1 -1
- package/dist/esm/components/cell/index.js +339 -39
- package/dist/esm/components/cell/number-format-layout.d.ts +35 -0
- package/dist/esm/components/cell/number-format-layout.d.ts.map +1 -0
- package/dist/esm/components/cell/number-format-layout.js +99 -0
- package/dist/esm/components/cell/typings.d.ts +22 -0
- package/dist/esm/components/cell/typings.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.js +2 -2
- package/dist/esm/components/cell-filter/index.d.ts.map +1 -1
- package/dist/esm/components/cell-filter/index.js +4 -4
- package/dist/esm/components/cell-overlay/index.d.ts +20 -0
- package/dist/esm/components/cell-overlay/index.d.ts.map +1 -1
- package/dist/esm/components/cell-overlay/index.js +111 -25
- package/dist/esm/components/cf-icon-preview/index.d.ts +16 -0
- package/dist/esm/components/cf-icon-preview/index.d.ts.map +1 -0
- package/dist/esm/components/cf-icon-preview/index.js +41 -0
- package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/esm/components/embeds/embed-component.js +4 -3
- package/dist/esm/components/hidden-markers/index.d.ts.map +1 -1
- package/dist/esm/components/hidden-markers/index.js +9 -8
- package/dist/esm/components/hidden-markers/style.d.ts +7 -0
- package/dist/esm/components/hidden-markers/style.d.ts.map +1 -1
- package/dist/esm/components/hidden-markers/style.js +8 -1
- package/dist/esm/components/protect-sheet-dialog/index.d.ts +67 -0
- package/dist/esm/components/protect-sheet-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/protect-sheet-dialog/index.js +154 -0
- package/dist/esm/components/toolbar/types.d.ts +1 -1
- package/dist/esm/components/toolbar/types.d.ts.map +1 -1
- package/dist/esm/config.d.ts +13 -1
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +13 -1
- package/dist/esm/helpers.d.ts +10 -5
- package/dist/esm/helpers.d.ts.map +1 -1
- package/dist/esm/helpers.js +40 -28
- package/dist/esm/hooks/use-border-styles.d.ts.map +1 -1
- package/dist/esm/hooks/use-border-styles.js +5 -26
- package/dist/esm/hooks/use-cell-renderer.d.ts +35 -3
- package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-cell-renderer.js +83 -42
- package/dist/esm/hooks/use-conditional-formatting.d.ts +13 -1
- package/dist/esm/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/esm/hooks/use-conditional-formatting.js +215 -120
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/release-date.js +1 -1
- package/dist/esm/themes.d.ts +8 -0
- package/dist/esm/themes.d.ts.map +1 -1
- package/dist/esm/themes.js +20 -3
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +81 -4
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +4 -4
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { CELL_TEXT_PADDING, getTextNodeBounds, Image, } from "@rowsncolumns/grid";
|
|
2
2
|
import { getContrastColor, isNil } from "@rowsncolumns/utils";
|
|
3
3
|
import React, { memo, useCallback } from "react";
|
|
4
|
-
import { Group, Label,
|
|
5
|
-
import {
|
|
4
|
+
import { Group, Label, Shape, Tag, Text } from "react-konva";
|
|
5
|
+
import { paintDataBar } from "../../api/cf-data-bar";
|
|
6
|
+
import { getCfIcon, paintCfIcon } from "../../api/cf-icons";
|
|
7
|
+
import { ARROW_DOWN_WIDTH, CELL_HORIZONTAL_ALIGNMENT, CELL_VERTICAL_ALIGNMENT, CF_ICON_SPACING, CF_ICON_WIDTH, CHIP_HEIGHT, CHIP_RADIUS, CHIP_SPACING, COMMENT_COLOR, DEFAULT_CELL_SPACING, ERROR_COLOR, INDENT_SPACING, INVALID_COLOR, NOTE_COLOR, } from "../../config";
|
|
6
8
|
import { getChipVerticalPosition, normalizeDisplayText, pointToPixel, } from "../../helpers";
|
|
7
|
-
import { getStringifiedColor } from "../../themes";
|
|
9
|
+
import { getStringifiedColor, resolveThemeFontFamily } from "../../themes";
|
|
8
10
|
import { CellArrow } from "../cell-arrow";
|
|
9
11
|
import { CellCalculated } from "../cell-calculated";
|
|
10
12
|
import { CellCheckbox } from "../cell-checkbox";
|
|
@@ -12,18 +14,17 @@ import { CellError, ERROR_TAG_WIDTH } from "../cell-error";
|
|
|
12
14
|
import { CellExpand } from "../cell-expand";
|
|
13
15
|
import { CellFilter } from "../cell-filter";
|
|
14
16
|
import { CellGroup } from "../cell-group";
|
|
17
|
+
import { alignmentImpliesWrap, computeShrinkToFitFontSize, fillCellText, getKonvaRotationDegrees, getRotatedTextLayout, getUnderlineStrokes, getVerticalSpreadLineHeight, layoutDistributedLines, resolveCanvasTextAlign, resolveCanvasVerticalAlign, resolveReadingOrderAlignment, stackTextVertically, verticalAlignmentImpliesWrap, } from "./alignment";
|
|
15
18
|
import { CellTextRuns } from "./cell-text-runs";
|
|
19
|
+
import { getLeftSpacing, getRightSpacing } from "./helpers";
|
|
16
20
|
import { getLineMetrics } from "./line-height";
|
|
21
|
+
import { layoutNumberFormatParts } from "./number-format-layout";
|
|
17
22
|
import { hasRenderableRuns } from "./rich-text";
|
|
18
|
-
import { alignmentImpliesWrap, computeShrinkToFitFontSize, fillCellText, getKonvaRotationDegrees, getRotatedTextLayout, resolveCanvasTextAlign, stackTextVertically, } from "./alignment";
|
|
19
|
-
import { getLeftSpacing, getRightSpacing } from "./helpers";
|
|
20
23
|
const LOADING_VALUE = "Loading...";
|
|
21
24
|
// CSS-equivalent scale for sup/sub baseline offset (matches browser default).
|
|
22
25
|
const BASELINE_OFFSET_FONT_SCALE = 0.58;
|
|
23
|
-
// Vertical inset of data-bar rect from cell edges (top/bottom).
|
|
24
|
-
const CF_DATABAR_INSET_Y = 2;
|
|
25
26
|
export const Cell = memo((props) => {
|
|
26
|
-
const { x, y, width, height, value, padding = CELL_TEXT_PADDING, scale = 1, lineHeight = 1.2, lineHeightMode = "fontMetrics", imageUrl, cellFormat, chipBackgroundColor, defaultColor, defaultBackgroundColor, sheetId, rowIndex, columnIndex, onRequestEdit, onArrowClick, isChecked, isInvalid, isError, showCheckbox, showArrow, showFilter, showChip, showNote, showComment, isFilterActive, filterIconColor, onFilterClick, onCheckboxClick, enableOverflow, cellWidth, textWidth: _textWidth, theme, showCalculatedColumnIcon, isCalculatedColumn, expandable, expanded, columnSpans, overflowRight, showGridLines, structuredValue, sparklineGroupAxis, defaultFontSizePoint, sortOrder, textFormatRuns, StructuredResult, readonly = false, onExpandCollapse, getDefaultHorizontalAlignment, } = props;
|
|
27
|
+
const { x, y, width, height, value, padding = CELL_TEXT_PADDING, scale = 1, lineHeight = 1.2, lineHeightMode = "fontMetrics", imageUrl, cellFormat, chipBackgroundColor, defaultColor, defaultBackgroundColor, sheetId, rowIndex, columnIndex, onRequestEdit, onArrowClick, isChecked, isInvalid, isError, showCheckbox, showArrow, showFilter, showChip, showNote, showComment, isFilterActive, filterIconColor, onFilterClick, onCheckboxClick, enableOverflow, cellWidth, textWidth: _textWidth, theme, showCalculatedColumnIcon, isCalculatedColumn, expandable, expanded, columnSpans, overflowRight, overflowLeft, centerOnSpan = false, arrowOutside = false, showGridLines, structuredValue, sparklineGroupAxis, defaultFontSizePoint, sortOrder, textFormatRuns, numberFormatLayout, StructuredResult, readonly = false, onExpandCollapse, getDefaultHorizontalAlignment, } = props;
|
|
27
28
|
const { textFormat, wrapStrategy } = cellFormat ?? {};
|
|
28
29
|
// openpyxl's `relativeIndent` is an additive delta on top of `indent`.
|
|
29
30
|
const indent = (cellFormat?.indent ?? 0) + (cellFormat?.relativeIndent ?? 0);
|
|
@@ -33,25 +34,37 @@ export const Cell = memo((props) => {
|
|
|
33
34
|
// we cross-package-duplicate the sentinel so apps/spreadsheet doesn't
|
|
34
35
|
// depend on spreadsheet-state.
|
|
35
36
|
const isLoading = value === LOADING_VALUE;
|
|
37
|
+
// Format Cells → Alignment → Text direction: a right-to-left reading
|
|
38
|
+
// order starts General-aligned text at the right edge.
|
|
39
|
+
const readingOrder = cellFormat?.readingOrder;
|
|
36
40
|
const horizontalAlignment = isLoading
|
|
37
41
|
? "center"
|
|
38
42
|
: cellFormat?.horizontalAlignment ??
|
|
39
|
-
(getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex) ||
|
|
43
|
+
(resolveReadingOrderAlignment(readingOrder, getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)) ||
|
|
40
44
|
CELL_HORIZONTAL_ALIGNMENT);
|
|
41
|
-
|
|
45
|
+
// Excel's vertical justify / distributed paint as top / middle for one
|
|
46
|
+
// line and spread wrapped lines over the cell height (below).
|
|
47
|
+
const cellVerticalAlignment = cellFormat?.verticalAlignment || CELL_VERTICAL_ALIGNMENT;
|
|
48
|
+
const verticalAlignment = resolveCanvasVerticalAlign(cellVerticalAlignment);
|
|
49
|
+
const isVerticalSpread = verticalAlignmentImpliesWrap(cellVerticalAlignment);
|
|
42
50
|
// Excel-only modes (justify / distributed / fill / centerContinuous)
|
|
43
51
|
// collapse onto what the canvas text node can paint.
|
|
44
52
|
const canvasTextAlign = resolveCanvasTextAlign(horizontalAlignment);
|
|
45
53
|
const isFillAlignment = horizontalAlignment === "fill";
|
|
46
54
|
// Text format
|
|
47
|
-
const { color: colorObject, bold, italic, strikethrough, underline, baselineOffset, } = textFormat ?? {};
|
|
55
|
+
const { color: colorObject, bold, italic, strikethrough, underline, underlineStyle, baselineOffset, } = textFormat ?? {};
|
|
48
56
|
const fontSizePt = textFormat?.fontSize ?? defaultFontSizePoint;
|
|
49
57
|
// Fall back to the theme's body font so the painter agrees with the
|
|
50
58
|
// sizer (AutoSizerCanvas measures with theme.primaryFontFamily).
|
|
51
|
-
const fontFamily = textFormat?.fontFamily ??
|
|
59
|
+
const fontFamily = resolveThemeFontFamily(textFormat?.fontFamily, theme) ??
|
|
60
|
+
theme?.primaryFontFamily;
|
|
52
61
|
const color = getStringifiedColor(colorObject, theme);
|
|
53
|
-
// Text decoration
|
|
54
|
-
|
|
62
|
+
// Text decoration. Konva draws only a single underline; the double and
|
|
63
|
+
// accounting underlines (Format Cells → Font → Underline) are stroked by
|
|
64
|
+
// `underlineSceneFunc` below, where the text is one unrotated line.
|
|
65
|
+
const hasStyledUnderline = !!underline && !!underlineStyle && underlineStyle !== "single";
|
|
66
|
+
const textDecorationFor = (konvaUnderline) => `${konvaUnderline ? "underline" : ""} ${strikethrough ? "line-through" : ""}`;
|
|
67
|
+
const textDecoration = textDecorationFor(!!underline);
|
|
55
68
|
/* Because of 1px + 0.5px (gridline width + spacing )*/
|
|
56
69
|
const borderWidth = 1;
|
|
57
70
|
const cellSpacingY = borderWidth;
|
|
@@ -68,9 +81,14 @@ export const Cell = memo((props) => {
|
|
|
68
81
|
const showText = !isNil(structuredValue) ? false : !isNil(value);
|
|
69
82
|
// Overflow line
|
|
70
83
|
const overflowFillFunc = useCallback((context, shape) => {
|
|
71
|
-
for (const [
|
|
84
|
+
for (const [spanColumnIndex, columnOffset, columnWidth, cellBackground,] of columnSpans) {
|
|
72
85
|
context.beginPath();
|
|
73
|
-
|
|
86
|
+
// A column left of the cell hides the gridline on its right edge,
|
|
87
|
+
// one to the right the gridline on its left edge.
|
|
88
|
+
const x = columnOffset -
|
|
89
|
+
shape.x() +
|
|
90
|
+
(spanColumnIndex < columnIndex ? columnWidth : 0) +
|
|
91
|
+
0.5;
|
|
74
92
|
const strokeColor = cellBackground
|
|
75
93
|
? getStringifiedColor(cellBackground, theme)
|
|
76
94
|
: defaultBackgroundColor;
|
|
@@ -81,7 +99,7 @@ export const Cell = memo((props) => {
|
|
|
81
99
|
context.stroke();
|
|
82
100
|
context.closePath();
|
|
83
101
|
}
|
|
84
|
-
}, [defaultBackgroundColor, columnSpans, theme,
|
|
102
|
+
}, [defaultBackgroundColor, columnSpans, theme, columnIndex, scale]);
|
|
85
103
|
// Show error tag
|
|
86
104
|
const showError = isError || isInvalid;
|
|
87
105
|
// Calculated
|
|
@@ -89,13 +107,26 @@ export const Cell = memo((props) => {
|
|
|
89
107
|
// Alignment
|
|
90
108
|
const isRightAligned = horizontalAlignment === "right";
|
|
91
109
|
// Spacing
|
|
92
|
-
const indentSpacing = indent ? indent * INDENT_SPACING : 0;
|
|
110
|
+
const indentSpacing = indent ? indent * INDENT_SPACING * scale : 0;
|
|
93
111
|
const iconSpacing = DEFAULT_CELL_SPACING;
|
|
94
112
|
const showExpand = false;
|
|
95
113
|
const leftSpacing = getLeftSpacing(isCalculatedCell, expandable, isRightAligned ? showExpand : false, iconSpacing, scale);
|
|
96
|
-
const rightSpacing = getRightSpacing(showFilter, showArrow, isRightAligned ? false : showExpand, iconSpacing, scale);
|
|
114
|
+
const rightSpacing = getRightSpacing(showFilter, showArrow && !arrowOutside, isRightAligned ? false : showExpand, iconSpacing, scale);
|
|
97
115
|
// final cell width
|
|
98
116
|
const finalCellWidth = cellWidth ?? 0;
|
|
117
|
+
// Centred overflow: the spill area is `[x - overflowLeft, x - overflowLeft
|
|
118
|
+
// + cellWidth]`. Centred text stays centred on its own cell (a blocked
|
|
119
|
+
// side clips it); Center Across Selection centres on the whole span.
|
|
120
|
+
const centredSpill = enableOverflow && overflowLeft !== undefined
|
|
121
|
+
? (() => {
|
|
122
|
+
const spanLeft = x - overflowLeft;
|
|
123
|
+
if (centerOnSpan) {
|
|
124
|
+
return { spanLeft, boxLeft: spanLeft, boxWidth: finalCellWidth };
|
|
125
|
+
}
|
|
126
|
+
const reach = Math.max(overflowLeft, finalCellWidth - width - overflowLeft);
|
|
127
|
+
return { spanLeft, boxLeft: x - reach, boxWidth: width + 2 * reach };
|
|
128
|
+
})()
|
|
129
|
+
: undefined;
|
|
99
130
|
// text
|
|
100
131
|
const textWidth = _textWidth + indentSpacing;
|
|
101
132
|
// Reserve room for the CF icon on the left side of the cell. Only adds
|
|
@@ -107,7 +138,11 @@ export const Cell = memo((props) => {
|
|
|
107
138
|
// doesn't render past the cell's right edge when an iconSet is present.
|
|
108
139
|
const finalTextWidth = (overflowRight
|
|
109
140
|
? Math.max(textWidth, width)
|
|
110
|
-
: (
|
|
141
|
+
: (centredSpill
|
|
142
|
+
? centredSpill.boxWidth
|
|
143
|
+
: enableOverflow
|
|
144
|
+
? finalCellWidth
|
|
145
|
+
: width) -
|
|
111
146
|
rightSpacing -
|
|
112
147
|
leftSpacing -
|
|
113
148
|
indentSpacing) - cfIconReserve;
|
|
@@ -116,8 +151,10 @@ export const Cell = memo((props) => {
|
|
|
116
151
|
? textWidth > width
|
|
117
152
|
? x - (textWidth - width)
|
|
118
153
|
: x
|
|
119
|
-
:
|
|
120
|
-
|
|
154
|
+
: centredSpill
|
|
155
|
+
? centredSpill.boxLeft
|
|
156
|
+
: x +
|
|
157
|
+
(isRightAligned ? -indentSpacing - Math.floor((padding * scale) / 2) : 0);
|
|
121
158
|
// Text left padding
|
|
122
159
|
const textLeftPadding = textX +
|
|
123
160
|
leftSpacing +
|
|
@@ -131,7 +168,9 @@ export const Cell = memo((props) => {
|
|
|
131
168
|
? enableOverflow
|
|
132
169
|
? textX + (textWidth - finalCellWidth)
|
|
133
170
|
: textX
|
|
134
|
-
:
|
|
171
|
+
: centredSpill
|
|
172
|
+
? centredSpill.spanLeft
|
|
173
|
+
: textX;
|
|
135
174
|
const overflowWidth = finalCellWidth;
|
|
136
175
|
// Clip
|
|
137
176
|
const clipX = (enableOverflow ? overflowX : x) +
|
|
@@ -158,7 +197,9 @@ export const Cell = memo((props) => {
|
|
|
158
197
|
: fontSize;
|
|
159
198
|
// Wrap — disabled in stacked, shrink, or rotated modes. Justify /
|
|
160
199
|
// distributed alignment wraps implicitly, as in Excel.
|
|
161
|
-
const wrapRequested = wrapStrategy === "wrap" ||
|
|
200
|
+
const wrapRequested = wrapStrategy === "wrap" ||
|
|
201
|
+
alignmentImpliesWrap(horizontalAlignment) ||
|
|
202
|
+
isVerticalSpread;
|
|
162
203
|
const textWrapStrategy = isStackedVertical || shrinkEnabled || hasNumericRotation
|
|
163
204
|
? "none"
|
|
164
205
|
: wrapRequested
|
|
@@ -178,7 +219,7 @@ export const Cell = memo((props) => {
|
|
|
178
219
|
// the same numbers (#1141). Konva's `lineHeight` is a multiple of the
|
|
179
220
|
// font size.
|
|
180
221
|
const textLineMetrics = getLineMetrics({ fontFamily, fontSize: effectiveFontSize, bold, italic }, lineHeightMode, lineHeight, scale);
|
|
181
|
-
const
|
|
222
|
+
const baseTextLineHeight = effectiveFontSize > 0
|
|
182
223
|
? textLineMetrics.advance / effectiveFontSize
|
|
183
224
|
: lineHeight;
|
|
184
225
|
// The sizer fitted the row to Konva's text block exactly — one advance
|
|
@@ -186,6 +227,16 @@ export const Cell = memo((props) => {
|
|
|
186
227
|
// by the padding on both sides, its padding box is the cell, and every
|
|
187
228
|
// fitted line shows (#1182, #1199).
|
|
188
229
|
const textNode = getTextNodeBounds(y + cellSpacingY, height, padding);
|
|
230
|
+
// Vertical justify / distributed: spread the wrapped lines over the
|
|
231
|
+
// cell's inner height. The line count is estimated from the natural
|
|
232
|
+
// width against the wrap width (and any hard line breaks).
|
|
233
|
+
const wrapWidth = Math.max(1, finalTextWidth - 2 * padding);
|
|
234
|
+
const estimatedLineCount = shouldWrap
|
|
235
|
+
? Math.max((displayValue ?? "").split("\n").length, Math.ceil(_textWidth / wrapWidth))
|
|
236
|
+
: 1;
|
|
237
|
+
const textLineHeight = isVerticalSpread
|
|
238
|
+
? getVerticalSpreadLineHeight(estimatedLineCount, textNode.height - 2 * padding, effectiveFontSize, baseTextLineHeight)
|
|
239
|
+
: baseTextLineHeight;
|
|
189
240
|
// For rotated text, anchor the rotation pivot to an alignment-aware
|
|
190
241
|
// corner of the cell so the text stays inside instead of sliding off
|
|
191
242
|
// (Konva otherwise rotates around the Text's top-left at x/y).
|
|
@@ -208,16 +259,37 @@ export const Cell = memo((props) => {
|
|
|
208
259
|
})
|
|
209
260
|
: undefined;
|
|
210
261
|
const rotationDegrees = rotatedLayout?.rotation ?? getKonvaRotationDegrees(textRotation);
|
|
211
|
-
// CF data
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
262
|
+
// CF data bar — painted behind the text Group so the cell value stays
|
|
263
|
+
// readable on top of the bar (`paintDataBar` owns the geometry).
|
|
264
|
+
const dataBarSceneFunc = useCallback((context, shape) => {
|
|
265
|
+
if (!conditionalDataBar)
|
|
266
|
+
return;
|
|
267
|
+
const barStart = conditionalDataBar.barStart ??
|
|
268
|
+
(conditionalDataBar.direction === "rightToLeft"
|
|
269
|
+
? 1 - conditionalDataBar.fillFraction
|
|
270
|
+
: 0);
|
|
271
|
+
const barEnd = conditionalDataBar.barEnd ??
|
|
272
|
+
barStart + Math.max(0, Math.min(1, conditionalDataBar.fillFraction));
|
|
273
|
+
paintDataBar(context, { width: shape.width(), height: shape.height(), scale }, { ...conditionalDataBar, barStart, barEnd });
|
|
274
|
+
}, [conditionalDataBar, scale]);
|
|
275
|
+
// CF icon — Excel's vector icon at the cell's left edge, aligned with the
|
|
276
|
+
// cell's vertical alignment.
|
|
277
|
+
const cfIconShape = conditionalIcon
|
|
278
|
+
? getCfIcon(conditionalIcon.iconSet, conditionalIcon.iconId)
|
|
279
|
+
: undefined;
|
|
280
|
+
const cfIconSceneFunc = useCallback((context, shape) => {
|
|
281
|
+
if (!cfIconShape)
|
|
282
|
+
return;
|
|
283
|
+
const size = Math.min(CF_ICON_WIDTH * scale, shape.height() - 2 * scale);
|
|
284
|
+
if (size <= 0)
|
|
285
|
+
return;
|
|
286
|
+
const iconY = verticalAlignment === "top"
|
|
287
|
+
? scale
|
|
288
|
+
: verticalAlignment === "middle"
|
|
289
|
+
? (shape.height() - size) / 2
|
|
290
|
+
: shape.height() - size - 2 * scale;
|
|
291
|
+
paintCfIcon(context, cfIconShape, 2 * scale, iconY, size);
|
|
292
|
+
}, [cfIconShape, scale, verticalAlignment]);
|
|
221
293
|
// Use the segment-based renderer only when per-segment formatting
|
|
222
294
|
// exists AND none of the unsupported display modes are active.
|
|
223
295
|
// Rotation, stacked-vertical, shrink-to-fit, and chips stay on the
|
|
@@ -234,10 +306,236 @@ export const Cell = memo((props) => {
|
|
|
234
306
|
!isStackedVertical &&
|
|
235
307
|
!hasNumericRotation &&
|
|
236
308
|
hasRenderableRuns(textFormatRuns ?? undefined);
|
|
309
|
+
// Double / accounting underlines on a single unrotated line (the default
|
|
310
|
+
// Text path); wrapped, rotated or rich-text cells keep Konva's single line.
|
|
311
|
+
const paintStyledUnderline = hasStyledUnderline &&
|
|
312
|
+
!useRichTextRunPath &&
|
|
313
|
+
!showChip &&
|
|
314
|
+
!shouldWrap &&
|
|
315
|
+
!isStackedVertical &&
|
|
316
|
+
!hasNumericRotation;
|
|
317
|
+
const textDirection = readingOrder === "rtl" ? "rtl" : readingOrder === "ltr" ? "ltr" : "inherit";
|
|
318
|
+
const underlineSceneFunc = useCallback((context) => {
|
|
319
|
+
if (!paintStyledUnderline || !underlineStyle)
|
|
320
|
+
return;
|
|
321
|
+
const advance = textLineMetrics.advance;
|
|
322
|
+
const innerLeft = textLeftPadding + padding;
|
|
323
|
+
const innerWidth = Math.max(0, finalTextWidth - 2 * padding);
|
|
324
|
+
const glyphWidth = Math.min(_textWidth, innerWidth);
|
|
325
|
+
const textStart = canvasTextAlign === "right"
|
|
326
|
+
? innerLeft + innerWidth - glyphWidth
|
|
327
|
+
: canvasTextAlign === "center"
|
|
328
|
+
? innerLeft + (innerWidth - glyphWidth) / 2
|
|
329
|
+
: innerLeft;
|
|
330
|
+
const innerTop = textNode.y + padding;
|
|
331
|
+
const innerHeight = textNode.height - 2 * padding;
|
|
332
|
+
const lineTop = effectiveVerticalAlignment === "top"
|
|
333
|
+
? innerTop
|
|
334
|
+
: effectiveVerticalAlignment === "middle"
|
|
335
|
+
? innerTop + (innerHeight - advance) / 2
|
|
336
|
+
: innerTop + innerHeight - advance;
|
|
337
|
+
const strokes = getUnderlineStrokes({
|
|
338
|
+
style: underlineStyle,
|
|
339
|
+
textX: textStart,
|
|
340
|
+
textWidth: glyphWidth,
|
|
341
|
+
cellX: x,
|
|
342
|
+
cellWidth: width,
|
|
343
|
+
baselineY: lineTop + advance / 2 + effectiveFontSize * 0.35,
|
|
344
|
+
fontSize: effectiveFontSize,
|
|
345
|
+
scale,
|
|
346
|
+
});
|
|
347
|
+
context.beginPath();
|
|
348
|
+
for (const { x1, x2, y: strokeY } of strokes) {
|
|
349
|
+
const yy = Math.round(strokeY) + 0.5;
|
|
350
|
+
context.moveTo(x1, yy);
|
|
351
|
+
context.lineTo(x2, yy);
|
|
352
|
+
}
|
|
353
|
+
context.setAttr("strokeStyle", color ?? defaultColor);
|
|
354
|
+
context.setAttr("lineWidth", Math.max(1, scale));
|
|
355
|
+
context.stroke();
|
|
356
|
+
}, [
|
|
357
|
+
paintStyledUnderline,
|
|
358
|
+
underlineStyle,
|
|
359
|
+
textLineMetrics.advance,
|
|
360
|
+
textLeftPadding,
|
|
361
|
+
padding,
|
|
362
|
+
finalTextWidth,
|
|
363
|
+
_textWidth,
|
|
364
|
+
canvasTextAlign,
|
|
365
|
+
textNode.y,
|
|
366
|
+
textNode.height,
|
|
367
|
+
effectiveVerticalAlignment,
|
|
368
|
+
x,
|
|
369
|
+
width,
|
|
370
|
+
effectiveFontSize,
|
|
371
|
+
scale,
|
|
372
|
+
color,
|
|
373
|
+
defaultColor,
|
|
374
|
+
]);
|
|
375
|
+
// Excel's `*x` fill and `_x` / `?` reserves (accounting `$` left, number
|
|
376
|
+
// right): one unrotated, unwrapped line whose parts are placed with
|
|
377
|
+
// measured glyph widths. Every other display mode keeps the plain string.
|
|
378
|
+
const paintNumberFormatLayout = !!numberFormatLayout &&
|
|
379
|
+
!useRichTextRunPath &&
|
|
380
|
+
!showChip &&
|
|
381
|
+
!shouldWrap &&
|
|
382
|
+
!isStackedVertical &&
|
|
383
|
+
!hasNumericRotation &&
|
|
384
|
+
!shrinkEnabled &&
|
|
385
|
+
!isFillAlignment &&
|
|
386
|
+
!enableOverflow &&
|
|
387
|
+
textDirection !== "rtl";
|
|
388
|
+
const numberFormatSceneFunc = useCallback((context, shape) => {
|
|
389
|
+
if (!numberFormatLayout)
|
|
390
|
+
return;
|
|
391
|
+
const ctx = context;
|
|
392
|
+
ctx.setAttr("font", `${textStyle} normal ${effectiveFontSize}px ${fontFamily ?? "sans-serif"}`);
|
|
393
|
+
ctx.setAttr("textBaseline", "alphabetic");
|
|
394
|
+
ctx.setAttr("textAlign", "left");
|
|
395
|
+
ctx.setAttr("fillStyle", color ?? defaultColor);
|
|
396
|
+
const innerWidth = Math.max(0, shape.width() - 2 * padding);
|
|
397
|
+
const measure = (text) => ctx.measureText(text).width;
|
|
398
|
+
const layout = layoutNumberFormatParts({
|
|
399
|
+
parts: numberFormatLayout,
|
|
400
|
+
measure,
|
|
401
|
+
availableWidth: innerWidth,
|
|
402
|
+
align: canvasTextAlign,
|
|
403
|
+
}) ?? {
|
|
404
|
+
// The fixed parts alone overflow the cell: paint the plain string
|
|
405
|
+
// aligned the way the text node would (the group clips it).
|
|
406
|
+
runs: [
|
|
407
|
+
{
|
|
408
|
+
text: value ?? "",
|
|
409
|
+
x: canvasTextAlign === "right"
|
|
410
|
+
? innerWidth - measure(value ?? "")
|
|
411
|
+
: canvasTextAlign === "center"
|
|
412
|
+
? (innerWidth - measure(value ?? "")) / 2
|
|
413
|
+
: 0,
|
|
414
|
+
width: measure(value ?? ""),
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
start: 0,
|
|
418
|
+
end: innerWidth,
|
|
419
|
+
};
|
|
420
|
+
// Konva's text baseline: the line box is `lineHeight × fontSize` tall,
|
|
421
|
+
// aligned in the node, glyphs centred on it by the font's M box.
|
|
422
|
+
const lineHeightPx = textLineHeight * effectiveFontSize;
|
|
423
|
+
const metrics = ctx.measureText("M");
|
|
424
|
+
const ascent = metrics.fontBoundingBoxAscent ?? metrics.actualBoundingBoxAscent ?? 0;
|
|
425
|
+
const descent = metrics.fontBoundingBoxDescent ??
|
|
426
|
+
metrics.actualBoundingBoxDescent ??
|
|
427
|
+
0;
|
|
428
|
+
const alignY = effectiveVerticalAlignment === "top"
|
|
429
|
+
? 0
|
|
430
|
+
: effectiveVerticalAlignment === "middle"
|
|
431
|
+
? (shape.height() - lineHeightPx - 2 * padding) / 2
|
|
432
|
+
: shape.height() - lineHeightPx - 2 * padding;
|
|
433
|
+
const baselineY = padding + alignY + (ascent - descent) / 2 + lineHeightPx / 2;
|
|
434
|
+
for (const run of layout.runs) {
|
|
435
|
+
ctx.fillText(run.text, padding + run.x, baselineY);
|
|
436
|
+
}
|
|
437
|
+
if (!underline && !strikethrough)
|
|
438
|
+
return;
|
|
439
|
+
context.beginPath();
|
|
440
|
+
const textStart = padding + layout.start;
|
|
441
|
+
const textWidthPx = layout.end - layout.start;
|
|
442
|
+
if (underline) {
|
|
443
|
+
for (const { x1, x2, y: strokeY } of getUnderlineStrokes({
|
|
444
|
+
style: underlineStyle ?? "single",
|
|
445
|
+
textX: textStart,
|
|
446
|
+
textWidth: textWidthPx,
|
|
447
|
+
cellX: x - shape.x(),
|
|
448
|
+
cellWidth: width,
|
|
449
|
+
baselineY,
|
|
450
|
+
fontSize: effectiveFontSize,
|
|
451
|
+
scale,
|
|
452
|
+
})) {
|
|
453
|
+
const yy = Math.round(strokeY) + 0.5;
|
|
454
|
+
context.moveTo(x1, yy);
|
|
455
|
+
context.lineTo(x2, yy);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (strikethrough) {
|
|
459
|
+
const yy = Math.round(baselineY - effectiveFontSize * 0.3) + 0.5;
|
|
460
|
+
context.moveTo(textStart, yy);
|
|
461
|
+
context.lineTo(textStart + textWidthPx, yy);
|
|
462
|
+
}
|
|
463
|
+
ctx.setAttr("strokeStyle", color ?? defaultColor);
|
|
464
|
+
ctx.setAttr("lineWidth", Math.max(1, scale));
|
|
465
|
+
context.stroke();
|
|
466
|
+
}, [
|
|
467
|
+
numberFormatLayout,
|
|
468
|
+
textStyle,
|
|
469
|
+
effectiveFontSize,
|
|
470
|
+
fontFamily,
|
|
471
|
+
color,
|
|
472
|
+
defaultColor,
|
|
473
|
+
padding,
|
|
474
|
+
canvasTextAlign,
|
|
475
|
+
value,
|
|
476
|
+
textLineHeight,
|
|
477
|
+
effectiveVerticalAlignment,
|
|
478
|
+
underline,
|
|
479
|
+
strikethrough,
|
|
480
|
+
underlineStyle,
|
|
481
|
+
x,
|
|
482
|
+
width,
|
|
483
|
+
scale,
|
|
484
|
+
]);
|
|
485
|
+
// Excel's horizontal `distributed`: wrapped lines spread edge to edge
|
|
486
|
+
// (the last one too), a lone word centred, the indent kept on both sides.
|
|
487
|
+
const paintDistributed = horizontalAlignment === "distributed" &&
|
|
488
|
+
!useRichTextRunPath &&
|
|
489
|
+
!showChip &&
|
|
490
|
+
!isStackedVertical &&
|
|
491
|
+
!hasNumericRotation &&
|
|
492
|
+
!shrinkEnabled;
|
|
493
|
+
const distributedSceneFunc = useCallback((context, shape) => {
|
|
494
|
+
const ctx = context;
|
|
495
|
+
ctx.setAttr("font", `${textStyle} normal ${effectiveFontSize}px ${fontFamily ?? "sans-serif"}`);
|
|
496
|
+
ctx.setAttr("textBaseline", "alphabetic");
|
|
497
|
+
ctx.setAttr("textAlign", "left");
|
|
498
|
+
ctx.setAttr("fillStyle", color ?? defaultColor);
|
|
499
|
+
const innerWidth = Math.max(0, shape.width() - 2 * padding - indentSpacing);
|
|
500
|
+
const lines = layoutDistributedLines(displayValue ?? "", innerWidth, (text) => ctx.measureText(text).width);
|
|
501
|
+
const lineHeightPx = textLineHeight * effectiveFontSize;
|
|
502
|
+
const metrics = ctx.measureText("M");
|
|
503
|
+
const ascent = metrics.fontBoundingBoxAscent ?? metrics.actualBoundingBoxAscent ?? 0;
|
|
504
|
+
const descent = metrics.fontBoundingBoxDescent ??
|
|
505
|
+
metrics.actualBoundingBoxDescent ??
|
|
506
|
+
0;
|
|
507
|
+
const blockHeight = lines.length * lineHeightPx;
|
|
508
|
+
const alignY = effectiveVerticalAlignment === "top"
|
|
509
|
+
? 0
|
|
510
|
+
: effectiveVerticalAlignment === "middle"
|
|
511
|
+
? (shape.height() - blockHeight - 2 * padding) / 2
|
|
512
|
+
: shape.height() - blockHeight - 2 * padding;
|
|
513
|
+
lines.forEach((words, lineIndex) => {
|
|
514
|
+
const baselineY = padding +
|
|
515
|
+
alignY +
|
|
516
|
+
lineIndex * lineHeightPx +
|
|
517
|
+
(ascent - descent) / 2 +
|
|
518
|
+
lineHeightPx / 2;
|
|
519
|
+
for (const word of words) {
|
|
520
|
+
ctx.fillText(word.text, padding + word.x, baselineY);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
}, [
|
|
524
|
+
textStyle,
|
|
525
|
+
effectiveFontSize,
|
|
526
|
+
fontFamily,
|
|
527
|
+
color,
|
|
528
|
+
defaultColor,
|
|
529
|
+
padding,
|
|
530
|
+
indentSpacing,
|
|
531
|
+
displayValue,
|
|
532
|
+
textLineHeight,
|
|
533
|
+
effectiveVerticalAlignment,
|
|
534
|
+
]);
|
|
237
535
|
return (React.createElement(React.Fragment, null,
|
|
238
536
|
enableOverflow && showGridLines ? (React.createElement(Shape, { x: x, y: y, width: width, height: height, sceneFunc: overflowFillFunc, listening: false })) : null,
|
|
239
|
-
conditionalDataBar
|
|
240
|
-
conditionalIcon ? (React.createElement(
|
|
537
|
+
conditionalDataBar ? (React.createElement(Shape, { x: x, y: y, width: width, height: height, sceneFunc: dataBarSceneFunc, listening: false, perfectDrawEnabled: false })) : null,
|
|
538
|
+
conditionalIcon && cfIconShape ? (React.createElement(Shape, { x: x, y: y, width: width, height: height, sceneFunc: cfIconSceneFunc, listening: false, perfectDrawEnabled: false })) : null,
|
|
241
539
|
showCheckbox ? (React.createElement(CellCheckbox, { x: x, y: y, scale: scale, width: width, height: height, checked: isChecked, onChange: onCheckboxClick, rowIndex: props.rowIndex, columnIndex: props.columnIndex, verticalAlignment: verticalAlignment, horizontalAlignment: horizontalAlignment })) : showText ? (React.createElement(Group, { clipY: y + borderWidth, clipHeight: height, clipX: clipX, clipWidth: clipWidth }, showChip ? (React.createElement(Label, { width: width - CHIP_SPACING * scale, height: CHIP_HEIGHT * scale, x: textLeftPadding, y: getChipVerticalPosition(y, height, scale, verticalAlignment), offsetX: -CHIP_SPACING * scale, offsetY: -CHIP_SPACING * scale },
|
|
242
540
|
React.createElement(Tag, { height: CHIP_HEIGHT * scale, fill: chipBackgroundColor, cornerRadius: CHIP_RADIUS * scale, perfectDrawEnabled: false, hitStrokeWidth: 0 }),
|
|
243
541
|
React.createElement(Text, { text: displayValue, fill: color ?? defaultColor, verticalAlign: effectiveVerticalAlignment, align: canvasTextAlign, fontFamily: fontFamily, fontStyle: textStyle, fontSize: fontSize, textDecoration: textDecoration, padding: padding, lineHeight: 1, offsetY: -1, perfectDrawEnabled: false, wrap: "none" }))) : useRichTextRunPath ? (
|
|
@@ -257,8 +555,10 @@ export const Cell = memo((props) => {
|
|
|
257
555
|
// sup/sub font scale itself (exactly once). Shrink-to-
|
|
258
556
|
// fit never reaches this path, so `baseFontSize` equals
|
|
259
557
|
// `effectiveFontSize` for every other cell.
|
|
260
|
-
baseFontSize: baseFontSize, baseBold: !!bold, baseItalic: !!italic, baseUnderline: !!underline, baseStrikethrough: !!strikethrough, baseBaselineOffset: baselineOffset ?? undefined, horizontalAlign: canvasTextAlign, verticalAlign: effectiveVerticalAlignment, lineHeight: lineHeight, lineHeightMode: lineHeightMode, scale: scale, wrap: shouldWrap, theme: theme })) : (React.createElement(
|
|
261
|
-
|
|
558
|
+
baseFontSize: baseFontSize, baseBold: !!bold, baseItalic: !!italic, baseUnderline: !!underline, baseStrikethrough: !!strikethrough, baseBaselineOffset: baselineOffset ?? undefined, horizontalAlign: canvasTextAlign, verticalAlign: effectiveVerticalAlignment, lineHeight: lineHeight, lineHeightMode: lineHeightMode, scale: scale, wrap: shouldWrap, theme: theme })) : paintDistributed ? (React.createElement(Shape, { x: textLeftPadding, y: textNode.y, width: finalTextWidth, height: textNode.height, sceneFunc: distributedSceneFunc, listening: false, perfectDrawEnabled: false })) : paintNumberFormatLayout ? (React.createElement(Shape, { x: textLeftPadding, y: textNode.y, width: finalTextWidth, height: textNode.height, sceneFunc: numberFormatSceneFunc, listening: false, perfectDrawEnabled: false })) : (React.createElement(React.Fragment, null,
|
|
559
|
+
React.createElement(Text, { x: rotatedLayout?.x ?? textLeftPadding, y: rotatedLayout?.y ?? textNode.y, offsetX: rotatedLayout?.offsetX, offsetY: rotatedLayout?.offsetY, height: rotatedLayout ? approxTextHeight : textNode.height, width: rotatedLayout?.width ?? finalTextWidth, text: displayValue, fill: color ?? defaultColor, verticalAlign: rotatedLayout ? "top" : effectiveVerticalAlignment, align: rotatedLayout ? "left" : canvasTextAlign, fontFamily: fontFamily, fontStyle: textStyle, fontSize: effectiveFontSize, textDecoration: paintStyledUnderline ? textDecorationFor(false) : textDecoration, padding: padding, wrap: textWrapStrategy, lineHeight: textLineHeight, rotation: rotationDegrees, direction: textDirection, hitStrokeWidth: 0, perfectDrawEnabled: false }),
|
|
560
|
+
paintStyledUnderline ? (React.createElement(Shape, { sceneFunc: underlineSceneFunc, listening: false, perfectDrawEnabled: false })) : null)))) : imageUrl ? (React.createElement(Image, { x: x, y: y, spacing: 1, width: width, height: height, url: imageUrl, perfectDrawEnabled: false, horizontalAlignment: canvasTextAlign === "justify" ? "left" : canvasTextAlign, verticalAlignment: verticalAlignment })) : null,
|
|
561
|
+
showFilter ? (React.createElement(CellFilter, { scale: scale, isActive: isFilterActive, onClick: readonly ? undefined : onFilterClick, rowIndex: props.rowIndex, columnIndex: props.columnIndex, x: x, y: y, height: height, width: width, filterIconColor: filterIconColor, verticalAlignment: verticalAlignment, backgroundColor: defaultBackgroundColor, sortOrder: sortOrder })) : showArrow && !readonly ? (React.createElement(CellArrow, { scale: scale, onClick: onArrowClick ?? onRequestEdit, rowIndex: rowIndex, columnIndex: columnIndex, x: arrowOutside ? x + (ARROW_DOWN_WIDTH + 4) * scale : x, y: y, height: height, width: width, verticalAlignment: verticalAlignment })) : null,
|
|
262
562
|
expandable ? (React.createElement(CellGroup, { scale: scale, onClick: (cell) => {
|
|
263
563
|
onExpandCollapse?.(cell, !expanded);
|
|
264
564
|
}, rowIndex: rowIndex, columnIndex: columnIndex, x: x + indentSpacing, y: y, height: height, width: width, verticalAlignment: verticalAlignment, expanded: expanded, theme: theme })) : null,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type NumberFormatLayoutPart } from "@rowsncolumns/utils";
|
|
2
|
+
export type NumberFormatRun = {
|
|
3
|
+
text: string;
|
|
4
|
+
x: number;
|
|
5
|
+
width: number;
|
|
6
|
+
};
|
|
7
|
+
export type NumberFormatRunLayout = {
|
|
8
|
+
runs: NumberFormatRun[];
|
|
9
|
+
/** Left edge of the first painted glyph (or reserve). */
|
|
10
|
+
start: number;
|
|
11
|
+
/** Right edge of the last one. */
|
|
12
|
+
end: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Position the parts inside `availableWidth`. Parts before the (first) fill
|
|
16
|
+
* start at the left edge and parts after it end at the right edge, with the
|
|
17
|
+
* fill character repeated in between as many whole times as it fits —
|
|
18
|
+
* Excel's accounting format (`$` left, number right). Without a fill the
|
|
19
|
+
* block is aligned by `align`. Returns `undefined` when the fixed parts
|
|
20
|
+
* alone do not fit, so the caller keeps its plain-text paint.
|
|
21
|
+
*/
|
|
22
|
+
export declare const layoutNumberFormatParts: ({ parts, measure, availableWidth, align, }: {
|
|
23
|
+
parts: readonly NumberFormatLayoutPart[];
|
|
24
|
+
measure: (text: string) => number;
|
|
25
|
+
availableWidth: number;
|
|
26
|
+
align: "left" | "center" | "right" | "justify";
|
|
27
|
+
}) => NumberFormatRunLayout | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* The layout parts for a cell, or `undefined` when the pattern has no
|
|
30
|
+
* width-aware token or when the parts would paint a different string than
|
|
31
|
+
* the cell's formatted value (another engine, locale or conditional number
|
|
32
|
+
* format produced it) — the plain string then stays authoritative.
|
|
33
|
+
*/
|
|
34
|
+
export declare const getCellNumberFormatLayout: (pattern: string | null | undefined, value: number | string | null | undefined, formattedValue: string | null | undefined, locale?: string) => NumberFormatLayoutPart[] | undefined;
|
|
35
|
+
//# sourceMappingURL=number-format-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-format-layout.d.ts","sourceRoot":"","sources":["../../../../components/cell/number-format-layout.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,sBAAsB,EAG5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,4CAKrC;IACD,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;CAChD,KAAG,qBAAqB,GAAG,SA+D3B,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,OAAO,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,gBAAgB,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,SAAS,MAAM,KACd,sBAAsB,EAAE,GAAG,SAa7B,CAAC"}
|