@rowsncolumns/spreadsheet 10.6.0 → 10.6.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.
Files changed (186) hide show
  1. package/dist/cjs/api/cf-data-bar.d.ts +73 -0
  2. package/dist/cjs/api/cf-data-bar.d.ts.map +1 -0
  3. package/dist/cjs/api/cf-data-bar.js +182 -0
  4. package/dist/cjs/api/cf-icons.d.ts +73 -0
  5. package/dist/cjs/api/cf-icons.d.ts.map +1 -0
  6. package/dist/cjs/api/cf-icons.js +322 -0
  7. package/dist/cjs/api/cf-overlays.d.ts +89 -3
  8. package/dist/cjs/api/cf-overlays.d.ts.map +1 -1
  9. package/dist/cjs/api/cf-overlays.js +256 -80
  10. package/dist/cjs/api/cf-priority.d.ts +34 -0
  11. package/dist/cjs/api/cf-priority.d.ts.map +1 -0
  12. package/dist/cjs/api/cf-priority.js +94 -0
  13. package/dist/cjs/api/heatmap.d.ts +7 -1
  14. package/dist/cjs/api/heatmap.d.ts.map +1 -1
  15. package/dist/cjs/api/heatmap.js +27 -11
  16. package/dist/cjs/api/selections.d.ts +2 -2
  17. package/dist/cjs/api/validation.d.ts +6 -0
  18. package/dist/cjs/api/validation.d.ts.map +1 -1
  19. package/dist/cjs/api/validation.js +10 -1
  20. package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
  21. package/dist/cjs/components/canvas-grid/index.js +194 -51
  22. package/dist/cjs/components/canvas-grid/overflow.d.ts +37 -0
  23. package/dist/cjs/components/canvas-grid/overflow.d.ts.map +1 -0
  24. package/dist/cjs/components/canvas-grid/overflow.js +49 -0
  25. package/dist/cjs/components/canvas-grid/page-breaks.d.ts +15 -0
  26. package/dist/cjs/components/canvas-grid/page-breaks.d.ts.map +1 -0
  27. package/dist/cjs/components/canvas-grid/page-breaks.js +77 -0
  28. package/dist/cjs/components/canvas-grid/typings.d.ts +25 -0
  29. package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
  30. package/dist/cjs/components/cell/alignment.d.ts +83 -1
  31. package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
  32. package/dist/cjs/components/cell/alignment.js +182 -1
  33. package/dist/cjs/components/cell/cell-background.d.ts +18 -0
  34. package/dist/cjs/components/cell/cell-background.d.ts.map +1 -1
  35. package/dist/cjs/components/cell/cell-background.js +145 -59
  36. package/dist/cjs/components/cell/cell-backgrounds.d.ts.map +1 -1
  37. package/dist/cjs/components/cell/cell-backgrounds.js +5 -1
  38. package/dist/cjs/components/cell/cell-text-runs.d.ts +12 -5
  39. package/dist/cjs/components/cell/cell-text-runs.d.ts.map +1 -1
  40. package/dist/cjs/components/cell/cell-text-runs.js +19 -21
  41. package/dist/cjs/components/cell/index.d.ts.map +1 -1
  42. package/dist/cjs/components/cell/index.js +392 -45
  43. package/dist/cjs/components/cell/number-format-layout.d.ts +35 -0
  44. package/dist/cjs/components/cell/number-format-layout.d.ts.map +1 -0
  45. package/dist/cjs/components/cell/number-format-layout.js +106 -0
  46. package/dist/cjs/components/cell/typings.d.ts +22 -0
  47. package/dist/cjs/components/cell/typings.d.ts.map +1 -1
  48. package/dist/cjs/components/cell-editor/index.js +2 -2
  49. package/dist/cjs/components/cell-filter/index.d.ts.map +1 -1
  50. package/dist/cjs/components/cell-filter/index.js +3 -3
  51. package/dist/cjs/components/cell-overlay/index.d.ts +20 -0
  52. package/dist/cjs/components/cell-overlay/index.d.ts.map +1 -1
  53. package/dist/cjs/components/cell-overlay/index.js +155 -28
  54. package/dist/cjs/components/cf-icon-preview/index.d.ts +16 -0
  55. package/dist/cjs/components/cf-icon-preview/index.d.ts.map +1 -0
  56. package/dist/cjs/components/cf-icon-preview/index.js +48 -0
  57. package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
  58. package/dist/cjs/components/embeds/embed-component.js +3 -2
  59. package/dist/cjs/components/hidden-markers/index.d.ts.map +1 -1
  60. package/dist/cjs/components/hidden-markers/index.js +8 -7
  61. package/dist/cjs/components/hidden-markers/style.d.ts +7 -0
  62. package/dist/cjs/components/hidden-markers/style.d.ts.map +1 -1
  63. package/dist/cjs/components/hidden-markers/style.js +13 -2
  64. package/dist/cjs/components/protect-sheet-dialog/index.d.ts +67 -0
  65. package/dist/cjs/components/protect-sheet-dialog/index.d.ts.map +1 -0
  66. package/dist/cjs/components/protect-sheet-dialog/index.js +196 -0
  67. package/dist/cjs/components/toolbar/types.d.ts +1 -1
  68. package/dist/cjs/components/toolbar/types.d.ts.map +1 -1
  69. package/dist/cjs/config.d.ts +13 -1
  70. package/dist/cjs/config.d.ts.map +1 -1
  71. package/dist/cjs/config.js +15 -3
  72. package/dist/cjs/helpers.d.ts +15 -5
  73. package/dist/cjs/helpers.d.ts.map +1 -1
  74. package/dist/cjs/helpers.js +51 -35
  75. package/dist/cjs/hooks/use-border-styles.d.ts.map +1 -1
  76. package/dist/cjs/hooks/use-border-styles.js +5 -28
  77. package/dist/cjs/hooks/use-cell-renderer.d.ts +35 -3
  78. package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
  79. package/dist/cjs/hooks/use-cell-renderer.js +85 -47
  80. package/dist/cjs/hooks/use-conditional-formatting.d.ts +13 -1
  81. package/dist/cjs/hooks/use-conditional-formatting.d.ts.map +1 -1
  82. package/dist/cjs/hooks/use-conditional-formatting.js +281 -230
  83. package/dist/cjs/index.d.ts +5 -0
  84. package/dist/cjs/index.d.ts.map +1 -1
  85. package/dist/cjs/index.js +5 -0
  86. package/dist/cjs/release-date.js +1 -1
  87. package/dist/cjs/themes.d.ts +8 -0
  88. package/dist/cjs/themes.d.ts.map +1 -1
  89. package/dist/cjs/themes.js +21 -3
  90. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  91. package/dist/cjs/types.d.ts +81 -4
  92. package/dist/cjs/types.d.ts.map +1 -1
  93. package/dist/esm/api/cf-data-bar.d.ts +73 -0
  94. package/dist/esm/api/cf-data-bar.d.ts.map +1 -0
  95. package/dist/esm/api/cf-data-bar.js +178 -0
  96. package/dist/esm/api/cf-icons.d.ts +73 -0
  97. package/dist/esm/api/cf-icons.d.ts.map +1 -0
  98. package/dist/esm/api/cf-icons.js +306 -0
  99. package/dist/esm/api/cf-overlays.d.ts +89 -3
  100. package/dist/esm/api/cf-overlays.d.ts.map +1 -1
  101. package/dist/esm/api/cf-overlays.js +223 -67
  102. package/dist/esm/api/cf-priority.d.ts +34 -0
  103. package/dist/esm/api/cf-priority.d.ts.map +1 -0
  104. package/dist/esm/api/cf-priority.js +61 -0
  105. package/dist/esm/api/heatmap.d.ts +7 -1
  106. package/dist/esm/api/heatmap.d.ts.map +1 -1
  107. package/dist/esm/api/heatmap.js +24 -9
  108. package/dist/esm/api/selections.d.ts +2 -2
  109. package/dist/esm/api/validation.d.ts +6 -0
  110. package/dist/esm/api/validation.d.ts.map +1 -1
  111. package/dist/esm/api/validation.js +6 -0
  112. package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
  113. package/dist/esm/components/canvas-grid/index.js +163 -47
  114. package/dist/esm/components/canvas-grid/overflow.d.ts +37 -0
  115. package/dist/esm/components/canvas-grid/overflow.d.ts.map +1 -0
  116. package/dist/esm/components/canvas-grid/overflow.js +39 -0
  117. package/dist/esm/components/canvas-grid/page-breaks.d.ts +15 -0
  118. package/dist/esm/components/canvas-grid/page-breaks.d.ts.map +1 -0
  119. package/dist/esm/components/canvas-grid/page-breaks.js +50 -0
  120. package/dist/esm/components/canvas-grid/typings.d.ts +25 -0
  121. package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
  122. package/dist/esm/components/cell/alignment.d.ts +83 -1
  123. package/dist/esm/components/cell/alignment.d.ts.map +1 -1
  124. package/dist/esm/components/cell/alignment.js +143 -1
  125. package/dist/esm/components/cell/cell-background.d.ts +18 -0
  126. package/dist/esm/components/cell/cell-background.d.ts.map +1 -1
  127. package/dist/esm/components/cell/cell-background.js +110 -41
  128. package/dist/esm/components/cell/cell-backgrounds.d.ts.map +1 -1
  129. package/dist/esm/components/cell/cell-backgrounds.js +5 -1
  130. package/dist/esm/components/cell/cell-text-runs.d.ts +12 -5
  131. package/dist/esm/components/cell/cell-text-runs.d.ts.map +1 -1
  132. package/dist/esm/components/cell/cell-text-runs.js +19 -24
  133. package/dist/esm/components/cell/index.d.ts.map +1 -1
  134. package/dist/esm/components/cell/index.js +352 -46
  135. package/dist/esm/components/cell/number-format-layout.d.ts +35 -0
  136. package/dist/esm/components/cell/number-format-layout.d.ts.map +1 -0
  137. package/dist/esm/components/cell/number-format-layout.js +99 -0
  138. package/dist/esm/components/cell/typings.d.ts +22 -0
  139. package/dist/esm/components/cell/typings.d.ts.map +1 -1
  140. package/dist/esm/components/cell-editor/index.js +2 -2
  141. package/dist/esm/components/cell-filter/index.d.ts.map +1 -1
  142. package/dist/esm/components/cell-filter/index.js +4 -4
  143. package/dist/esm/components/cell-overlay/index.d.ts +20 -0
  144. package/dist/esm/components/cell-overlay/index.d.ts.map +1 -1
  145. package/dist/esm/components/cell-overlay/index.js +111 -25
  146. package/dist/esm/components/cf-icon-preview/index.d.ts +16 -0
  147. package/dist/esm/components/cf-icon-preview/index.d.ts.map +1 -0
  148. package/dist/esm/components/cf-icon-preview/index.js +41 -0
  149. package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
  150. package/dist/esm/components/embeds/embed-component.js +4 -3
  151. package/dist/esm/components/hidden-markers/index.d.ts.map +1 -1
  152. package/dist/esm/components/hidden-markers/index.js +9 -8
  153. package/dist/esm/components/hidden-markers/style.d.ts +7 -0
  154. package/dist/esm/components/hidden-markers/style.d.ts.map +1 -1
  155. package/dist/esm/components/hidden-markers/style.js +8 -1
  156. package/dist/esm/components/protect-sheet-dialog/index.d.ts +67 -0
  157. package/dist/esm/components/protect-sheet-dialog/index.d.ts.map +1 -0
  158. package/dist/esm/components/protect-sheet-dialog/index.js +154 -0
  159. package/dist/esm/components/toolbar/types.d.ts +1 -1
  160. package/dist/esm/components/toolbar/types.d.ts.map +1 -1
  161. package/dist/esm/config.d.ts +13 -1
  162. package/dist/esm/config.d.ts.map +1 -1
  163. package/dist/esm/config.js +13 -1
  164. package/dist/esm/helpers.d.ts +15 -5
  165. package/dist/esm/helpers.d.ts.map +1 -1
  166. package/dist/esm/helpers.js +48 -32
  167. package/dist/esm/hooks/use-border-styles.d.ts.map +1 -1
  168. package/dist/esm/hooks/use-border-styles.js +5 -26
  169. package/dist/esm/hooks/use-cell-renderer.d.ts +35 -3
  170. package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
  171. package/dist/esm/hooks/use-cell-renderer.js +83 -42
  172. package/dist/esm/hooks/use-conditional-formatting.d.ts +13 -1
  173. package/dist/esm/hooks/use-conditional-formatting.d.ts.map +1 -1
  174. package/dist/esm/hooks/use-conditional-formatting.js +215 -120
  175. package/dist/esm/index.d.ts +5 -0
  176. package/dist/esm/index.d.ts.map +1 -1
  177. package/dist/esm/index.js +5 -0
  178. package/dist/esm/release-date.js +1 -1
  179. package/dist/esm/themes.d.ts +8 -0
  180. package/dist/esm/themes.d.ts.map +1 -1
  181. package/dist/esm/themes.js +20 -3
  182. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  183. package/dist/esm/types.d.ts +81 -4
  184. package/dist/esm/types.d.ts.map +1 -1
  185. package/dist/spreadsheet.min.css +1 -1
  186. package/package.json +4 -4
