@dou-ui/sheet 0.0.0-stage → 0.1.0-alpha.1

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/cell-types.js +20 -0
  4. package/dist/cell-types.js.map +1 -0
  5. package/dist/code/code-formula-bar.js +6 -0
  6. package/dist/code/code-formula-bar.js.map +1 -0
  7. package/dist/code/code-formula-bar.vue_vue_type_script_setup_true_lang.js +192 -0
  8. package/dist/code/code-formula-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  9. package/dist/code/index.js +12 -0
  10. package/dist/code/index.js.map +1 -0
  11. package/dist/code/language.js +51 -0
  12. package/dist/code/language.js.map +1 -0
  13. package/dist/code/style.d.ts +1 -0
  14. package/dist/code/style.js +2 -0
  15. package/dist/collaboration/anchor.js +143 -0
  16. package/dist/collaboration/anchor.js.map +1 -0
  17. package/dist/collaboration/index.js +770 -0
  18. package/dist/collaboration/index.js.map +1 -0
  19. package/dist/collaboration/peers.js +87 -0
  20. package/dist/collaboration/peers.js.map +1 -0
  21. package/dist/collaboration/shared.js +450 -0
  22. package/dist/collaboration/shared.js.map +1 -0
  23. package/dist/components/cell-panel.js +6 -0
  24. package/dist/components/cell-panel.js.map +1 -0
  25. package/dist/components/cell-panel.vue_vue_type_script_setup_true_lang.js +70 -0
  26. package/dist/components/cell-panel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  27. package/dist/components/cell-renders.js +40 -0
  28. package/dist/components/cell-renders.js.map +1 -0
  29. package/dist/components/date-panel.js +6 -0
  30. package/dist/components/date-panel.js.map +1 -0
  31. package/dist/components/date-panel.vue_vue_type_script_setup_true_lang.js +48 -0
  32. package/dist/components/date-panel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  33. package/dist/components/filter-panel.js +6 -0
  34. package/dist/components/filter-panel.js.map +1 -0
  35. package/dist/components/filter-panel.vue_vue_type_script_setup_true_lang.js +259 -0
  36. package/dist/components/filter-panel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  37. package/dist/components/format-dialog.js +6 -0
  38. package/dist/components/format-dialog.js.map +1 -0
  39. package/dist/components/format-dialog.vue_vue_type_script_setup_true_lang.js +432 -0
  40. package/dist/components/format-dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  41. package/dist/components/format-list.js +6 -0
  42. package/dist/components/format-list.js.map +1 -0
  43. package/dist/components/format-list.vue_vue_type_script_setup_true_lang.js +187 -0
  44. package/dist/components/format-list.vue_vue_type_script_setup_true_lang.js.map +1 -0
  45. package/dist/components/format-presets.js +97 -0
  46. package/dist/components/format-presets.js.map +1 -0
  47. package/dist/components/option-panel.js +6 -0
  48. package/dist/components/option-panel.js.map +1 -0
  49. package/dist/components/option-panel.vue_vue_type_script_setup_true_lang.js +117 -0
  50. package/dist/components/option-panel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  51. package/dist/components/sheet-comment.js +6 -0
  52. package/dist/components/sheet-comment.js.map +1 -0
  53. package/dist/components/sheet-comment.vue_vue_type_script_setup_true_lang.js +104 -0
  54. package/dist/components/sheet-comment.vue_vue_type_script_setup_true_lang.js.map +1 -0
  55. package/dist/components/sheet-formula-bar.js +6 -0
  56. package/dist/components/sheet-formula-bar.js.map +1 -0
  57. package/dist/components/sheet-formula-bar.vue_vue_type_script_setup_true_lang.js +124 -0
  58. package/dist/components/sheet-formula-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  59. package/dist/components/sheet-link.js +6 -0
  60. package/dist/components/sheet-link.js.map +1 -0
  61. package/dist/components/sheet-link.vue_vue_type_script_setup_true_lang.js +249 -0
  62. package/dist/components/sheet-link.vue_vue_type_script_setup_true_lang.js.map +1 -0
  63. package/dist/components/sheet-menu.js +6 -0
  64. package/dist/components/sheet-menu.js.map +1 -0
  65. package/dist/components/sheet-menu.vue_vue_type_script_setup_true_lang.js +115 -0
  66. package/dist/components/sheet-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  67. package/dist/components/sheet-name-box.js +6 -0
  68. package/dist/components/sheet-name-box.js.map +1 -0
  69. package/dist/components/sheet-name-box.vue_vue_type_script_setup_true_lang.js +91 -0
  70. package/dist/components/sheet-name-box.vue_vue_type_script_setup_true_lang.js.map +1 -0
  71. package/dist/components/sheet-note.js +6 -0
  72. package/dist/components/sheet-note.js.map +1 -0
  73. package/dist/components/sheet-note.vue_vue_type_script_setup_true_lang.js +146 -0
  74. package/dist/components/sheet-note.vue_vue_type_script_setup_true_lang.js.map +1 -0
  75. package/dist/components/sheet-outline.js +6 -0
  76. package/dist/components/sheet-outline.js.map +1 -0
  77. package/dist/components/sheet-outline.vue_vue_type_script_setup_true_lang.js +237 -0
  78. package/dist/components/sheet-outline.vue_vue_type_script_setup_true_lang.js.map +1 -0
  79. package/dist/components/sheet-tabs.js +6 -0
  80. package/dist/components/sheet-tabs.js.map +1 -0
  81. package/dist/components/sheet-tabs.vue_vue_type_script_setup_true_lang.js +487 -0
  82. package/dist/components/sheet-tabs.vue_vue_type_script_setup_true_lang.js.map +1 -0
  83. package/dist/components/sheet-toolbar.js +6 -0
  84. package/dist/components/sheet-toolbar.js.map +1 -0
  85. package/dist/components/sheet-toolbar.vue_vue_type_script_setup_true_lang.js +1562 -0
  86. package/dist/components/sheet-toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  87. package/dist/components/validation-dialog.js +6 -0
  88. package/dist/components/validation-dialog.js.map +1 -0
  89. package/dist/components/validation-dialog.vue_vue_type_script_setup_true_lang.js +370 -0
  90. package/dist/components/validation-dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  91. package/dist/context.js +12 -0
  92. package/dist/context.js.map +1 -0
  93. package/dist/core/address.js +256 -0
  94. package/dist/core/address.js.map +1 -0
  95. package/dist/core/axis.js +72 -0
  96. package/dist/core/axis.js.map +1 -0
  97. package/dist/core/cell-type.js +30 -0
  98. package/dist/core/cell-type.js.map +1 -0
  99. package/dist/core/clipboard.js +141 -0
  100. package/dist/core/clipboard.js.map +1 -0
  101. package/dist/core/commands.js +63 -0
  102. package/dist/core/commands.js.map +1 -0
  103. package/dist/core/comments.js +74 -0
  104. package/dist/core/comments.js.map +1 -0
  105. package/dist/core/conditional.js +362 -0
  106. package/dist/core/conditional.js.map +1 -0
  107. package/dist/core/csv.js +191 -0
  108. package/dist/core/csv.js.map +1 -0
  109. package/dist/core/data.js +118 -0
  110. package/dist/core/data.js.map +1 -0
  111. package/dist/core/document.js +187 -0
  112. package/dist/core/document.js.map +1 -0
  113. package/dist/core/extension.js +60 -0
  114. package/dist/core/extension.js.map +1 -0
  115. package/dist/core/file.js +336 -0
  116. package/dist/core/file.js.map +1 -0
  117. package/dist/core/fill.js +153 -0
  118. package/dist/core/fill.js.map +1 -0
  119. package/dist/core/find.js +42 -0
  120. package/dist/core/find.js.map +1 -0
  121. package/dist/core/format.js +404 -0
  122. package/dist/core/format.js.map +1 -0
  123. package/dist/core/group.js +134 -0
  124. package/dist/core/group.js.map +1 -0
  125. package/dist/core/history.js +68 -0
  126. package/dist/core/history.js.map +1 -0
  127. package/dist/core/index.js +17 -0
  128. package/dist/core/link.js +34 -0
  129. package/dist/core/link.js.map +1 -0
  130. package/dist/core/merge.js +105 -0
  131. package/dist/core/merge.js.map +1 -0
  132. package/dist/core/model.js +48 -0
  133. package/dist/core/model.js.map +1 -0
  134. package/dist/core/nav.js +44 -0
  135. package/dist/core/nav.js.map +1 -0
  136. package/dist/core/paint.js +106 -0
  137. package/dist/core/paint.js.map +1 -0
  138. package/dist/core/plain-cell-types.js +54 -0
  139. package/dist/core/plain-cell-types.js.map +1 -0
  140. package/dist/core/protect.js +246 -0
  141. package/dist/core/protect.js.map +1 -0
  142. package/dist/core/selection.js +37 -0
  143. package/dist/core/selection.js.map +1 -0
  144. package/dist/core/transaction.js +1582 -0
  145. package/dist/core/transaction.js.map +1 -0
  146. package/dist/core/validation.js +135 -0
  147. package/dist/core/validation.js.map +1 -0
  148. package/dist/core/versions.js +161 -0
  149. package/dist/core/versions.js.map +1 -0
  150. package/dist/export/color.js +36 -0
  151. package/dist/export/color.js.map +1 -0
  152. package/dist/export/draw.js +316 -0
  153. package/dist/export/draw.js.map +1 -0
  154. package/dist/export/index.js +390 -0
  155. package/dist/export/index.js.map +1 -0
  156. package/dist/formula/edit.js +210 -0
  157. package/dist/formula/edit.js.map +1 -0
  158. package/dist/formula/engine.js +526 -0
  159. package/dist/formula/engine.js.map +1 -0
  160. package/dist/formula/error.js +39 -0
  161. package/dist/formula/error.js.map +1 -0
  162. package/dist/formula/functions.js +1493 -0
  163. package/dist/formula/functions.js.map +1 -0
  164. package/dist/formula/help.js +408 -0
  165. package/dist/formula/help.js.map +1 -0
  166. package/dist/formula/index.js +7 -0
  167. package/dist/formula/parse.js +369 -0
  168. package/dist/formula/parse.js.map +1 -0
  169. package/dist/formula/values.js +123 -0
  170. package/dist/formula/values.js.map +1 -0
  171. package/dist/formulas.js +30 -0
  172. package/dist/formulas.js.map +1 -0
  173. package/dist/index.js +39 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/locale.js +1312 -0
  176. package/dist/locale.js.map +1 -0
  177. package/dist/sheet.js +6 -0
  178. package/dist/sheet.js.map +1 -0
  179. package/dist/sheet.vue_vue_type_script_setup_true_lang.js +7166 -0
  180. package/dist/sheet.vue_vue_type_script_setup_true_lang.js.map +1 -0
  181. package/dist/style.css +1 -0
  182. package/dist/style.d.ts +1 -0
  183. package/dist/style.js +17 -0
  184. package/dist/types/cell-types.d.ts +2 -0
  185. package/dist/types/code/code-formula-bar.vue.d.ts +3 -0
  186. package/dist/types/code/index.d.ts +12 -0
  187. package/dist/types/code/language.d.ts +13 -0
  188. package/dist/types/collaboration/anchor.d.ts +65 -0
  189. package/dist/types/collaboration/index.d.ts +59 -0
  190. package/dist/types/collaboration/peers.d.ts +21 -0
  191. package/dist/types/collaboration/shared.d.ts +75 -0
  192. package/dist/types/components/cell-panel.vue.d.ts +20 -0
  193. package/dist/types/components/cell-renders.d.ts +9 -0
  194. package/dist/types/components/date-panel.vue.d.ts +9 -0
  195. package/dist/types/components/filter-panel.vue.d.ts +21 -0
  196. package/dist/types/components/format-dialog.vue.d.ts +24 -0
  197. package/dist/types/components/format-list.vue.d.ts +25 -0
  198. package/dist/types/components/format-presets.d.ts +30 -0
  199. package/dist/types/components/option-panel.vue.d.ts +13 -0
  200. package/dist/types/components/sheet-comment.vue.d.ts +29 -0
  201. package/dist/types/components/sheet-formula-bar.vue.d.ts +3 -0
  202. package/dist/types/components/sheet-link.vue.d.ts +32 -0
  203. package/dist/types/components/sheet-menu.vue.d.ts +20 -0
  204. package/dist/types/components/sheet-name-box.vue.d.ts +3 -0
  205. package/dist/types/components/sheet-note.vue.d.ts +22 -0
  206. package/dist/types/components/sheet-outline.vue.d.ts +36 -0
  207. package/dist/types/components/sheet-tabs.vue.d.ts +9 -0
  208. package/dist/types/components/sheet-toolbar.vue.d.ts +11 -0
  209. package/dist/types/components/validation-dialog.vue.d.ts +25 -0
  210. package/dist/types/context.d.ts +189 -0
  211. package/dist/types/core/address.d.ts +109 -0
  212. package/dist/types/core/axis.d.ts +23 -0
  213. package/dist/types/core/cell-type.d.ts +157 -0
  214. package/dist/types/core/clipboard.d.ts +48 -0
  215. package/dist/types/core/commands.d.ts +76 -0
  216. package/dist/types/core/comments.d.ts +22 -0
  217. package/dist/types/core/conditional.d.ts +114 -0
  218. package/dist/types/core/csv.d.ts +93 -0
  219. package/dist/types/core/data.d.ts +26 -0
  220. package/dist/types/core/document.d.ts +49 -0
  221. package/dist/types/core/extension.d.ts +177 -0
  222. package/dist/types/core/file.d.ts +301 -0
  223. package/dist/types/core/fill.d.ts +23 -0
  224. package/dist/types/core/find.d.ts +39 -0
  225. package/dist/types/core/format.d.ts +47 -0
  226. package/dist/types/core/group.d.ts +76 -0
  227. package/dist/types/core/history.d.ts +27 -0
  228. package/dist/types/core/index.d.ts +33 -0
  229. package/dist/types/core/link.d.ts +14 -0
  230. package/dist/types/core/merge.d.ts +26 -0
  231. package/dist/types/core/model.d.ts +363 -0
  232. package/dist/types/core/nav.d.ts +15 -0
  233. package/dist/types/core/paint.d.ts +19 -0
  234. package/dist/types/core/plain-cell-types.d.ts +6 -0
  235. package/dist/types/core/protect.d.ts +34 -0
  236. package/dist/types/core/selection.d.ts +25 -0
  237. package/dist/types/core/transaction.d.ts +470 -0
  238. package/dist/types/core/validation.d.ts +55 -0
  239. package/dist/types/core/versions.d.ts +38 -0
  240. package/dist/types/export/color.d.ts +7 -0
  241. package/dist/types/export/draw.d.ts +98 -0
  242. package/dist/types/export/index.d.ts +47 -0
  243. package/dist/types/formula/edit.d.ts +62 -0
  244. package/dist/types/formula/engine.d.ts +99 -0
  245. package/dist/types/formula/error.d.ts +30 -0
  246. package/dist/types/formula/functions.d.ts +62 -0
  247. package/dist/types/formula/help.d.ts +12 -0
  248. package/dist/types/formula/index.d.ts +13 -0
  249. package/dist/types/formula/parse.d.ts +92 -0
  250. package/dist/types/formula/values.d.ts +65 -0
  251. package/dist/types/formulas.d.ts +32 -0
  252. package/dist/types/index.d.ts +225 -0
  253. package/dist/types/locale.d.ts +531 -0
  254. package/dist/types/sheet.vue.d.ts +60 -0
  255. package/dist/types/types.d.ts +498 -0
  256. package/dist/types/use-comments.d.ts +47 -0
  257. package/dist/types/use-messages.d.ts +7 -0
  258. package/dist/use-comments.js +130 -0
  259. package/dist/use-comments.js.map +1 -0
  260. package/dist/use-messages.js +29 -0
  261. package/dist/use-messages.js.map +1 -0
  262. package/package.json +93 -4
