@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.
- package/api-config.json +1 -1
- package/build/package.json +119 -0
- package/build/treb-base-types/src/api_types.d.ts +11 -0
- package/build/treb-base-types/src/api_types.js +22 -0
- package/build/treb-base-types/src/api_types.js.map +1 -0
- package/build/treb-base-types/src/area-utils.d.ts +9 -0
- package/build/treb-base-types/src/area-utils.js +50 -0
- package/build/treb-base-types/src/area-utils.js.map +1 -0
- package/build/treb-base-types/src/area.d.ts +182 -0
- package/build/treb-base-types/src/area.js +715 -0
- package/build/treb-base-types/src/area.js.map +1 -0
- package/build/treb-base-types/src/basic_types.d.ts +20 -0
- package/build/treb-base-types/src/basic_types.js +22 -0
- package/build/treb-base-types/src/basic_types.js.map +1 -0
- package/build/treb-base-types/src/cell.d.ts +167 -0
- package/build/treb-base-types/src/cell.js +432 -0
- package/build/treb-base-types/src/cell.js.map +1 -0
- package/build/treb-base-types/src/cells.d.ts +251 -0
- package/build/treb-base-types/src/cells.js +1136 -0
- package/build/treb-base-types/src/cells.js.map +1 -0
- package/build/treb-base-types/src/color.d.ts +35 -0
- package/build/treb-base-types/src/color.js +162 -0
- package/build/treb-base-types/src/color.js.map +1 -0
- package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
- package/build/treb-base-types/src/dom-utilities.js +144 -0
- package/build/treb-base-types/src/dom-utilities.js.map +1 -0
- package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
- package/build/treb-base-types/src/evaluate-options.js +22 -0
- package/build/treb-base-types/src/evaluate-options.js.map +1 -0
- package/build/treb-base-types/src/font-stack.d.ts +37 -0
- package/build/treb-base-types/src/font-stack.js +93 -0
- package/build/treb-base-types/src/font-stack.js.map +1 -0
- package/build/treb-base-types/src/gradient.d.ts +18 -0
- package/build/treb-base-types/src/gradient.js +86 -0
- package/build/treb-base-types/src/gradient.js.map +1 -0
- package/build/treb-base-types/src/import.d.ts +48 -0
- package/build/treb-base-types/src/import.js +22 -0
- package/build/treb-base-types/src/import.js.map +1 -0
- package/build/treb-base-types/src/index-standalone.d.ts +6 -0
- package/build/treb-base-types/src/index-standalone.js +27 -0
- package/build/treb-base-types/src/index-standalone.js.map +1 -0
- package/build/treb-base-types/src/index.d.ts +22 -0
- package/build/treb-base-types/src/index.js +45 -0
- package/build/treb-base-types/src/index.js.map +1 -0
- package/build/treb-base-types/src/layout.d.ts +22 -0
- package/build/treb-base-types/src/layout.js +22 -0
- package/build/treb-base-types/src/layout.js.map +1 -0
- package/build/treb-base-types/src/localization.d.ts +37 -0
- package/build/treb-base-types/src/localization.js +157 -0
- package/build/treb-base-types/src/localization.js.map +1 -0
- package/build/treb-base-types/src/rectangle.d.ts +51 -0
- package/build/treb-base-types/src/rectangle.js +123 -0
- package/build/treb-base-types/src/rectangle.js.map +1 -0
- package/build/treb-base-types/src/render_text.d.ts +34 -0
- package/build/treb-base-types/src/render_text.js +22 -0
- package/build/treb-base-types/src/render_text.js.map +1 -0
- package/build/treb-base-types/src/style.d.ts +214 -0
- package/build/treb-base-types/src/style.js +373 -0
- package/build/treb-base-types/src/style.js.map +1 -0
- package/build/treb-base-types/src/table.d.ts +58 -0
- package/build/treb-base-types/src/table.js +27 -0
- package/build/treb-base-types/src/table.js.map +1 -0
- package/build/treb-base-types/src/text_part.d.ts +26 -0
- package/build/treb-base-types/src/text_part.js +47 -0
- package/build/treb-base-types/src/text_part.js.map +1 -0
- package/build/treb-base-types/src/theme.d.ts +120 -0
- package/build/treb-base-types/src/theme.js +460 -0
- package/build/treb-base-types/src/theme.js.map +1 -0
- package/build/treb-base-types/src/union.d.ts +73 -0
- package/build/treb-base-types/src/union.js +61 -0
- package/build/treb-base-types/src/union.js.map +1 -0
- package/build/treb-base-types/src/value-type.d.ts +86 -0
- package/build/treb-base-types/src/value-type.js +168 -0
- package/build/treb-base-types/src/value-type.js.map +1 -0
- package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
- package/build/treb-base-types/src/worker-proxy.js +221 -0
- package/build/treb-base-types/src/worker-proxy.js.map +1 -0
- package/build/treb-calculator/src/calculator.d.ts +249 -0
- package/build/treb-calculator/src/calculator.js +2755 -0
- package/build/treb-calculator/src/calculator.js.map +1 -0
- package/build/treb-calculator/src/complex-math.d.ts +75 -0
- package/build/treb-calculator/src/complex-math.js +559 -0
- package/build/treb-calculator/src/complex-math.js.map +1 -0
- package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
- package/build/treb-calculator/src/dag/array-vertex.js +156 -0
- package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/graph.d.ts +134 -0
- package/build/treb-calculator/src/dag/graph.js +842 -0
- package/build/treb-calculator/src/dag/graph.js.map +1 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
- package/build/treb-calculator/src/dag/vertex.js +274 -0
- package/build/treb-calculator/src/dag/vertex.js.map +1 -0
- package/build/treb-calculator/src/descriptors.d.ts +189 -0
- package/build/treb-calculator/src/descriptors.js +22 -0
- package/build/treb-calculator/src/descriptors.js.map +1 -0
- package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
- package/build/treb-calculator/src/expression-calculator.js +1033 -0
- package/build/treb-calculator/src/expression-calculator.js.map +1 -0
- package/build/treb-calculator/src/function-error.d.ts +35 -0
- package/build/treb-calculator/src/function-error.js +85 -0
- package/build/treb-calculator/src/function-error.js.map +1 -0
- package/build/treb-calculator/src/function-library.d.ts +22 -0
- package/build/treb-calculator/src/function-library.js +96 -0
- package/build/treb-calculator/src/function-library.js.map +1 -0
- package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
- package/build/treb-calculator/src/functions/base-functions.js +2611 -0
- package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/beta.d.ts +17 -0
- package/build/treb-calculator/src/functions/beta.js +201 -0
- package/build/treb-calculator/src/functions/beta.js.map +1 -0
- package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
- package/build/treb-calculator/src/functions/checkbox.js +128 -0
- package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
- package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/complex-functions.js +217 -0
- package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
- package/build/treb-calculator/src/functions/date-utils.js +59 -0
- package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
- package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/finance-functions.js +547 -0
- package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/fp.d.ts +2 -0
- package/build/treb-calculator/src/functions/fp.js +463 -0
- package/build/treb-calculator/src/functions/fp.js.map +1 -0
- package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
- package/build/treb-calculator/src/functions/function-utilities.js +36 -0
- package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
- package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
- package/build/treb-calculator/src/functions/gamma.js +142 -0
- package/build/treb-calculator/src/functions/gamma.js.map +1 -0
- package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/information-functions.js +71 -0
- package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
- package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
- package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/normal.d.ts +2 -0
- package/build/treb-calculator/src/functions/normal.js +32 -0
- package/build/treb-calculator/src/functions/normal.js.map +1 -0
- package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/regex-functions.js +188 -0
- package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
- package/build/treb-calculator/src/functions/sparkline.js +264 -0
- package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
- package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
- package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
- package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
- package/build/treb-calculator/src/functions/students-t.js +64 -0
- package/build/treb-calculator/src/functions/students-t.js.map +1 -0
- package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
- package/build/treb-calculator/src/functions/text-functions.js +320 -0
- package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
- package/build/treb-calculator/src/index.d.ts +2 -0
- package/build/treb-calculator/src/index.js +22 -0
- package/build/treb-calculator/src/index.js.map +1 -0
- package/build/treb-calculator/src/notifier-types.d.ts +26 -0
- package/build/treb-calculator/src/notifier-types.js +22 -0
- package/build/treb-calculator/src/notifier-types.js.map +1 -0
- package/build/treb-calculator/src/primitives.d.ts +15 -0
- package/build/treb-calculator/src/primitives.js +398 -0
- package/build/treb-calculator/src/primitives.js.map +1 -0
- package/build/treb-calculator/src/utilities.d.ts +68 -0
- package/build/treb-calculator/src/utilities.js +324 -0
- package/build/treb-calculator/src/utilities.js.map +1 -0
- package/build/treb-charts/src/chart-functions.d.ts +8 -0
- package/build/treb-charts/src/chart-functions.js +209 -0
- package/build/treb-charts/src/chart-functions.js.map +1 -0
- package/build/treb-charts/src/chart-types.d.ts +233 -0
- package/build/treb-charts/src/chart-types.js +57 -0
- package/build/treb-charts/src/chart-types.js.map +1 -0
- package/build/treb-charts/src/chart-utils.d.ts +106 -0
- package/build/treb-charts/src/chart-utils.js +1060 -0
- package/build/treb-charts/src/chart-utils.js.map +1 -0
- package/build/treb-charts/src/chart.d.ts +23 -0
- package/build/treb-charts/src/chart.js +94 -0
- package/build/treb-charts/src/chart.js.map +1 -0
- package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
- package/build/treb-charts/src/default-chart-renderer.js +533 -0
- package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
- package/build/treb-charts/src/index.d.ts +5 -0
- package/build/treb-charts/src/index.js +24 -0
- package/build/treb-charts/src/index.js.map +1 -0
- package/build/treb-charts/src/main.d.ts +1 -0
- package/build/treb-charts/src/main.js +34 -0
- package/build/treb-charts/src/main.js.map +1 -0
- package/build/treb-charts/src/quicksort.d.ts +1 -0
- package/build/treb-charts/src/quicksort.js +49 -0
- package/build/treb-charts/src/quicksort.js.map +1 -0
- package/build/treb-charts/src/rectangle.d.ts +18 -0
- package/build/treb-charts/src/rectangle.js +41 -0
- package/build/treb-charts/src/rectangle.js.map +1 -0
- package/build/treb-charts/src/renderer-type.d.ts +24 -0
- package/build/treb-charts/src/renderer-type.js +22 -0
- package/build/treb-charts/src/renderer-type.js.map +1 -0
- package/build/treb-charts/src/renderer.d.ts +127 -0
- package/build/treb-charts/src/renderer.js +1518 -0
- package/build/treb-charts/src/renderer.js.map +1 -0
- package/build/treb-charts/src/util.d.ts +18 -0
- package/build/treb-charts/src/util.js +71 -0
- package/build/treb-charts/src/util.js.map +1 -0
- package/build/treb-data-model/src/annotation.d.ts +167 -0
- package/build/treb-data-model/src/annotation.js +120 -0
- package/build/treb-data-model/src/annotation.js.map +1 -0
- package/build/treb-data-model/src/conditional_format.d.ts +155 -0
- package/build/treb-data-model/src/conditional_format.js +62 -0
- package/build/treb-data-model/src/conditional_format.js.map +1 -0
- package/build/treb-data-model/src/data-validation.d.ts +28 -0
- package/build/treb-data-model/src/data-validation.js +22 -0
- package/build/treb-data-model/src/data-validation.js.map +1 -0
- package/build/treb-data-model/src/data_model.d.ts +173 -0
- package/build/treb-data-model/src/data_model.js +637 -0
- package/build/treb-data-model/src/data_model.js.map +1 -0
- package/build/treb-data-model/src/index.d.ts +13 -0
- package/build/treb-data-model/src/index.js +28 -0
- package/build/treb-data-model/src/index.js.map +1 -0
- package/build/treb-data-model/src/language-model.d.ts +22 -0
- package/build/treb-data-model/src/language-model.js +22 -0
- package/build/treb-data-model/src/language-model.js.map +1 -0
- package/build/treb-data-model/src/named.d.ts +124 -0
- package/build/treb-data-model/src/named.js +372 -0
- package/build/treb-data-model/src/named.js.map +1 -0
- package/build/treb-data-model/src/serialize_options.d.ts +49 -0
- package/build/treb-data-model/src/serialize_options.js +22 -0
- package/build/treb-data-model/src/serialize_options.js.map +1 -0
- package/build/treb-data-model/src/sheet.d.ts +499 -0
- package/build/treb-data-model/src/sheet.js +2904 -0
- package/build/treb-data-model/src/sheet.js.map +1 -0
- package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
- package/build/treb-data-model/src/sheet_collection.js +112 -0
- package/build/treb-data-model/src/sheet_collection.js.map +1 -0
- package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
- package/build/treb-data-model/src/sheet_selection.js +39 -0
- package/build/treb-data-model/src/sheet_selection.js.map +1 -0
- package/build/treb-data-model/src/sheet_types.d.ts +104 -0
- package/build/treb-data-model/src/sheet_types.js +22 -0
- package/build/treb-data-model/src/sheet_types.js.map +1 -0
- package/build/treb-data-model/src/types.d.ts +59 -0
- package/build/treb-data-model/src/types.js +22 -0
- package/build/treb-data-model/src/types.js.map +1 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
- package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
- package/build/treb-embed/src/custom-element/treb-global.js +64 -0
- package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
- package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
- package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
- package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
- package/build/treb-embed/src/index.d.ts +12 -0
- package/build/treb-embed/src/index.js +34 -0
- package/build/treb-embed/src/index.js.map +1 -0
- package/build/treb-embed/src/options.d.ts +266 -0
- package/build/treb-embed/src/options.js +56 -0
- package/build/treb-embed/src/options.js.map +1 -0
- package/build/treb-embed/src/plugin.d.ts +9 -0
- package/build/treb-embed/src/plugin.js +22 -0
- package/build/treb-embed/src/plugin.js.map +1 -0
- package/build/treb-embed/src/progress-dialog.d.ts +49 -0
- package/build/treb-embed/src/progress-dialog.js +178 -0
- package/build/treb-embed/src/progress-dialog.js.map +1 -0
- package/build/treb-embed/src/selection-state.d.ts +15 -0
- package/build/treb-embed/src/selection-state.js +22 -0
- package/build/treb-embed/src/selection-state.js.map +1 -0
- package/build/treb-embed/src/spinner.d.ts +8 -0
- package/build/treb-embed/src/spinner.js +40 -0
- package/build/treb-embed/src/spinner.js.map +1 -0
- package/build/treb-embed/src/toolbar-message.d.ts +72 -0
- package/build/treb-embed/src/toolbar-message.js +22 -0
- package/build/treb-embed/src/toolbar-message.js.map +1 -0
- package/build/treb-embed/src/types.d.ts +185 -0
- package/build/treb-embed/src/types.js +45 -0
- package/build/treb-embed/src/types.js.map +1 -0
- package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
- package/build/treb-export/src/address-type.d.ts +34 -0
- package/build/treb-export/src/address-type.js +53 -0
- package/build/treb-export/src/address-type.js.map +1 -0
- package/build/treb-export/src/base-template.d.ts +1 -0
- package/build/treb-export/src/base-template.js +22 -0
- package/build/treb-export/src/base-template.js.map +1 -0
- package/build/treb-export/src/column-width.d.ts +2 -0
- package/build/treb-export/src/column-width.js +80 -0
- package/build/treb-export/src/column-width.js.map +1 -0
- package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
- package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
- package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
- package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
- package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
- package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
- package/build/treb-export/src/drawing/chart.d.ts +26 -0
- package/build/treb-export/src/drawing/chart.js +247 -0
- package/build/treb-export/src/drawing/chart.js.map +1 -0
- package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
- package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
- package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
- package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
- package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
- package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
- package/build/treb-export/src/drawing/drawing.d.ts +49 -0
- package/build/treb-export/src/drawing/drawing.js +193 -0
- package/build/treb-export/src/drawing/drawing.js.map +1 -0
- package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
- package/build/treb-export/src/drawing/embedded-image.js +54 -0
- package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
- package/build/treb-export/src/export.d.ts +72 -0
- package/build/treb-export/src/export.js +2039 -0
- package/build/treb-export/src/export.js.map +1 -0
- package/build/treb-export/src/import-export-messages.d.ts +31 -0
- package/build/treb-export/src/import-export-messages.js +22 -0
- package/build/treb-export/src/import-export-messages.js.map +1 -0
- package/build/treb-export/src/import.d.ts +33 -0
- package/build/treb-export/src/import.js +1258 -0
- package/build/treb-export/src/import.js.map +1 -0
- package/build/treb-export/src/index.worker.d.ts +1 -0
- package/build/treb-export/src/index.worker.js +93 -0
- package/build/treb-export/src/index.worker.js.map +1 -0
- package/build/treb-export/src/metadata.d.ts +51 -0
- package/build/treb-export/src/metadata.js +153 -0
- package/build/treb-export/src/metadata.js.map +1 -0
- package/build/treb-export/src/ooxml.d.ts +7 -0
- package/build/treb-export/src/ooxml.js +41 -0
- package/build/treb-export/src/ooxml.js.map +1 -0
- package/build/treb-export/src/relationship.d.ts +8 -0
- package/build/treb-export/src/relationship.js +27 -0
- package/build/treb-export/src/relationship.js.map +1 -0
- package/build/treb-export/src/shared-strings.d.ts +11 -0
- package/build/treb-export/src/shared-strings.js +105 -0
- package/build/treb-export/src/shared-strings.js.map +1 -0
- package/build/treb-export/src/template-2.d.ts +1 -0
- package/build/treb-export/src/template-2.js +22 -0
- package/build/treb-export/src/template-2.js.map +1 -0
- package/build/treb-export/src/unescape_xml.d.ts +1 -0
- package/build/treb-export/src/unescape_xml.js +61 -0
- package/build/treb-export/src/unescape_xml.js.map +1 -0
- package/build/treb-export/src/workbook-sheet.d.ts +75 -0
- package/build/treb-export/src/workbook-sheet.js +128 -0
- package/build/treb-export/src/workbook-sheet.js.map +1 -0
- package/build/treb-export/src/workbook-style.d.ts +110 -0
- package/build/treb-export/src/workbook-style.js +1134 -0
- package/build/treb-export/src/workbook-style.js.map +1 -0
- package/build/treb-export/src/workbook-theme.d.ts +13 -0
- package/build/treb-export/src/workbook-theme.js +85 -0
- package/build/treb-export/src/workbook-theme.js.map +1 -0
- package/build/treb-export/src/workbook.d.ts +123 -0
- package/build/treb-export/src/workbook.js +644 -0
- package/build/treb-export/src/workbook.js.map +1 -0
- package/build/treb-export/src/xml-test.d.ts +9 -0
- package/build/treb-export/src/xml-test.js +52 -0
- package/build/treb-export/src/xml-test.js.map +1 -0
- package/build/treb-export/src/xml-utils.d.ts +76 -0
- package/build/treb-export/src/xml-utils.js +223 -0
- package/build/treb-export/src/xml-utils.js.map +1 -0
- package/build/treb-export/src/zip-wrapper.d.ts +22 -0
- package/build/treb-export/src/zip-wrapper.js +93 -0
- package/build/treb-export/src/zip-wrapper.js.map +1 -0
- package/build/treb-format/src/format.d.ts +130 -0
- package/build/treb-format/src/format.js +805 -0
- package/build/treb-format/src/format.js.map +1 -0
- package/build/treb-format/src/format_cache.d.ts +55 -0
- package/build/treb-format/src/format_cache.js +166 -0
- package/build/treb-format/src/format_cache.js.map +1 -0
- package/build/treb-format/src/format_parser.d.ts +70 -0
- package/build/treb-format/src/format_parser.js +618 -0
- package/build/treb-format/src/format_parser.js.map +1 -0
- package/build/treb-format/src/index.d.ts +4 -0
- package/build/treb-format/src/index.js +25 -0
- package/build/treb-format/src/index.js.map +1 -0
- package/build/treb-format/src/number_format_section.d.ts +58 -0
- package/build/treb-format/src/number_format_section.js +78 -0
- package/build/treb-format/src/number_format_section.js.map +1 -0
- package/build/treb-format/src/value_parser.d.ts +48 -0
- package/build/treb-format/src/value_parser.js +244 -0
- package/build/treb-format/src/value_parser.js.map +1 -0
- package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
- package/build/treb-grid/src/editors/autocomplete.js +316 -0
- package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
- package/build/treb-grid/src/editors/editor.d.ts +214 -0
- package/build/treb-grid/src/editors/editor.js +879 -0
- package/build/treb-grid/src/editors/editor.js.map +1 -0
- package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
- package/build/treb-grid/src/editors/external_editor.js +118 -0
- package/build/treb-grid/src/editors/external_editor.js.map +1 -0
- package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
- package/build/treb-grid/src/editors/formula_bar.js +444 -0
- package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
- package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
- package/build/treb-grid/src/editors/overlay_editor.js +353 -0
- package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
- package/build/treb-grid/src/index.d.ts +12 -0
- package/build/treb-grid/src/index.js +28 -0
- package/build/treb-grid/src/index.js.map +1 -0
- package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
- package/build/treb-grid/src/layout/base_layout.js +2050 -0
- package/build/treb-grid/src/layout/base_layout.js.map +1 -0
- package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
- package/build/treb-grid/src/layout/grid_layout.js +235 -0
- package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
- package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
- package/build/treb-grid/src/layout/mock-layout.js +37 -0
- package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
- package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
- package/build/treb-grid/src/render/selection-renderer.js +315 -0
- package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
- package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
- package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
- package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
- package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
- package/build/treb-grid/src/render/svg_selection_block.js +106 -0
- package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
- package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
- package/build/treb-grid/src/render/tile_renderer.js +1609 -0
- package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
- package/build/treb-grid/src/types/border_constants.d.ts +9 -0
- package/build/treb-grid/src/types/border_constants.js +34 -0
- package/build/treb-grid/src/types/border_constants.js.map +1 -0
- package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
- package/build/treb-grid/src/types/clipboard_data.js +22 -0
- package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
- package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
- package/build/treb-grid/src/types/clipboard_data2.js +22 -0
- package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
- package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
- package/build/treb-grid/src/types/drag_mask.js +78 -0
- package/build/treb-grid/src/types/drag_mask.js.map +1 -0
- package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
- package/build/treb-grid/src/types/external_editor_config.js +22 -0
- package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
- package/build/treb-grid/src/types/grid.d.ts +806 -0
- package/build/treb-grid/src/types/grid.js +6410 -0
- package/build/treb-grid/src/types/grid.js.map +1 -0
- package/build/treb-grid/src/types/grid_base.d.ts +442 -0
- package/build/treb-grid/src/types/grid_base.js +3523 -0
- package/build/treb-grid/src/types/grid_base.js.map +1 -0
- package/build/treb-grid/src/types/grid_command.d.ts +408 -0
- package/build/treb-grid/src/types/grid_command.js +75 -0
- package/build/treb-grid/src/types/grid_command.js.map +1 -0
- package/build/treb-grid/src/types/grid_events.d.ts +93 -0
- package/build/treb-grid/src/types/grid_events.js +36 -0
- package/build/treb-grid/src/types/grid_events.js.map +1 -0
- package/build/treb-grid/src/types/grid_options.d.ts +50 -0
- package/build/treb-grid/src/types/grid_options.js +34 -0
- package/build/treb-grid/src/types/grid_options.js.map +1 -0
- package/build/treb-grid/src/types/scale-control.d.ts +21 -0
- package/build/treb-grid/src/types/scale-control.js +148 -0
- package/build/treb-grid/src/types/scale-control.js.map +1 -0
- package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
- package/build/treb-grid/src/types/set_range_options.js +22 -0
- package/build/treb-grid/src/types/set_range_options.js.map +1 -0
- package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
- package/build/treb-grid/src/types/tab_bar.js +426 -0
- package/build/treb-grid/src/types/tab_bar.js.map +1 -0
- package/build/treb-grid/src/types/tile.d.ts +29 -0
- package/build/treb-grid/src/types/tile.js +22 -0
- package/build/treb-grid/src/types/tile.js.map +1 -0
- package/build/treb-grid/src/types/update_flags.d.ts +48 -0
- package/build/treb-grid/src/types/update_flags.js +22 -0
- package/build/treb-grid/src/types/update_flags.js.map +1 -0
- package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
- package/build/treb-grid/src/util/fontmetrics.js +82 -0
- package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
- package/build/treb-grid/src/util/ua.d.ts +33 -0
- package/build/treb-grid/src/util/ua.js +86 -0
- package/build/treb-grid/src/util/ua.js.map +1 -0
- package/build/treb-parser/src/csv-parser.d.ts +13 -0
- package/build/treb-parser/src/csv-parser.js +107 -0
- package/build/treb-parser/src/csv-parser.js.map +1 -0
- package/build/treb-parser/src/index.d.ts +4 -0
- package/build/treb-parser/src/index.js +25 -0
- package/build/treb-parser/src/index.js.map +1 -0
- package/build/treb-parser/src/md-parser.d.ts +97 -0
- package/build/treb-parser/src/md-parser.js +403 -0
- package/build/treb-parser/src/md-parser.js.map +1 -0
- package/build/treb-parser/src/parser-types.d.ts +345 -0
- package/build/treb-parser/src/parser-types.js +53 -0
- package/build/treb-parser/src/parser-types.js.map +1 -0
- package/build/treb-parser/src/parser.d.ts +422 -0
- package/build/treb-parser/src/parser.js +2418 -0
- package/build/treb-parser/src/parser.js.map +1 -0
- package/build/treb-utils/src/event_source.d.ts +34 -0
- package/build/treb-utils/src/event_source.js +110 -0
- package/build/treb-utils/src/event_source.js.map +1 -0
- package/build/treb-utils/src/ievent_source.d.ts +9 -0
- package/build/treb-utils/src/ievent_source.js +22 -0
- package/build/treb-utils/src/ievent_source.js.map +1 -0
- package/build/treb-utils/src/index.d.ts +6 -0
- package/build/treb-utils/src/index.js +30 -0
- package/build/treb-utils/src/index.js.map +1 -0
- package/build/treb-utils/src/measurement.d.ts +42 -0
- package/build/treb-utils/src/measurement.js +145 -0
- package/build/treb-utils/src/measurement.js.map +1 -0
- package/build/treb-utils/src/scale.d.ts +16 -0
- package/build/treb-utils/src/scale.js +106 -0
- package/build/treb-utils/src/scale.js.map +1 -0
- package/build/treb-utils/src/serialize_html.d.ts +5 -0
- package/build/treb-utils/src/serialize_html.js +128 -0
- package/build/treb-utils/src/serialize_html.js.map +1 -0
- package/build/treb-utils/src/validate_uri.d.ts +20 -0
- package/build/treb-utils/src/validate_uri.js +55 -0
- package/build/treb-utils/src/validate_uri.js.map +1 -0
- package/dist/{chunk-Z4XFMZ2X.mjs → chunk-E35ONJUS.mjs} +1 -1
- package/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +4 -4
- package/dist/treb.d.ts +1 -1
- package/esbuild-composite.mjs +5 -1
- package/package.json +67 -3
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
- package/treb-embed/src/embedded-spreadsheet.ts +1 -1
- package/treb-grid/src/types/grid_options.ts +1 -1
- package/tsproject.json +1 -2
- package/dist/chunk-43DLP2OX.mjs +0 -11
- package/dist/chunk-4CKS56PE.mjs +0 -11
- package/dist/chunk-75PARUQE.mjs +0 -11
- package/dist/chunk-7QD63AZS.mjs +0 -24601
- package/dist/chunk-A55ARVRD.mjs +0 -11
- package/dist/chunk-DESAKYW4.mjs +0 -11
- package/dist/chunk-EQ2R5W6P.mjs +0 -24565
- package/dist/chunk-IYJU2J6D.mjs +0 -24601
- package/dist/chunk-KSJFPGXT.mjs +0 -11
- package/dist/chunk-MQK4DNXI.mjs +0 -11
- package/dist/chunk-ORQFKLXM.mjs +0 -24601
- package/dist/chunk-SFDNNDHY.mjs +0 -11
- package/dist/chunk-T47DX5MI.mjs +0 -11
- package/dist/chunk-T6ILBVEX.mjs +0 -11
- package/dist/chunk-TPRCDYYG.mjs +0 -11
- package/dist/chunk-YAHNOOHO.mjs +0 -11
- 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: ' ',
|
|
268
|
+
});
|
|
269
|
+
// this.mock_selection.innerHTML = ' ';
|
|
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
|