@@ -6,6 +6,8 @@ var grid_1 = require("@rowsncolumns/grid");
6
6
  var utils_1 = require("@rowsncolumns/utils");
7
7
  var react_1 = tslib_1.__importStar(require("react"));
8
8
  var react_konva_1 = require("react-konva");
9
+ var cf_data_bar_1 = require("../../api/cf-data-bar");
10
+ var cf_icons_1 = require("../../api/cf-icons");
9
11
  var config_1 = require("../../config");
10
12
  var helpers_1 = require("../../helpers");
11
13
  var themes_1 = require("../../themes");
@@ -16,20 +18,19 @@ var cell_error_1 = require("../cell-error");
16
18
  var cell_expand_1 = require("../cell-expand");
17
19
  var cell_filter_1 = require("../cell-filter");
18
20
  var cell_group_1 = require("../cell-group");
21
+ var alignment_1 = require("./alignment");
19
22
  var cell_text_runs_1 = require("./cell-text-runs");
23
+ var helpers_2 = require("./helpers");
20
24
  var line_height_1 = require("./line-height");
25
+ var number_format_layout_1 = require("./number-format-layout");
21
26
  var rich_text_1 = require("./rich-text");
22
- var alignment_1 = require("./alignment");
23
- var helpers_2 = require("./helpers");
24
27
  var LOADING_VALUE = "Loading...";
25
28
  // CSS-equivalent scale for sup/sub baseline offset (matches browser default).
26
29
  var BASELINE_OFFSET_FONT_SCALE = 0.58;