@@ -0,0 +1,390 @@
1
+ import { boundsOf, columnName, parseRange } from "../core/address.js";
2
+ import { dataBounds } from "../core/csv.js";
3
+ import { optionOf } from "../core/cell-type.js";
4
+ import { CellCheck, CellOption } from "../components/cell-renders.js";
5
+ import { plainColor } from "./color.js";
6
+ import { drawSheet, drawingSize, escapeXML } from "./draw.js";
7
+ import { svgToPNG } from "dou-ui";
8
+ import { h, render } from "vue";
9
+ // src/export/index.ts
10
+ /** The lines from `from` to `to` that show: hidden ones and rows the filter hides are left out. */
11
+ function shownLines(axis, from, to) {
12
+ const list = [];
13
+ for (let i = from; i <= to; i++) if (axis.size(i) > 0) list.push(i);
14
+ return list;
15
+ }
16
+ var read = (el) => getComputedStyle(el);
17
+ var at = (value) => String(Math.round(value * 100) / 100);
18
+ /** A fill as a plain color, `null` for none. */
19
+ function fillOf(value) {
20
+ const plain = plainColor(value);
21
+ return /^rgba\(.*,\s*0\)$/.test(plain) || value === "transparent" ? null : plain;
22
+ }
23
+ var sum = (list, axis) => list.reduce((total, i) => total + axis.size(i), 0);
24
+ var BAR_SPAN = /--dou-sheet-bar-(from|to):[^;]*;/g;
25
+ var SELECTION_CLASSES = / is-(selected|active|edge-\w+|invalid|freeze-edge)\b/g;
26
+ /**
27
+ * Reads how cells look from cells made like the grid's own, in a sheet of
28
+ * their own inside this one so its tokens apply, or in the light theme
29
+ * for paper. `look` turns a cell's classes and style into plain colors.
30
+ */
31
+ function exportReader(root, light, gridlines) {
32
+ const box = document.createElement("div");
33
+ box.className = `dou-sheet${gridlines ? " has-gridlines" : ""}`;
34
+ if (light) box.dataset.douTheme = "light";
35
+ box.setAttribute("aria-hidden", "true");
36
+ box.style.cssText = "position:absolute;top:0;left:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none";
37
+ root.append(box);
38
+ const make = (className, css = "") => {
39
+ const el = document.createElement("div");
40
+ el.className = className;
41
+ if (css) el.style.cssText = css;
42
+ box.append(el);
43
+ return el;
44
+ };
45
+ const color = (value) => {
46
+ const el = make("");
47
+ el.style.color = value;
48
+ return plainColor(read(el).color);
49
+ };
50
+ const cell = read(make("dou-sheet__cell"));
51
+ const bold = read(make("dou-sheet__cell is-bold"));
52
+ const colHead = read(make("dou-sheet__col-head"));
53
+ const off = read(make("dou-sheet__check"));
54
+ const on = read(make("dou-sheet__check is-checked"));
55
+ const theme = {
56
+ fontFamily: cell.fontFamily,
57
+ fontSize: Number.parseFloat(cell.fontSize) || 13,
58
+ boldWeight: bold.fontWeight,
59
+ surface: color("var(--dou-color-bg-surface)"),
60
+ line: gridlines ? color("var(--dou-sheet-line)") : "none",
61
+ freezeLine: color("var(--dou-sheet-freeze-line)"),
62
+ headerBg: color("var(--dou-sheet-header-bg)"),
63
+ headerText: plainColor(colHead.color),
64
+ headerLine: color("var(--dou-sheet-line-strong)"),
65
+ checkBorder: plainColor(off.borderTopColor),
66
+ checkBg: plainColor(off.backgroundColor),
67
+ checkOn: plainColor(on.color),
68
+ checkOnBg: plainColor(on.backgroundColor)
69
+ };
70
+ const looks = /* @__PURE__ */ new Map();
71
+ const look = (raw, css) => {
72
+ const key = `${raw}\u0000${css}`;
73
+ let found = looks.get(key);
74
+ if (found) return found;
75
+ const cls = ` ${raw}`.replace(SELECTION_CLASSES, "");
76
+ const style = read(make(cls, css));
77
+ const deco = style.textDecorationLine;
78
+ const has = (name) => cls.includes(` ${name}`);
79
+ found = {
80
+ color: plainColor(style.color),
81
+ background: fillOf(style.backgroundColor),
82
+ bold: Number.parseInt(style.fontWeight) >= 600,
83
+ italic: style.fontStyle === "italic",
84
+ underline: deco.includes("underline"),
85
+ strike: deco.includes("line-through"),
86
+ align: has("is-align-center") ? "center" : has("is-align-right") ? "right" : "left",
87
+ valign: has("is-valign-top") ? "top" : has("is-valign-bottom") ? "bottom" : "middle",
88
+ wrap: has("is-wrap"),
89
+ line: gridlines ? plainColor(style.borderRightColor) : null
90
+ };
91
+ if (has("has-bar")) found.barColor = color(style.getPropertyValue("--dou-sheet-bar-color"));
92
+ looks.set(key, found);
93
+ return found;
94
+ };
95
+ const tags = /* @__PURE__ */ new Map();
96
+ const tag = (tone) => {
97
+ const key = tone ?? "";
98
+ let found = tags.get(key);
99
+ if (!found) {
100
+ const cls = "dou-tag dou-tag--soft dou-sheet__tag";
101
+ const style = read(make(tone && tone !== "neutral" ? `${cls} dou-tag--${tone}` : `${cls} is-neutral`));
102
+ found = {
103
+ background: plainColor(style.backgroundColor),
104
+ color: plainColor(style.color),
105
+ border: plainColor(style.borderTopColor)
106
+ };
107
+ tags.set(key, found);
108
+ }
109
+ return found;
110
+ };
111
+ const icons = /* @__PURE__ */ new Map();
112
+ const icon = (component, area, fill) => {
113
+ let markup = icons.get(component);
114
+ if (markup === void 0) {
115
+ const holder = make("");
116
+ try {
117
+ render(h(component), holder);
118
+ markup = holder.querySelector("svg")?.outerHTML ?? "";
119
+ } catch {
120
+ markup = "";
121
+ }
122
+ render(null, holder);
123
+ icons.set(component, markup);
124
+ }
125
+ if (!markup) return "";
126
+ return markup.replace(/^<svg\b([^>]*)>/, (_, attrs) => {
127
+ return `<svg${attrs.replace(/\s(width|height|x|y|class|style|aria-[\w-]+|role)="[^"]*"/g, "")} x="${at(area.x)}" y="${at(area.y)}" width="${at(area.width)}" height="${at(area.height)}">`;
128
+ }).replace(/currentColor/g, fill);
129
+ };
130
+ return {
131
+ theme,
132
+ look,
133
+ tag,
134
+ color,
135
+ icon,
136
+ done: () => box.remove()
137
+ };
138
+ }
139
+ /** A select cell's option tone, or `undefined` when its value isn't one of the options. */
140
+ function optionTone(props) {
141
+ const setup = props?.setup;
142
+ for (const option of setup?.options ?? []) {
143
+ const picked = optionOf(option);
144
+ if (picked.value === props.value) return { tone: picked.tone };
145
+ }
146
+ }
147
+ /** Wide enough for the longest row number drawn. */
148
+ var exportHeaderWidth = (rowList) => Math.max(40, String((rowList[rowList.length - 1] ?? 0) + 1).length * 8 + 16);
149
+ var exportMeasure;
150
+ var measureText = (text, font) => {
151
+ exportMeasure ??= document.createElement("canvas").getContext("2d");
152
+ if (!exportMeasure) return text.length * 7;
153
+ if (exportMeasure.font !== font) exportMeasure.font = font;
154
+ return exportMeasure.measureText(text).width;
155
+ };
156
+ /** A4 less 10mm margins, in px at 96 per inch: [width, height]. */
157
+ var PAGE = {
158
+ portrait: [718, 1047],
159
+ landscape: [1047, 718]
160
+ };
161
+ /**
162
+ * Runs of lines that fit `room` each, after the lines repeated on every
163
+ * page. A line bigger than the room gets a page of its own.
164
+ */
165
+ function pagesOf(list, size, room) {
166
+ const pages = [];
167
+ let page = [];
168
+ let used = 0;
169
+ for (const i of list) {
170
+ const s = size(i);
171
+ if (page.length && used + s > room) {
172
+ pages.push(page);
173
+ page = [];
174
+ used = 0;
175
+ }
176
+ page.push(i);
177
+ used += s;
178
+ }
179
+ if (page.length || !pages.length) pages.push(page);
180
+ return pages;
181
+ }
182
+ /** The cells an image or print shows: `range`, or the cells with data and the merges. */
183
+ function exportBounds(range, t) {
184
+ const lastRow = t.rows.count - 1;
185
+ const lastCol = t.cols.count - 1;
186
+ if (range != null) {
187
+ const parsed = parseRange(range);
188
+ if (!parsed) throw new RangeError(`[@dou-ui/sheet] "${range}" isn't a range`);
189
+ const b = boundsOf(parsed);
190
+ return {
191
+ top: Math.max(0, b.top),
192
+ left: Math.max(0, b.left),
193
+ bottom: Math.min(lastRow, b.bottom),
194
+ right: Math.min(lastCol, b.right)
195
+ };
196
+ }
197
+ const b = dataBounds(t.data) ?? {
198
+ top: 0,
199
+ left: 0,
200
+ bottom: 0,
201
+ right: 0
202
+ };
203
+ for (const m of t.merges.list) {
204
+ b.bottom = Math.max(b.bottom, m.bottom);
205
+ b.right = Math.max(b.right, m.right);
206
+ }
207
+ return {
208
+ top: 0,
209
+ left: 0,
210
+ bottom: Math.min(lastRow, b.bottom),
211
+ right: Math.min(lastCol, b.right)
212
+ };
213
+ }
214
+ /** `exportSVG`, `exportPNG` and `print` of a grid. */
215
+ function createSheetExport(src) {
216
+ /** What to draw for some rows and columns, in this order, each list sorted. */
217
+ function exportDrawing(rowList, colList, options, reader, t) {
218
+ const { rows: r, cols: c, merges, data } = t;
219
+ const rowAt = new Map(rowList.map((row, i) => [row, i]));
220
+ const colAt = new Map(colList.map((col, i) => [col, i]));
221
+ const ys = [0];
222
+ for (const row of rowList) ys.push(ys[ys.length - 1] + r.size(row));
223
+ const xs = [0];
224
+ for (const col of colList) xs.push(xs[xs.length - 1] + c.size(col));
225
+ const f = t.frozen;
226
+ const cells = [];
227
+ const add = (view, x, y, width, height, merged = false) => {
228
+ if (view.hidden) return;
229
+ const bar = view.cls.includes(" has-bar");
230
+ const look = reader.look(view.cls, bar ? view.css.replace(BAR_SPAN, "") : view.css);
231
+ const cell = {
232
+ x,
233
+ y,
234
+ width,
235
+ height,
236
+ text: view.text,
237
+ look
238
+ };
239
+ if (merged) cell.merged = true;
240
+ if (bar && look.barColor) cell.bar = {
241
+ from: Number.parseFloat(/--dou-sheet-bar-from:([\d.]+)%/.exec(view.css)?.[1] ?? "0") / 100,
242
+ to: Number.parseFloat(/--dou-sheet-bar-to:([\d.]+)%/.exec(view.css)?.[1] ?? "0") / 100,
243
+ color: look.barColor
244
+ };
245
+ if (view.draw) cell.draw = view.draw;
246
+ else if (view.render === CellCheck) cell.check = view.props?.value === true;
247
+ else if (view.render === CellOption) {
248
+ const option = view.text ? optionTone(view.props) : void 0;
249
+ if (option) cell.tag = reader.tag(option.tone);
250
+ } else if (view.render && !view.cls.includes(" is-error")) cell.text = view.label ?? view.text;
251
+ if (!merged && !cell.text && !look.background && !cell.bar && cell.check === void 0 && !cell.draw) return;
252
+ cells.push(cell);
253
+ };
254
+ const seen = /* @__PURE__ */ new Set();
255
+ rowList.forEach((row, ri) => {
256
+ const line = data.cells[row];
257
+ colList.forEach((col, ci) => {
258
+ const m = merges.size ? merges.at(row, col) : void 0;
259
+ if (!m) return add(t.cellView(row, col, line, c.size(col)), xs[ci], ys[ri], c.size(col), r.size(row));
260
+ if (seen.has(m)) return;
261
+ seen.add(m);
262
+ let last = ri;
263
+ while (last + 1 < rowList.length && rowList[last + 1] <= m.bottom) last++;
264
+ let right = ci;
265
+ while (right + 1 < colList.length && colList[right + 1] <= m.right) right++;
266
+ const view = t.cellView(m.top, m.left, data.cells[m.top], c.offset(m.right + 1) - c.offset(m.left), m);
267
+ if (!rowAt.has(m.top) || !colAt.has(m.left)) {
268
+ view.text = "";
269
+ view.render = void 0;
270
+ }
271
+ add(view, xs[ci], ys[ri], xs[right + 1] - xs[ci], ys[last + 1] - ys[ri], true);
272
+ });
273
+ });
274
+ const frozenRows = rowList.filter((row) => row < f.rows).length;
275
+ const frozenCols = colList.filter((col) => col < f.cols).length;
276
+ return {
277
+ colWidths: colList.map((col) => c.size(col)),
278
+ rowHeights: rowList.map((row) => r.size(row)),
279
+ colNames: options.headers ? colList.map(columnName) : void 0,
280
+ rowNames: options.headers ? rowList.map((row) => String(row + 1)) : void 0,
281
+ headerWidth: options.headers ? exportHeaderWidth(rowList) : 0,
282
+ headerHeight: options.headers ? src.rowHeight() : 0,
283
+ frozenRows: frozenRows < rowList.length ? frozenRows : 0,
284
+ frozenCols: frozenCols < colList.length ? frozenCols : 0,
285
+ cells,
286
+ gridlines: options.gridlines,
287
+ background: options.background,
288
+ theme: reader.theme,
289
+ icon: reader.icon,
290
+ color: reader.color
291
+ };
292
+ }
293
+ /**
294
+ * The cells of a sheet laid out as the grid draws them, in plain colors,
295
+ * given to `use` while the page's styles can still be read, as cell
296
+ * types' drawings read colors.
297
+ */
298
+ function drawingOf(options, use) {
299
+ const t = src.target(options.sheet);
300
+ const b = exportBounds(options.range, t);
301
+ const gridlines = options.gridlines ?? src.gridlines();
302
+ const reader = exportReader(src.root() ?? document.body, false, gridlines);
303
+ try {
304
+ return use(exportDrawing(shownLines(t.rows, b.top, b.bottom), shownLines(t.cols, b.left, b.right), {
305
+ headers: options.headers ?? false,
306
+ gridlines,
307
+ background: options.background ?? true
308
+ }, reader, t));
309
+ } finally {
310
+ reader.done();
311
+ }
312
+ }
313
+ /** The sheet as an SVG document, and its size. */
314
+ function exportImage(options) {
315
+ return drawingOf(options, (laid) => ({
316
+ svg: drawSheet(laid, measureText),
317
+ ...drawingSize(laid)
318
+ }));
319
+ }
320
+ function exportSVG(options = {}) {
321
+ return exportImage(options).svg;
322
+ }
323
+ async function exportPNG(options = {}) {
324
+ const { svg, width, height } = exportImage(options);
325
+ return svgToPNG(svg, {
326
+ width,
327
+ height,
328
+ pixelRatio: options.pixelRatio,
329
+ scope: "@dou-ui/sheet",
330
+ hint: "export a smaller range a part at a time, or use exportSVG() / toCSV()"
331
+ });
332
+ }
333
+ function print(options = {}) {
334
+ const t = src.target(options.sheet);
335
+ const b = exportBounds(options.range, t);
336
+ const gridlines = options.gridlines ?? src.gridlines();
337
+ const headers = options.headers ?? false;
338
+ const { rows: r, cols: c, frozen: f } = t;
339
+ const rowList = shownLines(r, b.top, b.bottom);
340
+ const colList = shownLines(c, b.left, b.right);
341
+ const repeatRows = rowList.filter((row) => row < f.rows);
342
+ const repeatCols = colList.filter((col) => col < f.cols);
343
+ const bodyRows = rowList.filter((row) => row >= f.rows);
344
+ const bodyCols = colList.filter((col) => col >= f.cols);
345
+ const head = headers ? exportHeaderWidth(rowList) : 0;
346
+ const orientation = options.orientation ?? (head + sum(colList, c) > PAGE.portrait[0] ? "landscape" : "portrait");
347
+ const [pageWidth, pageHeight] = PAGE[orientation];
348
+ const title = options.title ?? "";
349
+ const titleHeight = title ? 28 : 0;
350
+ const across = pagesOf(bodyCols, (i) => c.size(i), pageWidth - head - sum(repeatCols, c));
351
+ const down = pagesOf(bodyRows, (i) => r.size(i), pageHeight - titleHeight - (headers ? src.rowHeight() : 0) - sum(repeatRows, r));
352
+ const reader = exportReader(src.root() ?? document.body, true, gridlines);
353
+ const pages = [];
354
+ try {
355
+ for (const cols of across) for (const rows of down) {
356
+ const drawing = exportDrawing([...repeatRows, ...rows], [...repeatCols, ...cols], {
357
+ headers,
358
+ gridlines,
359
+ background: true
360
+ }, reader, t);
361
+ pages.push(`<div class="page">${title ? `<h1>${escapeXML(title)}</h1>` : ""}${drawSheet(drawing, measureText)}</div>`);
362
+ }
363
+ } finally {
364
+ reader.done();
365
+ }
366
+ const iframe = document.createElement("iframe");
367
+ iframe.setAttribute("aria-hidden", "true");
368
+ iframe.style.cssText = "position:fixed;width:0;height:0;border:0;visibility:hidden";
369
+ document.body.append(iframe);
370
+ const doc = iframe.contentDocument;
371
+ doc.open();
372
+ doc.write(`<!doctype html><html><head><meta charset="utf-8"><title>${escapeXML(title || document.title)}</title><style>@page{size:A4 ${orientation};margin:10mm;@bottom-center{content:counter(page) " / " counter(pages);font:9px ${reader.theme.fontFamily};color:#8a8a8a}}html,body{margin:0}h1{margin:0 0 10px;font:600 14px/18px ${reader.theme.fontFamily};color:#1f1f1f}.page{break-after:page;print-color-adjust:exact;-webkit-print-color-adjust:exact}.page:last-child{break-after:auto}svg{display:block;max-width:100%;height:auto}</style></head><body>${pages.join("")}</body></html>`);
373
+ doc.close();
374
+ const win = iframe.contentWindow;
375
+ const done = () => iframe.remove();
376
+ win.addEventListener("afterprint", done);
377
+ setTimeout(done, 6e4);
378
+ win.focus();
379
+ win.print();
380
+ }
381
+ return {
382
+ exportSVG,
383
+ exportPNG,
384
+ print,
385
+ drawing: (options = {}) => drawingOf(options, (laid) => laid)
386
+ };
387
+ }
388
+ export { createSheetExport };
389
+
390
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/export/index.ts"],"sourcesContent":["import { svgToPNG } from 'dou-ui'\nimport type { SheetAxis } from '../core/axis'\nimport type { SheetBounds } from '../core/address'\nimport { boundsOf, columnName, parseRange } from '../core/address'\nimport { h, render, type Component } from 'vue'\nimport {\n optionOf,\n type SheetCellType,\n type SheetDrawContext,\n type SheetDrawRect,\n} from '../core/cell-type'\nimport { dataBounds } from '../core/csv'\nimport type { SheetMerges } from '../core/merge'\nimport type { SheetData, SheetTypeSetup } from '../core/model'\nimport { CellCheck, CellOption } from '../components/cell-renders'\nimport type { SheetRef } from '../core/transaction'\nimport type { SheetExportOptions, SheetImageOptions, SheetPrintOptions } from '../types'\nimport { plainColor } from './color'\nimport {\n drawSheet,\n drawingSize,\n escapeXML,\n type DrawCell,\n type DrawLook,\n type DrawTheme,\n type MeasureText,\n type SheetDrawing,\n} from './draw'\n\n/** A cell as the grid draws it, which images and pages draw the same way. */\nexport interface ExportCellView {\n text: string\n cls: string\n css: string\n render?: SheetCellType['render']\n props?: Record<string, unknown>\n /** A cell type's own drawing, as SVG markup. */\n draw?: (draw: SheetDrawContext) => string\n label?: string\n hidden?: boolean\n}\n\n/** A sheet as images and pages draw it: its data, laid out, and its cells as the grid draws them. */\nexport interface SheetExportTarget {\n data: SheetData\n rows: SheetAxis\n cols: SheetAxis\n frozen: { rows: number; cols: number }\n merges: SheetMerges\n cellView: (\n row: number,\n col: number,\n line: SheetData['cells'][number],\n width: number,\n span?: SheetBounds,\n ) => ExportCellView\n}\n\n/** What of the grid images and pages are drawn from. */\nexport interface SheetExportSource {\n root: () => HTMLElement | undefined\n /** The sheet shown, or the one named. */\n target: (sheet?: SheetRef) => SheetExportTarget\n rowHeight: () => number\n gridlines: () => boolean\n}\n\ntype ExportReader = ReturnType<typeof exportReader>\n\n/** The lines from `from` to `to` that show: hidden ones and rows the filter hides are left out. */\nfunction shownLines(axis: SheetAxis, from: number, to: number) {\n const list: number[] = []\n for (let i = from; i <= to; i++) if (axis.size(i) > 0) list.push(i)\n return list\n}\n\nconst read = (el: Element) => getComputedStyle(el)\n\nconst at = (value: number) => String(Math.round(value * 100) / 100)\n\n/** A fill as a plain color, `null` for none. */\nfunction fillOf(value: string) {\n const plain = plainColor(value)\n return /^rgba\\(.*,\\s*0\\)$/.test(plain) || value === 'transparent' ? null : plain\n}\n\nconst sum = (list: number[], axis: SheetAxis) => list.reduce((total, i) => total + axis.size(i), 0)\n\nconst BAR_SPAN = /--dou-sheet-bar-(from|to):[^;]*;/g\nconst SELECTION_CLASSES = / is-(selected|active|edge-\\w+|invalid|freeze-edge)\\b/g\n\n/**\n * Reads how cells look from cells made like the grid's own, in a sheet of\n * their own inside this one so its tokens apply, or in the light theme\n * for paper. `look` turns a cell's classes and style into plain colors.\n */\nfunction exportReader(root: HTMLElement, light: boolean, gridlines: boolean) {\n const box = document.createElement('div')\n box.className = `dou-sheet${gridlines ? ' has-gridlines' : ''}`\n if (light) box.dataset.douTheme = 'light'\n box.setAttribute('aria-hidden', 'true')\n box.style.cssText =\n 'position:absolute;top:0;left:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none'\n root.append(box)\n const make = (className: string, css = '') => {\n const el = document.createElement('div')\n el.className = className\n if (css) el.style.cssText = css\n box.append(el)\n return el\n }\n const color = (value: string) => {\n const el = make('')\n el.style.color = value\n return plainColor(read(el).color)\n }\n const cell = read(make('dou-sheet__cell'))\n const bold = read(make('dou-sheet__cell is-bold'))\n const colHead = read(make('dou-sheet__col-head'))\n const off = read(make('dou-sheet__check'))\n const on = read(make('dou-sheet__check is-checked'))\n const theme: DrawTheme = {\n fontFamily: cell.fontFamily,\n fontSize: Number.parseFloat(cell.fontSize) || 13,\n boldWeight: bold.fontWeight,\n surface: color('var(--dou-color-bg-surface)'),\n line: gridlines ? color('var(--dou-sheet-line)') : 'none',\n freezeLine: color('var(--dou-sheet-freeze-line)'),\n headerBg: color('var(--dou-sheet-header-bg)'),\n headerText: plainColor(colHead.color),\n headerLine: color('var(--dou-sheet-line-strong)'),\n checkBorder: plainColor(off.borderTopColor),\n checkBg: plainColor(off.backgroundColor),\n checkOn: plainColor(on.color),\n checkOnBg: plainColor(on.backgroundColor),\n }\n const looks = new Map<string, DrawLook & { barColor?: string }>()\n const look = (raw: string, css: string) => {\n const key = `${raw}\\u0000${css}`\n let found = looks.get(key)\n if (found) return found\n // the selection isn't drawn\n const cls = ` ${raw}`.replace(SELECTION_CLASSES, '')\n const style = read(make(cls, css))\n const deco = style.textDecorationLine\n const has = (name: string) => cls.includes(` ${name}`)\n found = {\n color: plainColor(style.color),\n background: fillOf(style.backgroundColor),\n bold: Number.parseInt(style.fontWeight) >= 600,\n italic: style.fontStyle === 'italic',\n underline: deco.includes('underline'),\n strike: deco.includes('line-through'),\n align: has('is-align-center') ? 'center' : has('is-align-right') ? 'right' : 'left',\n valign: has('is-valign-top') ? 'top' : has('is-valign-bottom') ? 'bottom' : 'middle',\n wrap: has('is-wrap'),\n line: gridlines ? plainColor(style.borderRightColor) : null,\n }\n if (has('has-bar')) found.barColor = color(style.getPropertyValue('--dou-sheet-bar-color'))\n looks.set(key, found)\n return found\n }\n const tags = new Map<string, NonNullable<DrawCell['tag']>>()\n const tag = (tone: string | undefined) => {\n const key = tone ?? ''\n let found = tags.get(key)\n if (!found) {\n const cls = 'dou-tag dou-tag--soft dou-sheet__tag'\n const style = read(\n make(tone && tone !== 'neutral' ? `${cls} dou-tag--${tone}` : `${cls} is-neutral`),\n )\n found = {\n background: plainColor(style.backgroundColor),\n color: plainColor(style.color),\n border: plainColor(style.borderTopColor),\n }\n tags.set(key, found)\n }\n return found\n }\n // icons drawn once each, as SVG with their color left to fill in\n const icons = new Map<Component, string>()\n const icon = (component: Component, area: SheetDrawRect, fill: string) => {\n let markup = icons.get(component)\n if (markup === undefined) {\n const holder = make('')\n try {\n render(h(component), holder)\n markup = holder.querySelector('svg')?.outerHTML ?? ''\n } catch {\n markup = ''\n }\n render(null, holder)\n icons.set(component, markup)\n }\n if (!markup) return ''\n return markup\n .replace(/^<svg\\b([^>]*)>/, (_, attrs: string) => {\n const kept = attrs.replace(/\\s(width|height|x|y|class|style|aria-[\\w-]+|role)=\"[^\"]*\"/g, '')\n return `<svg${kept} x=\"${at(area.x)}\" y=\"${at(area.y)}\" width=\"${at(area.width)}\" height=\"${at(area.height)}\">`\n })\n .replace(/currentColor/g, fill)\n }\n return { theme, look, tag, color, icon, done: () => box.remove() }\n}\n\n/** A select cell's option tone, or `undefined` when its value isn't one of the options. */\nfunction optionTone(props: Record<string, unknown> | undefined) {\n const setup = props?.setup as SheetTypeSetup | undefined\n for (const option of setup?.options ?? []) {\n const picked = optionOf(option)\n if (picked.value === props!.value) return { tone: picked.tone }\n }\n return undefined\n}\n\n/** Wide enough for the longest row number drawn. */\nconst exportHeaderWidth = (rowList: number[]) =>\n Math.max(40, String((rowList[rowList.length - 1] ?? 0) + 1).length * 8 + 16)\n\nlet exportMeasure: CanvasRenderingContext2D | null | undefined\nconst measureText: MeasureText = (text, font) => {\n exportMeasure ??= document.createElement('canvas').getContext('2d')\n if (!exportMeasure) return text.length * 7\n if (exportMeasure.font !== font) exportMeasure.font = font\n return exportMeasure.measureText(text).width\n}\n\n/** A4 less 10mm margins, in px at 96 per inch: [width, height]. */\nconst PAGE = { portrait: [718, 1047], landscape: [1047, 718] } as const\n\n/**\n * Runs of lines that fit `room` each, after the lines repeated on every\n * page. A line bigger than the room gets a page of its own.\n */\nfunction pagesOf(list: number[], size: (i: number) => number, room: number) {\n const pages: number[][] = []\n let page: number[] = []\n let used = 0\n for (const i of list) {\n const s = size(i)\n if (page.length && used + s > room) {\n pages.push(page)\n page = []\n used = 0\n }\n page.push(i)\n used += s\n }\n if (page.length || !pages.length) pages.push(page)\n return pages\n}\n\n/** The cells an image or print shows: `range`, or the cells with data and the merges. */\nfunction exportBounds(range: string | undefined, t: SheetExportTarget): SheetBounds {\n const lastRow = t.rows.count - 1\n const lastCol = t.cols.count - 1\n if (range != null) {\n const parsed = parseRange(range)\n if (!parsed) throw new RangeError(`[@dou-ui/sheet] \"${range}\" isn't a range`)\n const b = boundsOf(parsed)\n return {\n top: Math.max(0, b.top),\n left: Math.max(0, b.left),\n bottom: Math.min(lastRow, b.bottom),\n right: Math.min(lastCol, b.right),\n }\n }\n const b = dataBounds(t.data) ?? { top: 0, left: 0, bottom: 0, right: 0 }\n for (const m of t.merges.list) {\n b.bottom = Math.max(b.bottom, m.bottom)\n b.right = Math.max(b.right, m.right)\n }\n return {\n top: 0,\n left: 0,\n bottom: Math.min(lastRow, b.bottom),\n right: Math.min(lastCol, b.right),\n }\n}\n/** `exportSVG`, `exportPNG` and `print` of a grid. */\nexport function createSheetExport(src: SheetExportSource) {\n /** What to draw for some rows and columns, in this order, each list sorted. */\n function exportDrawing(\n rowList: number[],\n colList: number[],\n options: { headers: boolean; gridlines: boolean; background: boolean },\n reader: ExportReader,\n t: SheetExportTarget,\n ): SheetDrawing {\n const { rows: r, cols: c, merges, data } = t\n const rowAt = new Map(rowList.map((row, i) => [row, i]))\n const colAt = new Map(colList.map((col, i) => [col, i]))\n const ys = [0]\n for (const row of rowList) ys.push(ys[ys.length - 1]! + r.size(row))\n const xs = [0]\n for (const col of colList) xs.push(xs[xs.length - 1]! + c.size(col))\n const f = t.frozen\n const cells: DrawCell[] = []\n const add = (\n view: ExportCellView,\n x: number,\n y: number,\n width: number,\n height: number,\n merged = false,\n ) => {\n if (view.hidden) return\n const bar = view.cls.includes(' has-bar')\n // the bar's length is the cell's own; its color is the look's\n const look = reader.look(view.cls, bar ? view.css.replace(BAR_SPAN, '') : view.css)\n const cell: DrawCell = { x, y, width, height, text: view.text, look }\n if (merged) cell.merged = true\n if (bar && look.barColor) {\n const from =\n Number.parseFloat(/--dou-sheet-bar-from:([\\d.]+)%/.exec(view.css)?.[1] ?? '0') / 100\n const to =\n Number.parseFloat(/--dou-sheet-bar-to:([\\d.]+)%/.exec(view.css)?.[1] ?? '0') / 100\n cell.bar = { from, to, color: look.barColor }\n }\n if (view.draw) cell.draw = view.draw\n else if (view.render === CellCheck) cell.check = view.props?.value === true\n else if (view.render === CellOption) {\n const option = view.text ? optionTone(view.props) : undefined\n if (option) cell.tag = reader.tag(option.tone)\n } else if (view.render && !view.cls.includes(' is-error')) cell.text = view.label ?? view.text\n if (\n !merged &&\n !cell.text &&\n !look.background &&\n !cell.bar &&\n cell.check === undefined &&\n !cell.draw\n )\n return\n cells.push(cell)\n }\n const seen = new Set<SheetBounds>()\n rowList.forEach((row, ri) => {\n const line = data.cells[row]\n colList.forEach((col, ci) => {\n const m = merges.size ? merges.at(row, col) : undefined\n if (!m)\n return add(\n t.cellView(row, col, line, c.size(col)),\n xs[ci]!,\n ys[ri]!,\n c.size(col),\n r.size(row),\n )\n if (seen.has(m)) return\n seen.add(m)\n // a merge from its first row and column drawn to its last\n let last = ri\n while (last + 1 < rowList.length && rowList[last + 1]! <= m.bottom) last++\n let right = ci\n while (right + 1 < colList.length && colList[right + 1]! <= m.right) right++\n const view = t.cellView(\n m.top,\n m.left,\n data.cells[m.top],\n c.offset(m.right + 1) - c.offset(m.left),\n m,\n )\n // its text shows where its top left cell is\n if (!rowAt.has(m.top) || !colAt.has(m.left)) {\n view.text = ''\n view.render = undefined\n }\n add(view, xs[ci]!, ys[ri]!, xs[right + 1]! - xs[ci]!, ys[last + 1]! - ys[ri]!, true)\n })\n })\n const frozenRows = rowList.filter((row) => row < f.rows).length\n const frozenCols = colList.filter((col) => col < f.cols).length\n return {\n colWidths: colList.map((col) => c.size(col)),\n rowHeights: rowList.map((row) => r.size(row)),\n colNames: options.headers ? colList.map(columnName) : undefined,\n rowNames: options.headers ? rowList.map((row) => String(row + 1)) : undefined,\n headerWidth: options.headers ? exportHeaderWidth(rowList) : 0,\n headerHeight: options.headers ? src.rowHeight() : 0,\n // a firmer line only between the frozen lines and the rest drawn\n frozenRows: frozenRows < rowList.length ? frozenRows : 0,\n frozenCols: frozenCols < colList.length ? frozenCols : 0,\n cells,\n gridlines: options.gridlines,\n background: options.background,\n theme: reader.theme,\n icon: reader.icon,\n color: reader.color,\n }\n }\n\n /**\n * The cells of a sheet laid out as the grid draws them, in plain colors,\n * given to `use` while the page's styles can still be read, as cell\n * types' drawings read colors.\n */\n function drawingOf<T>(options: SheetExportOptions, use: (drawing: SheetDrawing) => T): T {\n const t = src.target(options.sheet)\n const b = exportBounds(options.range, t)\n const gridlines = options.gridlines ?? src.gridlines()\n const reader = exportReader(src.root() ?? document.body, false, gridlines)\n try {\n return use(\n exportDrawing(\n shownLines(t.rows, b.top, b.bottom),\n shownLines(t.cols, b.left, b.right),\n { headers: options.headers ?? false, gridlines, background: options.background ?? true },\n reader,\n t,\n ),\n )\n } finally {\n reader.done()\n }\n }\n\n /** The sheet as an SVG document, and its size. */\n function exportImage(options: SheetExportOptions) {\n return drawingOf(options, (laid) => ({\n svg: drawSheet(laid, measureText),\n ...drawingSize(laid),\n }))\n }\n\n function exportSVG(options: SheetExportOptions = {}) {\n return exportImage(options).svg\n }\n\n async function exportPNG(options: SheetImageOptions = {}) {\n const { svg, width, height } = exportImage(options)\n return svgToPNG(svg, {\n width,\n height,\n pixelRatio: options.pixelRatio,\n scope: '@dou-ui/sheet',\n hint: 'export a smaller range a part at a time, or use exportSVG() / toCSV()',\n })\n }\n\n function print(options: SheetPrintOptions = {}) {\n const t = src.target(options.sheet)\n const b = exportBounds(options.range, t)\n const gridlines = options.gridlines ?? src.gridlines()\n const headers = options.headers ?? false\n const { rows: r, cols: c, frozen: f } = t\n const rowList = shownLines(r, b.top, b.bottom)\n const colList = shownLines(c, b.left, b.right)\n // the frozen rows and columns are printed on every page\n const repeatRows = rowList.filter((row) => row < f.rows)\n const repeatCols = colList.filter((col) => col < f.cols)\n const bodyRows = rowList.filter((row) => row >= f.rows)\n const bodyCols = colList.filter((col) => col >= f.cols)\n const head = headers ? exportHeaderWidth(rowList) : 0\n const orientation =\n options.orientation ?? (head + sum(colList, c) > PAGE.portrait[0] ? 'landscape' : 'portrait')\n const [pageWidth, pageHeight] = PAGE[orientation]\n const title = options.title ?? ''\n const titleHeight = title ? 28 : 0\n const across = pagesOf(bodyCols, (i) => c.size(i), pageWidth - head - sum(repeatCols, c))\n const down = pagesOf(\n bodyRows,\n (i) => r.size(i),\n pageHeight - titleHeight - (headers ? src.rowHeight() : 0) - sum(repeatRows, r),\n )\n const reader = exportReader(src.root() ?? document.body, true, gridlines)\n const pages: string[] = []\n try {\n // down first, then across, as spreadsheets print\n for (const cols of across)\n for (const rows of down) {\n const drawing = exportDrawing(\n [...repeatRows, ...rows],\n [...repeatCols, ...cols],\n { headers, gridlines, background: true },\n reader,\n t,\n )\n pages.push(\n `<div class=\"page\">${title ? `<h1>${escapeXML(title)}</h1>` : ''}${drawSheet(drawing, measureText)}</div>`,\n )\n }\n } finally {\n reader.done()\n }\n const iframe = document.createElement('iframe')\n iframe.setAttribute('aria-hidden', 'true')\n iframe.style.cssText = 'position:fixed;width:0;height:0;border:0;visibility:hidden'\n document.body.append(iframe)\n const doc = iframe.contentDocument!\n doc.open()\n doc.write(\n `<!doctype html><html><head><meta charset=\"utf-8\"><title>${escapeXML(title || document.title)}</title><style>` +\n `@page{size:A4 ${orientation};margin:10mm;@bottom-center{content:counter(page) \" / \" counter(pages);font:9px ${reader.theme.fontFamily};color:#8a8a8a}}` +\n `html,body{margin:0}h1{margin:0 0 10px;font:600 14px/18px ${reader.theme.fontFamily};color:#1f1f1f}` +\n `.page{break-after:page;print-color-adjust:exact;-webkit-print-color-adjust:exact}` +\n `.page:last-child{break-after:auto}svg{display:block;max-width:100%;height:auto}` +\n `</style></head><body>${pages.join('')}</body></html>`,\n )\n doc.close()\n const win = iframe.contentWindow!\n const done = () => iframe.remove()\n win.addEventListener('afterprint', done)\n // browsers that print without a dialog, or never send afterprint\n setTimeout(done, 60_000)\n win.focus()\n win.print()\n }\n\n return {\n exportSVG,\n exportPNG,\n print,\n drawing: (options: SheetExportOptions = {}) => drawingOf(options, (laid) => laid),\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ import { formulaTokens } from "./parse.js";
2
+ /** The references of a formula being typed, each with a color. */
3
+ function formulaReferences(formula) {
4
+ if (!formula.startsWith("=")) return [];
5
+ const colors = /* @__PURE__ */ new Map();
6
+ const out = [];
7
+ for (const token of formulaTokens(formula)) {
8
+ if (token.kind !== "ref") continue;
9
+ const { sheet, top, left, bottom, right } = token.ref;
10
+ const key = `${sheet?.toLowerCase() ?? ""}!${top}:${left}:${bottom}:${right}`;
11
+ let color = colors.get(key);
12
+ if (color == null) colors.set(key, color = colors.size % 7);
13
+ out.push({
14
+ start: token.start,
15
+ end: token.end,
16
+ text: token.text,
17
+ sheet,
18
+ bounds: {
19
+ top,
20
+ left,
21
+ bottom,
22
+ right
23
+ },
24
+ color
25
+ });
26
+ }
27
+ return out;
28
+ }
29
+ /** Operators after which a value comes, so a reference can go there. */
30
+ var BEFORE_VALUE = /* @__PURE__ */ new Set([
31
+ "=",
32
+ "(",
33
+ ",",
34
+ ";",
35
+ "+",
36
+ "-",
37
+ "*",
38
+ "/",
39
+ "^",
40
+ "&",
41
+ "<",
42
+ ">",
43
+ "<=",
44
+ ">=",
45
+ "<>"
46
+ ]);
47
+ /** What may follow a value. */
48
+ var AFTER_VALUE = /* @__PURE__ */ new Set([
49
+ ")",
50
+ ",",
51
+ ";",
52
+ "+",
53
+ "-",
54
+ "*",
55
+ "/",
56
+ "^",
57
+ "&",
58
+ "=",
59
+ "<",
60
+ ">",
61
+ "<=",
62
+ ">=",
63
+ "<>",
64
+ "%"
65
+ ]);
66
+ /**
67
+ * Whether a reference can be put at the caret of a formula being typed:
68
+ * right after `=`, an operator, `(` or `,`, and before nothing that is a
69
+ * value itself. There, the arrow keys and clicking cells write references.
70
+ */
71
+ function canPointAt(formula, caret) {
72
+ if (!formula.startsWith("=") || caret < 1) return false;
73
+ const tokens = formulaTokens(formula);
74
+ let before;
75
+ let after;
76
+ for (const token of tokens) if (token.end <= caret) before = token;
77
+ else if (token.start >= caret) {
78
+ after = token;
79
+ break;
80
+ } else return false;
81
+ const fits = !before || before.kind === "op" && BEFORE_VALUE.has(before.text);
82
+ const room = !after || after.kind === "op" && AFTER_VALUE.has(after.text);
83
+ return fits && room;
84
+ }
85
+ /** The function call the caret is in, innermost first, and which of its arguments, from 0. */
86
+ function callAt(formula, caret) {
87
+ if (!formula.startsWith("=")) return null;
88
+ const tokens = formulaTokens(formula);
89
+ const stack = [];
90
+ let last;
91
+ for (const [i, token] of tokens.entries()) {
92
+ if (token.start >= caret) break;
93
+ if (token.kind === "op") {
94
+ if (token.text === "(") stack.push({
95
+ name: last?.kind === "name" && last.end === token.start ? last.text.toUpperCase() : null,
96
+ arg: 0,
97
+ open: i
98
+ });
99
+ else if (token.text === ")") stack.pop();
100
+ else if ((token.text === "," || token.text === ";") && stack.length) stack[stack.length - 1].arg++;
101
+ }
102
+ last = token;
103
+ }
104
+ for (let i = stack.length - 1; i >= 0; i--) {
105
+ const call = stack[i];
106
+ if (!call.name) continue;
107
+ let count = 1;
108
+ let depth = 0;
109
+ for (const token of tokens.slice(call.open + 1)) {
110
+ if (token.kind !== "op") continue;
111
+ if (token.text === "(") depth++;
112
+ else if (token.text === ")") {
113
+ if (!depth--) break;
114
+ } else if ((token.text === "," || token.text === ";") && !depth) count++;
115
+ }
116
+ return {
117
+ name: call.name,
118
+ arg: call.arg,
119
+ count
120
+ };
121
+ }
122
+ return null;
123
+ }
124
+ /** The number an argument's name ends with, such as `2` of `[值2`. */
125
+ var numberOf = (name) => /(\d+)\]?$/.exec(name.replace(/^\[/, ""))?.[1];
126
+ /**
127
+ * Which of a function's argument names the argument at `index` (of `count`)
128
+ * is, or -1. After `…` the names before it repeat as a group, such as the
129
+ * pairs of `SUMIFS` or `SWITCH`, numbered alike (`区域1, 条件1`); names after
130
+ * `…`, such as `SWITCH`'s default, are the last arguments when the pairs come out even.
131
+ */
132
+ function argNameAt(args, index, count) {
133
+ const more = args.indexOf("…");
134
+ if (more === -1) return index < args.length ? index : -1;
135
+ if (index < more) return index;
136
+ const last = numberOf(args[more - 1] ?? "");
137
+ let group = 1;
138
+ if (last != null) while (group < more && numberOf(args[more - 1 - group]) === last) group++;
139
+ const tail = args.length - more - 1;
140
+ if (tail && count - tail >= more && (count - tail - more) % group === 0) {
141
+ const fromEnd = count - index;
142
+ if (fromEnd <= tail) return args.length - fromEnd;
143
+ }
144
+ const start = more - group;
145
+ return start + (index - start) % group;
146
+ }
147
+ /**
148
+ * The name being typed at the caret, to complete as a function: letters
149
+ * ending at the caret, not yet followed by `(`.
150
+ */
151
+ function nameAt(formula, caret) {
152
+ if (!formula.startsWith("=")) return null;
153
+ const tokens = formulaTokens(formula);
154
+ for (const [i, token] of tokens.entries()) {
155
+ if (token.end !== caret) continue;
156
+ const text = token.kind === "ref" && /^[a-z]+\d*$/i.test(token.text) ? token.text : token.kind === "name" ? token.text : null;
157
+ if (!text || !/^[\p{L}_]/u.test(text)) return null;
158
+ const next = tokens[i + 1];
159
+ if (next && next.start === token.end && next.text === "(") return null;
160
+ return {
161
+ start: token.start,
162
+ end: token.end,
163
+ text
164
+ };
165
+ }
166
+ return null;
167
+ }
168
+ /**
169
+ * A formula cut into its colored pieces: references in their colors, and
170
+ * function names, "text", numbers, error values and operators in the
171
+ * syntax colors of `@dou-ui/code`, so the cell and either formula bar color
172
+ * it alike. Ends with a zero-width space, so a last line break still takes
173
+ * a line.
174
+ */
175
+ function formulaParts(formula, marks = formulaReferences(formula)) {
176
+ const parts = [];
177
+ if (!formula.startsWith("=")) return [{ text: `${formula}​` }];
178
+ const colors = new Map(marks.map((mark) => [mark.start, mark.color]));
179
+ const tokens = formulaTokens(formula);
180
+ parts.push({
181
+ text: "=",
182
+ syntax: "operator"
183
+ });
184
+ let at = 1;
185
+ for (const [i, token] of tokens.entries()) {
186
+ if (token.start > at) parts.push({ text: formula.slice(at, token.start) });
187
+ const part = { text: token.text };
188
+ if (token.kind === "ref") {
189
+ const color = colors.get(token.start);
190
+ if (color == null) part.syntax = "variable";
191
+ else part.color = color;
192
+ } else if (token.kind === "number") part.syntax = "number";
193
+ else if (token.kind === "text") part.syntax = "string";
194
+ else if (token.kind === "error") part.syntax = "invalid";
195
+ else if (token.kind === "name") part.syntax = tokens[i + 1]?.text === "(" ? "function" : /^(?:TRUE|FALSE)$/i.test(token.text) ? "keyword" : "variable";
196
+ else part.syntax = /^[(),;{}]$/.test(token.text) ? "punctuation" : "operator";
197
+ parts.push(part);
198
+ at = token.end;
199
+ }
200
+ parts.push({ text: `${formula.slice(at)}​` });
201
+ return parts;
202
+ }
203
+ /** The class a piece of `formulaParts` is shown with. */
204
+ function partClass(part) {
205
+ if (part.color != null) return `is-ref-${part.color}`;
206
+ return part.syntax && `dou-syntax-${part.syntax}`;
207
+ }
208
+ export { argNameAt, callAt, canPointAt, formulaParts, formulaReferences, nameAt, partClass };
209
+
210
+ //# sourceMappingURL=edit.js.map