@trebco/treb 36.1.4 → 37.0.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 (552) hide show
  1. package/api-config.json +1 -1
  2. package/build/package.json +119 -0
  3. package/build/treb-base-types/src/api_types.d.ts +11 -0
  4. package/build/treb-base-types/src/api_types.js +22 -0
  5. package/build/treb-base-types/src/api_types.js.map +1 -0
  6. package/build/treb-base-types/src/area-utils.d.ts +9 -0
  7. package/build/treb-base-types/src/area-utils.js +50 -0
  8. package/build/treb-base-types/src/area-utils.js.map +1 -0
  9. package/build/treb-base-types/src/area.d.ts +182 -0
  10. package/build/treb-base-types/src/area.js +715 -0
  11. package/build/treb-base-types/src/area.js.map +1 -0
  12. package/build/treb-base-types/src/basic_types.d.ts +20 -0
  13. package/build/treb-base-types/src/basic_types.js +22 -0
  14. package/build/treb-base-types/src/basic_types.js.map +1 -0
  15. package/build/treb-base-types/src/cell.d.ts +167 -0
  16. package/build/treb-base-types/src/cell.js +432 -0
  17. package/build/treb-base-types/src/cell.js.map +1 -0
  18. package/build/treb-base-types/src/cells.d.ts +251 -0
  19. package/build/treb-base-types/src/cells.js +1136 -0
  20. package/build/treb-base-types/src/cells.js.map +1 -0
  21. package/build/treb-base-types/src/color.d.ts +35 -0
  22. package/build/treb-base-types/src/color.js +162 -0
  23. package/build/treb-base-types/src/color.js.map +1 -0
  24. package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
  25. package/build/treb-base-types/src/dom-utilities.js +144 -0
  26. package/build/treb-base-types/src/dom-utilities.js.map +1 -0
  27. package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
  28. package/build/treb-base-types/src/evaluate-options.js +22 -0
  29. package/build/treb-base-types/src/evaluate-options.js.map +1 -0
  30. package/build/treb-base-types/src/font-stack.d.ts +37 -0
  31. package/build/treb-base-types/src/font-stack.js +93 -0
  32. package/build/treb-base-types/src/font-stack.js.map +1 -0
  33. package/build/treb-base-types/src/gradient.d.ts +18 -0
  34. package/build/treb-base-types/src/gradient.js +86 -0
  35. package/build/treb-base-types/src/gradient.js.map +1 -0
  36. package/build/treb-base-types/src/import.d.ts +48 -0
  37. package/build/treb-base-types/src/import.js +22 -0
  38. package/build/treb-base-types/src/import.js.map +1 -0
  39. package/build/treb-base-types/src/index-standalone.d.ts +6 -0
  40. package/build/treb-base-types/src/index-standalone.js +27 -0
  41. package/build/treb-base-types/src/index-standalone.js.map +1 -0
  42. package/build/treb-base-types/src/index.d.ts +22 -0
  43. package/build/treb-base-types/src/index.js +45 -0
  44. package/build/treb-base-types/src/index.js.map +1 -0
  45. package/build/treb-base-types/src/layout.d.ts +22 -0
  46. package/build/treb-base-types/src/layout.js +22 -0
  47. package/build/treb-base-types/src/layout.js.map +1 -0
  48. package/build/treb-base-types/src/localization.d.ts +37 -0
  49. package/build/treb-base-types/src/localization.js +157 -0
  50. package/build/treb-base-types/src/localization.js.map +1 -0
  51. package/build/treb-base-types/src/rectangle.d.ts +51 -0
  52. package/build/treb-base-types/src/rectangle.js +123 -0
  53. package/build/treb-base-types/src/rectangle.js.map +1 -0
  54. package/build/treb-base-types/src/render_text.d.ts +34 -0
  55. package/build/treb-base-types/src/render_text.js +22 -0
  56. package/build/treb-base-types/src/render_text.js.map +1 -0
  57. package/build/treb-base-types/src/style.d.ts +214 -0
  58. package/build/treb-base-types/src/style.js +373 -0
  59. package/build/treb-base-types/src/style.js.map +1 -0
  60. package/build/treb-base-types/src/table.d.ts +58 -0
  61. package/build/treb-base-types/src/table.js +27 -0
  62. package/build/treb-base-types/src/table.js.map +1 -0
  63. package/build/treb-base-types/src/text_part.d.ts +26 -0
  64. package/build/treb-base-types/src/text_part.js +47 -0
  65. package/build/treb-base-types/src/text_part.js.map +1 -0
  66. package/build/treb-base-types/src/theme.d.ts +120 -0
  67. package/build/treb-base-types/src/theme.js +460 -0
  68. package/build/treb-base-types/src/theme.js.map +1 -0
  69. package/build/treb-base-types/src/union.d.ts +73 -0
  70. package/build/treb-base-types/src/union.js +61 -0
  71. package/build/treb-base-types/src/union.js.map +1 -0
  72. package/build/treb-base-types/src/value-type.d.ts +86 -0
  73. package/build/treb-base-types/src/value-type.js +168 -0
  74. package/build/treb-base-types/src/value-type.js.map +1 -0
  75. package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
  76. package/build/treb-base-types/src/worker-proxy.js +221 -0
  77. package/build/treb-base-types/src/worker-proxy.js.map +1 -0
  78. package/build/treb-calculator/src/calculator.d.ts +249 -0
  79. package/build/treb-calculator/src/calculator.js +2755 -0
  80. package/build/treb-calculator/src/calculator.js.map +1 -0
  81. package/build/treb-calculator/src/complex-math.d.ts +75 -0
  82. package/build/treb-calculator/src/complex-math.js +559 -0
  83. package/build/treb-calculator/src/complex-math.js.map +1 -0
  84. package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
  85. package/build/treb-calculator/src/dag/array-vertex.js +156 -0
  86. package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
  87. package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
  88. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
  89. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
  90. package/build/treb-calculator/src/dag/graph.d.ts +134 -0
  91. package/build/treb-calculator/src/dag/graph.js +842 -0
  92. package/build/treb-calculator/src/dag/graph.js.map +1 -0
  93. package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
  94. package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
  95. package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
  96. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
  97. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
  98. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
  99. package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
  100. package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
  101. package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
  102. package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
  103. package/build/treb-calculator/src/dag/vertex.js +274 -0
  104. package/build/treb-calculator/src/dag/vertex.js.map +1 -0
  105. package/build/treb-calculator/src/descriptors.d.ts +189 -0
  106. package/build/treb-calculator/src/descriptors.js +22 -0
  107. package/build/treb-calculator/src/descriptors.js.map +1 -0
  108. package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
  109. package/build/treb-calculator/src/expression-calculator.js +1033 -0
  110. package/build/treb-calculator/src/expression-calculator.js.map +1 -0
  111. package/build/treb-calculator/src/function-error.d.ts +35 -0
  112. package/build/treb-calculator/src/function-error.js +85 -0
  113. package/build/treb-calculator/src/function-error.js.map +1 -0
  114. package/build/treb-calculator/src/function-library.d.ts +22 -0
  115. package/build/treb-calculator/src/function-library.js +96 -0
  116. package/build/treb-calculator/src/function-library.js.map +1 -0
  117. package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
  118. package/build/treb-calculator/src/functions/base-functions.js +2611 -0
  119. package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
  120. package/build/treb-calculator/src/functions/beta.d.ts +17 -0
  121. package/build/treb-calculator/src/functions/beta.js +201 -0
  122. package/build/treb-calculator/src/functions/beta.js.map +1 -0
  123. package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
  124. package/build/treb-calculator/src/functions/checkbox.js +128 -0
  125. package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
  126. package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
  127. package/build/treb-calculator/src/functions/complex-functions.js +217 -0
  128. package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
  129. package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
  130. package/build/treb-calculator/src/functions/date-utils.js +59 -0
  131. package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
  132. package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
  133. package/build/treb-calculator/src/functions/finance-functions.js +547 -0
  134. package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
  135. package/build/treb-calculator/src/functions/fp.d.ts +2 -0
  136. package/build/treb-calculator/src/functions/fp.js +463 -0
  137. package/build/treb-calculator/src/functions/fp.js.map +1 -0
  138. package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
  139. package/build/treb-calculator/src/functions/function-utilities.js +36 -0
  140. package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
  141. package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
  142. package/build/treb-calculator/src/functions/gamma.js +142 -0
  143. package/build/treb-calculator/src/functions/gamma.js.map +1 -0
  144. package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
  145. package/build/treb-calculator/src/functions/information-functions.js +71 -0
  146. package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
  147. package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
  148. package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
  149. package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
  150. package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
  151. package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
  152. package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
  153. package/build/treb-calculator/src/functions/normal.d.ts +2 -0
  154. package/build/treb-calculator/src/functions/normal.js +32 -0
  155. package/build/treb-calculator/src/functions/normal.js.map +1 -0
  156. package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
  157. package/build/treb-calculator/src/functions/regex-functions.js +188 -0
  158. package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
  159. package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
  160. package/build/treb-calculator/src/functions/sparkline.js +264 -0
  161. package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
  162. package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
  163. package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
  164. package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
  165. package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
  166. package/build/treb-calculator/src/functions/students-t.js +64 -0
  167. package/build/treb-calculator/src/functions/students-t.js.map +1 -0
  168. package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
  169. package/build/treb-calculator/src/functions/text-functions.js +320 -0
  170. package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
  171. package/build/treb-calculator/src/index.d.ts +2 -0
  172. package/build/treb-calculator/src/index.js +22 -0
  173. package/build/treb-calculator/src/index.js.map +1 -0
  174. package/build/treb-calculator/src/notifier-types.d.ts +26 -0
  175. package/build/treb-calculator/src/notifier-types.js +22 -0
  176. package/build/treb-calculator/src/notifier-types.js.map +1 -0
  177. package/build/treb-calculator/src/primitives.d.ts +15 -0
  178. package/build/treb-calculator/src/primitives.js +398 -0
  179. package/build/treb-calculator/src/primitives.js.map +1 -0
  180. package/build/treb-calculator/src/utilities.d.ts +68 -0
  181. package/build/treb-calculator/src/utilities.js +324 -0
  182. package/build/treb-calculator/src/utilities.js.map +1 -0
  183. package/build/treb-charts/src/chart-functions.d.ts +8 -0
  184. package/build/treb-charts/src/chart-functions.js +209 -0
  185. package/build/treb-charts/src/chart-functions.js.map +1 -0
  186. package/build/treb-charts/src/chart-types.d.ts +233 -0
  187. package/build/treb-charts/src/chart-types.js +57 -0
  188. package/build/treb-charts/src/chart-types.js.map +1 -0
  189. package/build/treb-charts/src/chart-utils.d.ts +106 -0
  190. package/build/treb-charts/src/chart-utils.js +1060 -0
  191. package/build/treb-charts/src/chart-utils.js.map +1 -0
  192. package/build/treb-charts/src/chart.d.ts +23 -0
  193. package/build/treb-charts/src/chart.js +94 -0
  194. package/build/treb-charts/src/chart.js.map +1 -0
  195. package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
  196. package/build/treb-charts/src/default-chart-renderer.js +533 -0
  197. package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
  198. package/build/treb-charts/src/index.d.ts +5 -0
  199. package/build/treb-charts/src/index.js +24 -0
  200. package/build/treb-charts/src/index.js.map +1 -0
  201. package/build/treb-charts/src/main.d.ts +1 -0
  202. package/build/treb-charts/src/main.js +34 -0
  203. package/build/treb-charts/src/main.js.map +1 -0
  204. package/build/treb-charts/src/quicksort.d.ts +1 -0
  205. package/build/treb-charts/src/quicksort.js +49 -0
  206. package/build/treb-charts/src/quicksort.js.map +1 -0
  207. package/build/treb-charts/src/rectangle.d.ts +18 -0
  208. package/build/treb-charts/src/rectangle.js +41 -0
  209. package/build/treb-charts/src/rectangle.js.map +1 -0
  210. package/build/treb-charts/src/renderer-type.d.ts +24 -0
  211. package/build/treb-charts/src/renderer-type.js +22 -0
  212. package/build/treb-charts/src/renderer-type.js.map +1 -0
  213. package/build/treb-charts/src/renderer.d.ts +127 -0
  214. package/build/treb-charts/src/renderer.js +1518 -0
  215. package/build/treb-charts/src/renderer.js.map +1 -0
  216. package/build/treb-charts/src/util.d.ts +18 -0
  217. package/build/treb-charts/src/util.js +71 -0
  218. package/build/treb-charts/src/util.js.map +1 -0
  219. package/build/treb-data-model/src/annotation.d.ts +167 -0
  220. package/build/treb-data-model/src/annotation.js +120 -0
  221. package/build/treb-data-model/src/annotation.js.map +1 -0
  222. package/build/treb-data-model/src/conditional_format.d.ts +155 -0
  223. package/build/treb-data-model/src/conditional_format.js +62 -0
  224. package/build/treb-data-model/src/conditional_format.js.map +1 -0
  225. package/build/treb-data-model/src/data-validation.d.ts +28 -0
  226. package/build/treb-data-model/src/data-validation.js +22 -0
  227. package/build/treb-data-model/src/data-validation.js.map +1 -0
  228. package/build/treb-data-model/src/data_model.d.ts +173 -0
  229. package/build/treb-data-model/src/data_model.js +637 -0
  230. package/build/treb-data-model/src/data_model.js.map +1 -0
  231. package/build/treb-data-model/src/index.d.ts +13 -0
  232. package/build/treb-data-model/src/index.js +28 -0
  233. package/build/treb-data-model/src/index.js.map +1 -0
  234. package/build/treb-data-model/src/language-model.d.ts +22 -0
  235. package/build/treb-data-model/src/language-model.js +22 -0
  236. package/build/treb-data-model/src/language-model.js.map +1 -0
  237. package/build/treb-data-model/src/named.d.ts +124 -0
  238. package/build/treb-data-model/src/named.js +372 -0
  239. package/build/treb-data-model/src/named.js.map +1 -0
  240. package/build/treb-data-model/src/serialize_options.d.ts +49 -0
  241. package/build/treb-data-model/src/serialize_options.js +22 -0
  242. package/build/treb-data-model/src/serialize_options.js.map +1 -0
  243. package/build/treb-data-model/src/sheet.d.ts +499 -0
  244. package/build/treb-data-model/src/sheet.js +2904 -0
  245. package/build/treb-data-model/src/sheet.js.map +1 -0
  246. package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
  247. package/build/treb-data-model/src/sheet_collection.js +112 -0
  248. package/build/treb-data-model/src/sheet_collection.js.map +1 -0
  249. package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
  250. package/build/treb-data-model/src/sheet_selection.js +39 -0
  251. package/build/treb-data-model/src/sheet_selection.js.map +1 -0
  252. package/build/treb-data-model/src/sheet_types.d.ts +104 -0
  253. package/build/treb-data-model/src/sheet_types.js +22 -0
  254. package/build/treb-data-model/src/sheet_types.js.map +1 -0
  255. package/build/treb-data-model/src/types.d.ts +59 -0
  256. package/build/treb-data-model/src/types.js +22 -0
  257. package/build/treb-data-model/src/types.js.map +1 -0
  258. package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
  259. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
  260. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
  261. package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
  262. package/build/treb-embed/src/custom-element/treb-global.js +64 -0
  263. package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
  264. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
  265. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
  266. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
  267. package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
  268. package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
  269. package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
  270. package/build/treb-embed/src/index.d.ts +12 -0
  271. package/build/treb-embed/src/index.js +34 -0
  272. package/build/treb-embed/src/index.js.map +1 -0
  273. package/build/treb-embed/src/options.d.ts +266 -0
  274. package/build/treb-embed/src/options.js +56 -0
  275. package/build/treb-embed/src/options.js.map +1 -0
  276. package/build/treb-embed/src/plugin.d.ts +9 -0
  277. package/build/treb-embed/src/plugin.js +22 -0
  278. package/build/treb-embed/src/plugin.js.map +1 -0
  279. package/build/treb-embed/src/progress-dialog.d.ts +49 -0
  280. package/build/treb-embed/src/progress-dialog.js +178 -0
  281. package/build/treb-embed/src/progress-dialog.js.map +1 -0
  282. package/build/treb-embed/src/selection-state.d.ts +15 -0
  283. package/build/treb-embed/src/selection-state.js +22 -0
  284. package/build/treb-embed/src/selection-state.js.map +1 -0
  285. package/build/treb-embed/src/spinner.d.ts +8 -0
  286. package/build/treb-embed/src/spinner.js +40 -0
  287. package/build/treb-embed/src/spinner.js.map +1 -0
  288. package/build/treb-embed/src/toolbar-message.d.ts +72 -0
  289. package/build/treb-embed/src/toolbar-message.js +22 -0
  290. package/build/treb-embed/src/toolbar-message.js.map +1 -0
  291. package/build/treb-embed/src/types.d.ts +185 -0
  292. package/build/treb-embed/src/types.js +45 -0
  293. package/build/treb-embed/src/types.js.map +1 -0
  294. package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
  295. package/build/treb-export/src/address-type.d.ts +34 -0
  296. package/build/treb-export/src/address-type.js +53 -0
  297. package/build/treb-export/src/address-type.js.map +1 -0
  298. package/build/treb-export/src/base-template.d.ts +1 -0
  299. package/build/treb-export/src/base-template.js +22 -0
  300. package/build/treb-export/src/base-template.js.map +1 -0
  301. package/build/treb-export/src/column-width.d.ts +2 -0
  302. package/build/treb-export/src/column-width.js +80 -0
  303. package/build/treb-export/src/column-width.js.map +1 -0
  304. package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
  305. package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
  306. package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
  307. package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
  308. package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
  309. package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
  310. package/build/treb-export/src/drawing/chart.d.ts +26 -0
  311. package/build/treb-export/src/drawing/chart.js +247 -0
  312. package/build/treb-export/src/drawing/chart.js.map +1 -0
  313. package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
  314. package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
  315. package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
  316. package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
  317. package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
  318. package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
  319. package/build/treb-export/src/drawing/drawing.d.ts +49 -0
  320. package/build/treb-export/src/drawing/drawing.js +193 -0
  321. package/build/treb-export/src/drawing/drawing.js.map +1 -0
  322. package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
  323. package/build/treb-export/src/drawing/embedded-image.js +54 -0
  324. package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
  325. package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
  326. package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
  327. package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
  328. package/build/treb-export/src/export.d.ts +72 -0
  329. package/build/treb-export/src/export.js +2039 -0
  330. package/build/treb-export/src/export.js.map +1 -0
  331. package/build/treb-export/src/import-export-messages.d.ts +31 -0
  332. package/build/treb-export/src/import-export-messages.js +22 -0
  333. package/build/treb-export/src/import-export-messages.js.map +1 -0
  334. package/build/treb-export/src/import.d.ts +33 -0
  335. package/build/treb-export/src/import.js +1258 -0
  336. package/build/treb-export/src/import.js.map +1 -0
  337. package/build/treb-export/src/index.worker.d.ts +1 -0
  338. package/build/treb-export/src/index.worker.js +93 -0
  339. package/build/treb-export/src/index.worker.js.map +1 -0
  340. package/build/treb-export/src/metadata.d.ts +51 -0
  341. package/build/treb-export/src/metadata.js +153 -0
  342. package/build/treb-export/src/metadata.js.map +1 -0
  343. package/build/treb-export/src/ooxml.d.ts +7 -0
  344. package/build/treb-export/src/ooxml.js +41 -0
  345. package/build/treb-export/src/ooxml.js.map +1 -0
  346. package/build/treb-export/src/relationship.d.ts +8 -0
  347. package/build/treb-export/src/relationship.js +27 -0
  348. package/build/treb-export/src/relationship.js.map +1 -0
  349. package/build/treb-export/src/shared-strings.d.ts +11 -0
  350. package/build/treb-export/src/shared-strings.js +105 -0
  351. package/build/treb-export/src/shared-strings.js.map +1 -0
  352. package/build/treb-export/src/template-2.d.ts +1 -0
  353. package/build/treb-export/src/template-2.js +22 -0
  354. package/build/treb-export/src/template-2.js.map +1 -0
  355. package/build/treb-export/src/unescape_xml.d.ts +1 -0
  356. package/build/treb-export/src/unescape_xml.js +61 -0
  357. package/build/treb-export/src/unescape_xml.js.map +1 -0
  358. package/build/treb-export/src/workbook-sheet.d.ts +75 -0
  359. package/build/treb-export/src/workbook-sheet.js +128 -0
  360. package/build/treb-export/src/workbook-sheet.js.map +1 -0
  361. package/build/treb-export/src/workbook-style.d.ts +110 -0
  362. package/build/treb-export/src/workbook-style.js +1134 -0
  363. package/build/treb-export/src/workbook-style.js.map +1 -0
  364. package/build/treb-export/src/workbook-theme.d.ts +13 -0
  365. package/build/treb-export/src/workbook-theme.js +85 -0
  366. package/build/treb-export/src/workbook-theme.js.map +1 -0
  367. package/build/treb-export/src/workbook.d.ts +123 -0
  368. package/build/treb-export/src/workbook.js +644 -0
  369. package/build/treb-export/src/workbook.js.map +1 -0
  370. package/build/treb-export/src/xml-test.d.ts +9 -0
  371. package/build/treb-export/src/xml-test.js +52 -0
  372. package/build/treb-export/src/xml-test.js.map +1 -0
  373. package/build/treb-export/src/xml-utils.d.ts +76 -0
  374. package/build/treb-export/src/xml-utils.js +223 -0
  375. package/build/treb-export/src/xml-utils.js.map +1 -0
  376. package/build/treb-export/src/zip-wrapper.d.ts +22 -0
  377. package/build/treb-export/src/zip-wrapper.js +93 -0
  378. package/build/treb-export/src/zip-wrapper.js.map +1 -0
  379. package/build/treb-format/src/format.d.ts +130 -0
  380. package/build/treb-format/src/format.js +805 -0
  381. package/build/treb-format/src/format.js.map +1 -0
  382. package/build/treb-format/src/format_cache.d.ts +55 -0
  383. package/build/treb-format/src/format_cache.js +166 -0
  384. package/build/treb-format/src/format_cache.js.map +1 -0
  385. package/build/treb-format/src/format_parser.d.ts +70 -0
  386. package/build/treb-format/src/format_parser.js +618 -0
  387. package/build/treb-format/src/format_parser.js.map +1 -0
  388. package/build/treb-format/src/index.d.ts +4 -0
  389. package/build/treb-format/src/index.js +25 -0
  390. package/build/treb-format/src/index.js.map +1 -0
  391. package/build/treb-format/src/number_format_section.d.ts +58 -0
  392. package/build/treb-format/src/number_format_section.js +78 -0
  393. package/build/treb-format/src/number_format_section.js.map +1 -0
  394. package/build/treb-format/src/value_parser.d.ts +48 -0
  395. package/build/treb-format/src/value_parser.js +244 -0
  396. package/build/treb-format/src/value_parser.js.map +1 -0
  397. package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
  398. package/build/treb-grid/src/editors/autocomplete.js +316 -0
  399. package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
  400. package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
  401. package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
  402. package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
  403. package/build/treb-grid/src/editors/editor.d.ts +214 -0
  404. package/build/treb-grid/src/editors/editor.js +879 -0
  405. package/build/treb-grid/src/editors/editor.js.map +1 -0
  406. package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
  407. package/build/treb-grid/src/editors/external_editor.js +118 -0
  408. package/build/treb-grid/src/editors/external_editor.js.map +1 -0
  409. package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
  410. package/build/treb-grid/src/editors/formula_bar.js +444 -0
  411. package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
  412. package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
  413. package/build/treb-grid/src/editors/overlay_editor.js +353 -0
  414. package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
  415. package/build/treb-grid/src/index.d.ts +12 -0
  416. package/build/treb-grid/src/index.js +28 -0
  417. package/build/treb-grid/src/index.js.map +1 -0
  418. package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
  419. package/build/treb-grid/src/layout/base_layout.js +2050 -0
  420. package/build/treb-grid/src/layout/base_layout.js.map +1 -0
  421. package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
  422. package/build/treb-grid/src/layout/grid_layout.js +235 -0
  423. package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
  424. package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
  425. package/build/treb-grid/src/layout/mock-layout.js +37 -0
  426. package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
  427. package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
  428. package/build/treb-grid/src/render/selection-renderer.js +315 -0
  429. package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
  430. package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
  431. package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
  432. package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
  433. package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
  434. package/build/treb-grid/src/render/svg_selection_block.js +106 -0
  435. package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
  436. package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
  437. package/build/treb-grid/src/render/tile_renderer.js +1609 -0
  438. package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
  439. package/build/treb-grid/src/types/border_constants.d.ts +9 -0
  440. package/build/treb-grid/src/types/border_constants.js +34 -0
  441. package/build/treb-grid/src/types/border_constants.js.map +1 -0
  442. package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
  443. package/build/treb-grid/src/types/clipboard_data.js +22 -0
  444. package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
  445. package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
  446. package/build/treb-grid/src/types/clipboard_data2.js +22 -0
  447. package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
  448. package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
  449. package/build/treb-grid/src/types/drag_mask.js +78 -0
  450. package/build/treb-grid/src/types/drag_mask.js.map +1 -0
  451. package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
  452. package/build/treb-grid/src/types/external_editor_config.js +22 -0
  453. package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
  454. package/build/treb-grid/src/types/grid.d.ts +806 -0
  455. package/build/treb-grid/src/types/grid.js +6410 -0
  456. package/build/treb-grid/src/types/grid.js.map +1 -0
  457. package/build/treb-grid/src/types/grid_base.d.ts +442 -0
  458. package/build/treb-grid/src/types/grid_base.js +3523 -0
  459. package/build/treb-grid/src/types/grid_base.js.map +1 -0
  460. package/build/treb-grid/src/types/grid_command.d.ts +408 -0
  461. package/build/treb-grid/src/types/grid_command.js +75 -0
  462. package/build/treb-grid/src/types/grid_command.js.map +1 -0
  463. package/build/treb-grid/src/types/grid_events.d.ts +93 -0
  464. package/build/treb-grid/src/types/grid_events.js +36 -0
  465. package/build/treb-grid/src/types/grid_events.js.map +1 -0
  466. package/build/treb-grid/src/types/grid_options.d.ts +50 -0
  467. package/build/treb-grid/src/types/grid_options.js +34 -0
  468. package/build/treb-grid/src/types/grid_options.js.map +1 -0
  469. package/build/treb-grid/src/types/scale-control.d.ts +21 -0
  470. package/build/treb-grid/src/types/scale-control.js +148 -0
  471. package/build/treb-grid/src/types/scale-control.js.map +1 -0
  472. package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
  473. package/build/treb-grid/src/types/set_range_options.js +22 -0
  474. package/build/treb-grid/src/types/set_range_options.js.map +1 -0
  475. package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
  476. package/build/treb-grid/src/types/tab_bar.js +426 -0
  477. package/build/treb-grid/src/types/tab_bar.js.map +1 -0
  478. package/build/treb-grid/src/types/tile.d.ts +29 -0
  479. package/build/treb-grid/src/types/tile.js +22 -0
  480. package/build/treb-grid/src/types/tile.js.map +1 -0
  481. package/build/treb-grid/src/types/update_flags.d.ts +48 -0
  482. package/build/treb-grid/src/types/update_flags.js +22 -0
  483. package/build/treb-grid/src/types/update_flags.js.map +1 -0
  484. package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
  485. package/build/treb-grid/src/util/fontmetrics.js +82 -0
  486. package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
  487. package/build/treb-grid/src/util/ua.d.ts +33 -0
  488. package/build/treb-grid/src/util/ua.js +86 -0
  489. package/build/treb-grid/src/util/ua.js.map +1 -0
  490. package/build/treb-parser/src/csv-parser.d.ts +13 -0
  491. package/build/treb-parser/src/csv-parser.js +107 -0
  492. package/build/treb-parser/src/csv-parser.js.map +1 -0
  493. package/build/treb-parser/src/index.d.ts +4 -0
  494. package/build/treb-parser/src/index.js +25 -0
  495. package/build/treb-parser/src/index.js.map +1 -0
  496. package/build/treb-parser/src/md-parser.d.ts +97 -0
  497. package/build/treb-parser/src/md-parser.js +403 -0
  498. package/build/treb-parser/src/md-parser.js.map +1 -0
  499. package/build/treb-parser/src/parser-types.d.ts +345 -0
  500. package/build/treb-parser/src/parser-types.js +53 -0
  501. package/build/treb-parser/src/parser-types.js.map +1 -0
  502. package/build/treb-parser/src/parser.d.ts +422 -0
  503. package/build/treb-parser/src/parser.js +2418 -0
  504. package/build/treb-parser/src/parser.js.map +1 -0
  505. package/build/treb-utils/src/event_source.d.ts +34 -0
  506. package/build/treb-utils/src/event_source.js +110 -0
  507. package/build/treb-utils/src/event_source.js.map +1 -0
  508. package/build/treb-utils/src/ievent_source.d.ts +9 -0
  509. package/build/treb-utils/src/ievent_source.js +22 -0
  510. package/build/treb-utils/src/ievent_source.js.map +1 -0
  511. package/build/treb-utils/src/index.d.ts +6 -0
  512. package/build/treb-utils/src/index.js +30 -0
  513. package/build/treb-utils/src/index.js.map +1 -0
  514. package/build/treb-utils/src/measurement.d.ts +42 -0
  515. package/build/treb-utils/src/measurement.js +145 -0
  516. package/build/treb-utils/src/measurement.js.map +1 -0
  517. package/build/treb-utils/src/scale.d.ts +16 -0
  518. package/build/treb-utils/src/scale.js +106 -0
  519. package/build/treb-utils/src/scale.js.map +1 -0
  520. package/build/treb-utils/src/serialize_html.d.ts +5 -0
  521. package/build/treb-utils/src/serialize_html.js +128 -0
  522. package/build/treb-utils/src/serialize_html.js.map +1 -0
  523. package/build/treb-utils/src/validate_uri.d.ts +20 -0
  524. package/build/treb-utils/src/validate_uri.js +55 -0
  525. package/build/treb-utils/src/validate_uri.js.map +1 -0
  526. package/dist/{chunk-Z4XFMZ2X.mjs → chunk-E35ONJUS.mjs} +1 -1
  527. package/dist/treb-export-worker.mjs +2 -2
  528. package/dist/treb-spreadsheet.mjs +4 -4
  529. package/dist/treb.d.ts +1 -1
  530. package/esbuild-composite.mjs +5 -1
  531. package/package.json +67 -3
  532. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
  533. package/treb-embed/src/embedded-spreadsheet.ts +1 -1
  534. package/treb-grid/src/types/grid_options.ts +1 -1
  535. package/tsproject.json +1 -2
  536. package/dist/chunk-43DLP2OX.mjs +0 -11
  537. package/dist/chunk-4CKS56PE.mjs +0 -11
  538. package/dist/chunk-75PARUQE.mjs +0 -11
  539. package/dist/chunk-7QD63AZS.mjs +0 -24601
  540. package/dist/chunk-A55ARVRD.mjs +0 -11
  541. package/dist/chunk-DESAKYW4.mjs +0 -11
  542. package/dist/chunk-EQ2R5W6P.mjs +0 -24565
  543. package/dist/chunk-IYJU2J6D.mjs +0 -24601
  544. package/dist/chunk-KSJFPGXT.mjs +0 -11
  545. package/dist/chunk-MQK4DNXI.mjs +0 -11
  546. package/dist/chunk-ORQFKLXM.mjs +0 -24601
  547. package/dist/chunk-SFDNNDHY.mjs +0 -11
  548. package/dist/chunk-T47DX5MI.mjs +0 -11
  549. package/dist/chunk-T6ILBVEX.mjs +0 -11
  550. package/dist/chunk-TPRCDYYG.mjs +0 -11
  551. package/dist/chunk-YAHNOOHO.mjs +0 -11
  552. package/dist/chunk-YLCFKX2G.mjs +0 -24601