27
- // Vertical inset of data-bar rect from cell edges (top/bottom).
28
- var CF_DATABAR_INSET_Y = 2;
29
30
  exports.Cell = (0, react_1.memo)(function (props) {
30
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
31
- var x = props.x, y = props.y, width = props.width, height = props.height, value = props.value, _p = props.padding, padding = _p === void 0 ? grid_1.CELL_TEXT_PADDING : _p, _q = props.scale, scale = _q === void 0 ? 1 : _q, _r = props.lineHeight, lineHeight = _r === void 0 ? 1.2 : _r, _s = props.lineHeightMode, lineHeightMode = _s === void 0 ? "fontMetrics" : _s, imageUrl = props.imageUrl, cellFormat = props.cellFormat, chipBackgroundColor = props.chipBackgroundColor, defaultColor = props.defaultColor, defaultBackgroundColor = props.defaultBackgroundColor, sheetId = props.sheetId, rowIndex = props.rowIndex, columnIndex = props.columnIndex, onRequestEdit = props.onRequestEdit, onArrowClick = props.onArrowClick, isChecked = props.isChecked, isInvalid = props.isInvalid, isError = props.isError, showCheckbox = props.showCheckbox, showArrow = props.showArrow, showFilter = props.showFilter, showChip = props.showChip, showNote = props.showNote, showComment = props.showComment, isFilterActive = props.isFilterActive, filterIconColor = props.filterIconColor, onFilterClick = props.onFilterClick, onCheckboxClick = props.onCheckboxClick, enableOverflow = props.enableOverflow, cellWidth = props.cellWidth, _textWidth = props.textWidth, theme = props.theme, showCalculatedColumnIcon = props.showCalculatedColumnIcon, isCalculatedColumn = props.isCalculatedColumn, expandable = props.expandable, expanded = props.expanded, columnSpans = props.columnSpans, overflowRight = props.overflowRight, showGridLines = props.showGridLines, structuredValue = props.structuredValue, sparklineGroupAxis = props.sparklineGroupAxis, defaultFontSizePoint = props.defaultFontSizePoint, sortOrder = props.sortOrder, textFormatRuns = props.textFormatRuns, StructuredResult = props.StructuredResult, _t = props.readonly, readonly = _t === void 0 ? false : _t, onExpandCollapse = props.onExpandCollapse, getDefaultHorizontalAlignment = props.getDefaultHorizontalAlignment;
32
- var _u = cellFormat !== null && cellFormat !== void 0 ? cellFormat : {}, textFormat = _u.textFormat, wrapStrategy = _u.wrapStrategy;
31
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
32
+ var x = props.x, y = props.y, width = props.width, height = props.height, value = props.value, _r = props.padding, padding = _r === void 0 ? grid_1.CELL_TEXT_PADDING : _r, _s = props.scale, scale = _s === void 0 ? 1 : _s, _t = props.lineHeight, lineHeight = _t === void 0 ? 1.2 : _t, _u = props.lineHeightMode, lineHeightMode = _u === void 0 ? "fontMetrics" : _u, imageUrl = props.imageUrl, cellFormat = props.cellFormat, chipBackgroundColor = props.chipBackgroundColor, defaultColor = props.defaultColor, defaultBackgroundColor = props.defaultBackgroundColor, sheetId = props.sheetId, rowIndex = props.rowIndex, columnIndex = props.columnIndex, onRequestEdit = props.onRequestEdit, onArrowClick = props.onArrowClick, isChecked = props.isChecked, isInvalid = props.isInvalid, isError = props.isError, showCheckbox = props.showCheckbox, showArrow = props.showArrow, showFilter = props.showFilter, showChip = props.showChip, showNote = props.showNote, showComment = props.showComment, isFilterActive = props.isFilterActive, filterIconColor = props.filterIconColor, onFilterClick = props.onFilterClick, onCheckboxClick = props.onCheckboxClick, enableOverflow = props.enableOverflow, cellWidth = props.cellWidth, _textWidth = props.textWidth, theme = props.theme, showCalculatedColumnIcon = props.showCalculatedColumnIcon, isCalculatedColumn = props.isCalculatedColumn, expandable = props.expandable, expanded = props.expanded, columnSpans = props.columnSpans, overflowRight = props.overflowRight, overflowLeft = props.overflowLeft, _v = props.centerOnSpan, centerOnSpan = _v === void 0 ? false : _v, _w = props.arrowOutside, arrowOutside = _w === void 0 ? false : _w, showGridLines = props.showGridLines, structuredValue = props.structuredValue, sparklineGroupAxis = props.sparklineGroupAxis, defaultFontSizePoint = props.defaultFontSizePoint, sortOrder = props.sortOrder, textFormatRuns = props.textFormatRuns, numberFormatLayout = props.numberFormatLayout, StructuredResult = props.StructuredResult, _x = props.readonly, readonly = _x === void 0 ? false : _x, onExpandCollapse = props.onExpandCollapse, getDefaultHorizontalAlignment = props.getDefaultHorizontalAlignment;
33
+ var _y = cellFormat !== null && cellFormat !== void 0 ? cellFormat : {}, textFormat = _y.textFormat, wrapStrategy = _y.wrapStrategy;
33
34
  // openpyxl's `relativeIndent` is an additive delta on top of `indent`.
34
35
  var indent = ((_a = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.indent) !== null && _a !== void 0 ? _a : 0) + ((_b = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.relativeIndent) !== null && _b !== void 0 ? _b : 0);
35
36
  var conditionalIcon = (_c = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.conditionalIcon) !== null && _c !== void 0 ? _c : null;
@@ -38,24 +39,37 @@ exports.Cell = (0, react_1.memo)(function (props) {
38
39
  // we cross-package-duplicate the sentinel so apps/spreadsheet doesn't
39
40
  // depend on spreadsheet-state.
40
41
  var isLoading = value === LOADING_VALUE;
42
+ // Format Cells → Alignment → Text direction: a right-to-left reading
43
+ // order starts General-aligned text at the right edge.
44
+ var readingOrder = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.readingOrder;
41
45
  var horizontalAlignment = isLoading
42
46
  ? "center"
43
- : (_e = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.horizontalAlignment) !== null && _e !== void 0 ? _e : (getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex) ||
47
+ : (_e = cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.horizontalAlignment) !== null && _e !== void 0 ? _e : ((0, alignment_1.resolveReadingOrderAlignment)(readingOrder, getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)) ||
44
48
  config_1.CELL_HORIZONTAL_ALIGNMENT);
45
- var verticalAlignment = (cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.verticalAlignment) || config_1.CELL_VERTICAL_ALIGNMENT;
49
+ // Excel's vertical justify / distributed paint as top / middle for one
50
+ // line and spread wrapped lines over the cell height (below).
51
+ var cellVerticalAlignment = (cellFormat === null || cellFormat === void 0 ? void 0 : cellFormat.verticalAlignment) || config_1.CELL_VERTICAL_ALIGNMENT;
52
+ var verticalAlignment = (0, alignment_1.resolveCanvasVerticalAlign)(cellVerticalAlignment);
53
+ var isVerticalSpread = (0, alignment_1.verticalAlignmentImpliesWrap)(cellVerticalAlignment);
46
54
  // Excel-only modes (justify / distributed / fill / centerContinuous)
47
55
  // collapse onto what the canvas text node can paint.
48
56
  var canvasTextAlign = (0, alignment_1.resolveCanvasTextAlign)(horizontalAlignment);
49
57
  var isFillAlignment = horizontalAlignment === "fill";
50
58
  // Text format
51
- var _v = textFormat !== null && textFormat !== void 0 ? textFormat : {}, colorObject = _v.color, bold = _v.bold, italic = _v.italic, strikethrough = _v.strikethrough, underline = _v.underline, baselineOffset = _v.baselineOffset;
59
+ var _z = textFormat !== null && textFormat !== void 0 ? textFormat : {}, colorObject = _z.color, bold = _z.bold, italic = _z.italic, strikethrough = _z.strikethrough, underline = _z.underline, underlineStyle = _z.underlineStyle, baselineOffset = _z.baselineOffset;
52
60
  var fontSizePt = (_f = textFormat === null || textFormat === void 0 ? void 0 : textFormat.fontSize) !== null && _f !== void 0 ? _f : defaultFontSizePoint;
53
61
  // Fall back to the theme's body font so the painter agrees with the
54
62
  // sizer (AutoSizerCanvas measures with theme.primaryFontFamily).
55
- var fontFamily = (_g = textFormat === null || textFormat === void 0 ? void 0 : textFormat.fontFamily) !== null && _g !== void 0 ? _g : theme === null || theme === void 0 ? void 0 : theme.primaryFontFamily;
63
+ var fontFamily = (_g = (0, themes_1.resolveThemeFontFamily)(textFormat === null || textFormat === void 0 ? void 0 : textFormat.fontFamily, theme)) !== null && _g !== void 0 ? _g : theme === null || theme === void 0 ? void 0 : theme.primaryFontFamily;
56
64
  var color = (0, themes_1.getStringifiedColor)(colorObject, theme);
57
- // Text decoration
58
- var textDecoration = "".concat(underline ? "underline" : "", " ").concat(strikethrough ? "line-through" : "");
65
+ // Text decoration. Konva draws only a single underline; the double and
66
+ // accounting underlines (Format Cells → Font → Underline) are stroked by
67
+ // `underlineSceneFunc` below, where the text is one unrotated line.
68
+ var hasStyledUnderline = !!underline && !!underlineStyle && underlineStyle !== "single";
69
+ var textDecorationFor = function (konvaUnderline) {
70
+ return "".concat(konvaUnderline ? "underline" : "", " ").concat(strikethrough ? "line-through" : "");
71
+ };
72
+ var textDecoration = textDecorationFor(!!underline);
59
73
  /* Because of 1px + 0.5px (gridline width + spacing )*/
60
74
  var borderWidth = 1;
61
75
  var cellSpacingY = borderWidth;
@@ -63,11 +77,9 @@ exports.Cell = (0, react_1.memo)(function (props) {
63
77
  var fontSize = baselineOffset
64
78
  ? baseFontSize * BASELINE_OFFSET_FONT_SCALE
65
79
  : baseFontSize;
66
- var effectiveVerticalAlignment = baselineOffset === "superscript"
67
- ? "top"
68
- : baselineOffset === "subscript"
69
- ? "bottom"
70
- : verticalAlignment;
80
+ // Superscript / subscript keep the cell's alignment and move the
81
+ // baseline instead (`scriptOffsetY` below), as Excel does.
82
+ var effectiveVerticalAlignment = verticalAlignment;
71
83
  var textStyle = "".concat(bold ? "bold" : "normal", " ").concat(italic ? "italic" : "normal");
72
84
  var showText = !(0, utils_1.isNil)(structuredValue) ? false : !(0, utils_1.isNil)(value);
73
85
  // Overflow line
@@ -75,9 +87,14 @@ exports.Cell = (0, react_1.memo)(function (props) {
75
87
  var e_1, _a;
76
88
  try {
77
89
  for (var columnSpans_1 = tslib_1.__values(columnSpans), columnSpans_1_1 = columnSpans_1.next(); !columnSpans_1_1.done; columnSpans_1_1 = columnSpans_1.next()) {
78
- var _b = tslib_1.__read(columnSpans_1_1.value, 4), _1 = _b[0], columnOffset = _b[1], columnWidth = _b[2], cellBackground = _b[3];
90
+ var _b = tslib_1.__read(columnSpans_1_1.value, 4), spanColumnIndex = _b[0], columnOffset = _b[1], columnWidth = _b[2], cellBackground = _b[3];
79
91
  context.beginPath();
80
- var x_1 = columnOffset - shape.x() + (overflowRight ? columnWidth : 0) + 0.5;
92
+ // A column left of the cell hides the gridline on its right edge,
93
+ // one to the right the gridline on its left edge.
94
+ var x_1 = columnOffset -
95
+ shape.x() +
96
+ (spanColumnIndex < columnIndex ? columnWidth : 0) +
97
+ 0.5;
81
98
  var strokeColor = cellBackground
82
99
  ? (0, themes_1.getStringifiedColor)(cellBackground, theme)
83
100
  : defaultBackgroundColor;
@@ -96,7 +113,7 @@ exports.Cell = (0, react_1.memo)(function (props) {
96
113
  }
97
114
  finally { if (e_1) throw e_1.error; }
98
115
  }
99
- }, [defaultBackgroundColor, columnSpans, theme, overflowRight, scale]);
116
+ }, [defaultBackgroundColor, columnSpans, theme, columnIndex, scale]);
100
117
  // Show error tag
101
118
  var showError = isError || isInvalid;
102
119
  // Calculated
@@ -104,13 +121,26 @@ exports.Cell = (0, react_1.memo)(function (props) {
104
121
  // Alignment
105
122
  var isRightAligned = horizontalAlignment === "right";
106
123
  // Spacing
107
- var indentSpacing = indent ? indent * config_1.INDENT_SPACING : 0;
124
+ var indentSpacing = indent ? indent * config_1.INDENT_SPACING * scale : 0;
108
125
  var iconSpacing = config_1.DEFAULT_CELL_SPACING;
109
126
  var showExpand = false;
110
127
  var leftSpacing = (0, helpers_2.getLeftSpacing)(isCalculatedCell, expandable, isRightAligned ? showExpand : false, iconSpacing, scale);
111
- var rightSpacing = (0, helpers_2.getRightSpacing)(showFilter, showArrow, isRightAligned ? false : showExpand, iconSpacing, scale);
128
+ var rightSpacing = (0, helpers_2.getRightSpacing)(showFilter, showArrow && !arrowOutside, isRightAligned ? false : showExpand, iconSpacing, scale);
112
129
  // final cell width
113
130
  var finalCellWidth = cellWidth !== null && cellWidth !== void 0 ? cellWidth : 0;
131
+ // Centred overflow: the spill area is `[x - overflowLeft, x - overflowLeft
132
+ // + cellWidth]`. Centred text stays centred on its own cell (a blocked
133
+ // side clips it); Center Across Selection centres on the whole span.
134
+ var centredSpill = enableOverflow && overflowLeft !== undefined
135
+ ? (function () {
136
+ var spanLeft = x - overflowLeft;
137
+ if (centerOnSpan) {
138
+ return { spanLeft: spanLeft, boxLeft: spanLeft, boxWidth: finalCellWidth };
139
+ }
140
+ var reach = Math.max(overflowLeft, finalCellWidth - width - overflowLeft);
141
+ return { spanLeft: spanLeft, boxLeft: x - reach, boxWidth: width + 2 * reach };
142
+ })()
143
+ : undefined;
114
144
  // text
115
145
  var textWidth = _textWidth + indentSpacing;
116
146
  // Reserve room for the CF icon on the left side of the cell. Only adds
@@ -122,7 +152,11 @@ exports.Cell = (0, react_1.memo)(function (props) {
122
152
  // doesn't render past the cell's right edge when an iconSet is present.
123
153
  var finalTextWidth = (overflowRight
124
154
  ? Math.max(textWidth, width)
125
- : (enableOverflow ? finalCellWidth : width) -
155
+ : (centredSpill
156
+ ? centredSpill.boxWidth
157
+ : enableOverflow
158
+ ? finalCellWidth
159
+ : width) -
126
160
  rightSpacing -
127
161
  leftSpacing -
128
162
  indentSpacing) - cfIconReserve;
@@ -131,8 +165,10 @@ exports.Cell = (0, react_1.memo)(function (props) {
131
165
  ? textWidth > width
132
166
  ? x - (textWidth - width)
133
167
  : x
134
- : x +
135
- (isRightAligned ? -indentSpacing - Math.floor((padding * scale) / 2) : 0);
168
+ : centredSpill
169
+ ? centredSpill.boxLeft
170
+ : x +
171
+ (isRightAligned ? -indentSpacing - Math.floor((padding * scale) / 2) : 0);
136
172
  // Text left padding
137
173
  var textLeftPadding = textX +
138
174
  leftSpacing +
@@ -146,7 +182,9 @@ exports.Cell = (0, react_1.memo)(function (props) {
146
182
  ? enableOverflow
147
183
  ? textX + (textWidth - finalCellWidth)
148
184
  : textX
149
- : textX;
185
+ : centredSpill
186
+ ? centredSpill.spanLeft
187
+ : textX;
150
188
  var overflowWidth = finalCellWidth;
151
189
  // Clip
152
190
  var clipX = (enableOverflow ? overflowX : x) +
@@ -173,7 +211,9 @@ exports.Cell = (0, react_1.memo)(function (props) {
173
211
  : fontSize;
174
212
  // Wrap — disabled in stacked, shrink, or rotated modes. Justify /
175
213
  // distributed alignment wraps implicitly, as in Excel.
176
- var wrapRequested = wrapStrategy === "wrap" || (0, alignment_1.alignmentImpliesWrap)(horizontalAlignment);
214
+ var wrapRequested = wrapStrategy === "wrap" ||
215
+ (0, alignment_1.alignmentImpliesWrap)(horizontalAlignment) ||
216
+ isVerticalSpread;
177
217
  var textWrapStrategy = isStackedVertical || shrinkEnabled || hasNumericRotation
178
218
  ? "none"
179
219
  : wrapRequested
@@ -193,7 +233,7 @@ exports.Cell = (0, react_1.memo)(function (props) {
193
233
  // the same numbers (#1141). Konva's `lineHeight` is a multiple of the
194
234
  // font size.
195
235
  var textLineMetrics = (0, line_height_1.getLineMetrics)({ fontFamily: fontFamily, fontSize: effectiveFontSize, bold: bold, italic: italic }, lineHeightMode, lineHeight, scale);
196
- var textLineHeight = effectiveFontSize > 0
236
+ var baseTextLineHeight = effectiveFontSize > 0
197
237
  ? textLineMetrics.advance / effectiveFontSize
198
238
  : lineHeight;
199
239
  // The sizer fitted the row to Konva's text block exactly — one advance
@@ -201,6 +241,16 @@ exports.Cell = (0, react_1.memo)(function (props) {
201
241
  // by the padding on both sides, its padding box is the cell, and every
202
242
  // fitted line shows (#1182, #1199).
203
243
  var textNode = (0, grid_1.getTextNodeBounds)(y + cellSpacingY, height, padding);
244
+ // Vertical justify / distributed: spread the wrapped lines over the
245
+ // cell's inner height. The line count is estimated from the natural
246
+ // width against the wrap width (and any hard line breaks).
247
+ var wrapWidth = Math.max(1, finalTextWidth - 2 * padding);
248
+ var estimatedLineCount = shouldWrap
249
+ ? Math.max((displayValue !== null && displayValue !== void 0 ? displayValue : "").split("\n").length, Math.ceil(_textWidth / wrapWidth))
250
+ : 1;
251
+ var textLineHeight = isVerticalSpread
252
+ ? (0, alignment_1.getVerticalSpreadLineHeight)(estimatedLineCount, textNode.height - 2 * padding, effectiveFontSize, baseTextLineHeight)
253
+ : baseTextLineHeight;
204
254
  // For rotated text, anchor the rotation pivot to an alignment-aware
205
255
  // corner of the cell so the text stays inside instead of sliding off
206
256
  // (Konva otherwise rotates around the Text's top-left at x/y).
@@ -223,16 +273,36 @@ exports.Cell = (0, react_1.memo)(function (props) {
223
273
  })
224
274
  : undefined;
225
275
  var rotationDegrees = (_h = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.rotation) !== null && _h !== void 0 ? _h : (0, alignment_1.getKonvaRotationDegrees)(textRotation);
226
- // CF data-bar geometry. Painted behind the text Group so the cell value
227
- // remains readable on top of the bar. We inset 2px vertically so the bar
228
- // doesn't touch the borders.
229
- var dataBarHeight = Math.max(0, height - 2 * CF_DATABAR_INSET_Y * scale);
230
- var dataBarMaxWidth = Math.max(0, width - 2);
231
- var dataBarFill = conditionalDataBar
232
- ? Math.max(0, Math.min(1, conditionalDataBar.fillFraction))
233
- : 0;
234
- var dataBarWidth = dataBarMaxWidth * dataBarFill;
235
- var dataBarFromRight = (conditionalDataBar === null || conditionalDataBar === void 0 ? void 0 : conditionalDataBar.direction) === "rightToLeft";
276
+ // CF data bar — painted behind the text Group so the cell value stays
277
+ // readable on top of the bar (`paintDataBar` owns the geometry).
278
+ var dataBarSceneFunc = (0, react_1.useCallback)(function (context, shape) {
279
+ var _a, _b;
280
+ if (!conditionalDataBar)
281
+ return;
282
+ var barStart = (_a = conditionalDataBar.barStart) !== null && _a !== void 0 ? _a : (conditionalDataBar.direction === "rightToLeft"
283
+ ? 1 - conditionalDataBar.fillFraction
284
+ : 0);
285
+ var barEnd = (_b = conditionalDataBar.barEnd) !== null && _b !== void 0 ? _b : barStart + Math.max(0, Math.min(1, conditionalDataBar.fillFraction));
286
+ (0, cf_data_bar_1.paintDataBar)(context, { width: shape.width(), height: shape.height(), scale: scale }, tslib_1.__assign(tslib_1.__assign({}, conditionalDataBar), { barStart: barStart, barEnd: barEnd }));
287
+ }, [conditionalDataBar, scale]);
288
+ // CF icon — Excel's vector icon at the cell's left edge, aligned with the
289
+ // cell's vertical alignment.
290
+ var cfIconShape = conditionalIcon
291
+ ? (0, cf_icons_1.getCfIcon)(conditionalIcon.iconSet, conditionalIcon.iconId)
292
+ : undefined;
293
+ var cfIconSceneFunc = (0, react_1.useCallback)(function (context, shape) {
294
+ if (!cfIconShape)
295
+ return;
296
+ var size = Math.min(config_1.CF_ICON_WIDTH * scale, shape.height() - 2 * scale);
297
+ if (size <= 0)
298
+ return;
299
+ var iconY = verticalAlignment === "top"
300
+ ? scale
301
+ : verticalAlignment === "middle"
302
+ ? (shape.height() - size) / 2
303
+ : shape.height() - size - 2 * scale;
304
+ (0, cf_icons_1.paintCfIcon)(context, cfIconShape, 2 * scale, iconY, size);
305
+ }, [cfIconShape, scale, verticalAlignment]);
236
306
  // Use the segment-based renderer only when per-segment formatting
237
307
  // exists AND none of the unsupported display modes are active.
238
308
  // Rotation, stacked-vertical, shrink-to-fit, and chips stay on the
@@ -244,15 +314,290 @@ exports.Cell = (0, react_1.memo)(function (props) {
244
314
  // own styling instead of being flattened; plain no-run cells with
245
315
  // a cell-level baselineOffset keep the single-style approximation
246
316
  // below (scaled font + top/bottom alignment).
317
+ // Shrink-to-fit and rotated cells keep their runs: the run painter takes
318
+ // the shrink factor and the rotated layout (stacked text stays plain).
247
319
  var useRichTextRunPath = !showChip &&
320
+ !isStackedVertical &&
321
+ (0, rich_text_1.hasRenderableRuns)(textFormatRuns !== null && textFormatRuns !== void 0 ? textFormatRuns : undefined);
322
+ var shrinkFactor = fontSize > 0 ? effectiveFontSize / fontSize : 1;
323
+ var scriptOffsetY = (0, alignment_1.getScriptTextOffsetY)({
324
+ baselineOffset: baselineOffset,
325
+ verticalAlign: effectiveVerticalAlignment,
326
+ baseFontSize: baseFontSize * shrinkFactor,
327
+ scriptFontSize: effectiveFontSize,
328
+ lineHeight: textLineHeight,
329
+ });
330
+ // Double / accounting underlines on a single unrotated line (the default
331
+ // Text path); wrapped, rotated or rich-text cells keep Konva's single line.
332
+ var paintStyledUnderline = hasStyledUnderline &&
333
+ !useRichTextRunPath &&
334
+ !showChip &&
335
+ !shouldWrap &&
336
+ !isStackedVertical &&
337
+ !hasNumericRotation;
338
+ var textDirection = readingOrder === "rtl" ? "rtl" : readingOrder === "ltr" ? "ltr" : "inherit";
339
+ var underlineSceneFunc = (0, react_1.useCallback)(function (context) {
340
+ var e_2, _a;
341
+ if (!paintStyledUnderline || !underlineStyle)
342
+ return;
343
+ var advance = textLineMetrics.advance;
344
+ var innerLeft = textLeftPadding + padding;
345
+ var innerWidth = Math.max(0, finalTextWidth - 2 * padding);
346
+ var glyphWidth = Math.min(_textWidth, innerWidth);
347
+ var textStart = canvasTextAlign === "right"
348
+ ? innerLeft + innerWidth - glyphWidth
349
+ : canvasTextAlign === "center"
350
+ ? innerLeft + (innerWidth - glyphWidth) / 2
351
+ : innerLeft;
352
+ var innerTop = textNode.y + padding;
353
+ var innerHeight = textNode.height - 2 * padding;
354
+ var lineTop = effectiveVerticalAlignment === "top"
355
+ ? innerTop
356
+ : effectiveVerticalAlignment === "middle"
357
+ ? innerTop + (innerHeight - advance) / 2
358
+ : innerTop + innerHeight - advance;
359
+ var strokes = (0, alignment_1.getUnderlineStrokes)({
360
+ style: underlineStyle,
361
+ textX: textStart,
362
+ textWidth: glyphWidth,
363
+ cellX: x,
364
+ cellWidth: width,
365
+ baselineY: lineTop + advance / 2 + effectiveFontSize * 0.35,
366
+ fontSize: effectiveFontSize,
367
+ scale: scale,
368
+ });
369
+ context.beginPath();
370
+ try {
371
+ for (var strokes_1 = tslib_1.__values(strokes), strokes_1_1 = strokes_1.next(); !strokes_1_1.done; strokes_1_1 = strokes_1.next()) {
372
+ var _b = strokes_1_1.value, x1 = _b.x1, x2 = _b.x2, strokeY = _b.y;
373
+ var yy = Math.round(strokeY) + 0.5;
374
+ context.moveTo(x1, yy);
375
+ context.lineTo(x2, yy);
376
+ }
377
+ }
378
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
379
+ finally {
380
+ try {
381
+ if (strokes_1_1 && !strokes_1_1.done && (_a = strokes_1.return)) _a.call(strokes_1);
382
+ }
383
+ finally { if (e_2) throw e_2.error; }
384
+ }
385
+ context.setAttr("strokeStyle", color !== null && color !== void 0 ? color : defaultColor);
386
+ context.setAttr("lineWidth", Math.max(1, scale));
387
+ context.stroke();
388
+ }, [
389
+ paintStyledUnderline,
390
+ underlineStyle,
391
+ textLineMetrics.advance,
392
+ textLeftPadding,
393
+ padding,
394
+ finalTextWidth,
395
+ _textWidth,
396
+ canvasTextAlign,
397
+ textNode.y,
398
+ textNode.height,
399
+ effectiveVerticalAlignment,
400
+ x,
401
+ width,
402
+ effectiveFontSize,
403
+ scale,
404
+ color,
405
+ defaultColor,
406
+ ]);
407
+ // Excel's `*x` fill and `_x` / `?` reserves (accounting `$` left, number
408
+ // right): one unrotated, unwrapped line whose parts are placed with
409
+ // measured glyph widths. Every other display mode keeps the plain string.
410
+ var paintNumberFormatLayout = !!numberFormatLayout &&
411
+ !useRichTextRunPath &&
412
+ !showChip &&
413
+ !shouldWrap &&
414
+ !isStackedVertical &&
415
+ !hasNumericRotation &&
248
416
  !shrinkEnabled &&
417
+ !isFillAlignment &&
418
+ !enableOverflow &&
419
+ textDirection !== "rtl";
420
+ var numberFormatSceneFunc = (0, react_1.useCallback)(function (context, shape) {
421
+ var e_3, _a, e_4, _b;
422
+ var _c, _d, _e, _f, _g;
423
+ if (!numberFormatLayout)
424
+ return;
425
+ var ctx = context;
426
+ ctx.setAttr("font", "".concat(textStyle, " normal ").concat(effectiveFontSize, "px ").concat(fontFamily !== null && fontFamily !== void 0 ? fontFamily : "sans-serif"));
427
+ ctx.setAttr("textBaseline", "alphabetic");
428
+ ctx.setAttr("textAlign", "left");
429
+ ctx.setAttr("fillStyle", color !== null && color !== void 0 ? color : defaultColor);
430
+ var innerWidth = Math.max(0, shape.width() - 2 * padding);
431
+ var measure = function (text) { return ctx.measureText(text).width; };
432
+ var layout = (_c = (0, number_format_layout_1.layoutNumberFormatParts)({
433
+ parts: numberFormatLayout,
434
+ measure: measure,
435
+ availableWidth: innerWidth,
436
+ align: canvasTextAlign,
437
+ })) !== null && _c !== void 0 ? _c : {
438
+ // The fixed parts alone overflow the cell: paint the plain string
439
+ // aligned the way the text node would (the group clips it).
440
+ runs: [
441
+ {
442
+ text: value !== null && value !== void 0 ? value : "",
443
+ x: canvasTextAlign === "right"
444
+ ? innerWidth - measure(value !== null && value !== void 0 ? value : "")
445
+ : canvasTextAlign === "center"
446
+ ? (innerWidth - measure(value !== null && value !== void 0 ? value : "")) / 2
447
+ : 0,
448
+ width: measure(value !== null && value !== void 0 ? value : ""),
449
+ },
450
+ ],
451
+ start: 0,
452
+ end: innerWidth,
453
+ };
454
+ // Konva's text baseline: the line box is `lineHeight × fontSize` tall,
455
+ // aligned in the node, glyphs centred on it by the font's M box.
456
+ var lineHeightPx = textLineHeight * effectiveFontSize;
457
+ var metrics = ctx.measureText("M");
458
+ var ascent = (_e = (_d = metrics.fontBoundingBoxAscent) !== null && _d !== void 0 ? _d : metrics.actualBoundingBoxAscent) !== null && _e !== void 0 ? _e : 0;
459
+ var descent = (_g = (_f = metrics.fontBoundingBoxDescent) !== null && _f !== void 0 ? _f : metrics.actualBoundingBoxDescent) !== null && _g !== void 0 ? _g : 0;
460
+ var alignY = effectiveVerticalAlignment === "top"
461
+ ? 0
462
+ : effectiveVerticalAlignment === "middle"
463
+ ? (shape.height() - lineHeightPx - 2 * padding) / 2
464
+ : shape.height() - lineHeightPx - 2 * padding;
465
+ var baselineY = padding + alignY + (ascent - descent) / 2 + lineHeightPx / 2;
466
+ try {
467
+ for (var _h = tslib_1.__values(layout.runs), _j = _h.next(); !_j.done; _j = _h.next()) {
468
+ var run = _j.value;
469
+ ctx.fillText(run.text, padding + run.x, baselineY);
470
+ }
471
+ }
472
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
473
+ finally {
474
+ try {
475
+ if (_j && !_j.done && (_a = _h.return)) _a.call(_h);
476
+ }
477
+ finally { if (e_3) throw e_3.error; }
478
+ }
479
+ if (!underline && !strikethrough)
480
+ return;
481
+ context.beginPath();
482
+ var textStart = padding + layout.start;
483
+ var textWidthPx = layout.end - layout.start;
484
+ if (underline) {
485
+ try {
486
+ for (var _k = tslib_1.__values((0, alignment_1.getUnderlineStrokes)({
487
+ style: underlineStyle !== null && underlineStyle !== void 0 ? underlineStyle : "single",
488
+ textX: textStart,
489
+ textWidth: textWidthPx,
490
+ cellX: x - shape.x(),
491
+ cellWidth: width,
492
+ baselineY: baselineY,
493
+ fontSize: effectiveFontSize,
494
+ scale: scale,
495
+ })), _l = _k.next(); !_l.done; _l = _k.next()) {
496
+ var _m = _l.value, x1 = _m.x1, x2 = _m.x2, strokeY = _m.y;
497
+ var yy = Math.round(strokeY) + 0.5;
498
+ context.moveTo(x1, yy);
499
+ context.lineTo(x2, yy);
500
+ }
501
+ }
502
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
503
+ finally {
504
+ try {
505
+ if (_l && !_l.done && (_b = _k.return)) _b.call(_k);
506
+ }
507
+ finally { if (e_4) throw e_4.error; }
508
+ }
509
+ }
510
+ if (strikethrough) {
511
+ var yy = Math.round(baselineY - effectiveFontSize * 0.3) + 0.5;
512
+ context.moveTo(textStart, yy);
513
+ context.lineTo(textStart + textWidthPx, yy);
514
+ }
515
+ ctx.setAttr("strokeStyle", color !== null && color !== void 0 ? color : defaultColor);
516
+ ctx.setAttr("lineWidth", Math.max(1, scale));
517
+ context.stroke();
518
+ }, [
519
+ numberFormatLayout,
520
+ textStyle,
521
+ effectiveFontSize,
522
+ fontFamily,
523
+ color,
524
+ defaultColor,
525
+ padding,
526
+ canvasTextAlign,
527
+ value,
528
+ textLineHeight,
529
+ effectiveVerticalAlignment,
530
+ underline,
531
+ strikethrough,
532
+ underlineStyle,
533
+ x,
534
+ width,
535
+ scale,
536
+ ]);
537
+ // Excel's horizontal `distributed`: wrapped lines spread edge to edge
538
+ // (the last one too), a lone word centred, the indent kept on both sides.
539
+ var paintDistributed = horizontalAlignment === "distributed" &&
540
+ !useRichTextRunPath &&
541
+ !showChip &&
249
542
  !isStackedVertical &&
250
543
  !hasNumericRotation &&
251
- (0, rich_text_1.hasRenderableRuns)(textFormatRuns !== null && textFormatRuns !== void 0 ? textFormatRuns : undefined);
544
+ !shrinkEnabled;
545
+ var distributedSceneFunc = (0, react_1.useCallback)(function (context, shape) {
546
+ var _a, _b, _c, _d;
547
+ var ctx = context;
548
+ ctx.setAttr("font", "".concat(textStyle, " normal ").concat(effectiveFontSize, "px ").concat(fontFamily !== null && fontFamily !== void 0 ? fontFamily : "sans-serif"));
549
+ ctx.setAttr("textBaseline", "alphabetic");
550
+ ctx.setAttr("textAlign", "left");
551
+ ctx.setAttr("fillStyle", color !== null && color !== void 0 ? color : defaultColor);
552
+ var innerWidth = Math.max(0, shape.width() - 2 * padding - indentSpacing);
553
+ var lines = (0, alignment_1.layoutDistributedLines)(displayValue !== null && displayValue !== void 0 ? displayValue : "", innerWidth, function (text) { return ctx.measureText(text).width; });
554
+ var lineHeightPx = textLineHeight * effectiveFontSize;
555
+ var metrics = ctx.measureText("M");
556
+ var ascent = (_b = (_a = metrics.fontBoundingBoxAscent) !== null && _a !== void 0 ? _a : metrics.actualBoundingBoxAscent) !== null && _b !== void 0 ? _b : 0;
557
+ var descent = (_d = (_c = metrics.fontBoundingBoxDescent) !== null && _c !== void 0 ? _c : metrics.actualBoundingBoxDescent) !== null && _d !== void 0 ? _d : 0;
558
+ var blockHeight = lines.length * lineHeightPx;
559
+ var alignY = effectiveVerticalAlignment === "top"
560
+ ? 0
561
+ : effectiveVerticalAlignment === "middle"
562
+ ? (shape.height() - blockHeight - 2 * padding) / 2
563
+ : shape.height() - blockHeight - 2 * padding;
564
+ lines.forEach(function (words, lineIndex) {
565
+ var e_5, _a;
566
+ var baselineY = padding +
567
+ alignY +
568
+ lineIndex * lineHeightPx +
569
+ (ascent - descent) / 2 +
570
+ lineHeightPx / 2;
571
+ try {
572
+ for (var words_1 = tslib_1.__values(words), words_1_1 = words_1.next(); !words_1_1.done; words_1_1 = words_1.next()) {
573
+ var word = words_1_1.value;
574
+ ctx.fillText(word.text, padding + word.x, baselineY);
575
+ }
576
+ }
577
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
578
+ finally {
579
+ try {
580
+ if (words_1_1 && !words_1_1.done && (_a = words_1.return)) _a.call(words_1);
581
+ }
582
+ finally { if (e_5) throw e_5.error; }
583
+ }
584
+ });
585
+ }, [
586
+ textStyle,
587
+ effectiveFontSize,
588
+ fontFamily,
589
+ color,
590
+ defaultColor,
591
+ padding,
592
+ indentSpacing,
593
+ displayValue,
594
+ textLineHeight,
595
+ effectiveVerticalAlignment,
596
+ ]);
252
597
  return (react_1.default.createElement(react_1.default.Fragment, null,
253
598
  enableOverflow && showGridLines ? (react_1.default.createElement(react_konva_1.Shape, { x: x, y: y, width: width, height: height, sceneFunc: overflowFillFunc, listening: false })) : null,
254
- conditionalDataBar && dataBarWidth > 0 ? (react_1.default.createElement(react_konva_1.Rect, { x: dataBarFromRight ? x + width - 1 - dataBarWidth : x + 1, y: y + CF_DATABAR_INSET_Y * scale, width: dataBarWidth, height: dataBarHeight, fill: conditionalDataBar.color, stroke: conditionalDataBar.borderColor, strokeWidth: conditionalDataBar.borderColor ? 1 : 0, listening: false, perfectDrawEnabled: false })) : null,
255
- conditionalIcon ? (react_1.default.createElement(react_konva_1.Text, { x: x + 2, y: y, width: config_1.CF_ICON_WIDTH * scale, height: height, text: conditionalIcon.glyph, fill: (_k = (_j = conditionalIcon.color) !== null && _j !== void 0 ? _j : color) !== null && _k !== void 0 ? _k : defaultColor, verticalAlign: "middle", align: "center", fontSize: fontSize, listening: false, perfectDrawEnabled: false })) : null,
599
+ conditionalDataBar ? (react_1.default.createElement(react_konva_1.Shape, { x: x, y: y, width: width, height: height, sceneFunc: dataBarSceneFunc, listening: false, perfectDrawEnabled: false })) : null,
600
+ conditionalIcon && cfIconShape ? (react_1.default.createElement(react_konva_1.Shape, { x: x, y: y, width: width, height: height, sceneFunc: cfIconSceneFunc, listening: false, perfectDrawEnabled: false })) : null,
256
601
  showCheckbox ? (react_1.default.createElement(cell_checkbox_1.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_1.default.createElement(react_konva_1.Group, { clipY: y + borderWidth, clipHeight: height, clipX: clipX, clipWidth: clipWidth }, showChip ? (react_1.default.createElement(react_konva_1.Label, { width: width - config_1.CHIP_SPACING * scale, height: config_1.CHIP_HEIGHT * scale, x: textLeftPadding, y: (0, helpers_1.getChipVerticalPosition)(y, height, scale, verticalAlignment), offsetX: -config_1.CHIP_SPACING * scale, offsetY: -config_1.CHIP_SPACING * scale },
257
602
  react_1.default.createElement(react_konva_1.Tag, { height: config_1.CHIP_HEIGHT * scale, fill: chipBackgroundColor, cornerRadius: config_1.CHIP_RADIUS * scale, perfectDrawEnabled: false, hitStrokeWidth: 0 }),
258
603
  react_1.default.createElement(react_konva_1.Text, { text: displayValue, fill: color !== null && color !== void 0 ? 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 ? (
@@ -266,14 +611,16 @@ exports.Cell = (0, react_1.memo)(function (props) {
266
611
  // lines, and `textFormatRuns` indices reference the raw
267
612
  // string. `normalizeDisplayText` is bypassed because the
268
613
  // renderer handles multi-line layout itself.
269
- react_1.default.createElement(cell_text_runs_1.CellTextRuns, { x: textLeftPadding, y: textNode.y, width: finalTextWidth, height: textNode.height, padding: padding, value: displayValue !== null && displayValue !== void 0 ? displayValue : "", runs: textFormatRuns !== null && textFormatRuns !== void 0 ? textFormatRuns : [], baseColor: color !== null && color !== void 0 ? color : defaultColor, baseFontFamily: fontFamily,
614
+ react_1.default.createElement(cell_text_runs_1.CellTextRuns, { x: (_j = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.x) !== null && _j !== void 0 ? _j : textLeftPadding, y: (_k = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.y) !== null && _k !== void 0 ? _k : textNode.y, width: (_l = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.width) !== null && _l !== void 0 ? _l : finalTextWidth, height: rotatedLayout ? approxTextHeight : textNode.height, rotation: rotatedLayout ? rotationDegrees : undefined, fontScale: scale * shrinkFactor, padding: padding, value: displayValue !== null && displayValue !== void 0 ? displayValue : "", runs: textFormatRuns !== null && textFormatRuns !== void 0 ? textFormatRuns : [], baseColor: color !== null && color !== void 0 ? color : defaultColor, baseFontFamily: fontFamily,
270
615
  // UN-scaled size on purpose: when the cell carries a
271
616
  // baselineOffset the segment resolver applies the
272
617
  // sup/sub font scale itself (exactly once). Shrink-to-
273
618
  // fit never reaches this path, so `baseFontSize` equals
274
619
  // `effectiveFontSize` for every other cell.
275
- baseFontSize: baseFontSize, baseBold: !!bold, baseItalic: !!italic, baseUnderline: !!underline, baseStrikethrough: !!strikethrough, baseBaselineOffset: baselineOffset !== null && baselineOffset !== void 0 ? baselineOffset : undefined, horizontalAlign: canvasTextAlign, verticalAlign: effectiveVerticalAlignment, lineHeight: lineHeight, lineHeightMode: lineHeightMode, scale: scale, wrap: shouldWrap, theme: theme })) : (react_1.default.createElement(react_konva_1.Text, { x: (_l = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.x) !== null && _l !== void 0 ? _l : textLeftPadding, y: (_m = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.y) !== null && _m !== void 0 ? _m : textNode.y, offsetX: rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.offsetX, offsetY: rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.offsetY, height: rotatedLayout ? approxTextHeight : textNode.height, width: (_o = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.width) !== null && _o !== void 0 ? _o : finalTextWidth, text: displayValue, fill: color !== null && color !== void 0 ? color : defaultColor, verticalAlign: rotatedLayout ? "top" : effectiveVerticalAlignment, align: rotatedLayout ? "left" : canvasTextAlign, fontFamily: fontFamily, fontStyle: textStyle, fontSize: effectiveFontSize, textDecoration: textDecoration, padding: padding, wrap: textWrapStrategy, lineHeight: textLineHeight, rotation: rotationDegrees, hitStrokeWidth: 0, perfectDrawEnabled: false })))) : imageUrl ? (react_1.default.createElement(grid_1.Image, { x: x, y: y, spacing: 1, width: width, height: height, url: imageUrl, perfectDrawEnabled: false, horizontalAlignment: canvasTextAlign === "justify" ? "left" : canvasTextAlign, verticalAlignment: verticalAlignment })) : null,
276
- showFilter ? (react_1.default.createElement(cell_filter_1.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_1.default.createElement(cell_arrow_1.CellArrow, { scale: scale, onClick: onArrowClick !== null && onArrowClick !== void 0 ? onArrowClick : onRequestEdit, rowIndex: rowIndex, columnIndex: columnIndex, x: x, y: y, height: height, width: width, verticalAlignment: verticalAlignment })) : null,
620
+ baseFontSize: baseFontSize * shrinkFactor, baseBold: !!bold, baseItalic: !!italic, baseUnderline: !!underline, baseStrikethrough: !!strikethrough, baseBaselineOffset: baselineOffset !== null && baselineOffset !== void 0 ? baselineOffset : undefined, horizontalAlign: rotatedLayout ? "left" : canvasTextAlign, verticalAlign: rotatedLayout ? "top" : effectiveVerticalAlignment, lineHeight: lineHeight, lineHeightMode: lineHeightMode, scale: scale, wrap: shouldWrap && !rotatedLayout, theme: theme })) : paintDistributed ? (react_1.default.createElement(react_konva_1.Shape, { x: textLeftPadding, y: textNode.y, width: finalTextWidth, height: textNode.height, sceneFunc: distributedSceneFunc, listening: false, perfectDrawEnabled: false })) : paintNumberFormatLayout ? (react_1.default.createElement(react_konva_1.Shape, { x: textLeftPadding, y: textNode.y, width: finalTextWidth, height: textNode.height, sceneFunc: numberFormatSceneFunc, listening: false, perfectDrawEnabled: false })) : (react_1.default.createElement(react_1.default.Fragment, null,
621
+ react_1.default.createElement(react_konva_1.Text, { x: (_m = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.x) !== null && _m !== void 0 ? _m : textLeftPadding, y: (_o = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.y) !== null && _o !== void 0 ? _o : textNode.y, offsetX: rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.offsetX, offsetY: ((_p = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.offsetY) !== null && _p !== void 0 ? _p : 0) + scriptOffsetY, height: rotatedLayout ? approxTextHeight : textNode.height, width: (_q = rotatedLayout === null || rotatedLayout === void 0 ? void 0 : rotatedLayout.width) !== null && _q !== void 0 ? _q : finalTextWidth, text: displayValue, fill: color !== null && color !== void 0 ? 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 }),
622
+ paintStyledUnderline ? (react_1.default.createElement(react_konva_1.Shape, { sceneFunc: underlineSceneFunc, listening: false, perfectDrawEnabled: false })) : null)))) : imageUrl ? (react_1.default.createElement(grid_1.Image, { x: x, y: y, spacing: 1, width: width, height: height, url: imageUrl, perfectDrawEnabled: false, horizontalAlignment: canvasTextAlign === "justify" ? "left" : canvasTextAlign, verticalAlignment: verticalAlignment })) : null,
623
+ showFilter ? (react_1.default.createElement(cell_filter_1.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_1.default.createElement(cell_arrow_1.CellArrow, { scale: scale, onClick: onArrowClick !== null && onArrowClick !== void 0 ? onArrowClick : onRequestEdit, rowIndex: rowIndex, columnIndex: columnIndex, x: arrowOutside ? x + (config_1.ARROW_DOWN_WIDTH + 4) * scale : x, y: y, height: height, width: width, verticalAlignment: verticalAlignment })) : null,
277
624
  expandable ? (react_1.default.createElement(cell_group_1.CellGroup, { scale: scale, onClick: function (cell) {
278
625
  onExpandCollapse === null || onExpandCollapse === void 0 ? void 0 : onExpandCollapse(cell, !expanded);
279
626
  }, 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