@@ -0,0 +1,2050 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2026 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+ import { DOMContext } from 'treb-base-types';
22
+ import { Style, Area, Rectangle, ResolveThemeColor } from 'treb-base-types';
23
+ import { MouseDrag } from '../types/drag_mask';
24
+ import { Area as TileRange } from 'treb-base-types';
25
+ export { Area as TileRange } from 'treb-base-types';
26
+ /**
27
+ * layout structure and management functions
28
+ */
29
+ export class BaseLayout {
30
+ model;
31
+ view;
32
+ DOM;
33
+ column_header;
34
+ row_header;
35
+ contents;
36
+ buffer_canvas;
37
+ corner;
38
+ corner_canvas; // HTMLDivElement;
39
+ grid_selection;
40
+ grid_cover;
41
+ column_header_cover;
42
+ row_header_cover;
43
+ annotation_container;
44
+ mask;
45
+ mock_selection;
46
+ container; // reference to container
47
+ grid_tiles = [];
48
+ column_header_tiles = [];
49
+ row_header_tiles = [];
50
+ corner_selection;
51
+ row_header_selection;
52
+ column_header_selection;
53
+ corner_annotations;
54
+ row_header_annotations;
55
+ column_header_annotations;
56
+ frozen_row_tiles = [];
57
+ frozen_column_tiles = [];
58
+ header_size = { width: 0, height: 0 };
59
+ applied_theme_colors = [];
60
+ /**
61
+ * last rendered column. this is used to calculate the limits of
62
+ * cell overflows, which may exceed actual data in the sheet.
63
+ */
64
+ last_column = 0;
65
+ total_height = 0;
66
+ total_width = 0;
67
+ default_row_height = 0;
68
+ default_column_width = 0;
69
+ header_offset = {
70
+ x: 0, y: 0,
71
+ };
72
+ /** freeze rows/columns */
73
+ // public freeze = { rows: 0, columns: 0 };
74
+ /**
75
+ * NOTE: dpr can probably change, on zoom; but I'm not sure there's
76
+ * an event we can trap for that. it might be necessary to test this
77
+ * periodically.
78
+ */
79
+ dpr = 1; // Math.max(1, self.devicePixelRatio || 1);
80
+ /** separate scale, user-controlled (testing...) */
81
+ scale = 1;
82
+ /**
83
+ * this is a reference to the node that handles scrolling. it needs
84
+ * to be different for legacy renderer.
85
+ */
86
+ scroll_reference_node;
87
+ get scroll_offset() {
88
+ if (!this.scroll_reference_node) {
89
+ return { x: 0, y: 0 };
90
+ }
91
+ return {
92
+ x: this.scroll_reference_node.scrollLeft,
93
+ y: this.scroll_reference_node.scrollTop,
94
+ };
95
+ }
96
+ set scroll_offset(offset) {
97
+ if (!this.scroll_reference_node) {
98
+ return;
99
+ }
100
+ this.scroll_reference_node.scrollLeft = offset.x;
101
+ this.scroll_reference_node.scrollTop = offset.y;
102
+ }
103
+ dropdown_caret;
104
+ spill_border;
105
+ /** we have to disable mock selection for IE or it breaks key handling */
106
+ trident = ((typeof navigator !== 'undefined') &&
107
+ navigator.userAgent && /trident/i.test(navigator.userAgent));
108
+ // private default_tile_size: Size = { width: 600, height: 400 };
109
+ default_tile_size = { width: 1200, height: 800 };
110
+ tooltip_state;
111
+ tooltip;
112
+ dropdown_list;
113
+ dropdown_caret_visible = false;
114
+ dropdown_callback;
115
+ dropdown_selected;
116
+ // private selection_layout_token?: any;
117
+ // private error_highlight: HTMLDivElement;
118
+ // private error_highlight_timeout?: any;
119
+ note_node;
120
+ sort_button;
121
+ title_node;
122
+ row_cache = [];
123
+ column_cache = [];
124
+ /**
125
+ * flag so we don't try to paint before we have tiles
126
+ */
127
+ initialized = false;
128
+ constructor(model, view, mock = false, DOM = DOMContext.GetInstance()) {
129
+ this.model = model;
130
+ this.view = view;
131
+ this.DOM = DOM;
132
+ if (mock) {
133
+ return;
134
+ }
135
+ if (!DOM) {
136
+ throw new Error('missing DOM context');
137
+ }
138
+ this.dpr = Math.max(1, self.devicePixelRatio || 1);
139
+ // now attaching to node... no longer global
140
+ // actually if we are not in a web component, we might as well
141
+ // use global...
142
+ // can't use global if it's inside a block because of z-stacking
143
+ // contexts; the mask will be under the next sheet. so either
144
+ // global in body, or instance local.
145
+ this.mask = DOM.Div('treb-mouse-mask');
146
+ this.tooltip = DOM.Div('treb-tooltip');
147
+ this.spill_border = DOM.SVG('svg', 'treb-spill-border');
148
+ this.spill_border.tabIndex = -1;
149
+ this.dropdown_caret = DOM.SVG('svg', 'treb-dropdown-caret');
150
+ this.dropdown_caret.setAttribute('viewBox', '0 0 24 24');
151
+ this.dropdown_caret.tabIndex = -1;
152
+ const caret = DOM.SVG('path');
153
+ caret.setAttribute('d', 'M5,7 L12,17 L19,7');
154
+ this.dropdown_caret.appendChild(caret);
155
+ this.dropdown_caret.addEventListener('click', (event) => {
156
+ event.stopPropagation();
157
+ event.preventDefault();
158
+ this.grid_cover.classList.remove('nub-select');
159
+ // the classList polyfill doesn't apply to svg elements (not sure
160
+ // if that's an oversight, or IE11 just won't support it) -- but
161
+ // either way we can't use it
162
+ const class_name = this.dropdown_caret.getAttribute('class') || '';
163
+ if (/active/i.test(class_name)) {
164
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
165
+ }
166
+ else {
167
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret active');
168
+ this.dropdown_list.focus();
169
+ }
170
+ });
171
+ // we used to focus on caret. that broke when we started supporting
172
+ // long lists and scrolling. so now we focus on the list.
173
+ /*
174
+ this.dropdown_caret.addEventListener('focusout', () => {
175
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
176
+ this.container?.focus();
177
+ });
178
+ */
179
+ this.dropdown_list = DOM.Div('treb-dropdown-list', undefined, {
180
+ attrs: { tabindex: '-1' },
181
+ });
182
+ // this.dropdown_list.setAttribute('tabindex', '-1'); // focusable
183
+ // this.dropdown_caret.addEventListener('keydown', (event) => {
184
+ this.dropdown_list.addEventListener('keydown', (event) => {
185
+ let delta = 0;
186
+ switch (event.key) {
187
+ case 'ArrowDown':
188
+ delta = 1;
189
+ break;
190
+ case 'ArrowUp':
191
+ delta = -1;
192
+ break;
193
+ case 'Escape':
194
+ break;
195
+ case 'Enter':
196
+ break;
197
+ default:
198
+ console.info(event.key);
199
+ return;
200
+ }
201
+ event.stopPropagation();
202
+ event.preventDefault();
203
+ if (event.key === 'Escape' || event.key === 'Enter') {
204
+ this.container?.focus();
205
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
206
+ if (event.key === 'Enter' && this.dropdown_callback) {
207
+ if (this.dropdown_selected) {
208
+ const value = this.dropdown_selected.dataset.dropdown_value;
209
+ // this.dropdown_callback.call(0, (this.dropdown_selected as any).dropdown_value);
210
+ this.dropdown_callback.call(0, value ? JSON.parse(value) : undefined);
211
+ }
212
+ }
213
+ }
214
+ else if (delta) {
215
+ if (this.dropdown_selected) {
216
+ if (delta > 0 && this.dropdown_selected.nextSibling) {
217
+ this.dropdown_selected.nextSibling.classList.add('selected');
218
+ this.dropdown_selected.classList.remove('selected');
219
+ this.dropdown_selected = this.dropdown_selected.nextSibling;
220
+ // support scrolling
221
+ const bottom = this.dropdown_selected.offsetTop + this.dropdown_selected.offsetHeight;
222
+ if (bottom >
223
+ this.dropdown_list.offsetHeight + this.dropdown_list.scrollTop) {
224
+ this.dropdown_list.scrollTop = bottom - this.dropdown_list.offsetHeight;
225
+ }
226
+ }
227
+ else if (delta < 0 && this.dropdown_selected.previousSibling) {
228
+ this.dropdown_selected.previousSibling.classList.add('selected');
229
+ this.dropdown_selected.classList.remove('selected');
230
+ this.dropdown_selected = this.dropdown_selected.previousSibling;
231
+ // support scrolling
232
+ if (this.dropdown_selected.offsetTop < this.dropdown_list.scrollTop) {
233
+ this.dropdown_list.scrollTop = this.dropdown_selected.offsetTop;
234
+ }
235
+ }
236
+ }
237
+ }
238
+ });
239
+ this.dropdown_list.addEventListener('mousedown', (event) => {
240
+ const target = event.target;
241
+ if (event.target === this.dropdown_list) {
242
+ return;
243
+ }
244
+ event.stopPropagation();
245
+ event.preventDefault();
246
+ this.container?.focus();
247
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
248
+ if (this.dropdown_callback) {
249
+ // this.dropdown_callback.call(0, (target as any).dropdown_value);
250
+ const value = target.dataset.dropdown_value;
251
+ this.dropdown_callback.call(0, value ? JSON.parse(value) : undefined);
252
+ }
253
+ });
254
+ this.dropdown_list.addEventListener('mousemove', (event) => {
255
+ const target = event.target;
256
+ if (target === this.dropdown_selected) {
257
+ return;
258
+ }
259
+ this.grid_cover.classList.remove('nub-select');
260
+ if (this.dropdown_selected) {
261
+ this.dropdown_selected.classList.remove('selected');
262
+ }
263
+ target.classList.add('selected');
264
+ this.dropdown_selected = target;
265
+ });
266
+ this.mock_selection = DOM.Div('mock-selection-node', undefined, {
267
+ html: '&nbsp;',
268
+ });
269
+ // this.mock_selection.innerHTML = '&nbsp;';
270
+ this.note_node = DOM.Div('treb-note');
271
+ this.title_node = DOM.Div('treb-hover-title');
272
+ this.sort_button = DOM.Create('button', 'treb-sort-button', undefined, { attrs: { title: 'Sort table', tabindex: '-1' } });
273
+ this.HideNote();
274
+ }
275
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
276
+ FocusInLayout(target) {
277
+ return false;
278
+ }
279
+ /**
280
+ * if the DPR has changed, update it and return true. otherwise return
281
+ * false. this is used on resize events: if the scale has changed, we
282
+ * probably want to repaint (and we need to update scale).
283
+ */
284
+ UpdateDPR() {
285
+ const dpr = Math.max(1, self.devicePixelRatio || 1);
286
+ if (dpr === this.dpr) {
287
+ return false;
288
+ }
289
+ this.dpr = dpr;
290
+ return true;
291
+ }
292
+ /** wrapper around sheet method, incorporating scale */
293
+ ColumnWidth(column) {
294
+ return Math.round(this.view.active_sheet.GetColumnWidth(column) * this.scale);
295
+ }
296
+ /** wrapper around sheet method, incorporating scale */
297
+ RowHeight(row) {
298
+ return Math.round(this.view.active_sheet.GetRowHeight(row) * this.scale);
299
+ }
300
+ /**
301
+ * wrapper around sheet method, incorporating scale
302
+ *
303
+ * NOTE: this does not update total size, so unless there's a subsequent call
304
+ * to a layout update, total size will be out of sync
305
+ */
306
+ SetRowHeight(row, height) {
307
+ this.view.active_sheet.SetRowHeight(row, Math.round(height / this.scale));
308
+ }
309
+ /**
310
+ * wrapper around sheet method, incorporating scale
311
+ *
312
+ * NOTE: this does not update total size, so unless there's a subsequent call
313
+ * to a layout update, total size will be out of sync
314
+ */
315
+ SetColumnWidth(column, width) {
316
+ this.view.active_sheet.SetColumnWidth(column, Math.round(width / this.scale));
317
+ }
318
+ /**
319
+ * show/hide grid selections. used when selecting annotations.
320
+ */
321
+ ShowSelections(show = true) {
322
+ this.grid_selection.style.display = show ? 'block' : 'none';
323
+ }
324
+ HideTitle() {
325
+ this.title_node.style.opacity = '0';
326
+ // this.title_node.style.pointerEvents = 'none';
327
+ }
328
+ ShowTitle(text, address /*, event?: MouseEvent */) {
329
+ this.title_node.textContent = text;
330
+ if (!this.title_node.parentElement)
331
+ return;
332
+ // const note_size = this.title_node.getBoundingClientRect();
333
+ const container = this.title_node.parentElement.getBoundingClientRect();
334
+ const rect = this.OffsetCellAddressToRectangle(address).Shift(this.header_size.width, this.header_size.height);
335
+ this.title_node.style.left = (container.left + rect.left - this.scroll_reference_node.scrollLeft + 0) + 'px';
336
+ this.title_node.style.top = (container.top + rect.bottom - this.scroll_reference_node.scrollTop + 8) + 'px';
337
+ // FIXME: use class
338
+ this.title_node.style.opacity = '1';
339
+ // this.title_node.style.pointerEvents = 'auto';
340
+ }
341
+ HideNote() {
342
+ // FIXME: use class
343
+ this.note_node.style.opacity = '0';
344
+ this.note_node.style.pointerEvents = 'none';
345
+ }
346
+ HideTableSortButton() {
347
+ this.sort_button.style.opacity = '0';
348
+ this.sort_button.style.pointerEvents = 'none';
349
+ this.sort_button.setAttribute('tabindex', '-1');
350
+ }
351
+ ShowTableSortButton(table, column, address) {
352
+ if (!this.sort_button.parentElement) {
353
+ return;
354
+ }
355
+ let asc = true;
356
+ let initial = false;
357
+ if (table.sort) {
358
+ if (table.sort.column === column) {
359
+ asc = !table.sort.asc;
360
+ initial = true;
361
+ }
362
+ }
363
+ this.sort_button.setAttribute('tabindex', '0 ');
364
+ this.sort_button.style.opacity = '1';
365
+ this.sort_button.style.pointerEvents = 'initial';
366
+ this.sort_button.classList.remove('asc', 'desc');
367
+ if (initial) {
368
+ this.sort_button.classList.add(asc ? 'asc' : 'desc');
369
+ }
370
+ this.sort_button.dataset.asc = asc.toString();
371
+ this.sort_button.dataset.table = table.name;
372
+ this.sort_button.dataset.column = column.toString();
373
+ const rect = this.OffsetCellAddressToRectangle(address).Shift(this.header_size.width, this.header_size.height);
374
+ const button_size = this.sort_button.getBoundingClientRect();
375
+ // const container = this.sort_button.parentElement.getBoundingClientRect();
376
+ // const offset = { x: 8, y: 2 };
377
+ this.sort_button.style.left = (rect.right - button_size.width - button_size.width / 2) + 'px';
378
+ this.sort_button.style.top =
379
+ (rect.top + (rect.height - button_size.height) / 2) + 'px';
380
+ }
381
+ /**
382
+ * adding html parameter at the end, so we can keep the function
383
+ * signature otherwise the same. this is for markdown formatting.
384
+ */
385
+ ShowNote(note, address, event, markdown) {
386
+ // UPDATE for MD (optional)
387
+ if (markdown) {
388
+ this.note_node.innerHTML = markdown;
389
+ }
390
+ else {
391
+ this.note_node.textContent = note;
392
+ }
393
+ if (!this.note_node.parentElement)
394
+ return;
395
+ const note_size = this.note_node.getBoundingClientRect();
396
+ const container = this.note_node.parentElement.getBoundingClientRect();
397
+ const offset = { x: 8, y: 2 };
398
+ const rect = this.OffsetCellAddressToRectangle(address).Shift(this.header_size.width, this.header_size.height);
399
+ this.note_node.style.left = (container.left + rect.right - this.scroll_reference_node.scrollLeft + offset.x) + 'px';
400
+ this.note_node.style.top = (container.top + rect.top - this.scroll_reference_node.scrollTop - (note_size.height / 5) - offset.y) + 'px';
401
+ // FIXME: use class
402
+ this.note_node.style.opacity = '1';
403
+ this.note_node.style.pointerEvents = 'auto';
404
+ }
405
+ /* * needed for IE11, legacy only * /
406
+ public FixBrokenSelection() {
407
+ // ...
408
+ }
409
+ */
410
+ /**
411
+ * raise or lower annotation in z-order (implicit)
412
+ *
413
+ * returns true if we've made changes, so you can trigger any necessary
414
+ * events or side-effects
415
+ */
416
+ AnnotationLayoutOrder(annotation, delta) {
417
+ // find index
418
+ let index = -1;
419
+ for (let i = 0; i < this.view.active_sheet.annotations.length; i++) {
420
+ if (this.view.active_sheet.annotations[i] === annotation) {
421
+ index = i;
422
+ break;
423
+ }
424
+ }
425
+ if (index < 0) {
426
+ return false; // not found
427
+ }
428
+ const target = Math.min(Math.max(0, index + delta), this.view.active_sheet.annotations.length - 1);
429
+ if (target === index) {
430
+ return false; // not moving (probably at edge)
431
+ }
432
+ // change in array order, so it's preserved
433
+ this.view.active_sheet.annotations.splice(index, 1);
434
+ this.view.active_sheet.annotations.splice(target, 0, annotation);
435
+ // update layout, use z-indexes
436
+ for (let i = 0; i < this.view.active_sheet.annotations.length; i++) {
437
+ // updating to shift frozen annotations as well
438
+ const key = this.view.active_sheet.annotations[i].key;
439
+ const elements = this.container?.querySelectorAll(`.annotation[data-key="${key}"]`);
440
+ if (elements) {
441
+ for (let j = 0; j < elements?.length; j++) {
442
+ elements[j].style.zIndex = (i + 1).toString();
443
+ }
444
+ }
445
+ /*
446
+ const node = this.view.active_sheet.annotations[i].node;
447
+ if (node) {
448
+ node.style.zIndex = (i + 1).toString();
449
+ }
450
+ */
451
+ }
452
+ return true;
453
+ }
454
+ /**
455
+ *
456
+ */
457
+ PointToAnnotationCorner(point) {
458
+ const address = this.PointToAddress_Grid(point, false);
459
+ const cell_rect = this.CellAddressToRectangle(address);
460
+ return {
461
+ address,
462
+ offset: {
463
+ x: (point.x - cell_rect.left) / cell_rect.width,
464
+ y: (point.y - cell_rect.top) / cell_rect.height,
465
+ },
466
+ };
467
+ }
468
+ /**
469
+ * utility for managing (new) annotation layout. we offset the {top, left}
470
+ * by {1, 1} pixel so that the alignment snaps to cell boundaries.
471
+ */
472
+ RectToAnnotationLayout(rect) {
473
+ return {
474
+ tl: this.PointToAnnotationCorner({ x: (rect.left || 0) + 1, y: (rect.top || 0) + 1 }),
475
+ br: this.PointToAnnotationCorner({ x: rect.right || rect.left || 100, y: rect.bottom || rect.top || 100 }),
476
+ };
477
+ }
478
+ AddressToAnnotationLayout(tl, br) {
479
+ const rects = {
480
+ tl: this.CellAddressToRectangle(tl),
481
+ br: this.CellAddressToRectangle(br),
482
+ };
483
+ return {
484
+ tl: this.PointToAnnotationCorner({ x: (rects.tl.left || 0), y: (rects.tl.top || 0) }),
485
+ br: this.PointToAnnotationCorner({ x: rects.br.right || rects.tl.left || 100, y: rects.br.bottom || rects.tl.left || 100 }),
486
+ };
487
+ }
488
+ /**
489
+ * @see RectToAnnotationLayout regarding the 1 pixel shift
490
+ */
491
+ AnnotationLayoutToRect(layout) {
492
+ const tl = this.CellAddressToRectangle(layout.tl.address);
493
+ const br = this.CellAddressToRectangle(layout.br.address);
494
+ const left = tl.left + tl.width * layout.tl.offset.x - 1;
495
+ const top = tl.top + tl.height * layout.tl.offset.y - 1;
496
+ return new Rectangle(left, top, br.left + br.width * layout.br.offset.x - left, br.top + br.height * layout.br.offset.y - top);
497
+ }
498
+ UpdateAnnotation(elements, theme) {
499
+ if (!Array.isArray(elements))
500
+ elements = [elements];
501
+ for (const annotation of elements) {
502
+ const view = annotation.view[this.view.view_index] || {};
503
+ if (view.node) {
504
+ /*
505
+ if (annotation.node.dataset.scale && annotation.node.dataset.scale !== this.scale.toString()) {
506
+ console.info('scale out of sync');
507
+ }
508
+ */
509
+ let font_size_pt = 10 * this.scale;
510
+ // console.info(annotation.data.style);
511
+ if (annotation.data.style?.font_size?.unit === 'em') {
512
+ font_size_pt *= annotation.data.style.font_size.value;
513
+ // console.info("scaling up...", font_size_pt)
514
+ }
515
+ view.node.dataset.scale = this.scale.toString();
516
+ // maybe we should just reset and then apply?
517
+ if (annotation.data.style?.font_face && annotation.data.style.font_face.startsWith('stack:')) {
518
+ const name = annotation.data.style.font_face.substring(6);
519
+ const stack = theme.font_stacks[name];
520
+ if (stack) {
521
+ view.node.classList.add('treb-inherit-font');
522
+ view.node.style.fontFamily = stack.font || '';
523
+ // font_size_pt *= stack.scale;
524
+ if (stack.variants) {
525
+ view.node.style.fontVariant = stack.variants;
526
+ }
527
+ }
528
+ else {
529
+ view.node.classList.remove('treb-inherit-font');
530
+ view.node.style.fontFamily = '';
531
+ view.node.style.fontVariant = '';
532
+ }
533
+ }
534
+ else {
535
+ view.node.classList.remove('treb-inherit-font');
536
+ view.node.style.fontFamily = '';
537
+ view.node.style.fontVariant = '';
538
+ }
539
+ if (annotation.data.style?.bold) {
540
+ view.node.style.fontWeight = '600';
541
+ }
542
+ else {
543
+ view.node.style.fontWeight = '400';
544
+ }
545
+ if (annotation.data.style?.italic) {
546
+ view.node.style.fontStyle = 'italic';
547
+ }
548
+ else {
549
+ view.node.style.fontStyle = '';
550
+ }
551
+ if (annotation.data.style?.underline) {
552
+ view.node.style.textDecoration = 'underline';
553
+ }
554
+ else {
555
+ view.node.style.textDecoration = '';
556
+ }
557
+ view.node.style.fontSize = `${font_size_pt}pt`;
558
+ // update the layout here if necessary. after that it should
559
+ // be persisted (assuming it's saved). eventually this should
560
+ // be superfluous...
561
+ if (annotation.rect && !annotation.data.layout) {
562
+ // this is breaking on freeze when the spreadsheet is scrolled because
563
+ // the top-left uses the freeze panes. stop doing that.
564
+ annotation.scaled_rect = annotation.rect.Scale(this.scale);
565
+ annotation.data.layout = this.RectToAnnotationLayout(annotation.scaled_rect);
566
+ }
567
+ // FIXME: merge cells? [...]
568
+ if (annotation.data.layout) {
569
+ const rect = this.AnnotationLayoutToRect(annotation.data.layout);
570
+ rect.ApplyStyle(view.node);
571
+ // NOTE: we still set the scaled rect, because that's used in
572
+ // manipulating at scale. we will need to make sure that we update
573
+ // the layout when the scaled rect / regular rect changes...
574
+ annotation.scaled_rect = rect; // .Scale(this.scale);
575
+ }
576
+ view.node.dataset.key = annotation.key.toString();
577
+ // FIXME: only do this if necessary (if frozen).
578
+ if (this.view.active_sheet.freeze.rows || this.view.active_sheet.freeze.columns) {
579
+ this.CloneFrozenAnnotation(annotation);
580
+ }
581
+ }
582
+ }
583
+ }
584
+ /** returns a list of copies painted to frozen panes, for move/size */
585
+ GetFrozenAnnotations(annotation) {
586
+ const containers = [this.row_header_annotations, this.column_header_annotations, this.corner_annotations];
587
+ return containers.map((container) => container.querySelector(`.annotation[data-key="${annotation.key}"]`)).filter(test => test !== null);
588
+ }
589
+ /**
590
+ * clone all annotations into freeze panes
591
+ */
592
+ CloneFrozenAnnotations() {
593
+ for (const annotation of this.view.active_sheet.annotations) {
594
+ const view = annotation.view[this.view.view_index];
595
+ if (view?.node && annotation.key) {
596
+ this.CloneFrozenAnnotation(annotation);
597
+ }
598
+ }
599
+ }
600
+ /**
601
+ * remove all annotations from freeze panes
602
+ *
603
+ */
604
+ ClearFrozenAnnotations() {
605
+ for (const container of [this.row_header_annotations, this.column_header_annotations, this.corner_annotations]) {
606
+ const elements = container.querySelectorAll('.annotation');
607
+ for (let i = 0; i < elements.length; i++) {
608
+ // FIXME: remove event listeners
609
+ elements[i].parentElement?.removeChild(elements[i]);
610
+ }
611
+ }
612
+ }
613
+ /**
614
+ * remove a frozen annotation
615
+ * @param annotation
616
+ */
617
+ RemoveFrozenAnnotation(annotation) {
618
+ for (const container of [this.row_header_annotations, this.column_header_annotations, this.corner_annotations]) {
619
+ const element = container.querySelector(`.annotation[data-key="${annotation.key}"]`);
620
+ if (element) {
621
+ // FIXME: remove event listeners
622
+ element.parentElement?.removeChild(element);
623
+ }
624
+ }
625
+ }
626
+ /**
627
+ * clone a single annotation. usually this will be used on create, but
628
+ * we batch them when we freeze panes (from unfrozen)
629
+ */
630
+ CloneFrozenAnnotation(annotation) {
631
+ for (const container of [this.row_header_annotations, this.column_header_annotations, this.corner_annotations]) {
632
+ // FIXME: could reuse? not sure it's worth it
633
+ let element = container.querySelector(`.annotation[data-key="${annotation.key}"]`);
634
+ if (element) {
635
+ element.parentElement?.removeChild(element);
636
+ }
637
+ const view = annotation.view[this.view.view_index];
638
+ element = view?.node?.cloneNode(true);
639
+ if (element) {
640
+ const move_target = element.querySelector('.annotation-move-target');
641
+ const resize_target = element.querySelector('.annotation-resize-target');
642
+ element.addEventListener('mousedown', (event) => {
643
+ const node = view.node;
644
+ requestAnimationFrame(() => {
645
+ // console.info('calling focus on', node);
646
+ node?.focus();
647
+ });
648
+ this.AnnotationMouseDown(annotation, view.node, event, move_target, resize_target);
649
+ });
650
+ container.appendChild(element);
651
+ }
652
+ }
653
+ }
654
+ RemoveAnnotation(annotation) {
655
+ const view = annotation.view[this.view.view_index] || {};
656
+ if (view.node) {
657
+ view.node.parentElement?.removeChild(view.node);
658
+ }
659
+ this.RemoveFrozenAnnotation(annotation);
660
+ }
661
+ /**
662
+ * remove annotation nodes from the container, without impacting
663
+ * the underlying data. annotations will still retain nodes, they
664
+ * just won't be attached to anything.
665
+ *
666
+ * NOTE: IE destroys nodes if you do this? (...)
667
+ * patch in legacy... actually we'll do it here
668
+ */
669
+ RemoveAnnotationNodes() {
670
+ // we were using a shortcut, innerText = '', but if you do that
671
+ // in IE it destroys the nodes (!) -- so we need to explicitly
672
+ // remove them
673
+ // FIXME: we are explicitly adding them, why not just maintain a list?
674
+ const children = Array.prototype.map.call(this.annotation_container.children, (node) => node);
675
+ for (const child of children) {
676
+ this.annotation_container.removeChild(child);
677
+ }
678
+ if (this.view.active_sheet.freeze.rows || this.view.active_sheet.freeze.columns) {
679
+ this.ClearFrozenAnnotations();
680
+ }
681
+ }
682
+ AddAnnotation(annotation, theme) {
683
+ const view = annotation.view[this.view.view_index] || {};
684
+ if (!view.node) {
685
+ throw new Error('annotation view/node missing');
686
+ }
687
+ this.annotation_container.appendChild(view.node);
688
+ this.UpdateAnnotation(annotation, theme);
689
+ }
690
+ // testing moving this here...
691
+ AnnotationMouseDown(annotation, node, event, move_target, resize_target) {
692
+ // console.info('annotation mousedown (in layout)', annotation);
693
+ const rect = annotation.scaled_rect;
694
+ if (!rect) {
695
+ console.info('missing scaled rect!');
696
+ return Promise.reject(); // ?
697
+ }
698
+ return new Promise((resolve) => {
699
+ const origin = {
700
+ left: rect.left,
701
+ top: rect.top,
702
+ width: rect.width,
703
+ height: rect.height,
704
+ };
705
+ const scroll_node = this.scroll_reference_node;
706
+ const scroll_rect = scroll_node.getBoundingClientRect();
707
+ const bounding_rect = node.getBoundingClientRect();
708
+ // IE11 is not targeting the child nodes? why not? (...)
709
+ // console.info('target', (event.target as HTMLElement)?.className);
710
+ if (event.target === move_target || (event.target !== resize_target && event.altKey)) {
711
+ event.stopPropagation();
712
+ event.preventDefault();
713
+ node.focus();
714
+ const offset = {
715
+ x: bounding_rect.left + event.offsetX - rect.left,
716
+ y: bounding_rect.top + event.offsetY - rect.top,
717
+ };
718
+ const elements = [node, ...this.GetFrozenAnnotations(annotation)];
719
+ const scroll_delta = 25;
720
+ const grid_rect = this.CellAddressToRectangle({ row: 0, column: 0 }).Combine(this.CellAddressToRectangle({
721
+ row: this.view.active_sheet.rows - 1,
722
+ column: this.view.active_sheet.columns - 1,
723
+ })).Expand(-1, -1);
724
+ MouseDrag(this.mask, 'move', (move_event) => {
725
+ // check if we are oob the grid
726
+ // FIXME: clamp annotation to cell bounds (...) this is OK for now though
727
+ if (move_event.offsetY - scroll_rect.top < this.header_offset.y) {
728
+ const delta = Math.min(scroll_delta, scroll_node.scrollTop);
729
+ scroll_node.scrollTop -= delta;
730
+ offset.y += delta;
731
+ }
732
+ else if (move_event.offsetY - scroll_rect.top >= scroll_rect.height) {
733
+ if (scroll_node.scrollTop + scroll_rect.height < grid_rect.height) {
734
+ const delta = scroll_delta;
735
+ scroll_node.scrollTop += delta;
736
+ offset.y -= delta;
737
+ }
738
+ }
739
+ if (move_event.offsetX - scroll_rect.left < this.header_offset.x) {
740
+ const delta = Math.min(scroll_delta, scroll_node.scrollLeft);
741
+ scroll_node.scrollLeft -= delta;
742
+ offset.x += delta;
743
+ }
744
+ else if (move_event.offsetX - scroll_rect.left >= scroll_rect.width) {
745
+ if (scroll_node.scrollLeft + scroll_rect.width < grid_rect.width) {
746
+ const delta = scroll_delta;
747
+ scroll_node.scrollLeft += delta;
748
+ offset.x -= delta;
749
+ }
750
+ }
751
+ rect.top = move_event.offsetY - offset.y;
752
+ rect.left = move_event.offsetX - offset.x;
753
+ if (move_event.shiftKey) {
754
+ // move in one direction at a time
755
+ const dx = Math.abs(rect.left - origin.left);
756
+ const dy = Math.abs(rect.top - origin.top);
757
+ if (dx <= dy) {
758
+ rect.left = origin.left;
759
+ }
760
+ else {
761
+ rect.top = origin.top;
762
+ }
763
+ }
764
+ if (move_event.ctrlKey) {
765
+ const point = this.ClampToGrid({
766
+ x: rect.left, y: rect.top,
767
+ });
768
+ rect.left = point.x;
769
+ rect.top = point.y;
770
+ }
771
+ // node.style.top = (rect.top) + 'px';
772
+ // node.style.left = (rect.left) + 'px';
773
+ for (const element of elements) {
774
+ element.style.top = (rect.top) + 'px';
775
+ element.style.left = (rect.left) + 'px';
776
+ }
777
+ }, () => {
778
+ annotation.data.extent = undefined; // reset
779
+ // annotation.rect = rect.Scale(1/this.scale);
780
+ annotation.data.layout = this.RectToAnnotationLayout(rect);
781
+ // this.grid_events.Publish({ type: 'annotation', annotation, event: 'move' });
782
+ resolve({ type: 'annotation', annotation, event: 'move' });
783
+ });
784
+ return;
785
+ }
786
+ else if (event.target === resize_target) {
787
+ //if ((bounding_rect.width - event.offsetX <= 13) &&
788
+ // (bounding_rect.height - event.offsetY <= 13)) {
789
+ event.stopPropagation();
790
+ event.preventDefault();
791
+ node.focus();
792
+ let aspect = 0;
793
+ if (annotation.data.type === 'image') {
794
+ if (annotation.data.data
795
+ && annotation.data.data.original_size
796
+ && annotation.data.data.original_size.width
797
+ && annotation.data.data.original_size.height) {
798
+ aspect = annotation.data.data.original_size.width /
799
+ annotation.data.data.original_size.height;
800
+ }
801
+ }
802
+ const bounds = node.getBoundingClientRect();
803
+ const offset = {
804
+ x: bounds.left + event.offsetX - rect.width + resize_target.offsetLeft,
805
+ y: bounds.top + event.offsetY - rect.height + resize_target.offsetTop,
806
+ };
807
+ MouseDrag(this.mask, 'nw-resize', (move_event) => {
808
+ const elements = [node, ...this.GetFrozenAnnotations(annotation)];
809
+ rect.height = move_event.offsetY - offset.y;
810
+ rect.width = move_event.offsetX - offset.x;
811
+ if (move_event.shiftKey && move_event.ctrlKey) {
812
+ if (aspect) {
813
+ const dx = Math.abs(rect.width - origin.width);
814
+ const dy = Math.abs(rect.height - origin.height);
815
+ if (dx < dy) {
816
+ rect.width = aspect * rect.height;
817
+ }
818
+ else {
819
+ rect.height = rect.width / aspect;
820
+ }
821
+ }
822
+ }
823
+ else if (move_event.shiftKey) {
824
+ // move in one direction at a time [is this backwards? ...]
825
+ const dx = Math.abs(rect.height - origin.height);
826
+ const dy = Math.abs(rect.width - origin.width);
827
+ if (dx > dy) {
828
+ rect.width = origin.width;
829
+ }
830
+ else {
831
+ rect.height = origin.height;
832
+ }
833
+ }
834
+ else if (move_event.ctrlKey) {
835
+ const point = this.ClampToGrid({
836
+ x: rect.right, y: rect.bottom,
837
+ });
838
+ rect.width = point.x - rect.left + 1;
839
+ rect.height = point.y - rect.top + 1;
840
+ }
841
+ // node.style.height = (rect.height) + 'px';
842
+ // node.style.width = (rect.width) + 'px';
843
+ for (const element of elements) {
844
+ element.style.height = (rect.height) + 'px';
845
+ element.style.width = (rect.width) + 'px';
846
+ }
847
+ }, () => {
848
+ annotation.data.extent = undefined; // reset
849
+ // annotation.rect = rect.Scale(1/this.scale);
850
+ annotation.data.layout = this.RectToAnnotationLayout(rect);
851
+ // this.grid_events.Publish({ type: 'annotation', annotation, event: 'resize' });
852
+ resolve({ type: 'annotation', annotation, event: 'resize' });
853
+ });
854
+ return;
855
+ }
856
+ else {
857
+ resolve();
858
+ }
859
+ });
860
+ }
861
+ /**
862
+ * this used to be an abstract method for initializing. we're taking it
863
+ * over to do some additional work post init, and renaming the subclass-specific
864
+ * method (@see InitializeInternal).
865
+ */
866
+ Initialize(container, callbacks,
867
+ // scroll_callback: () => void,
868
+ // dropdown_callback: (value: CellValue) => void,
869
+ // sort_callback: (table: string, column: number, asc: boolean) => void,
870
+ // focus_callback: () => void,
871
+ scroll = true) {
872
+ // this is getting stuck under the toolbar... we need better z-stacking
873
+ // and this needs to be higher up in the node list
874
+ if (!this.mask.parentElement) {
875
+ container.parentElement?.parentElement?.appendChild(this.mask);
876
+ }
877
+ //if (!this.error_highlight.parentElement) {
878
+ // container.appendChild(this.error_highlight);
879
+ //}
880
+ if (!this.tooltip.parentElement) {
881
+ container.appendChild(this.tooltip);
882
+ }
883
+ // FIXME: -> instance specific, b/c trident
884
+ if (!this.spill_border.parentElement) {
885
+ container.appendChild(this.spill_border);
886
+ }
887
+ if (!this.dropdown_caret.parentElement) {
888
+ container.appendChild(this.dropdown_caret);
889
+ }
890
+ if (!this.dropdown_list.parentElement) {
891
+ container.appendChild(this.dropdown_list);
892
+ }
893
+ if (!this.note_node.parentElement) {
894
+ container.appendChild(this.note_node);
895
+ }
896
+ if (!this.sort_button.parentElement) {
897
+ container.appendChild(this.sort_button);
898
+ this.sort_button.addEventListener('click', () => {
899
+ // console.info(this.sort_button.dataset);
900
+ callbacks.sort(this.sort_button.dataset.table || '', Number(this.sort_button.dataset.column || '0') || 0, /true/i.test(this.sort_button.dataset.asc || ''));
901
+ this.sort_button.classList.remove('asc', 'desc');
902
+ if (this.sort_button.dataset.asc === 'true') {
903
+ this.sort_button.dataset.asc = 'false';
904
+ this.sort_button.classList.add('desc');
905
+ }
906
+ else {
907
+ this.sort_button.dataset.asc = 'true';
908
+ this.sort_button.classList.add('asc');
909
+ }
910
+ callbacks.focus();
911
+ });
912
+ }
913
+ if (!this.title_node.parentElement) {
914
+ container.appendChild(this.title_node);
915
+ }
916
+ this.InitializeInternal(container, callbacks.scroll);
917
+ if (!scroll && this.scroll_reference_node) {
918
+ this.scroll_reference_node.style.overflow = 'hidden';
919
+ }
920
+ this.dropdown_callback = callbacks.dropdown;
921
+ this.initialized = true;
922
+ }
923
+ /**
924
+ * create a selection so that this node (and parents) receive
925
+ * a copy event on ctrl+c (or any other system copy event).
926
+ * seems to break IE, so split.
927
+ */
928
+ MockSelection() {
929
+ if (!this.container) {
930
+ return;
931
+ }
932
+ // disable for IE, but leave in legacy renderer because it works
933
+ // in safari/edge. there may be some way to fix IE... although copy
934
+ // events aren't available, so we would have to do the fake-csv thing
935
+ // (which I don't want to do).
936
+ if (this.trident) {
937
+ return;
938
+ }
939
+ // edge handles this differently than chrome/ffx. in edge, the
940
+ // cursor does not move to the end of the selection, which is
941
+ // what we want. so we need to fix that for edge:
942
+ // FIXME: limit to edge (causing problems in chrome? ...)
943
+ if (this.DOM.doc) {
944
+ const selection = this.DOM.GetSelection();
945
+ if (selection) {
946
+ const range = this.DOM.doc.createRange();
947
+ range.selectNodeContents(this.mock_selection);
948
+ selection.removeAllRanges();
949
+ selection.addRange(range);
950
+ }
951
+ }
952
+ }
953
+ /**
954
+ * FIXME: this is public for now but tiles should move into
955
+ * this class, then this method can become private
956
+ */
957
+ CreateTile(classes, size, position, first_cell, cell_extent, pixel_start, parent, mark_dirty = true) {
958
+ const tile = this.DOM.Create('canvas');
959
+ tile.setAttribute('class', classes);
960
+ tile.logical_size = size;
961
+ tile.width = size.width * this.dpr;
962
+ tile.height = size.height * this.dpr;
963
+ tile.style.width = `${size.width}px`;
964
+ tile.style.height = `${size.height}px`;
965
+ tile.tile_position = position;
966
+ tile.first_cell = first_cell;
967
+ this.UpdateTileGridPosition(tile);
968
+ tile.last_cell = {
969
+ row: first_cell.row + cell_extent.rows - 1,
970
+ column: first_cell.column + cell_extent.columns - 1,
971
+ };
972
+ tile.pixel_start = pixel_start;
973
+ tile.pixel_end = {
974
+ x: pixel_start.x + size.width,
975
+ y: pixel_start.y + size.height,
976
+ };
977
+ tile.dirty = !!mark_dirty;
978
+ tile.needs_full_repaint = true; // never painted
979
+ parent.appendChild(tile);
980
+ return tile;
981
+ }
982
+ ApplyThemeColors() {
983
+ // what's the best node for this? (...)
984
+ if (this.container) {
985
+ for (const [index, entry] of this.applied_theme_colors.entries()) {
986
+ this.container.style.setProperty(`--treb-applied-theme-color-${index + 1}`, entry);
987
+ }
988
+ }
989
+ }
990
+ /**
991
+ * applies theme to nodes, as necessary
992
+ */
993
+ ApplyTheme(theme) {
994
+ this.row_header.style.backgroundColor =
995
+ this.column_header.style.backgroundColor =
996
+ this.corner.style.backgroundColor =
997
+ theme.headers?.fill ? ResolveThemeColor(theme, theme.headers.fill, 0) : '';
998
+ // theme.headers?.background || '';
999
+ // theme.header_background_color || ''; // this.theme.header_background;
1000
+ this.corner.style.borderColor =
1001
+ theme.grid_color || ''; // this.theme.header_border_color;
1002
+ // this.row_header.style.backgroundColor = this.theme.header_background;
1003
+ // this.column_header.style.backgroundColor = this.theme.header_background;
1004
+ for (const row of this.grid_tiles) {
1005
+ for (const tile of row) {
1006
+ tile.style.backgroundColor = ResolveThemeColor(theme, theme.grid_cell?.fill, 0) || '#fff';
1007
+ }
1008
+ }
1009
+ // TODO: dropdown caret
1010
+ this.dropdown_list.style.font = Style.CompositeFont(theme.grid_cell_font_size, {
1011
+ font_face: 'stack:default',
1012
+ }, this.scale, theme).font;
1013
+ // testing
1014
+ this.applied_theme_colors = theme.theme_colors?.slice(4, 10) || [];
1015
+ if (this.container) {
1016
+ this.ApplyThemeColors();
1017
+ }
1018
+ }
1019
+ UpdateTotalSize() {
1020
+ this.total_height = 0;
1021
+ const rows = this.view.active_sheet.rows;
1022
+ for (let i = 0; i < rows; i++) {
1023
+ this.total_height += this.RowHeight(i);
1024
+ }
1025
+ this.total_width = 0;
1026
+ const columns = this.view.active_sheet.columns;
1027
+ for (let i = 0; i < columns; i++) {
1028
+ this.total_width += this.ColumnWidth(i);
1029
+ }
1030
+ }
1031
+ UpdateContentsSize() {
1032
+ const height = this.row_header_tiles.reduce((a, tile) => a + tile.logical_size.height, 0);
1033
+ const width = this.column_header_tiles.reduce((a, tile) => a + tile.logical_size.width, 0);
1034
+ this.column_header.style.width = this.contents.style.width = `${width}px`;
1035
+ this.row_header.style.height = this.contents.style.height = `${height}px`;
1036
+ }
1037
+ /** hides column/row resize tooltip and removes any specific classes */
1038
+ HideTooltip() {
1039
+ this.tooltip.style.display = 'none';
1040
+ this.tooltip_state = undefined;
1041
+ this.tooltip.classList.remove('arrow-up');
1042
+ this.tooltip.classList.remove('arrow-left');
1043
+ }
1044
+ /*
1045
+
1046
+ highlight error removed in favor of container errors, event reporting
1047
+
1048
+ * briefly flash red, to indicate an error * /
1049
+ public HighlightError(address: ICellAddress): void {
1050
+
1051
+ const target_rect = this.OffsetCellAddressToRectangle(address).Shift(
1052
+ this.header_size.width, this.header_size.height);
1053
+
1054
+ target_rect.ApplyStyle(this.error_highlight);
1055
+ this.error_highlight.style.opacity = '1';
1056
+
1057
+ // we don't like to rely on transitionend events. the concern is that
1058
+ // if they overlap eventually one will get lost... because this can be
1059
+ // triggered faster than the transition, we can almost always make that
1060
+ // happen
1061
+
1062
+ if (this.error_highlight_timeout) {
1063
+ clearTimeout(this.error_highlight_timeout);
1064
+ }
1065
+
1066
+ this.error_highlight_timeout = setTimeout(() => {
1067
+ this.error_highlight.style.opacity = '0';
1068
+ this.error_highlight_timeout = undefined;
1069
+ }, 250)
1070
+
1071
+ }
1072
+ */
1073
+ /** show column/row resize tooltip */
1074
+ ShowTooltip(options = {}) {
1075
+ if (options.up) {
1076
+ this.tooltip.classList.add('arrow-up');
1077
+ this.tooltip_state = 'up';
1078
+ }
1079
+ else if (options.left) {
1080
+ this.tooltip.classList.add('arrow-left');
1081
+ this.tooltip_state = 'left';
1082
+ }
1083
+ this.tooltip.style.display = 'block';
1084
+ this.UpdateTooltip(options);
1085
+ }
1086
+ ShowDropdownCaret(area, list, current) {
1087
+ let target_rect = this.OffsetCellAddressToRectangle(area.start);
1088
+ if (area.count > 1) {
1089
+ target_rect = target_rect.Combine(this.OffsetCellAddressToRectangle(area.end));
1090
+ }
1091
+ target_rect = target_rect.Shift(this.header_size.width, this.header_size.height);
1092
+ // FIXME: max size? (...)
1093
+ const height = Math.round(this.scale * Math.max(8, Math.min(20, target_rect.height)));
1094
+ this.dropdown_caret.style.height = `${height}px`;
1095
+ this.dropdown_caret.style.width = `${height}px`;
1096
+ this.dropdown_caret.style.left = `${target_rect.right + 1}px`;
1097
+ this.dropdown_caret.style.top = `${target_rect.bottom - height}px`;
1098
+ this.dropdown_list.style.top = `${target_rect.bottom + 2}px`;
1099
+ this.dropdown_list.style.left = `${target_rect.left + 2}px`;
1100
+ this.dropdown_list.style.minWidth = `${target_rect.width}px`;
1101
+ this.dropdown_list.style.fontSize = (this.scale.toFixed(2) + 'em');
1102
+ this.dropdown_list.textContent = '';
1103
+ for (const value of list) {
1104
+ const entry = this.DOM.Div(undefined, this.dropdown_list);
1105
+ if (current === value) {
1106
+ this.dropdown_selected = entry;
1107
+ entry.classList.add('selected');
1108
+ }
1109
+ // we're attaching random data to DOM nodes here. that works, but
1110
+ // it's sloppy. I think the reason is we want to preserve type, and
1111
+ // this is simpler than any other solution.
1112
+ // (entry as any).dropdown_value = value;
1113
+ entry.dataset.dropdown_value = JSON.stringify(value);
1114
+ entry.textContent = value?.toString() || '';
1115
+ }
1116
+ //this.dropdown_caret.classList.remove('active');
1117
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
1118
+ this.dropdown_caret.style.display = 'block';
1119
+ this.dropdown_caret_visible = true;
1120
+ }
1121
+ ShowSpillBorder(area) {
1122
+ this.spill_border.textContent = '';
1123
+ if (area) {
1124
+ const resolved = new Area(area.start, area.end);
1125
+ let target_rect = this.OffsetCellAddressToRectangle(resolved.start);
1126
+ if (resolved.count > 1) {
1127
+ target_rect = target_rect.Combine(this.OffsetCellAddressToRectangle(resolved.end));
1128
+ }
1129
+ target_rect = target_rect.Shift(this.header_size.width, this.header_size.height);
1130
+ this.spill_border.style.display = 'block';
1131
+ this.spill_border.style.top = (target_rect.top - 5).toString() + 'px';
1132
+ this.spill_border.style.left = (target_rect.left - 5).toString() + 'px';
1133
+ this.spill_border.style.width = (target_rect.width + 10).toString() + 'px';
1134
+ this.spill_border.style.height = (target_rect.height + 10).toString() + 'px';
1135
+ const rect = this.DOM.SVG('rect', undefined, this.spill_border);
1136
+ rect.setAttribute('x', '4.5');
1137
+ rect.setAttribute('y', '4.5');
1138
+ rect.setAttribute('width', (target_rect.width + 1).toString());
1139
+ rect.setAttribute('height', (target_rect.height + 1).toString());
1140
+ }
1141
+ else {
1142
+ this.spill_border.style.display = 'none';
1143
+ }
1144
+ }
1145
+ HideDropdownCaret() {
1146
+ if (this.dropdown_caret_visible) {
1147
+ // this.dropdown_caret.classList.remove('active');
1148
+ this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
1149
+ this.dropdown_caret_visible = false;
1150
+ this.dropdown_caret.style.display = 'none';
1151
+ }
1152
+ }
1153
+ /**
1154
+ * this method returns the scroll offset adjusted for headers.
1155
+ * if you just want the raw scroll offset, use the accessor.
1156
+ *
1157
+ * @param offset_headers
1158
+ * @returns
1159
+ */
1160
+ GetScrollOffset() {
1161
+ return {
1162
+ x: this.scroll_reference_node.scrollLeft + this.header_offset.x,
1163
+ y: this.scroll_reference_node.scrollTop + this.header_offset.y,
1164
+ };
1165
+ }
1166
+ ScrollTo(address, x = true, y = true, smooth = false) {
1167
+ const target_rect = this.CellAddressToRectangle(address);
1168
+ if (smooth && !!this.scroll_reference_node.scrollTo) {
1169
+ const current = {
1170
+ left: this.scroll_reference_node.scrollLeft,
1171
+ top: this.scroll_reference_node.scrollTop,
1172
+ };
1173
+ const options = {
1174
+ left: x ? target_rect.left : current.left,
1175
+ top: y ? target_rect.top : current.top,
1176
+ behavior: 'smooth',
1177
+ };
1178
+ this.scroll_reference_node.scrollTo(options);
1179
+ }
1180
+ else {
1181
+ if (y) {
1182
+ this.scroll_reference_node.scrollTop = target_rect.top;
1183
+ }
1184
+ if (x) {
1185
+ this.scroll_reference_node.scrollLeft = target_rect.left;
1186
+ }
1187
+ }
1188
+ }
1189
+ /**
1190
+ * scroll address into view, at top-left or bottom-right depending on
1191
+ * target and current position. also offsets for frozen rows, columns.
1192
+ */
1193
+ ScrollIntoView(address, smooth = false) {
1194
+ const target_rect = this.CellAddressToRectangle(address);
1195
+ const width = this.scroll_reference_node.clientWidth - this.row_header.offsetWidth;
1196
+ const height = this.scroll_reference_node.clientHeight - this.column_header.offsetHeight;
1197
+ const offset = { x: 0, y: 0 };
1198
+ const lock = { x: false, y: false };
1199
+ const viewport = new Rectangle(this.scroll_reference_node.scrollLeft, this.scroll_reference_node.scrollTop, width, height);
1200
+ // if there are frozen rows/columns, we need to scroll such that the
1201
+ // cell is visible outside of the frozen area. but only if we're *outside*
1202
+ // the frozen area, because otherwise we're on screen essentially by default.
1203
+ if (this.view.active_sheet.freeze.rows || this.view.active_sheet.freeze.columns) {
1204
+ if (this.view.active_sheet.freeze.rows && address.row >= this.view.active_sheet.freeze.rows) {
1205
+ offset.y = this.frozen_row_tiles[0].logical_size.height;
1206
+ }
1207
+ else if (this.view.active_sheet.freeze.rows)
1208
+ lock.y = true;
1209
+ if (this.view.active_sheet.freeze.columns && address.column >= this.view.active_sheet.freeze.columns) {
1210
+ offset.x = this.frozen_column_tiles[0].logical_size.width;
1211
+ }
1212
+ else if (this.view.active_sheet.freeze.columns)
1213
+ lock.x = true;
1214
+ }
1215
+ // NOTE: in theory it's possible we scroll twice, which would result
1216
+ // in two scroll events. however in practice this is called on key events,
1217
+ // so it's unlikely.
1218
+ const options = {
1219
+ behavior: smooth ? 'smooth' : 'auto',
1220
+ };
1221
+ if (address.row !== Infinity) {
1222
+ if (target_rect.top < viewport.top + offset.y && !lock.y) {
1223
+ // this.scroll_reference_node.scrollTop = target_rect.top - offset.y;
1224
+ options.top = target_rect.top - offset.y;
1225
+ }
1226
+ else if (target_rect.bottom > viewport.bottom) {
1227
+ // this.scroll_reference_node.scrollTop = target_rect.bottom - height;
1228
+ options.top = target_rect.bottom - height;
1229
+ }
1230
+ }
1231
+ if (address.column !== Infinity) {
1232
+ if (target_rect.left < viewport.left + offset.x && !lock.x) {
1233
+ // this.scroll_reference_node.scrollLeft = target_rect.left - offset.x;
1234
+ options.left = target_rect.left - offset.x;
1235
+ }
1236
+ else if (target_rect.right > viewport.right) {
1237
+ // this.scroll_reference_node.scrollLeft = target_rect.right - width;
1238
+ options.left = target_rect.right - width;
1239
+ }
1240
+ }
1241
+ this.scroll_reference_node.scrollTo(options);
1242
+ }
1243
+ UpdateTooltip(options = {}) {
1244
+ if (typeof options.text !== 'undefined') {
1245
+ this.tooltip.textContent = options.text;
1246
+ }
1247
+ if (typeof options.x !== 'undefined') {
1248
+ let x = options.x || 0;
1249
+ if (this.tooltip_state === 'up') {
1250
+ x -= this.tooltip.offsetWidth / 2;
1251
+ }
1252
+ this.tooltip.style.left = Math.round(x) + 'px';
1253
+ }
1254
+ if (typeof options.y !== 'undefined') {
1255
+ let y = options.y || 0;
1256
+ if (this.tooltip_state === 'left') {
1257
+ y -= this.tooltip.offsetHeight / 2;
1258
+ }
1259
+ this.tooltip.style.top = Math.round(y) + 'px';
1260
+ }
1261
+ }
1262
+ /**
1263
+ * y coordinate to row header. for consistency we return an address.
1264
+ */
1265
+ CoordinateToRowHeader(y) {
1266
+ const result = { column: Infinity, row: 0 };
1267
+ if (this.view.active_sheet.freeze.rows &&
1268
+ this.frozen_row_tiles[0].pixel_end.y >= y - this.scroll_reference_node.scrollTop) {
1269
+ let height = 0;
1270
+ y -= this.scroll_reference_node.scrollTop;
1271
+ for (let i = 0; i < this.view.active_sheet.freeze.rows; i++) {
1272
+ height += this.RowHeight(i);
1273
+ if (height >= y) {
1274
+ result.row = i;
1275
+ return result;
1276
+ }
1277
+ }
1278
+ }
1279
+ for (const tile of this.row_header_tiles) {
1280
+ if (tile.pixel_end.y >= y) {
1281
+ // now map within the tile
1282
+ let top = y - tile.pixel_start.y;
1283
+ let height = 0;
1284
+ result.row = tile.first_cell.row;
1285
+ for (; result.row <= tile.last_cell.row; result.row++, top -= height) {
1286
+ height = this.RowHeight(result.row);
1287
+ if (height > top) {
1288
+ return result;
1289
+ }
1290
+ }
1291
+ return result;
1292
+ }
1293
+ }
1294
+ return result;
1295
+ }
1296
+ /**
1297
+ * x coordinate to colum header. for consistency we return an address.
1298
+ */
1299
+ CoordinateToColumnHeader(x) {
1300
+ const result = { row: Infinity, column: 0 };
1301
+ if (this.view.active_sheet.freeze.columns &&
1302
+ this.frozen_column_tiles[0].pixel_end.x >= x - this.scroll_reference_node.scrollLeft) {
1303
+ let width = 0;
1304
+ x -= this.scroll_reference_node.scrollLeft;
1305
+ for (let i = 0; i < this.view.active_sheet.freeze.columns; i++) {
1306
+ width += this.ColumnWidth(i);
1307
+ if (width >= x) {
1308
+ result.column = i;
1309
+ return result;
1310
+ }
1311
+ }
1312
+ }
1313
+ for (const tile of this.column_header_tiles) {
1314
+ if (tile.pixel_end.x >= x) {
1315
+ // now map within the tile
1316
+ let left = x - tile.pixel_start.x;
1317
+ let width = 0;
1318
+ result.column = tile.first_cell.column;
1319
+ for (; result.column <= tile.last_cell.column; result.column++, left -= width) {
1320
+ width = this.ColumnWidth(result.column);
1321
+ if (width > left)
1322
+ return result;
1323
+ }
1324
+ return result;
1325
+ }
1326
+ }
1327
+ return result;
1328
+ }
1329
+ /**
1330
+ * point to cell address (grid only)
1331
+ *
1332
+ * FIXME: implement cap_maximum parameter (not sure where we would need it)
1333
+ */
1334
+ PointToAddress_Grid(point, offset_freeze = true) {
1335
+ // offset for freeze pane
1336
+ if (offset_freeze) {
1337
+ if (this.view.active_sheet.freeze.rows) {
1338
+ const frozen_height = this.frozen_row_tiles[0].logical_size.height;
1339
+ if (point.y - this.scroll_reference_node.scrollTop < frozen_height) {
1340
+ point.y -= this.scroll_reference_node.scrollTop;
1341
+ }
1342
+ }
1343
+ if (this.view.active_sheet.freeze.columns) {
1344
+ const frozen_width = this.frozen_column_tiles[0].logical_size.width;
1345
+ if (point.x - this.scroll_reference_node.scrollLeft < frozen_width) {
1346
+ point.x -= this.scroll_reference_node.scrollLeft;
1347
+ }
1348
+ }
1349
+ }
1350
+ // we used to find the containing tile and then calculate row and column.
1351
+ // to support overflow, we now have two separate loops.
1352
+ const result = {
1353
+ row: 0,
1354
+ column: 0,
1355
+ };
1356
+ // FIXME: these could be cached when created
1357
+ const last_column = this.grid_tiles[this.grid_tiles.length - 1];
1358
+ const last_tile = last_column[last_column.length - 1];
1359
+ // ---- find row -----------------------------------------------------------
1360
+ if (point.y > last_tile.pixel_end.y) {
1361
+ // overflow case
1362
+ let top = point.y - last_tile.pixel_end.y;
1363
+ result.row = last_tile.last_cell.row;
1364
+ while (top > 0) {
1365
+ result.row++;
1366
+ top -= this.default_row_height;
1367
+ }
1368
+ }
1369
+ else {
1370
+ // normal behavior
1371
+ for (const cell of last_column) {
1372
+ if (cell.pixel_start.y <= point.y && cell.pixel_end.y >= point.y) {
1373
+ let top = point.y - cell.pixel_start.y;
1374
+ let height = 0;
1375
+ result.row = cell.first_cell.row;
1376
+ for (; result.row <= cell.last_cell.row; result.row++, top -= height) {
1377
+ height = this.RowHeight(result.row);
1378
+ if (height > top) {
1379
+ break;
1380
+ }
1381
+ }
1382
+ break;
1383
+ }
1384
+ }
1385
+ }
1386
+ // ---- find column --------------------------------------------------------
1387
+ if (point.x > last_tile.pixel_end.x) {
1388
+ // overflow case
1389
+ let left = point.x - last_tile.pixel_end.x;
1390
+ result.column = last_tile.last_cell.column;
1391
+ while (left > 0) {
1392
+ result.column++;
1393
+ left -= this.default_column_width;
1394
+ }
1395
+ }
1396
+ else {
1397
+ // normal behavior
1398
+ for (const column of this.grid_tiles) {
1399
+ if (column[0].pixel_start.x <= point.x && column[0].pixel_end.x >= point.x) {
1400
+ const cell = column[0];
1401
+ let left = point.x - cell.pixel_start.x;
1402
+ let width = 0;
1403
+ result.column = cell.first_cell.column;
1404
+ for (; result.column <= cell.last_cell.column; result.column++, left -= width) {
1405
+ width = this.ColumnWidth(result.column);
1406
+ if (width > left) {
1407
+ break;
1408
+ }
1409
+ }
1410
+ break;
1411
+ }
1412
+ }
1413
+ }
1414
+ return result;
1415
+ }
1416
+ /**
1417
+ * get an adjacent tile. this is used by the renderer when a merge or
1418
+ * overflow runs out of the painted tile, and we need to paint it.
1419
+ */
1420
+ AdjacentTile(tile, row_offset = 0, column_offset = 0) {
1421
+ if (!row_offset && !column_offset) {
1422
+ return tile;
1423
+ }
1424
+ const position = tile.tile_position;
1425
+ const row = tile.tile_position.row + row_offset;
1426
+ const column = tile.tile_position.column + column_offset;
1427
+ if (row < 0 || column < 0)
1428
+ return undefined;
1429
+ // check various stores for match
1430
+ if (this.grid_tiles[position.column] && this.grid_tiles[position.column][position.row] === tile) {
1431
+ if (this.grid_tiles[column])
1432
+ return this.grid_tiles[column][row];
1433
+ }
1434
+ if (!position.column && this.frozen_column_tiles[position.row] === tile) {
1435
+ return this.frozen_column_tiles[row];
1436
+ }
1437
+ if (!position.row && this.frozen_row_tiles[position.column] === tile) {
1438
+ return this.frozen_row_tiles[column];
1439
+ }
1440
+ return undefined;
1441
+ }
1442
+ UpdateTiles() {
1443
+ // so the new layout uses variable-sized tiles, which are sized
1444
+ // to a number of rows/columns (FIXME: nearest to a given size?)
1445
+ // that way we don't have to worry about overlap, and resizing
1446
+ // is much easier.
1447
+ // note that this doesn't mean there isn't overlapping rendering,
1448
+ // because there will be on merges.
1449
+ if (!this.container)
1450
+ throw new Error('invalid container');
1451
+ // flush... FIXME: why not reuse? maybe more trouble than it's worth?
1452
+ this.grid_tiles.forEach((arr) => {
1453
+ arr.forEach((tile) => {
1454
+ if (tile.parentElement) {
1455
+ tile.parentElement.removeChild(tile);
1456
+ }
1457
+ });
1458
+ });
1459
+ for (const tileset of [
1460
+ this.column_header_tiles,
1461
+ this.row_header_tiles,
1462
+ this.frozen_row_tiles,
1463
+ this.frozen_column_tiles,
1464
+ ]) {
1465
+ for (const tile of tileset) {
1466
+ if (tile.parentElement) {
1467
+ tile.parentElement.removeChild(tile);
1468
+ }
1469
+ }
1470
+ }
1471
+ /*
1472
+ this.column_header_tiles.forEach((tile) => {
1473
+ if (tile.parentElement) {
1474
+ tile.parentElement.removeChild(tile);
1475
+ }
1476
+ });
1477
+
1478
+ this.row_header_tiles.forEach((tile) => {
1479
+ if (tile.parentElement) {
1480
+ tile.parentElement.removeChild(tile);
1481
+ }
1482
+ });
1483
+ */
1484
+ this.frozen_row_tiles = [];
1485
+ this.frozen_column_tiles = [];
1486
+ this.row_header_tiles = [];
1487
+ this.column_header_tiles = [];
1488
+ this.grid_tiles = [];
1489
+ // update local references (scaled). this has to be done before layout.
1490
+ const sheet = this.view.active_sheet;
1491
+ this.default_row_height = Math.round(sheet.default_row_height * this.scale);
1492
+ this.default_column_width = Math.round(sheet.default_column_width * this.scale);
1493
+ this.header_offset = {
1494
+ x: Math.round(sheet.header_offset.x * this.scale),
1495
+ y: Math.round(sheet.header_offset.y * this.scale),
1496
+ };
1497
+ this.UpdateContainingGrid();
1498
+ let rows = this.view.active_sheet.rows;
1499
+ let columns = this.view.active_sheet.columns;
1500
+ if (!rows)
1501
+ rows = 100;
1502
+ if (!columns)
1503
+ columns = 40;
1504
+ // get total size of the grid from sheet
1505
+ let total_height = 0;
1506
+ let total_width = 0;
1507
+ for (let i = 0; i < rows; i++) {
1508
+ total_height += this.RowHeight(i);
1509
+ }
1510
+ for (let i = 0; i < columns; i++) {
1511
+ total_width += this.ColumnWidth(i);
1512
+ }
1513
+ if (!total_width || !total_height) {
1514
+ throw ('unexpected missing total size');
1515
+ }
1516
+ // console.info(`${rows} rows; total size: ${total_width} x ${total_height}`);
1517
+ if (!total_height)
1518
+ total_height = this.default_row_height * rows;
1519
+ if (!total_width)
1520
+ total_width = this.default_column_width * columns;
1521
+ if (this.container.clientWidth > total_width + this.header_size.width) {
1522
+ const add_columns = Math.ceil((this.container.offsetWidth - total_width) /
1523
+ this.default_column_width);
1524
+ total_width += add_columns * this.default_column_width;
1525
+ columns += add_columns;
1526
+ }
1527
+ this.last_column = columns;
1528
+ // FIXME: header size should be scaled?
1529
+ if (this.container.clientHeight > total_height + this.header_size.height) {
1530
+ const add_rows = Math.ceil((this.container.offsetHeight - total_height) /
1531
+ this.default_row_height);
1532
+ total_height += add_rows * this.default_row_height;
1533
+ rows += add_rows;
1534
+ }
1535
+ // console.info(this.container.offsetWidth, this.container.offsetHeight)
1536
+ // console.info('total size:', total_width, ', ', total_height);
1537
+ // update node sizes to match
1538
+ this.column_header.style.width = this.contents.style.width = `${total_width}px`;
1539
+ this.row_header.style.height = this.contents.style.height = `${total_height}px`;
1540
+ // generate a set of tiles given an approximate target size.
1541
+ // keep track of the tile width/height and the starting column/row.
1542
+ // rows:
1543
+ const tile_widths = [];
1544
+ const tile_columns = [];
1545
+ let tile_width = 0;
1546
+ let tile_column = 0;
1547
+ for (let c = 0; c < columns; c++) {
1548
+ const column_width = this.ColumnWidth(c);
1549
+ if (tile_width && tile_width + column_width > this.default_tile_size.width) {
1550
+ // push and move to the next tile, starting with this column
1551
+ tile_widths.push(tile_width);
1552
+ tile_columns.push(tile_column);
1553
+ // for the next one, start here
1554
+ tile_column = c;
1555
+ tile_width = 0;
1556
+ }
1557
+ tile_width += column_width;
1558
+ }
1559
+ // last one
1560
+ tile_widths.push(tile_width);
1561
+ tile_columns.push(tile_column);
1562
+ // columns:
1563
+ const tile_heights = [];
1564
+ const tile_rows = [];
1565
+ let tile_height = 0;
1566
+ let tile_row = 0;
1567
+ for (let r = 0; r < rows; r++) {
1568
+ const row_height = this.RowHeight(r);
1569
+ if (tile_height && tile_height + row_height > this.default_tile_size.height) {
1570
+ tile_heights.push(tile_height);
1571
+ tile_rows.push(tile_row);
1572
+ tile_height = 0;
1573
+ tile_row = r;
1574
+ }
1575
+ tile_height += row_height;
1576
+ }
1577
+ tile_heights.push(tile_height);
1578
+ tile_rows.push(tile_row);
1579
+ // now create tiles and set metadata
1580
+ const column_tile_count = tile_widths.length;
1581
+ const row_tile_count = tile_heights.length;
1582
+ let pixel_y = 0;
1583
+ let pixel_x = 0;
1584
+ let header_height = 0;
1585
+ let header_width = 0;
1586
+ for (let i = 0; i < this.view.active_sheet.freeze.rows; i++) {
1587
+ header_height += this.RowHeight(i);
1588
+ }
1589
+ for (let i = 0; i < this.view.active_sheet.freeze.columns; i++) {
1590
+ header_width += this.ColumnWidth(i);
1591
+ }
1592
+ for (let c = 0; c < column_tile_count; c++) {
1593
+ const column = [];
1594
+ pixel_y = 0; // reset
1595
+ const column_extent = (c === column_tile_count - 1) ?
1596
+ columns - tile_columns[c] :
1597
+ tile_columns[c + 1] - tile_columns[c];
1598
+ // create a column header tile for this column
1599
+ this.column_header_tiles.push(this.CreateTile('column-header-tile', { height: this.header_offset.y, width: tile_widths[c] }, { row: 0, column: c }, { row: 0, column: tile_columns[c] }, { rows: 0, columns: column_extent }, { x: pixel_x, y: 0 }, this.column_header));
1600
+ // also frozen
1601
+ if (this.view.active_sheet.freeze.rows) {
1602
+ this.frozen_row_tiles.push(this.CreateTile('frozen-row-tile', { height: header_height, width: tile_widths[c] }, { row: 1, column: c }, { row: 0, column: tile_columns[c] }, { rows: 0, columns: column_extent }, { x: pixel_x, y: 0 }, this.column_header));
1603
+ }
1604
+ // loop over rows
1605
+ for (let r = 0; r < row_tile_count; r++) {
1606
+ const row_extent = (r === row_tile_count - 1) ?
1607
+ rows - tile_rows[r] :
1608
+ tile_rows[r + 1] - tile_rows[r];
1609
+ // first column, create header
1610
+ if (!c) {
1611
+ this.row_header_tiles.push(this.CreateTile('row-header-tile', { height: tile_heights[r], width: this.header_offset.x }, { row: r, column: 0 }, { row: tile_rows[r], column: 0 }, // first cell
1612
+ { rows: row_extent, columns: 1 }, { x: 0, y: pixel_y }, this.row_header));
1613
+ // also frozen
1614
+ if (this.view.active_sheet.freeze.columns) {
1615
+ this.frozen_column_tiles.push(this.CreateTile('frozen-column-tile', { height: tile_heights[r], width: header_width }, { row: r, column: 1 }, { row: tile_rows[r], column: 0 }, { rows: row_extent, columns: 1 }, { x: 0, y: pixel_y }, this.row_header));
1616
+ }
1617
+ }
1618
+ column.push(this.CreateTile('grid-tile', { height: tile_heights[r], width: tile_widths[c] }, // tile size in pixels
1619
+ { row: r, column: c }, // grid position
1620
+ { row: tile_rows[r], column: tile_columns[c] }, // first cell
1621
+ { rows: row_extent, columns: column_extent }, // extent
1622
+ { x: pixel_x, y: pixel_y }, this.contents));
1623
+ pixel_y += tile_heights[r];
1624
+ }
1625
+ this.grid_tiles.push(column);
1626
+ pixel_x += tile_widths[c];
1627
+ }
1628
+ this.total_height = total_height;
1629
+ this.total_width = total_width;
1630
+ this.ClearLayoutCaches();
1631
+ this.UpdateGridTemplates(true, true);
1632
+ }
1633
+ ClearLayoutCaches() {
1634
+ this.row_cache = [];
1635
+ this.column_cache = [];
1636
+ }
1637
+ /**
1638
+ * returns the tile index for a given column. this is used to map
1639
+ * a column to a tile in either the header or the grid.
1640
+ * FIXME: speed up w/ lookup cache
1641
+ */
1642
+ TileIndexForColumn(column) {
1643
+ for (const tile of this.column_header_tiles) {
1644
+ if (tile.first_cell.column <= column && tile.last_cell.column >= column) {
1645
+ return tile.tile_position.column;
1646
+ }
1647
+ }
1648
+ return -1;
1649
+ }
1650
+ /**
1651
+ * returns the tile index for a given row. this is used to map
1652
+ * a column to a tile in either the header or the grid.
1653
+ * FIXME: speed up w/ lookup cache
1654
+ */
1655
+ TileIndexForRow(row) {
1656
+ for (const tile of this.row_header_tiles) {
1657
+ if (tile.first_cell.row <= row && tile.last_cell.row >= row) {
1658
+ return tile.tile_position.row;
1659
+ }
1660
+ }
1661
+ return -1;
1662
+ }
1663
+ /**
1664
+ * marks header tiles as dirty, for next repaint call
1665
+ *
1666
+ * UPDATE fix for entire column/row/sheet (the Intersects() method
1667
+ * doesn't support infinities, for some reason: FIX THAT)
1668
+ */
1669
+ DirtyHeaders(area) {
1670
+ if (!area)
1671
+ return;
1672
+ // FIXME: visible only?
1673
+ // why, who cares? render should be based on visible, not dirty
1674
+ for (const tile of this.column_header_tiles) {
1675
+ if (tile.dirty)
1676
+ continue;
1677
+ const test = new Area({ row: area.start.row, column: tile.first_cell.column }, { row: area.start.row, column: tile.last_cell.column });
1678
+ if (area.entire_row || test.Intersects(area)) {
1679
+ tile.dirty = true;
1680
+ }
1681
+ }
1682
+ for (const tile of this.row_header_tiles) {
1683
+ if (tile.dirty)
1684
+ continue;
1685
+ const test = new Area({ column: area.start.column, row: tile.first_cell.row }, { column: area.start.column, row: tile.last_cell.row });
1686
+ if (area.entire_column || test.Intersects(area)) {
1687
+ tile.dirty = true;
1688
+ }
1689
+ }
1690
+ }
1691
+ DirtyAll() {
1692
+ for (const column of this.grid_tiles) {
1693
+ for (const tile of column) {
1694
+ tile.dirty = true;
1695
+ }
1696
+ }
1697
+ }
1698
+ DirtyArea(areas) {
1699
+ if (!this.initialized)
1700
+ return;
1701
+ if (!Array.isArray(areas)) {
1702
+ areas = [areas];
1703
+ }
1704
+ for (const area of areas) {
1705
+ const start = { row: 0, column: 0 };
1706
+ const end = { row: this.grid_tiles[0].length - 1, column: this.grid_tiles.length - 1 };
1707
+ if (area.start.column !== Infinity) {
1708
+ start.column = end.column = this.TileIndexForColumn(area.start.column);
1709
+ if (area.end.column !== area.start.column)
1710
+ end.column = this.TileIndexForColumn(area.end.column);
1711
+ }
1712
+ if (area.start.row !== Infinity) {
1713
+ start.row = end.row = this.TileIndexForRow(area.start.row);
1714
+ if (area.end.row !== area.start.row)
1715
+ end.row = this.TileIndexForRow(area.end.row);
1716
+ }
1717
+ for (let column = start.column; column <= end.column; column++) {
1718
+ for (let row = start.row; row <= end.row; row++) {
1719
+ this.grid_tiles[column][row].dirty = true;
1720
+ }
1721
+ }
1722
+ }
1723
+ }
1724
+ /** calculate first visible tile based on scroll position */
1725
+ VisibleTiles() {
1726
+ const tiles = [{ row: 0, column: 0 }, { row: 0, column: 0 }];
1727
+ if (!this.container || !this.grid_tiles.length || !this.grid_tiles[0].length) {
1728
+ return new TileRange(tiles[0], tiles[1]); // too much?
1729
+ }
1730
+ const left = this.scroll_reference_node.scrollLeft;
1731
+ const right = left + this.scroll_reference_node.offsetWidth;
1732
+ const top = this.scroll_reference_node.scrollTop;
1733
+ const bottom = top + this.scroll_reference_node.offsetHeight;
1734
+ for (const column of this.grid_tiles) {
1735
+ let cell = column[0];
1736
+ if (cell.pixel_start.x <= left && cell.pixel_end.x >= left) {
1737
+ for (cell of column) {
1738
+ if (cell.pixel_start.y <= top && cell.pixel_end.y >= top) {
1739
+ tiles[0] = cell.tile_position;
1740
+ break;
1741
+ }
1742
+ }
1743
+ }
1744
+ if (column === this.grid_tiles[this.grid_tiles.length - 1] ||
1745
+ cell.pixel_start.x <= right && cell.pixel_end.x >= right) {
1746
+ for (cell of column) {
1747
+ if (cell === column[column.length - 1] ||
1748
+ cell.pixel_start.y <= bottom && cell.pixel_end.y >= bottom) {
1749
+ tiles[1] = cell.tile_position;
1750
+ // return tiles;
1751
+ return new TileRange(tiles[0], tiles[1]); // too much?
1752
+ }
1753
+ }
1754
+ }
1755
+ }
1756
+ // return tiles;
1757
+ return new TileRange(tiles[0], tiles[1]); // too much?
1758
+ }
1759
+ UpdateTileHeights(mark_dirty = true, start_row = -1) {
1760
+ let y = 0;
1761
+ for (let i = 0; i < this.row_header_tiles.length; i++) {
1762
+ const tile = this.row_header_tiles[i];
1763
+ // const column = this.grid_tiles[i];
1764
+ if (start_row > tile.last_cell.row) {
1765
+ y += tile.logical_size.height;
1766
+ continue;
1767
+ }
1768
+ let height = 0;
1769
+ for (let r = tile.first_cell.row; r <= tile.last_cell.row; r++) {
1770
+ height += this.RowHeight(r);
1771
+ }
1772
+ const height_match = (tile.logical_size.height === height);
1773
+ tile.pixel_start.y = y;
1774
+ y += height;
1775
+ tile.pixel_end.y = y;
1776
+ if (!height_match) {
1777
+ tile.logical_size.height = height;
1778
+ tile.style.height = `${height}px`;
1779
+ tile.height = this.dpr * height;
1780
+ if (this.view.active_sheet.freeze.columns) {
1781
+ const frozen_tile = this.frozen_column_tiles[i];
1782
+ frozen_tile.logical_size.height = height;
1783
+ frozen_tile.style.height = `${height}px`;
1784
+ frozen_tile.height = this.dpr * height;
1785
+ }
1786
+ if (mark_dirty) {
1787
+ tile.dirty = true;
1788
+ tile.needs_full_repaint = true;
1789
+ }
1790
+ }
1791
+ for (const column of this.grid_tiles) {
1792
+ const grid_tile = column[i];
1793
+ grid_tile.pixel_start.y = tile.pixel_start.y;
1794
+ grid_tile.pixel_end.y = tile.pixel_end.y;
1795
+ if (!height_match) {
1796
+ grid_tile.logical_size.height = height;
1797
+ grid_tile.style.height = `${height}px`;
1798
+ grid_tile.height = this.dpr * height;
1799
+ if (mark_dirty) {
1800
+ grid_tile.dirty = true;
1801
+ grid_tile.needs_full_repaint = true;
1802
+ }
1803
+ }
1804
+ }
1805
+ }
1806
+ if (this.view.active_sheet.freeze.rows) {
1807
+ let freeze_height = 0;
1808
+ for (let i = 0; i < this.view.active_sheet.freeze.rows; i++)
1809
+ freeze_height += this.RowHeight(i);
1810
+ for (const tile of this.frozen_row_tiles) {
1811
+ tile.style.height = `${freeze_height}px`;
1812
+ tile.height = freeze_height * this.dpr;
1813
+ }
1814
+ // corner includes header size
1815
+ freeze_height += this.header_offset.y;
1816
+ this.corner_canvas.style.height = `${freeze_height}px`;
1817
+ this.corner_canvas.height = freeze_height * this.dpr;
1818
+ // mark these as dirty so we get painted
1819
+ for (const column of this.grid_tiles) {
1820
+ column[0].dirty = true;
1821
+ }
1822
+ }
1823
+ this.UpdateGridTemplates(false, true);
1824
+ this.row_header.style.height = this.contents.style.height = `${y}px`;
1825
+ this.ClearLayoutCaches();
1826
+ }
1827
+ /**
1828
+ * update all widths. start_column is a hint that can save some work
1829
+ * by skipping unaffected tiles
1830
+ */
1831
+ UpdateTileWidths(mark_dirty = true, start_column = -1) {
1832
+ let x = 0;
1833
+ for (let i = 0; i < this.column_header_tiles.length; i++) {
1834
+ const tile = this.column_header_tiles[i];
1835
+ const column = this.grid_tiles[i];
1836
+ if (start_column > tile.last_cell.column) {
1837
+ x += tile.logical_size.width;
1838
+ continue;
1839
+ }
1840
+ let width = 0;
1841
+ for (let c = tile.first_cell.column; c <= tile.last_cell.column; c++) {
1842
+ width += this.ColumnWidth(c);
1843
+ }
1844
+ const width_match = (tile.logical_size.width === width);
1845
+ tile.pixel_start.x = x;
1846
+ x += width;
1847
+ tile.pixel_end.x = x;
1848
+ if (!width_match) {
1849
+ tile.logical_size.width = width;
1850
+ tile.style.width = `${width}px`;
1851
+ tile.width = this.dpr * width;
1852
+ if (this.view.active_sheet.freeze.rows) {
1853
+ const frozen_tile = this.frozen_row_tiles[i];
1854
+ frozen_tile.logical_size.width = width;
1855
+ frozen_tile.style.width = `${width}px`;
1856
+ frozen_tile.width = this.dpr * width;
1857
+ }
1858
+ if (mark_dirty) {
1859
+ tile.dirty = true;
1860
+ tile.needs_full_repaint = true;
1861
+ }
1862
+ }
1863
+ for (const grid_tile of column) {
1864
+ grid_tile.pixel_start.x = tile.pixel_start.x;
1865
+ grid_tile.pixel_end.x = tile.pixel_end.x;
1866
+ if (!width_match) {
1867
+ grid_tile.logical_size.width = width;
1868
+ grid_tile.style.width = `${width}px`;
1869
+ grid_tile.width = this.dpr * width;
1870
+ if (mark_dirty) {
1871
+ grid_tile.dirty = true;
1872
+ grid_tile.needs_full_repaint = true;
1873
+ }
1874
+ }
1875
+ }
1876
+ }
1877
+ if (this.view.active_sheet.freeze.columns) {
1878
+ let freeze_width = 0;
1879
+ for (let i = 0; i < this.view.active_sheet.freeze.columns; i++)
1880
+ freeze_width += this.ColumnWidth(i);
1881
+ for (const tile of this.frozen_column_tiles) {
1882
+ tile.style.width = `${freeze_width}px`;
1883
+ tile.width = freeze_width * this.dpr;
1884
+ }
1885
+ // corner includes header size
1886
+ freeze_width += this.header_offset.x;
1887
+ this.corner_canvas.style.width = `${freeze_width}px`;
1888
+ this.corner_canvas.width = freeze_width * this.dpr;
1889
+ // mark these as dirty so we get painted
1890
+ for (const tile of this.grid_tiles[0]) {
1891
+ tile.dirty = true;
1892
+ }
1893
+ }
1894
+ this.UpdateGridTemplates(true, false);
1895
+ this.column_header.style.width = this.contents.style.width = `${x}px`;
1896
+ this.ClearLayoutCaches();
1897
+ }
1898
+ ClampToGrid(point) {
1899
+ const address = this.PointToAddress_Grid(point);
1900
+ const rect = this.OffsetCellAddressToRectangle(address);
1901
+ if (point.x > rect.left + rect.width / 2) {
1902
+ point.x = rect.left + rect.width - 1;
1903
+ }
1904
+ else {
1905
+ point.x = rect.left - 1;
1906
+ }
1907
+ if (point.y > rect.top + rect.height / 2) {
1908
+ point.y = rect.top + rect.height - 1;
1909
+ }
1910
+ else {
1911
+ point.y = rect.top - 1;
1912
+ }
1913
+ return point;
1914
+ }
1915
+ /**
1916
+ * wrapper method for CellAddressToRectangle allows us to offset for
1917
+ * frozen rows/columns. in some cases we may not want to do this, so
1918
+ * the underlying method is still visible (and cache contains the raw
1919
+ * rectangles, not offset).
1920
+ */
1921
+ OffsetCellAddressToRectangle(address) {
1922
+ let rect = this.CellAddressToRectangle(address);
1923
+ if (address.column >= 0 && address.column < this.view.active_sheet.freeze.columns) {
1924
+ rect = rect.Shift(this.scroll_reference_node.scrollLeft, 0);
1925
+ }
1926
+ if (address.row >= 0 && address.row < this.view.active_sheet.freeze.rows) {
1927
+ rect = rect.Shift(0, this.scroll_reference_node.scrollTop);
1928
+ }
1929
+ return rect;
1930
+ }
1931
+ /**
1932
+ * finds the rectangle, in the coordinate space of the grid node,
1933
+ * of the cell with the given address. uses a cache since we wind
1934
+ * up looking up the same rectangles a lot.
1935
+ *
1936
+ * UPDATE dropping rectangle cache in favor of holding row and
1937
+ * column edges. I realized we were holding a lot of redundant
1938
+ * information, and this should be resonably fast.
1939
+ *
1940
+ * TODO could probably be slightly more efficient by holding the
1941
+ * left edge of the column/row at the index; then we don't have to
1942
+ * have special behavior for column/row 0.
1943
+ */
1944
+ CellAddressToRectangle(address) {
1945
+ // limit
1946
+ const row = address.row === Infinity || address.row < 0 ? 0 : address.row;
1947
+ const column = address.column === Infinity || address.column < 0 ? 0 : address.column;
1948
+ // build out the caches if necessary
1949
+ if (this.column_cache.length <= column + 1) {
1950
+ if (!this.column_cache.length) {
1951
+ this.column_cache[0] = 0;
1952
+ }
1953
+ for (let i = this.column_cache.length - 1; i <= column; i++) {
1954
+ this.column_cache[i + 1] = this.column_cache[i] + this.ColumnWidth(i);
1955
+ }
1956
+ }
1957
+ if (this.row_cache.length <= row + 1) {
1958
+ if (!this.row_cache.length) {
1959
+ this.row_cache[0] = 0;
1960
+ }
1961
+ for (let i = this.row_cache.length - 1; i <= row; i++) {
1962
+ this.row_cache[i + 1] = this.row_cache[i] + this.RowHeight(i);
1963
+ }
1964
+ }
1965
+ // now we can construct the rectangle
1966
+ const left = this.column_cache[column];
1967
+ const top = this.row_cache[row];
1968
+ return new Rectangle(left, top, this.column_cache[column + 1] - left, this.row_cache[row + 1] - top);
1969
+ }
1970
+ /**
1971
+ * resizes the tile at this index, rebuilds structure of subsequent tiles.
1972
+ * this is necessary because tiles keep track of pixel position: so if a
1973
+ * tile is resized, subsequent tiles have to change.
1974
+ */
1975
+ ResizeTileWidth(index, width, mark_dirty = true) {
1976
+ // start with headers...
1977
+ let tile = this.column_header_tiles[index];
1978
+ const delta = width - tile.logical_size.width;
1979
+ tile.logical_size.width = width;
1980
+ tile.style.width = `${width}px`;
1981
+ tile.width = this.dpr * width;
1982
+ tile.pixel_end.x += delta;
1983
+ if (mark_dirty) {
1984
+ tile.dirty = true;
1985
+ tile.needs_full_repaint = true;
1986
+ }
1987
+ for (let i = index + 1; i < this.column_header_tiles.length; i++) {
1988
+ this.column_header_tiles[i].pixel_start.x += delta;
1989
+ this.column_header_tiles[i].pixel_end.x += delta;
1990
+ for (const cell of this.grid_tiles[i]) {
1991
+ cell.pixel_start.x += delta;
1992
+ cell.pixel_end.x += delta;
1993
+ }
1994
+ }
1995
+ const column = this.grid_tiles[index];
1996
+ for (tile of column) {
1997
+ tile.logical_size.width = width;
1998
+ tile.style.width = `${width}px`;
1999
+ tile.width = this.dpr * width;
2000
+ tile.pixel_end.x += delta;
2001
+ if (mark_dirty) {
2002
+ tile.dirty = true;
2003
+ tile.needs_full_repaint = true;
2004
+ }
2005
+ }
2006
+ this.UpdateTotalSize();
2007
+ this.UpdateGridTemplates(true, false);
2008
+ this.UpdateContentsSize();
2009
+ }
2010
+ /**
2011
+ * resizes the tile at this index, rebuilds structure of subsequent tiles
2012
+ */
2013
+ ResizeTileHeight(index, height, mark_dirty = true) {
2014
+ // start with headers...
2015
+ let tile = this.row_header_tiles[index];
2016
+ const delta = height - tile.logical_size.height;
2017
+ tile.logical_size.height = height;
2018
+ tile.style.height = `${height}px`;
2019
+ tile.height = this.dpr * height;
2020
+ tile.pixel_end.y += delta;
2021
+ if (mark_dirty) {
2022
+ tile.dirty = true;
2023
+ tile.needs_full_repaint = true;
2024
+ }
2025
+ for (let r = index + 1; r < this.row_header_tiles.length; r++) {
2026
+ tile = this.row_header_tiles[r];
2027
+ tile.pixel_start.y += delta;
2028
+ tile.pixel_end.y += delta;
2029
+ }
2030
+ for (const column of this.grid_tiles) {
2031
+ tile = column[index];
2032
+ tile.logical_size.height = height;
2033
+ tile.style.height = `${height}px`;
2034
+ tile.height = this.dpr * height;
2035
+ tile.pixel_end.y += delta;
2036
+ if (mark_dirty) {
2037
+ tile.dirty = true;
2038
+ tile.needs_full_repaint = true;
2039
+ }
2040
+ for (let i = index + 1; i < column.length; i++) {
2041
+ column[i].pixel_start.y += delta;
2042
+ column[i].pixel_end.y += delta;
2043
+ }
2044
+ }
2045
+ this.UpdateTotalSize();
2046
+ this.UpdateGridTemplates(false, true);
2047
+ this.UpdateContentsSize();
2048
+ }
2049
+ }
2050
+ //# sourceMappingURL=base_layout.js.map