From 1ea5f1d129b7904d70f65d54a64ac5cc0610eba5 Mon Sep 17 00:00:00 2001 From: adrianspdev Date: Mon, 4 Mar 2024 12:57:38 +0100 Subject: [PATCH] Move code samples to separate files and update links in documentation --- docs/README-EDITING.md | 4 +- docs/README.md | 2 +- .../guides/accessibility/accessibility.md | 2319 ----------------- .../accessibility/accessibility.md | 233 ++ .../accessibility/accessibility/example1.css | 71 + .../accessibility/accessibility/example1.html | 327 +++ .../accessibility/accessibility/example1.js | 645 +++++ .../accessibility/accessibility/example2.css | 70 + .../accessibility/accessibility/example2.jsx | 967 +++++++ .../{ => context-menu}/context-menu.md | 236 +- .../context-menu/example1.js | 21 + .../context-menu/example1.jsx | 32 + .../context-menu/example2.js | 21 + .../context-menu/example2.jsx | 32 + .../context-menu/example3.js | 88 + .../context-menu/example3.jsx | 98 + .../context-menu/example4.jsx | 48 + .../export-to-csv/example1.html | 5 + .../export-to-csv/example1.js | 41 + .../export-to-csv/example1.jsx | 65 + .../export-to-csv/example2.html | 5 + .../export-to-csv/example2.js | 41 + .../export-to-csv/example2.jsx | 65 + .../export-to-csv/example3.html | 5 + .../export-to-csv/example3.js | 40 + .../export-to-csv/example3.jsx | 64 + .../{ => export-to-csv}/export-to-csv.md | 205 +- .../{ => icon-pack}/icon-pack.md | 0 .../undo-redo/example.javascript | 24 + .../undo-redo/example.jsx | 35 + .../{ => undo-redo}/undo-redo.md | 46 +- .../{ => autofill-values}/autofill-values.md | 77 +- .../cell-features/autofill-values/example1.js | 25 + .../autofill-values/example1.jsx | 34 + .../cell-features/autofill-values/example2.js | 26 + .../autofill-values/example2.jsx | 37 + .../{ => clipboard}/clipboard.md | 162 +- .../cell-features/clipboard/example1.js | 20 + .../cell-features/clipboard/example1.jsx | 31 + .../cell-features/clipboard/example2.js | 29 + .../cell-features/clipboard/example2.jsx | 41 + .../cell-features/clipboard/example3.html | 5 + .../cell-features/clipboard/example3.js | 39 + .../cell-features/clipboard/example3.jsx | 73 + .../cell-features/{ => comments}/comments.md | 159 +- .../guides/cell-features/comments/example1.js | 26 + .../cell-features/comments/example1.jsx | 37 + .../guides/cell-features/comments/example2.js | 24 + .../cell-features/comments/example2.jsx | 35 + .../guides/cell-features/comments/example3.js | 25 + .../cell-features/comments/example3.jsx | 36 + .../guides/cell-features/comments/example4.js | 26 + .../cell-features/comments/example4.jsx | 37 + .../conditional-formatting.md | 98 +- .../conditional-formatting/example1.css | 3 + .../conditional-formatting/example1.js | 75 + .../conditional-formatting/example1.jsx | 87 + .../{ => disabled-cells}/disabled-cells.md | 234 +- .../cell-features/disabled-cells/example1.js | 32 + .../cell-features/disabled-cells/example1.jsx | 43 + .../cell-features/disabled-cells/example2.js | 29 + .../cell-features/disabled-cells/example2.jsx | 48 + .../cell-features/disabled-cells/example3.js | 35 + .../cell-features/disabled-cells/example3.jsx | 46 + .../cell-features/disabled-cells/example4.js | 32 + .../cell-features/disabled-cells/example4.jsx | 51 + .../disabled-cells/exampleReadOnlyGrid.js | 19 + .../disabled-cells/exampleReadOnlyGrid.jsx | 29 + .../formatting-cells/example1.css | 8 + .../formatting-cells/example1.javascript | 28 + .../formatting-cells/example1.jsx | 39 + .../formatting-cells/example2.javascript | 35 + .../formatting-cells/example2.jsx | 47 + .../formatting-cells/example3.js | 62 + .../formatting-cells/example3.jsx | 73 + .../formatting-cells.md | 183 +- .../cell-features/merge-cells/example1.js | 28 + .../cell-features/merge-cells/example1.jsx | 39 + .../{ => merge-cells}/merge-cells.md | 42 +- .../cell-features/selection/example1.html | 8 + .../cell-features/selection/example1.js | 37 + .../cell-features/selection/example1.jsx | 70 + .../cell-features/selection/example2.html | 5 + .../cell-features/selection/example2.js | 43 + .../cell-features/selection/example2.jsx | 74 + .../cell-features/selection/example3.css | 3 + .../cell-features/selection/example3.html | 5 + .../cell-features/selection/example3.js | 54 + .../cell-features/selection/example3.jsx | 79 + .../{ => selection}/selection.md | 240 +- .../cell-features/text-alignment/example1.js | 36 + .../cell-features/text-alignment/example1.jsx | 47 + .../{ => text-alignment}/text-alignment.md | 50 +- .../{ => cell-editor}/cell-editor.md | 199 +- .../cell-functions/cell-editor/example1.jsx | 134 + .../cell-functions/cell-editor/example2.jsx | 43 + .../{ => cell-function}/cell-function.md | 14 +- .../{ => cell-renderer}/cell-renderer.md | 337 +-- .../cell-functions/cell-renderer/example1.jsx | 50 + .../cell-functions/cell-renderer/example2.css | 4 + .../cell-functions/cell-renderer/example2.jsx | 60 + .../cell-functions/cell-renderer/example3.jsx | 48 + .../cell-functions/cell-renderer/example4.js | 61 + .../cell-functions/cell-renderer/example4.jsx | 71 + .../cell-renderer/example5.html | 3 + .../cell-functions/cell-renderer/example5.js | 49 + .../cell-functions/cell-renderer/example5.jsx | 72 + .../{ => cell-validator}/cell-validator.md | 86 +- .../cell-validator/example1.html | 2 + .../cell-functions/cell-validator/example1.js | 69 + .../cell-validator/example1.jsx | 83 + .../autocomplete-cell-type.md | 158 +- .../autocomplete-cell-type/example1.js | 39 + .../autocomplete-cell-type/example1.jsx | 50 + .../autocomplete-cell-type/example2.js | 42 + .../autocomplete-cell-type/example2.jsx | 53 + .../autocomplete-cell-type/example3.js | 31 + .../autocomplete-cell-type/example3.jsx | 40 + .../cell-types/{ => cell-type}/cell-type.md | 147 +- .../guides/cell-types/cell-type/example1.js | 46 + .../guides/cell-types/cell-type/example1.jsx | 57 + .../guides/cell-types/cell-type/example2.js | 45 + .../guides/cell-types/cell-type/example2.jsx | 56 + .../checkbox-cell-type.md | 146 +- .../cell-types/checkbox-cell-type/example1.js | 31 + .../checkbox-cell-type/example1.jsx | 41 + .../cell-types/checkbox-cell-type/example2.js | 33 + .../checkbox-cell-type/example2.jsx | 43 + .../cell-types/checkbox-cell-type/example3.js | 41 + .../checkbox-cell-type/example3.jsx | 51 + .../{ => date-cell-type}/date-cell-type.md | 65 +- .../cell-types/date-cell-type/example1.js | 49 + .../cell-types/date-cell-type/example1.jsx | 60 + .../dropdown-cell-type.md | 50 +- .../cell-types/dropdown-cell-type/example1.js | 28 + .../dropdown-cell-type/example1.jsx | 39 + .../handsontable-cell-type/example1.js | 58 + .../handsontable-cell-type/example1.jsx | 77 + .../handsontable-cell-type.md | 82 +- .../cell-types/numeric-cell-type/example1.js | 52 + .../cell-types/numeric-cell-type/example1.jsx | 63 + .../cell-types/numeric-cell-type/example3.js | 68 + .../cell-types/numeric-cell-type/example3.jsx | 64 + .../numeric-cell-type.md | 139 +- .../cell-types/password-cell-type/example1.js | 22 + .../password-cell-type/example1.jsx | 33 + .../cell-types/password-cell-type/example2.js | 22 + .../password-cell-type/example2.jsx | 33 + .../cell-types/password-cell-type/example3.js | 22 + .../password-cell-type/example3.jsx | 33 + .../password-cell-type.md | 110 +- .../cell-types/select-cell-type/example1.js | 24 + .../cell-types/select-cell-type/example1.jsx | 35 + .../select-cell-type.md | 48 +- .../cell-types/time-cell-type/example1.js | 40 + .../cell-types/time-cell-type/example1.jsx | 58 + .../{ => time-cell-type}/time-cell-type.md | 63 +- .../{ => column-filter}/column-filter.md | 1580 +---------- .../exampleCustomFilterButton.css | 14 + .../exampleCustomFilterButton.html | 1 + .../exampleCustomFilterButton.js | 101 + .../exampleCustomFilterButton.jsx | 112 + .../exampleCustomFilterButton2.css | 9 + .../exampleCustomFilterButton2.html | 1 + .../exampleCustomFilterButton2.js | 101 + .../exampleCustomFilterButton2.jsx | 112 + .../exampleEnableFilterInColumns.html | 1 + .../exampleEnableFilterInColumns.js | 136 + .../exampleEnableFilterInColumns.jsx | 148 ++ .../exampleExcludeRowsFromFiltering.html | 1 + .../exampleExcludeRowsFromFiltering.js | 183 ++ .../exampleExcludeRowsFromFiltering.jsx | 199 ++ .../column-filter/exampleFilterBasicDemo.html | 1 + .../column-filter/exampleFilterBasicDemo.js | 100 + .../column-filter/exampleFilterBasicDemo.jsx | 111 + .../exampleFilterDifferentTypes.html | 1 + .../exampleFilterDifferentTypes.js | 120 + .../exampleFilterDifferentTypes.jsx | 131 + .../exampleFilterOnInitialization.html | 1 + .../exampleFilterOnInitialization.js | 110 + .../exampleFilterOnInitialization.jsx | 126 + .../exampleFilterThroughAPI1.html | 11 + .../column-filter/exampleFilterThroughAPI1.js | 123 + .../exampleFilterThroughAPI1.jsx | 155 ++ .../column-filter/exampleQuickFilter.css | 14 + .../column-filter/exampleQuickFilter.html | 16 + .../column-filter/exampleQuickFilter.js | 110 + .../column-filter/exampleQuickFilter.jsx | 146 ++ .../exampleServerSideFilter.html | 1 + .../column-filter/exampleServerSideFilter.js | 114 + .../column-filter/exampleServerSideFilter.jsx | 125 + .../exampleShowFilterItemsOnly.html | 1 + .../exampleShowFilterItemsOnly.js | 99 + .../exampleShowFilterItemsOnly.jsx | 123 + .../{ => column-freezing}/column-freezing.md | 82 +- .../columns/column-freezing/example1.js | 24 + .../columns/column-freezing/example1.jsx | 35 + .../columns/column-freezing/example2.js | 26 + .../columns/column-freezing/example2.jsx | 37 + .../{ => column-groups}/column-groups.md | 85 +- .../guides/columns/column-groups/example1.js | 25 + .../guides/columns/column-groups/example1.jsx | 36 + .../guides/columns/column-groups/example2.js | 32 + .../guides/columns/column-groups/example2.jsx | 43 + .../{ => column-header}/column-header.md | 99 +- .../guides/columns/column-header/example1.js | 17 + .../guides/columns/column-header/example1.jsx | 28 + .../guides/columns/column-header/example2.js | 17 + .../guides/columns/column-header/example2.jsx | 28 + .../guides/columns/column-header/example3.js | 19 + .../guides/columns/column-header/example3.jsx | 30 + .../{ => column-hiding}/column-hiding.md | 242 +- .../guides/columns/column-hiding/example1.js | 25 + .../guides/columns/column-hiding/example1.jsx | 36 + .../guides/columns/column-hiding/example2.js | 24 + .../guides/columns/column-hiding/example2.jsx | 35 + .../guides/columns/column-hiding/example3.js | 24 + .../guides/columns/column-hiding/example3.jsx | 35 + .../guides/columns/column-hiding/example4.js | 27 + .../guides/columns/column-hiding/example4.jsx | 38 + .../guides/columns/column-hiding/example5.js | 25 + .../guides/columns/column-hiding/example5.jsx | 35 + .../guides/columns/column-hiding/example6.js | 26 + .../guides/columns/column-hiding/example6.jsx | 37 + .../columns/{ => column-menu}/column-menu.md | 74 +- .../guides/columns/column-menu/example1.js | 17 + .../guides/columns/column-menu/example1.jsx | 28 + .../guides/columns/column-menu/example2.js | 23 + .../guides/columns/column-menu/example2.jsx | 34 + .../{ => column-moving}/column-moving.md | 100 +- .../guides/columns/column-moving/example1.js | 24 + .../guides/columns/column-moving/example1.jsx | 35 + .../guides/columns/column-moving/example2.js | 17 + .../guides/columns/column-moving/example2.jsx | 28 + .../guides/columns/column-moving/example3.js | 17 + .../guides/columns/column-moving/example3.jsx | 28 + .../{ => column-summary}/column-summary.md | 497 +--- .../guides/columns/column-summary/example1.js | 52 + .../columns/column-summary/example1.jsx | 62 + .../columns/column-summary/example10.js | 34 + .../columns/column-summary/example10.jsx | 46 + .../columns/column-summary/example11.js | 34 + .../columns/column-summary/example11.jsx | 46 + .../columns/column-summary/example12.js | 33 + .../columns/column-summary/example12.jsx | 44 + .../guides/columns/column-summary/example2.js | 40 + .../columns/column-summary/example2.jsx | 51 + .../guides/columns/column-summary/example7.js | 51 + .../columns/column-summary/example7.jsx | 60 + .../guides/columns/column-summary/example8.js | 71 + .../columns/column-summary/example8.jsx | 82 + .../guides/columns/column-summary/example9.js | 73 + .../columns/column-summary/example9.jsx | 82 + .../column-virtualization.md | 34 +- .../columns/column-virtualization/example1.js | 24 + .../column-virtualization/example1.jsx | 25 + .../{ => column-width}/column-width.md | 223 +- .../guides/columns/column-width/example1.js | 22 + .../guides/columns/column-width/example1.jsx | 33 + .../guides/columns/column-width/example2.js | 22 + .../guides/columns/column-width/example2.jsx | 33 + .../guides/columns/column-width/example3.js | 24 + .../guides/columns/column-width/example3.jsx | 35 + .../guides/columns/column-width/example4.js | 22 + .../guides/columns/column-width/example4.jsx | 33 + .../guides/columns/column-width/example5.js | 22 + .../guides/columns/column-width/example5.jsx | 33 + .../guides/columns/column-width/example6.js | 23 + .../guides/columns/column-width/example6.jsx | 34 + .../guides/columns/react-hot-column.md | 169 -- .../columns/react-hot-column/example1.jsx | 39 + .../columns/react-hot-column/example3.jsx | 91 + .../react-hot-column/react-hot-column.md | 37 + .../formulas/formula-calculation/example.html | 5 + .../formulas/formula-calculation/example.js | 39 + .../formulas/formula-calculation/example.jsx | 74 + .../formulas/formula-calculation/example1.css | 5 + .../formula-calculation/example1.html | 4 + .../formulas/formula-calculation/example1.js | 56 + .../formulas/formula-calculation/example1.jsx | 67 + .../formula-calculation.md | 219 +- .../{ => binding-to-data}/binding-to-data.md | 438 +--- .../binding-to-data/example1.js | 25 + .../binding-to-data/example1.jsx | 36 + .../binding-to-data/example10.js | 23 + .../binding-to-data/example10.jsx | 41 + .../binding-to-data/example11.js | 20 + .../binding-to-data/example11.jsx | 31 + .../binding-to-data/example2.js | 32 + .../binding-to-data/example2.jsx | 43 + .../binding-to-data/example3.js | 22 + .../binding-to-data/example3.jsx | 33 + .../binding-to-data/example4.js | 37 + .../binding-to-data/example4.jsx | 48 + .../binding-to-data/example5.js | 26 + .../binding-to-data/example5.jsx | 37 + .../binding-to-data/example6.js | 23 + .../binding-to-data/example6.jsx | 41 + .../binding-to-data/example7.js | 62 + .../binding-to-data/example7.jsx | 75 + .../binding-to-data/example9.js | 9 + .../binding-to-data/example9.jsx | 19 + .../configuration-options.md | 302 +-- .../configuration-options/example1.js | 27 + .../configuration-options/example1.jsx | 34 + .../configuration-options/example2.js | 36 + .../configuration-options/example2.jsx | 40 + .../configuration-options/example3.js | 37 + .../configuration-options/example3.jsx | 40 + .../configuration-options/example4.js | 45 + .../configuration-options/example4.jsx | 49 + .../configuration-options/example5.js | 30 + .../configuration-options/example6.js | 60 + .../configuration-options/example6.jsx | 65 + .../guides/getting-started/demo/demo.md | 14 +- .../events-and-hooks.md | 149 +- .../events-and-hooks/example2.js | 50 + .../events-and-hooks/example2.jsx | 69 + .../events-and-hooks/example3.jsx | 74 + .../getting-started/grid-size/example.css | 3 + .../getting-started/grid-size/example.html | 7 + .../getting-started/grid-size/example.js | 39 + .../getting-started/grid-size/example.jsx | 64 + .../{ => grid-size}/grid-size.md | 81 +- .../getting-started/installation/example.js | 18 + .../getting-started/installation/example.jsx | 30 + .../{ => installation}/installation.md | 12 +- .../{ => introduction}/introduction.md | 42 +- .../{ => license-key}/license-key.md | 2 +- .../guides/getting-started/react-methods.md | 79 - .../react-methods/example1.jsx | 43 + .../react-methods/react-methods.md | 35 + .../getting-started/react-redux/example1.jsx | 139 + .../example6.jsx} | 199 +- .../react-redux/react-redux.md | 56 + .../getting-started/saving-data/example1.html | 12 + .../getting-started/saving-data/example1.js | 75 + .../getting-started/saving-data/example1.jsx | 108 + .../{ => saving-data}/saving-data.md | 111 +- .../angular-custom-context-menu-example.md | 8 +- .../angular-custom-editor-example.md | 2 +- .../angular-custom-id.md | 0 .../angular-custom-renderer-example.md | 2 +- .../angular-hot-reference.md | 0 .../angular-installation.md | 2 +- .../angular-language-change-example.md | 6 +- .../{ => angular-modules}/angular-modules.md | 18 +- .../angular-setting-up-a-translation.md | 6 +- .../angular-simple-example.md | 0 .../example1.html | 3 + .../example1.js | 55 + .../vue-custom-context-menu-example.md | 72 +- .../vue-custom-editor-example/example1.html | 3 + .../vue-custom-editor-example/example1.js | 57 + .../vue-custom-editor-example.md | 68 +- .../vue-custom-id-class-style.md | 73 - .../vue-custom-id-class-style/example1.html | 3 + .../vue-custom-id-class-style/example1.js | 37 + .../vue-custom-id-class-style.md | 31 + .../vue-custom-renderer-example/example1.html | 3 + .../vue-custom-renderer-example/example1.js | 57 + .../vue-custom-renderer-example.md | 68 +- .../integrate-with-vue/vue-hot-column.md | 731 ------ .../vue-hot-column/advanced.css | 9 + .../vue-hot-column/advanced.html | 10 + .../vue-hot-column/advanced.js | 208 ++ .../vue-hot-column/custom.html | 17 + .../vue-hot-column/custom.js | 105 + .../vue-hot-column/example1.html | 8 + .../vue-hot-column/example1.js | 47 + .../vue-hot-column/example2.html | 12 + .../vue-hot-column/example2.js | 28 + .../integrate-with-vue/vue-hot-column/v.html | 12 + .../integrate-with-vue/vue-hot-column/v.js | 56 + .../vue-hot-column/vue-hot-column.md | 264 ++ .../vue-hot-reference/example1.html | 4 + .../example1.js} | 34 +- .../vue-hot-reference/vue-hot-reference.md | 26 + .../vue-installation.md | 2 +- .../vue-language-change-example/example1.html | 6 + .../example1.js} | 65 +- .../vue-language-change-example.md | 55 + .../{ => vue-modules}/vue-modules.md | 18 +- .../vue-setting-up-a-language.md | 140 - .../vue-setting-up-a-language/example1.html | 24 + .../vue-setting-up-a-language/example1.js | 64 + .../vue-setting-up-a-language.md | 50 + .../vue-simple-example/example1.html | 3 + .../example1.js} | 33 +- .../vue-simple-example/vue-simple-example.md | 26 + .../vue-vuex-example/example1.html | 15 + .../example1.js} | 47 +- .../vue-vuex-example/vue-vuex-example.md | 28 + .../example1.html | 3 + .../example1.js | 57 + .../vue3-custom-context-menu-example.md | 76 +- .../vue3-custom-editor-example/example1.html | 3 + .../vue3-custom-editor-example/example1.js | 54 + .../vue3-custom-editor-example.md | 67 +- .../vue3-custom-id-class-style.md | 76 - .../vue3-custom-id-class-style/example1.html | 3 + .../vue3-custom-id-class-style/example1.js | 39 + .../vue3-custom-id-class-style.md | 32 + .../example1.html | 3 + .../vue3-custom-renderer-example/example1.js | 59 + .../vue3-custom-renderer-example.md | 72 +- .../integrate-with-vue3/vue3-hot-column.md | 155 -- .../vue3-hot-column/example1.html | 8 + .../vue3-hot-column/example1.js | 49 + .../vue3-hot-column/example2.html | 12 + .../vue3-hot-column/example2.js | 42 + .../vue3-hot-column/vue3-hot-column.md | 40 + .../integrate-with-vue3/vue3-hot-reference.md | 80 - .../vue3-hot-reference/example1.html | 4 + .../vue3-hot-reference/example1.js | 46 + .../vue3-hot-reference/vue3-hot-reference.md | 28 + .../vue3-installation.md | 2 +- .../example1.html | 6 + .../example1.js} | 67 +- .../vue3-language-change-example.md | 57 + .../{ => vue3-modules}/vue3-modules.md | 20 +- .../vue3-setting-up-a-language.md | 144 - .../vue3-setting-up-a-language/example1.html | 24 + .../vue3-setting-up-a-language/example1.js | 66 + .../vue3-setting-up-a-language.md | 52 + .../vue3-simple-example/example1.html | 3 + .../example1.js} | 37 +- .../vue3-simple-example.md | 30 + .../vue3-vuex-example/example1.html | 15 + .../example1.js} | 47 +- .../vue3-vuex-example/vue3-vuex-example.md | 28 + .../{ => ime-support}/ime-support.md | 0 .../internationalization/language/example1.js | 24 + .../language/example1.jsx | 32 + .../language/example2.jsx | 98 + .../{ => language}/language.md | 150 +- .../layout-direction/example1.js | 53 + .../layout-direction/example1.jsx | 58 + .../layout-direction/example2.html | 3 + .../layout-direction/example2.js | 21 + .../layout-direction/example2.jsx | 32 + .../layout-direction/example3.js | 21 + .../layout-direction/example3.jsx | 30 + .../layout-direction/example4.js | 21 + .../layout-direction/example4.jsx | 30 + .../layout-direction/example5.js | 31 + .../layout-direction/example5.jsx | 40 + .../layout-direction.md | 235 +- .../{ => locale}/locale.md | 10 +- .../custom-shortcuts.md | 6 +- .../keyboard-shortcuts.md | 18 +- .../navigation/searching-values/example1.html | 4 + .../navigation/searching-values/example1.js | 34 + .../navigation/searching-values/example1.jsx | 57 + .../navigation/searching-values/example2.css | 5 + .../navigation/searching-values/example2.html | 4 + .../navigation/searching-values/example2.js | 33 + .../navigation/searching-values/example2.jsx | 57 + .../navigation/searching-values/example3.html | 4 + .../navigation/searching-values/example3.js | 40 + .../navigation/searching-values/example3.jsx | 64 + .../navigation/searching-values/example4.html | 5 + .../navigation/searching-values/example4.js | 53 + .../navigation/searching-values/example4.jsx | 66 + .../searching-values.md | 265 +- .../batch-operations.md | 109 +- .../batch-operations/example1.html | 6 + .../optimization/batch-operations/example1.js | 84 + .../batch-operations/example1.jsx | 102 + .../{ => bundle-size}/bundle-size.md | 4 +- .../{ => performance}/performance.md | 24 +- .../guides/rows/row-freezing/example1.js | 24 + .../guides/rows/row-freezing/example1.jsx | 35 + .../rows/{ => row-freezing}/row-freezing.md | 38 +- .../guides/rows/row-header/example1.js | 32 + .../guides/rows/row-header/example1.jsx | 43 + .../rows/{ => row-header}/row-header.md | 50 +- .../guides/rows/row-height/example1.js | 20 + .../guides/rows/row-height/example1.jsx | 31 + .../guides/rows/row-height/example2.js | 21 + .../guides/rows/row-height/example2.jsx | 32 + .../guides/rows/row-height/example3.js | 22 + .../guides/rows/row-height/example3.jsx | 33 + .../guides/rows/row-height/example4.js | 21 + .../guides/rows/row-height/example4.jsx | 32 + .../rows/{ => row-height}/row-height.md | 142 +- .../guides/rows/row-hiding/example1.js | 32 + .../guides/rows/row-hiding/example1.jsx | 42 + .../guides/rows/row-hiding/example2.js | 31 + .../guides/rows/row-hiding/example2.jsx | 41 + .../guides/rows/row-hiding/example3.js | 31 + .../guides/rows/row-hiding/example3.jsx | 42 + .../guides/rows/row-hiding/example4.js | 34 + .../guides/rows/row-hiding/example4.jsx | 42 + .../guides/rows/row-hiding/example5.js | 32 + .../guides/rows/row-hiding/example5.jsx | 42 + .../guides/rows/row-hiding/example6.js | 33 + .../guides/rows/row-hiding/example6.jsx | 44 + .../rows/{ => row-hiding}/row-hiding.md | 279 +- .../guides/rows/row-moving/example1.js | 24 + .../guides/rows/row-moving/example1.jsx | 35 + .../rows/{ => row-moving}/row-moving.md | 38 +- .../guides/rows/row-parent-child/example1.js | 142 + .../guides/rows/row-parent-child/example1.jsx | 153 ++ .../row-parent-child.md | 164 +- .../guides/rows/row-prepopulating/example1.js | 86 + .../rows/row-prepopulating/example1.jsx | 107 + .../row-prepopulating.md | 110 +- .../guides/rows/row-trimming/example1.js | 24 + .../guides/rows/row-trimming/example1.jsx | 35 + .../rows/{ => row-trimming}/row-trimming.md | 42 +- .../rows/row-virtualization/example1.js | 23 + .../rows/row-virtualization/example1.jsx | 34 + .../row-virtualization.md | 43 +- .../rows-sorting/exampleCustomSortIcons.css | 16 + .../rows-sorting/exampleCustomSortIcons.html | 1 + .../rows-sorting/exampleCustomSortIcons.js | 103 + .../rows-sorting/exampleCustomSortIcons.jsx | 114 + .../rows-sorting/exampleCustomSortIcons2.css | 11 + .../rows-sorting/exampleCustomSortIcons2.html | 1 + .../rows-sorting/exampleCustomSortIcons2.js | 103 + .../rows-sorting/exampleCustomSortIcons2.jsx | 114 + .../rows-sorting/exampleCustomSortIcons3.css | 33 + .../rows-sorting/exampleCustomSortIcons3.html | 1 + .../rows-sorting/exampleCustomSortIcons3.js | 139 + .../rows-sorting/exampleCustomSortIcons3.jsx | 150 ++ .../exampleEnableSortingForColumns.html | 1 + .../exampleEnableSortingForColumns.js | 110 + .../exampleEnableSortingForColumns.jsx | 121 + .../exampleExcludeRowsFromSorting.html | 1 + .../exampleExcludeRowsFromSorting.js | 222 ++ .../exampleExcludeRowsFromSorting.jsx | 241 ++ .../rows-sorting/exampleInitialSortOrder.html | 1 + .../rows-sorting/exampleInitialSortOrder.js | 110 + .../rows-sorting/exampleInitialSortOrder.jsx | 121 + .../rows/rows-sorting/exampleSortByAPI.html | 8 + .../rows/rows-sorting/exampleSortByAPI.js | 112 + .../rows/rows-sorting/exampleSortByAPI.jsx | 136 + .../exampleSortByAPIMultipleColumns.html | 5 + .../exampleSortByAPIMultipleColumns.js | 113 + .../exampleSortByAPIMultipleColumns.jsx | 132 + .../exampleSortByMultipleColumns.html | 1 + .../exampleSortByMultipleColumns.js | 99 + .../exampleSortByMultipleColumns.jsx | 109 + .../exampleSortDifferentTypes.html | 1 + .../rows-sorting/exampleSortDifferentTypes.js | 129 + .../exampleSortDifferentTypes.jsx | 140 + .../rows/rows-sorting/exampleSortingDemo.html | 1 + .../rows/rows-sorting/exampleSortingDemo.js | 99 + .../rows/rows-sorting/exampleSortingDemo.jsx | 111 + .../rows/{ => rows-sorting}/rows-sorting.md | 1629 +----------- .../security/{ => security}/security.md | 0 docs/content/guides/sidebar.js | 258 +- .../documentation-license.md | 0 .../software-license.md | 8 +- .../supported-browsers.md | 0 .../third-party-licenses.md | 2 +- .../{ => custom-builds}/custom-builds.md | 20 +- .../{ => custom-plugins}/custom-plugins.md | 2 +- .../folder-structure.md | 0 .../{ => modules}/modules.md | 46 +- .../{ => packages}/packages.md | 8 +- .../{ => testing}/testing.md | 4 +- .../migrating-from-10.0-to-11.0.md | 12 +- .../migrating-from-11.1-to-12.0.md | 28 +- .../migrating-from-12.4-to-13.0.md | 8 +- .../migrating-from-13.1-to-14.0.md | 2 +- .../migrating-from-7.4-to-8.0.md | 2 +- .../migrating-from-8.4-to-9.0.md | 4 +- .../migrating-from-9.0-to-10.0.md | 2 +- .../{ => release-notes}/release-notes.md | 48 +- .../versioning-policy.md | 14 +- handsontable/dist/handsontable.full.js | 774 +++--- handsontable/dist/handsontable.js | 774 +++--- handsontable/dist/handsontable.js.map | 2 +- .../3rdparty/walkontable/src/cell/range.js | 2 +- handsontable/src/core.js | 42 +- .../src/dataMap/metaManager/metaSchema.js | 540 ++-- handsontable/src/pluginHooks.js | 86 +- .../plugins/columnSummary/columnSummary.js | 28 +- .../src/plugins/contextMenu/contextMenu.js | 6 +- .../src/plugins/copyPaste/copyPaste.js | 6 +- .../plugins/customBorders/customBorders.js | 2 +- .../src/plugins/dropdownMenu/dropdownMenu.js | 4 +- .../src/plugins/exportFile/exportFile.js | 2 +- handsontable/src/plugins/filters/filters.js | 2 +- handsontable/src/plugins/formulas/formulas.js | 2 +- .../plugins/hiddenColumns/hiddenColumns.js | 8 +- .../src/plugins/hiddenRows/hiddenRows.js | 8 +- .../manualColumnMove/manualColumnMove.js | 14 +- .../plugins/manualRowMove/manualRowMove.js | 14 +- .../src/plugins/nestedRows/nestedRows.js | 4 +- .../nestedRows/utils/rowMoveController.js | 8 +- handsontable/src/shortcuts/context.js | 2 +- handsontable/src/shortcuts/manager.js | 4 +- 595 files changed, 24301 insertions(+), 17025 deletions(-) delete mode 100644 docs/content/guides/accessibility/accessibility.md create mode 100644 docs/content/guides/accessibility/accessibility/accessibility.md create mode 100644 docs/content/guides/accessibility/accessibility/example1.css create mode 100644 docs/content/guides/accessibility/accessibility/example1.html create mode 100644 docs/content/guides/accessibility/accessibility/example1.js create mode 100644 docs/content/guides/accessibility/accessibility/example2.css create mode 100644 docs/content/guides/accessibility/accessibility/example2.jsx rename docs/content/guides/accessories-and-menus/{ => context-menu}/context-menu.md (71%) create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example1.js create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example1.jsx create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example2.js create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example2.jsx create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example3.js create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example3.jsx create mode 100644 docs/content/guides/accessories-and-menus/context-menu/example4.jsx create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example1.html create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example1.js create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example1.jsx create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example2.html create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example2.js create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example2.jsx create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example3.html create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example3.js create mode 100644 docs/content/guides/accessories-and-menus/export-to-csv/example3.jsx rename docs/content/guides/accessories-and-menus/{ => export-to-csv}/export-to-csv.md (61%) rename docs/content/guides/accessories-and-menus/{ => icon-pack}/icon-pack.md (100%) create mode 100644 docs/content/guides/accessories-and-menus/undo-redo/example.javascript create mode 100644 docs/content/guides/accessories-and-menus/undo-redo/example.jsx rename docs/content/guides/accessories-and-menus/{ => undo-redo}/undo-redo.md (77%) rename docs/content/guides/cell-features/{ => autofill-values}/autofill-values.md (63%) create mode 100644 docs/content/guides/cell-features/autofill-values/example1.js create mode 100644 docs/content/guides/cell-features/autofill-values/example1.jsx create mode 100644 docs/content/guides/cell-features/autofill-values/example2.js create mode 100644 docs/content/guides/cell-features/autofill-values/example2.jsx rename docs/content/guides/cell-features/{ => clipboard}/clipboard.md (73%) create mode 100644 docs/content/guides/cell-features/clipboard/example1.js create mode 100644 docs/content/guides/cell-features/clipboard/example1.jsx create mode 100644 docs/content/guides/cell-features/clipboard/example2.js create mode 100644 docs/content/guides/cell-features/clipboard/example2.jsx create mode 100644 docs/content/guides/cell-features/clipboard/example3.html create mode 100644 docs/content/guides/cell-features/clipboard/example3.js create mode 100644 docs/content/guides/cell-features/clipboard/example3.jsx rename docs/content/guides/cell-features/{ => comments}/comments.md (61%) create mode 100644 docs/content/guides/cell-features/comments/example1.js create mode 100644 docs/content/guides/cell-features/comments/example1.jsx create mode 100644 docs/content/guides/cell-features/comments/example2.js create mode 100644 docs/content/guides/cell-features/comments/example2.jsx create mode 100644 docs/content/guides/cell-features/comments/example3.js create mode 100644 docs/content/guides/cell-features/comments/example3.jsx create mode 100644 docs/content/guides/cell-features/comments/example4.js create mode 100644 docs/content/guides/cell-features/comments/example4.jsx rename docs/content/guides/cell-features/{ => conditional-formatting}/conditional-formatting.md (62%) create mode 100644 docs/content/guides/cell-features/conditional-formatting/example1.css create mode 100644 docs/content/guides/cell-features/conditional-formatting/example1.js create mode 100644 docs/content/guides/cell-features/conditional-formatting/example1.jsx rename docs/content/guides/cell-features/{ => disabled-cells}/disabled-cells.md (58%) create mode 100644 docs/content/guides/cell-features/disabled-cells/example1.js create mode 100644 docs/content/guides/cell-features/disabled-cells/example1.jsx create mode 100644 docs/content/guides/cell-features/disabled-cells/example2.js create mode 100644 docs/content/guides/cell-features/disabled-cells/example2.jsx create mode 100644 docs/content/guides/cell-features/disabled-cells/example3.js create mode 100644 docs/content/guides/cell-features/disabled-cells/example3.jsx create mode 100644 docs/content/guides/cell-features/disabled-cells/example4.js create mode 100644 docs/content/guides/cell-features/disabled-cells/example4.jsx create mode 100644 docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.js create mode 100644 docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.jsx create mode 100644 docs/content/guides/cell-features/formatting-cells/example1.css create mode 100644 docs/content/guides/cell-features/formatting-cells/example1.javascript create mode 100644 docs/content/guides/cell-features/formatting-cells/example1.jsx create mode 100644 docs/content/guides/cell-features/formatting-cells/example2.javascript create mode 100644 docs/content/guides/cell-features/formatting-cells/example2.jsx create mode 100644 docs/content/guides/cell-features/formatting-cells/example3.js create mode 100644 docs/content/guides/cell-features/formatting-cells/example3.jsx rename docs/content/guides/cell-features/{ => formatting-cells}/formatting-cells.md (59%) create mode 100644 docs/content/guides/cell-features/merge-cells/example1.js create mode 100644 docs/content/guides/cell-features/merge-cells/example1.jsx rename docs/content/guides/cell-features/{ => merge-cells}/merge-cells.md (74%) create mode 100644 docs/content/guides/cell-features/selection/example1.html create mode 100644 docs/content/guides/cell-features/selection/example1.js create mode 100644 docs/content/guides/cell-features/selection/example1.jsx create mode 100644 docs/content/guides/cell-features/selection/example2.html create mode 100644 docs/content/guides/cell-features/selection/example2.js create mode 100644 docs/content/guides/cell-features/selection/example2.jsx create mode 100644 docs/content/guides/cell-features/selection/example3.css create mode 100644 docs/content/guides/cell-features/selection/example3.html create mode 100644 docs/content/guides/cell-features/selection/example3.js create mode 100644 docs/content/guides/cell-features/selection/example3.jsx rename docs/content/guides/cell-features/{ => selection}/selection.md (66%) create mode 100644 docs/content/guides/cell-features/text-alignment/example1.js create mode 100644 docs/content/guides/cell-features/text-alignment/example1.jsx rename docs/content/guides/cell-features/{ => text-alignment}/text-alignment.md (67%) rename docs/content/guides/cell-functions/{ => cell-editor}/cell-editor.md (90%) create mode 100644 docs/content/guides/cell-functions/cell-editor/example1.jsx create mode 100644 docs/content/guides/cell-functions/cell-editor/example2.jsx rename docs/content/guides/cell-functions/{ => cell-function}/cell-function.md (92%) rename docs/content/guides/cell-functions/{ => cell-renderer}/cell-renderer.md (63%) create mode 100644 docs/content/guides/cell-functions/cell-renderer/example1.jsx create mode 100644 docs/content/guides/cell-functions/cell-renderer/example2.css create mode 100644 docs/content/guides/cell-functions/cell-renderer/example2.jsx create mode 100644 docs/content/guides/cell-functions/cell-renderer/example3.jsx create mode 100644 docs/content/guides/cell-functions/cell-renderer/example4.js create mode 100644 docs/content/guides/cell-functions/cell-renderer/example4.jsx create mode 100644 docs/content/guides/cell-functions/cell-renderer/example5.html create mode 100644 docs/content/guides/cell-functions/cell-renderer/example5.js create mode 100644 docs/content/guides/cell-functions/cell-renderer/example5.jsx rename docs/content/guides/cell-functions/{ => cell-validator}/cell-validator.md (73%) create mode 100644 docs/content/guides/cell-functions/cell-validator/example1.html create mode 100644 docs/content/guides/cell-functions/cell-validator/example1.js create mode 100644 docs/content/guides/cell-functions/cell-validator/example1.jsx rename docs/content/guides/cell-types/{ => autocomplete-cell-type}/autocomplete-cell-type.md (61%) create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example2.js create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example2.jsx create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example3.js create mode 100644 docs/content/guides/cell-types/autocomplete-cell-type/example3.jsx rename docs/content/guides/cell-types/{ => cell-type}/cell-type.md (75%) create mode 100644 docs/content/guides/cell-types/cell-type/example1.js create mode 100644 docs/content/guides/cell-types/cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/cell-type/example2.js create mode 100644 docs/content/guides/cell-types/cell-type/example2.jsx rename docs/content/guides/cell-types/{ => checkbox-cell-type}/checkbox-cell-type.md (61%) create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example2.js create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example2.jsx create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example3.js create mode 100644 docs/content/guides/cell-types/checkbox-cell-type/example3.jsx rename docs/content/guides/cell-types/{ => date-cell-type}/date-cell-type.md (73%) create mode 100644 docs/content/guides/cell-types/date-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/date-cell-type/example1.jsx rename docs/content/guides/cell-types/{ => dropdown-cell-type}/dropdown-cell-type.md (69%) create mode 100644 docs/content/guides/cell-types/dropdown-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/dropdown-cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/handsontable-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/handsontable-cell-type/example1.jsx rename docs/content/guides/cell-types/{ => handsontable-cell-type}/handsontable-cell-type.md (67%) create mode 100644 docs/content/guides/cell-types/numeric-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/numeric-cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/numeric-cell-type/example3.js create mode 100644 docs/content/guides/cell-types/numeric-cell-type/example3.jsx rename docs/content/guides/cell-types/{ => numeric-cell-type}/numeric-cell-type.md (66%) create mode 100644 docs/content/guides/cell-types/password-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/password-cell-type/example1.jsx create mode 100644 docs/content/guides/cell-types/password-cell-type/example2.js create mode 100644 docs/content/guides/cell-types/password-cell-type/example2.jsx create mode 100644 docs/content/guides/cell-types/password-cell-type/example3.js create mode 100644 docs/content/guides/cell-types/password-cell-type/example3.jsx rename docs/content/guides/cell-types/{ => password-cell-type}/password-cell-type.md (58%) create mode 100644 docs/content/guides/cell-types/select-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/select-cell-type/example1.jsx rename docs/content/guides/cell-types/{ => select-cell-type}/select-cell-type.md (68%) create mode 100644 docs/content/guides/cell-types/time-cell-type/example1.js create mode 100644 docs/content/guides/cell-types/time-cell-type/example1.jsx rename docs/content/guides/cell-types/{ => time-cell-type}/time-cell-type.md (69%) rename docs/content/guides/columns/{ => column-filter}/column-filter.md (53%) create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton.css create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton.html create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton.js create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton2.css create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton2.html create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton2.js create mode 100644 docs/content/guides/columns/column-filter/exampleCustomFilterButton2.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.html create mode 100644 docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.js create mode 100644 docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.html create mode 100644 docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.js create mode 100644 docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleFilterBasicDemo.html create mode 100644 docs/content/guides/columns/column-filter/exampleFilterBasicDemo.js create mode 100644 docs/content/guides/columns/column-filter/exampleFilterBasicDemo.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.html create mode 100644 docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.js create mode 100644 docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleFilterOnInitialization.html create mode 100644 docs/content/guides/columns/column-filter/exampleFilterOnInitialization.js create mode 100644 docs/content/guides/columns/column-filter/exampleFilterOnInitialization.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.html create mode 100644 docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.js create mode 100644 docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleQuickFilter.css create mode 100644 docs/content/guides/columns/column-filter/exampleQuickFilter.html create mode 100644 docs/content/guides/columns/column-filter/exampleQuickFilter.js create mode 100644 docs/content/guides/columns/column-filter/exampleQuickFilter.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleServerSideFilter.html create mode 100644 docs/content/guides/columns/column-filter/exampleServerSideFilter.js create mode 100644 docs/content/guides/columns/column-filter/exampleServerSideFilter.jsx create mode 100644 docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.html create mode 100644 docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.js create mode 100644 docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.jsx rename docs/content/guides/columns/{ => column-freezing}/column-freezing.md (60%) create mode 100644 docs/content/guides/columns/column-freezing/example1.js create mode 100644 docs/content/guides/columns/column-freezing/example1.jsx create mode 100644 docs/content/guides/columns/column-freezing/example2.js create mode 100644 docs/content/guides/columns/column-freezing/example2.jsx rename docs/content/guides/columns/{ => column-groups}/column-groups.md (71%) create mode 100644 docs/content/guides/columns/column-groups/example1.js create mode 100644 docs/content/guides/columns/column-groups/example1.jsx create mode 100644 docs/content/guides/columns/column-groups/example2.js create mode 100644 docs/content/guides/columns/column-groups/example2.jsx rename docs/content/guides/columns/{ => column-header}/column-header.md (63%) create mode 100644 docs/content/guides/columns/column-header/example1.js create mode 100644 docs/content/guides/columns/column-header/example1.jsx create mode 100644 docs/content/guides/columns/column-header/example2.js create mode 100644 docs/content/guides/columns/column-header/example2.jsx create mode 100644 docs/content/guides/columns/column-header/example3.js create mode 100644 docs/content/guides/columns/column-header/example3.jsx rename docs/content/guides/columns/{ => column-hiding}/column-hiding.md (61%) create mode 100644 docs/content/guides/columns/column-hiding/example1.js create mode 100644 docs/content/guides/columns/column-hiding/example1.jsx create mode 100644 docs/content/guides/columns/column-hiding/example2.js create mode 100644 docs/content/guides/columns/column-hiding/example2.jsx create mode 100644 docs/content/guides/columns/column-hiding/example3.js create mode 100644 docs/content/guides/columns/column-hiding/example3.jsx create mode 100644 docs/content/guides/columns/column-hiding/example4.js create mode 100644 docs/content/guides/columns/column-hiding/example4.jsx create mode 100644 docs/content/guides/columns/column-hiding/example5.js create mode 100644 docs/content/guides/columns/column-hiding/example5.jsx create mode 100644 docs/content/guides/columns/column-hiding/example6.js create mode 100644 docs/content/guides/columns/column-hiding/example6.jsx rename docs/content/guides/columns/{ => column-menu}/column-menu.md (68%) create mode 100644 docs/content/guides/columns/column-menu/example1.js create mode 100644 docs/content/guides/columns/column-menu/example1.jsx create mode 100644 docs/content/guides/columns/column-menu/example2.js create mode 100644 docs/content/guides/columns/column-menu/example2.jsx rename docs/content/guides/columns/{ => column-moving}/column-moving.md (69%) create mode 100644 docs/content/guides/columns/column-moving/example1.js create mode 100644 docs/content/guides/columns/column-moving/example1.jsx create mode 100644 docs/content/guides/columns/column-moving/example2.js create mode 100644 docs/content/guides/columns/column-moving/example2.jsx create mode 100644 docs/content/guides/columns/column-moving/example3.js create mode 100644 docs/content/guides/columns/column-moving/example3.jsx rename docs/content/guides/columns/{ => column-summary}/column-summary.md (66%) create mode 100644 docs/content/guides/columns/column-summary/example1.js create mode 100644 docs/content/guides/columns/column-summary/example1.jsx create mode 100644 docs/content/guides/columns/column-summary/example10.js create mode 100644 docs/content/guides/columns/column-summary/example10.jsx create mode 100644 docs/content/guides/columns/column-summary/example11.js create mode 100644 docs/content/guides/columns/column-summary/example11.jsx create mode 100644 docs/content/guides/columns/column-summary/example12.js create mode 100644 docs/content/guides/columns/column-summary/example12.jsx create mode 100644 docs/content/guides/columns/column-summary/example2.js create mode 100644 docs/content/guides/columns/column-summary/example2.jsx create mode 100644 docs/content/guides/columns/column-summary/example7.js create mode 100644 docs/content/guides/columns/column-summary/example7.jsx create mode 100644 docs/content/guides/columns/column-summary/example8.js create mode 100644 docs/content/guides/columns/column-summary/example8.jsx create mode 100644 docs/content/guides/columns/column-summary/example9.js create mode 100644 docs/content/guides/columns/column-summary/example9.jsx rename docs/content/guides/columns/{ => column-virtualization}/column-virtualization.md (74%) create mode 100644 docs/content/guides/columns/column-virtualization/example1.js create mode 100644 docs/content/guides/columns/column-virtualization/example1.jsx rename docs/content/guides/columns/{ => column-width}/column-width.md (61%) create mode 100644 docs/content/guides/columns/column-width/example1.js create mode 100644 docs/content/guides/columns/column-width/example1.jsx create mode 100644 docs/content/guides/columns/column-width/example2.js create mode 100644 docs/content/guides/columns/column-width/example2.jsx create mode 100644 docs/content/guides/columns/column-width/example3.js create mode 100644 docs/content/guides/columns/column-width/example3.jsx create mode 100644 docs/content/guides/columns/column-width/example4.js create mode 100644 docs/content/guides/columns/column-width/example4.jsx create mode 100644 docs/content/guides/columns/column-width/example5.js create mode 100644 docs/content/guides/columns/column-width/example5.jsx create mode 100644 docs/content/guides/columns/column-width/example6.js create mode 100644 docs/content/guides/columns/column-width/example6.jsx delete mode 100644 docs/content/guides/columns/react-hot-column.md create mode 100644 docs/content/guides/columns/react-hot-column/example1.jsx create mode 100644 docs/content/guides/columns/react-hot-column/example3.jsx create mode 100644 docs/content/guides/columns/react-hot-column/react-hot-column.md create mode 100644 docs/content/guides/formulas/formula-calculation/example.html create mode 100644 docs/content/guides/formulas/formula-calculation/example.js create mode 100644 docs/content/guides/formulas/formula-calculation/example.jsx create mode 100644 docs/content/guides/formulas/formula-calculation/example1.css create mode 100644 docs/content/guides/formulas/formula-calculation/example1.html create mode 100644 docs/content/guides/formulas/formula-calculation/example1.js create mode 100644 docs/content/guides/formulas/formula-calculation/example1.jsx rename docs/content/guides/formulas/{ => formula-calculation}/formula-calculation.md (73%) rename docs/content/guides/getting-started/{ => binding-to-data}/binding-to-data.md (66%) create mode 100644 docs/content/guides/getting-started/binding-to-data/example1.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example1.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example10.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example10.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example11.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example11.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example2.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example2.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example3.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example3.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example4.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example4.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example5.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example5.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example6.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example6.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example7.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example7.jsx create mode 100644 docs/content/guides/getting-started/binding-to-data/example9.js create mode 100644 docs/content/guides/getting-started/binding-to-data/example9.jsx rename docs/content/guides/getting-started/{ => configuration-options}/configuration-options.md (72%) create mode 100644 docs/content/guides/getting-started/configuration-options/example1.js create mode 100644 docs/content/guides/getting-started/configuration-options/example1.jsx create mode 100644 docs/content/guides/getting-started/configuration-options/example2.js create mode 100644 docs/content/guides/getting-started/configuration-options/example2.jsx create mode 100644 docs/content/guides/getting-started/configuration-options/example3.js create mode 100644 docs/content/guides/getting-started/configuration-options/example3.jsx create mode 100644 docs/content/guides/getting-started/configuration-options/example4.js create mode 100644 docs/content/guides/getting-started/configuration-options/example4.jsx create mode 100644 docs/content/guides/getting-started/configuration-options/example5.js create mode 100644 docs/content/guides/getting-started/configuration-options/example6.js create mode 100644 docs/content/guides/getting-started/configuration-options/example6.jsx rename docs/content/guides/getting-started/{ => events-and-hooks}/events-and-hooks.md (81%) create mode 100644 docs/content/guides/getting-started/events-and-hooks/example2.js create mode 100644 docs/content/guides/getting-started/events-and-hooks/example2.jsx create mode 100644 docs/content/guides/getting-started/events-and-hooks/example3.jsx create mode 100644 docs/content/guides/getting-started/grid-size/example.css create mode 100644 docs/content/guides/getting-started/grid-size/example.html create mode 100644 docs/content/guides/getting-started/grid-size/example.js create mode 100644 docs/content/guides/getting-started/grid-size/example.jsx rename docs/content/guides/getting-started/{ => grid-size}/grid-size.md (73%) create mode 100644 docs/content/guides/getting-started/installation/example.js create mode 100644 docs/content/guides/getting-started/installation/example.jsx rename docs/content/guides/getting-started/{ => installation}/installation.md (96%) rename docs/content/guides/getting-started/{ => introduction}/introduction.md (84%) rename docs/content/guides/getting-started/{ => license-key}/license-key.md (99%) delete mode 100644 docs/content/guides/getting-started/react-methods.md create mode 100644 docs/content/guides/getting-started/react-methods/example1.jsx create mode 100644 docs/content/guides/getting-started/react-methods/react-methods.md create mode 100644 docs/content/guides/getting-started/react-redux/example1.jsx rename docs/content/guides/getting-started/{react-redux.md => react-redux/example6.jsx} (56%) create mode 100644 docs/content/guides/getting-started/react-redux/react-redux.md create mode 100644 docs/content/guides/getting-started/saving-data/example1.html create mode 100644 docs/content/guides/getting-started/saving-data/example1.js create mode 100644 docs/content/guides/getting-started/saving-data/example1.jsx rename docs/content/guides/getting-started/{ => saving-data}/saving-data.md (62%) rename docs/content/guides/integrate-with-angular/{ => angular-custom-context-menu-example}/angular-custom-context-menu-example.md (93%) rename docs/content/guides/integrate-with-angular/{ => angular-custom-editor-example}/angular-custom-editor-example.md (98%) rename docs/content/guides/integrate-with-angular/{ => angular-custom-id}/angular-custom-id.md (100%) rename docs/content/guides/integrate-with-angular/{ => angular-custom-renderer-example}/angular-custom-renderer-example.md (98%) rename docs/content/guides/integrate-with-angular/{ => angular-hot-reference}/angular-hot-reference.md (100%) rename docs/content/guides/integrate-with-angular/{ => angular-installation}/angular-installation.md (96%) rename docs/content/guides/integrate-with-angular/{ => angular-language-change-example}/angular-language-change-example.md (96%) rename docs/content/guides/integrate-with-angular/{ => angular-modules}/angular-modules.md (73%) rename docs/content/guides/integrate-with-angular/{ => angular-setting-up-a-translation}/angular-setting-up-a-translation.md (96%) rename docs/content/guides/integrate-with-angular/{ => angular-simple-example}/angular-simple-example.md (100%) create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-context-menu-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-context-menu-example/example1.js rename docs/content/guides/integrate-with-vue/{ => vue-custom-context-menu-example}/vue-custom-context-menu-example.md (56%) create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-editor-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-editor-example/example1.js rename docs/content/guides/integrate-with-vue/{ => vue-custom-editor-example}/vue-custom-editor-example.md (56%) delete mode 100644 docs/content/guides/integrate-with-vue/vue-custom-id-class-style.md create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-id-class-style/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-id-class-style/example1.js create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-id-class-style/vue-custom-id-class-style.md create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-renderer-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-custom-renderer-example/example1.js rename docs/content/guides/integrate-with-vue/{ => vue-custom-renderer-example}/vue-custom-renderer-example.md (55%) delete mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column.md create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/advanced.css create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/advanced.html create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/advanced.js create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/custom.html create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/custom.js create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/example1.js create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/example2.html create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/example2.js create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/v.html create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/v.js create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-column/vue-hot-column.md create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-reference/example1.html rename docs/content/guides/integrate-with-vue/{vue-hot-reference.md => vue-hot-reference/example1.js} (50%) create mode 100644 docs/content/guides/integrate-with-vue/vue-hot-reference/vue-hot-reference.md rename docs/content/guides/integrate-with-vue/{ => vue-installation}/vue-installation.md (96%) create mode 100644 docs/content/guides/integrate-with-vue/vue-language-change-example/example1.html rename docs/content/guides/integrate-with-vue/{vue-language-change-example.md => vue-language-change-example/example1.js} (53%) create mode 100644 docs/content/guides/integrate-with-vue/vue-language-change-example/vue-language-change-example.md rename docs/content/guides/integrate-with-vue/{ => vue-modules}/vue-modules.md (70%) delete mode 100644 docs/content/guides/integrate-with-vue/vue-setting-up-a-language.md create mode 100644 docs/content/guides/integrate-with-vue/vue-setting-up-a-language/example1.html create mode 100644 docs/content/guides/integrate-with-vue/vue-setting-up-a-language/example1.js create mode 100644 docs/content/guides/integrate-with-vue/vue-setting-up-a-language/vue-setting-up-a-language.md create mode 100644 docs/content/guides/integrate-with-vue/vue-simple-example/example1.html rename docs/content/guides/integrate-with-vue/{vue-simple-example.md => vue-simple-example/example1.js} (51%) create mode 100644 docs/content/guides/integrate-with-vue/vue-simple-example/vue-simple-example.md create mode 100644 docs/content/guides/integrate-with-vue/vue-vuex-example/example1.html rename docs/content/guides/integrate-with-vue/{vue-vuex-example.md => vue-vuex-example/example1.js} (70%) create mode 100644 docs/content/guides/integrate-with-vue/vue-vuex-example/vue-vuex-example.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-context-menu-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-context-menu-example/example1.js rename docs/content/guides/integrate-with-vue3/{ => vue3-custom-context-menu-example}/vue3-custom-context-menu-example.md (55%) create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-editor-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-editor-example/example1.js rename docs/content/guides/integrate-with-vue3/{ => vue3-custom-editor-example}/vue3-custom-editor-example.md (56%) delete mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-id-class-style.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-id-class-style/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-id-class-style/example1.js create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-id-class-style/vue3-custom-id-class-style.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-renderer-example/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-custom-renderer-example/example1.js rename docs/content/guides/integrate-with-vue3/{ => vue3-custom-renderer-example}/vue3-custom-renderer-example.md (54%) delete mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column/example1.js create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column/example2.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column/example2.js create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-column/vue3-hot-column.md delete mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-reference.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-reference/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-reference/example1.js create mode 100644 docs/content/guides/integrate-with-vue3/vue3-hot-reference/vue3-hot-reference.md rename docs/content/guides/integrate-with-vue3/{ => vue3-installation}/vue3-installation.md (96%) create mode 100644 docs/content/guides/integrate-with-vue3/vue3-language-change-example/example1.html rename docs/content/guides/integrate-with-vue3/{vue3-language-change-example.md => vue3-language-change-example/example1.js} (52%) create mode 100644 docs/content/guides/integrate-with-vue3/vue3-language-change-example/vue3-language-change-example.md rename docs/content/guides/integrate-with-vue3/{ => vue3-modules}/vue3-modules.md (69%) delete mode 100644 docs/content/guides/integrate-with-vue3/vue3-setting-up-a-language.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-setting-up-a-language/example1.html create mode 100644 docs/content/guides/integrate-with-vue3/vue3-setting-up-a-language/example1.js create mode 100644 docs/content/guides/integrate-with-vue3/vue3-setting-up-a-language/vue3-setting-up-a-language.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-simple-example/example1.html rename docs/content/guides/integrate-with-vue3/{vue3-simple-example.md => vue3-simple-example/example1.js} (54%) create mode 100644 docs/content/guides/integrate-with-vue3/vue3-simple-example/vue3-simple-example.md create mode 100644 docs/content/guides/integrate-with-vue3/vue3-vuex-example/example1.html rename docs/content/guides/integrate-with-vue3/{vue3-vuex-example.md => vue3-vuex-example/example1.js} (69%) create mode 100644 docs/content/guides/integrate-with-vue3/vue3-vuex-example/vue3-vuex-example.md rename docs/content/guides/internationalization/{ => ime-support}/ime-support.md (100%) create mode 100644 docs/content/guides/internationalization/language/example1.js create mode 100644 docs/content/guides/internationalization/language/example1.jsx create mode 100644 docs/content/guides/internationalization/language/example2.jsx rename docs/content/guides/internationalization/{ => language}/language.md (74%) create mode 100644 docs/content/guides/internationalization/layout-direction/example1.js create mode 100644 docs/content/guides/internationalization/layout-direction/example1.jsx create mode 100644 docs/content/guides/internationalization/layout-direction/example2.html create mode 100644 docs/content/guides/internationalization/layout-direction/example2.js create mode 100644 docs/content/guides/internationalization/layout-direction/example2.jsx create mode 100644 docs/content/guides/internationalization/layout-direction/example3.js create mode 100644 docs/content/guides/internationalization/layout-direction/example3.jsx create mode 100644 docs/content/guides/internationalization/layout-direction/example4.js create mode 100644 docs/content/guides/internationalization/layout-direction/example4.jsx create mode 100644 docs/content/guides/internationalization/layout-direction/example5.js create mode 100644 docs/content/guides/internationalization/layout-direction/example5.jsx rename docs/content/guides/internationalization/{ => layout-direction}/layout-direction.md (75%) rename docs/content/guides/internationalization/{ => locale}/locale.md (91%) rename docs/content/guides/navigation/{ => custom-shortcuts}/custom-shortcuts.md (95%) rename docs/content/guides/navigation/{ => keyboard-shortcuts}/keyboard-shortcuts.md (95%) create mode 100644 docs/content/guides/navigation/searching-values/example1.html create mode 100644 docs/content/guides/navigation/searching-values/example1.js create mode 100644 docs/content/guides/navigation/searching-values/example1.jsx create mode 100644 docs/content/guides/navigation/searching-values/example2.css create mode 100644 docs/content/guides/navigation/searching-values/example2.html create mode 100644 docs/content/guides/navigation/searching-values/example2.js create mode 100644 docs/content/guides/navigation/searching-values/example2.jsx create mode 100644 docs/content/guides/navigation/searching-values/example3.html create mode 100644 docs/content/guides/navigation/searching-values/example3.js create mode 100644 docs/content/guides/navigation/searching-values/example3.jsx create mode 100644 docs/content/guides/navigation/searching-values/example4.html create mode 100644 docs/content/guides/navigation/searching-values/example4.js create mode 100644 docs/content/guides/navigation/searching-values/example4.jsx rename docs/content/guides/navigation/{ => searching-values}/searching-values.md (64%) rename docs/content/guides/optimization/{ => batch-operations}/batch-operations.md (78%) create mode 100644 docs/content/guides/optimization/batch-operations/example1.html create mode 100644 docs/content/guides/optimization/batch-operations/example1.js create mode 100644 docs/content/guides/optimization/batch-operations/example1.jsx rename docs/content/guides/optimization/{ => bundle-size}/bundle-size.md (95%) rename docs/content/guides/optimization/{ => performance}/performance.md (82%) create mode 100644 docs/content/guides/rows/row-freezing/example1.js create mode 100644 docs/content/guides/rows/row-freezing/example1.jsx rename docs/content/guides/rows/{ => row-freezing}/row-freezing.md (67%) create mode 100644 docs/content/guides/rows/row-header/example1.js create mode 100644 docs/content/guides/rows/row-header/example1.jsx rename docs/content/guides/rows/{ => row-header}/row-header.md (73%) create mode 100644 docs/content/guides/rows/row-height/example1.js create mode 100644 docs/content/guides/rows/row-height/example1.jsx create mode 100644 docs/content/guides/rows/row-height/example2.js create mode 100644 docs/content/guides/rows/row-height/example2.jsx create mode 100644 docs/content/guides/rows/row-height/example3.js create mode 100644 docs/content/guides/rows/row-height/example3.jsx create mode 100644 docs/content/guides/rows/row-height/example4.js create mode 100644 docs/content/guides/rows/row-height/example4.jsx rename docs/content/guides/rows/{ => row-height}/row-height.md (62%) create mode 100644 docs/content/guides/rows/row-hiding/example1.js create mode 100644 docs/content/guides/rows/row-hiding/example1.jsx create mode 100644 docs/content/guides/rows/row-hiding/example2.js create mode 100644 docs/content/guides/rows/row-hiding/example2.jsx create mode 100644 docs/content/guides/rows/row-hiding/example3.js create mode 100644 docs/content/guides/rows/row-hiding/example3.jsx create mode 100644 docs/content/guides/rows/row-hiding/example4.js create mode 100644 docs/content/guides/rows/row-hiding/example4.jsx create mode 100644 docs/content/guides/rows/row-hiding/example5.js create mode 100644 docs/content/guides/rows/row-hiding/example5.jsx create mode 100644 docs/content/guides/rows/row-hiding/example6.js create mode 100644 docs/content/guides/rows/row-hiding/example6.jsx rename docs/content/guides/rows/{ => row-hiding}/row-hiding.md (57%) create mode 100644 docs/content/guides/rows/row-moving/example1.js create mode 100644 docs/content/guides/rows/row-moving/example1.jsx rename docs/content/guides/rows/{ => row-moving}/row-moving.md (78%) create mode 100644 docs/content/guides/rows/row-parent-child/example1.js create mode 100644 docs/content/guides/rows/row-parent-child/example1.jsx rename docs/content/guides/rows/{ => row-parent-child}/row-parent-child.md (66%) create mode 100644 docs/content/guides/rows/row-prepopulating/example1.js create mode 100644 docs/content/guides/rows/row-prepopulating/example1.jsx rename docs/content/guides/rows/{ => row-prepopulating}/row-prepopulating.md (52%) create mode 100644 docs/content/guides/rows/row-trimming/example1.js create mode 100644 docs/content/guides/rows/row-trimming/example1.jsx rename docs/content/guides/rows/{ => row-trimming}/row-trimming.md (79%) create mode 100644 docs/content/guides/rows/row-virtualization/example1.js create mode 100644 docs/content/guides/rows/row-virtualization/example1.jsx rename docs/content/guides/rows/{ => row-virtualization}/row-virtualization.md (75%) create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons.css create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons2.css create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons2.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons2.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons2.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons3.css create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons3.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons3.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleCustomSortIcons3.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleEnableSortingForColumns.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleEnableSortingForColumns.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleEnableSortingForColumns.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleExcludeRowsFromSorting.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleExcludeRowsFromSorting.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleExcludeRowsFromSorting.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleInitialSortOrder.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleInitialSortOrder.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleInitialSortOrder.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPI.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPI.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPI.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPIMultipleColumns.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPIMultipleColumns.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByAPIMultipleColumns.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByMultipleColumns.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByMultipleColumns.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortByMultipleColumns.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortDifferentTypes.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortDifferentTypes.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortDifferentTypes.jsx create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortingDemo.html create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortingDemo.js create mode 100644 docs/content/guides/rows/rows-sorting/exampleSortingDemo.jsx rename docs/content/guides/rows/{ => rows-sorting}/rows-sorting.md (56%) rename docs/content/guides/security/{ => security}/security.md (100%) rename docs/content/guides/technical-specification/{ => documentation-license}/documentation-license.md (100%) rename docs/content/guides/technical-specification/{ => software-license}/software-license.md (91%) rename docs/content/guides/technical-specification/{ => supported-browsers}/supported-browsers.md (100%) rename docs/content/guides/technical-specification/{ => third-party-licenses}/third-party-licenses.md (98%) rename docs/content/guides/tools-and-building/{ => custom-builds}/custom-builds.md (93%) rename docs/content/guides/tools-and-building/{ => custom-plugins}/custom-plugins.md (99%) rename docs/content/guides/tools-and-building/{ => folder-structure}/folder-structure.md (100%) rename docs/content/guides/tools-and-building/{ => modules}/modules.md (93%) rename docs/content/guides/tools-and-building/{ => packages}/packages.md (92%) rename docs/content/guides/tools-and-building/{ => testing}/testing.md (96%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-10.0-to-11.0}/migrating-from-10.0-to-11.0.md (91%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-11.1-to-12.0}/migrating-from-11.1-to-12.0.md (95%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-12.4-to-13.0}/migrating-from-12.4-to-13.0.md (94%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-13.1-to-14.0}/migrating-from-13.1-to-14.0.md (97%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-7.4-to-8.0}/migrating-from-7.4-to-8.0.md (99%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-8.4-to-9.0}/migrating-from-8.4-to-9.0.md (98%) rename docs/content/guides/upgrade-and-migration/{ => migrating-from-9.0-to-10.0}/migrating-from-9.0-to-10.0.md (97%) rename docs/content/guides/upgrade-and-migration/{ => release-notes}/release-notes.md (98%) rename docs/content/guides/upgrade-and-migration/{ => versioning-policy}/versioning-policy.md (85%) diff --git a/docs/README-EDITING.md b/docs/README-EDITING.md index 3bd59d18eb5..1c6db5d8224 100644 --- a/docs/README-EDITING.md +++ b/docs/README-EDITING.md @@ -162,7 +162,7 @@ To link to another page but for other framework (and still for the same document For example, to link to a file called `./content/guides/getting-started/react-methods.md` that should be accessible only for React framework, use: ```markdown -[React methods](@/react/guides/getting-started/react-methods.md) +[React methods](@/react/guides/getting-started/react-methods/react-methods.md) ``` When there is no framework defined in the link URL, the generated link will be pointed to the currently viewed framework. For example, link `[Core](@/api/core.md)` for Javascript will point to `/docs/javascript-data-grid/api/core` and for chosen React framework to `/docs/react-data-grid/api/core`. @@ -171,7 +171,7 @@ List of available frameworks: `javascript`, `react`. Follow these rules: * After the `@` character, provide the target's relative file path (from the current version's root directory).
- For example: `[Clipboard][@/guides/cell-features/clipboard.md]`. + For example: `[Clipboard][@/guides/cell-features/clipboard/clipboard.md]`. * After the target file's name, add the `.md` [extension](#filenames)
For example: `[Autofill](@/api/autofill.md)`. * To link to a specific section, use anchors.
diff --git a/docs/README.md b/docs/README.md index c804e97d900..80160de2d6c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -84,7 +84,7 @@ docs # All documentation files │ │ ├── jsdoc-convert # JSDoc-to-Markdown converter │ │ ├── utils.js # Tools utilities │ ├── config.js # VuePress configuration -│   ├── docs-links.js # Lets us link within the currently-selected docs version and framework with `@` (e.g. [link](@/guides/path/file.md).) +│   ├── docs-links.js # Lets us link within the currently-selected docs version and framework with `@` (e.g. [link](@/guides/path/file/file.md).) │   ├── enhanceApp.js # VuePress app-level enhancements │   ├── helpers.js # Common helpers that set up sidebars and the documentation version and framework picker │   └── highlight.js # Code highlight configuration diff --git a/docs/content/guides/accessibility/accessibility.md b/docs/content/guides/accessibility/accessibility.md deleted file mode 100644 index 7b5d05d97b3..00000000000 --- a/docs/content/guides/accessibility/accessibility.md +++ /dev/null @@ -1,2319 +0,0 @@ ---- -id: o4qhm1bg -title: Accessibility -metaTitle: Accessibility - JavaScript Data Grid | Handsontable -description: Learn about Handsontable's accessibility features. -permalink: /accessibility -canonicalUrl: /accessibility -tags: - - accessibility - - a11y - - aria - - jaws - - nvda - - voiceover - - wcag - - section 508 - - ada - - compliance - - vpat -react: - id: x82phf34 - metaTitle: Accessibility - React Data Grid | Handsontable -searchCategory: Guides ---- - -# Accessibility - -Handsontable is designed to be accessible, aligning with global standards. We prioritize inclusivity, ensuring web applications are usable by people with disabilities. - -[[toc]] - -## Overview - -Accessibility features of Handsontable include: - -- Keyboard navigation that lets you use the grid without a mouse. -- Support for the most popular screen readers. -- Flexible API to configure keyboard shortcuts and navigation methods. - -## Conformance with standards - -Most global standards and regulations are created in accordance with WCAG (Web Content Accessibility Guidelines). -Handsontable meets requirements outlined in the [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/) guidelines, -which makes it compatible with most local standards, such as: - -|
Region
| Standards | -| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| USA | [Section 508 of the US Rehabilitation Act](https://www.section508.gov/)
[Americans with Disabilities Act (ADA)](https://www.ada.gov/resources/web-guidance/) | -| Europe | [European Accessibility Act (EAA)](https://ec.europa.eu/social/main.jsp?catId=1202)
[Web Accessibility Directive (WAD)](https://eur-lex.europa.eu/legal-content/EN/LSU/?uri=CELEX:32016L2102) | -| Canada | [Standard on Web Accessibility](https://www.tbs-sct.canada.ca/pol/doc-eng.aspx?id=23601) | - -## Keyboard navigation - -Handsontable doesn't require a mouse to navigate across the grid's elements. This is an important feature for those users with temporary or permanent motor impairments for whom following the mouse cursor is difficult. Keyboard navigation is also a great way to improve productivity, which is why many users choose the keyboard over the mouse regardless of their accessibility needs. - -Our experience with hundreds of implementations shows that Handsontable tends to be used either as a **spreadsheet application** or a **data grid component**. While at first the difference seems subtle, it significantly impacts user expectations regarding navigation. - -In a typical spreadsheet application (think of Microsoft Excel or Google Sheets), you can't move the focus onto headers. This makes it difficult to sort or filter data without knowing complex [keyboard shortcuts](@/guides/navigation/keyboard-shortcuts.md). Additionally, opening a [column menu](@/guides/columns/column-menu.md) is not trivial. -Handsontable offers flexibility in this regard, allowing users to switch between data grid and spreadsheet "modes". To do that switch, you can use a combination of two options: [`navigableHeaders`](@/api/options.md#navigableheaders) to enable or disable moving focus onto headers, and [`tabNavigation`](@/api/options.md#tabnavigation) to decide if the **Tab** key can be used to navigate across cells and headers. - -The following table provides more details about these two scenarios: - -| Aspect | Data grid mode | Spreadsheet mode (default) | -| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Configuration | [`navigableHeaders: true`](@/api/options.md#navigableheaders)
[`tabNavigation: false`](@/api/options.md#tabnavigation) | [`navigableHeaders: false`](@/api/options.md#navigableheaders)
[`tabNavigation: true`](@/api/options.md#tabnavigation) | -| Primary navigation method | Arrow keys | **Tab** / **Shift**+**Tab** | -| Navigable headers | Yes | No | -| Navigation | Use the arrow keys navigate across the grid. Use simple shortcuts such as **Enter** or **Space** to open menus or interact with headers, cells, or cell editors.

You can't use the **Tab** key for navigation. | Use **Tab** / **Shift**+**Tab** to navigate across the grid.
This behavior is similar to Excel or Google Sheets.

To open menus, use more complex shortcuts. | -| Focus behavior | One tab stop. The grid is included in the page tab sequence only once. | Multiple tab stops. All tabbable elements of the grid, such as cells, are included in the page tab sequence. | - -## Navigation shortcuts - -Handsontable provides a wide range of [keyboard shortcuts](@/guides/navigation/keyboard-shortcuts.md), but some of them are particularly important for users who navigate the grid with the keyboard only. For example, actions triggered while navigating across headers involve simple key combinations, making them intuitive and useful. For more complex scenarios, you can [customize the shortcuts keys](@/guides/navigation/custom-shortcuts.md) through the API. - -| Windows | macOS | Action | Focused element | -| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------- | --------------- | -| **Shift**+**Alt**+**↓** | **Shift**+**Option**+**↓** | Open a column menu | Any cell | -| **Ctrl**+**Enter** | **Cmd**+**Enter** | Open a column menu | Column header | -| **Enter** | **Enter** | Sort data | Column header | -| **Alt**+**A** | **Option**+**A** | Clear filters | Any cell | -| **Ctrl**+**Space** | **Ctrl**+**Space*** | Select a column | Any cell | -| **Shift**+**Space** | **Shift**+**Space** | Select a row | Any cell | -| **Ctrl**+**Shift**+**\\**
**Shift**+**F10** | **Cmd**+**Shift**+**\\**
**Shift**+**F10** | Open a context menu | Any cell | - -*To use this shortcut, disable the default macOS behavior for the **Ctrl**+**Space** key combination, under **System Settings** > **Keyboard** > **Keyboard Shortcuts** > **Input Sources**. - -## Support for screen readers - -Although semantic HTML doesn't need any additional attributes to be properly interpreted by assistive technologies, some of Handsontable's complex features are not fully covered by the HTML specification. That's why Handsontable provides support for screen readers with ARIA attributes (Accessible Rich Internet Applications) applied to its HTML markup. - -Each new version of Handsontable is thoroughly tested for accessibility with the following screen readers: - -- NVDA (Windows) -- JAWS (Windows) -- VoiceOver (macOS) - -## Accessible data grid demo - -Check out the interactive demo below to see how various Handsontable settings impact its accessibility level and affect the user experience. - -::: only-for javascript angular vue - -::: example #example1 --html 1 --css 2 --js 3 - -```html -
-
-
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
-
-
- - - - -
-
- - - - -
-
- - - - -
-
-
- - - -
- - -
-``` - -```css -.checkbox-container { - padding-bottom: 1rem; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.checkbox-group { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.checkbox-group > div { - display: flex; -} - -.checkbox-group > div > label { - display: flex; - gap: 0.2rem; -} - -.external-link { - margin-left: 0.5rem; - position: relative; - top: 2px; - color: black; -} - -.external-link:hover { - color: #0000ee; -} - -.placeholder-input { - max-width: 20rem; - padding: 0.5rem 0.75rem; - font-size: 0.875rem; - line-height: 1.25rem; - color: black; - border: 1px solid #e4e4e7; - border-radius: 6px; -} - -.option-label { - align-items: flex-start; -} - -/* - We want the focus to be around input and label, in order to achieve this, - we remove focus from the input and add it to the label (wrapper in this case) - we then use the :focus-within pseudo class plus native focus styles - https://css-tricks.com/copy-the-browsers-native-focus-styles/ -*/ -.option-label:focus-within { - outline: 5px auto Highlight; - outline: 5px auto -webkit-focus-ring-color; -} - -.option-label > input:focus { - outline: none; -} - -/* fixes dark theme conflicting with text color */ -html.theme-dark .option-label { - color: #e5ebf1 !important; -} - -.example-container { - gap: 1rem; - display: flex; - flex-direction: column; -} -``` - -```js - - /* start:skip-in-preview */ -const data = [ - { - companyName: "Hodkiewicz - Hintz", - productName: "Rustic Soft Ball", - sellDate: "05/07/2023", - inStock: false, - qty: 82, - orderId: "16-3974628", - country: "United Kingdom", - }, - { - companyName: "Rath LLC", - productName: "Small Frozen Tuna", - sellDate: "31/05/2023", - inStock: false, - qty: 459, - orderId: "77-7839351", - country: "Costa Rica", - }, - { - companyName: "Reichert LLC", - productName: "Rustic Soft Ball", - sellDate: "16/03/2023", - inStock: false, - qty: 318, - orderId: "75-6343150", - country: "United States of America", - }, - { - companyName: "Kozey Inc", - productName: "Sleek Wooden Bacon", - sellDate: "24/04/2023", - inStock: true, - qty: 177, - orderId: "56-3608689", - country: "Pitcairn Islands", - }, - { - companyName: "Nader - Fritsch", - productName: "Awesome Wooden Hat", - sellDate: "29/04/2023", - inStock: true, - qty: 51, - orderId: "58-1204318", - country: "Argentina", - }, - { - companyName: "Gerhold - Rowe", - productName: "Tasty Frozen Table", - sellDate: "27/03/2023", - inStock: false, - qty: 439, - orderId: "62-6066132", - country: "Senegal", - }, - { - companyName: "Rath LLC", - productName: "Awesome Wooden Hat", - sellDate: "24/11/2022", - inStock: false, - qty: 493, - orderId: "76-7785471", - country: "Cyprus", - }, - { - companyName: "Kozey Inc", - productName: "Rustic Soft Ball", - sellDate: "11/08/2023", - inStock: false, - qty: 225, - orderId: "34-3551159", - country: "Saint Martin", - }, - { - companyName: "Hodkiewicz - Hintz", - productName: "Awesome Wooden Hat", - sellDate: "07/02/2023", - inStock: false, - qty: 261, - orderId: "77-1112514", - country: "Chile", - }, - { - companyName: "Hegmann Inc", - productName: "Tasty Frozen Table", - sellDate: "06/05/2023", - inStock: false, - qty: 439, - orderId: "12-3252385", - country: "Switzerland", - }, - { - companyName: "Weber Inc", - productName: "Awesome Wooden Hat", - sellDate: "22/04/2023", - inStock: true, - qty: 235, - orderId: "71-7639998", - country: "Brazil", - }, - { - companyName: "Jacobi - Kutch", - productName: "Sleek Wooden Bacon", - sellDate: "13/12/2022", - inStock: true, - qty: 163, - orderId: "68-1588829", - country: "Burkina Faso", - }, - { - companyName: "Jenkins LLC", - productName: "Small Rubber Shoes", - sellDate: "26/03/2023", - inStock: true, - qty: 8, - orderId: "61-6324553", - country: "Virgin Islands, U.S.", - }, - { - companyName: "Koepp and Sons", - productName: "Sleek Wooden Bacon", - sellDate: "04/05/2023", - inStock: true, - qty: 355, - orderId: "74-6985005", - country: "Mozambique", - }, - { - companyName: "Doyle Group", - productName: "Awesome Wooden Hat", - sellDate: "01/08/2023", - inStock: false, - qty: 186, - orderId: "84-4370131", - country: "Cocos (Keeling) Islands", - }, - { - companyName: "Rempel - Durgan", - productName: "Tasty Frozen Table", - sellDate: "30/09/2023", - inStock: false, - qty: 284, - orderId: "13-6461825", - country: "Monaco", - }, - { - companyName: "Lesch - Jakubowski", - productName: "Small Fresh Bacon", - sellDate: "26/09/2023", - inStock: true, - qty: 492, - orderId: "13-9465439", - country: "Iran", - }, - { - companyName: "Jacobi - Kutch", - productName: "Rustic Cotton Ball", - sellDate: "04/05/2023", - inStock: true, - qty: 300, - orderId: "76-5194058", - country: "Indonesia", - }, - { - companyName: "Gerhold - Rowe", - productName: "Rustic Cotton Ball", - sellDate: "07/07/2023", - inStock: true, - qty: 493, - orderId: "61-8600792", - country: "Norfolk Island", - }, - { - companyName: "Johnston - Wisozk", - productName: "Small Fresh Fish", - sellDate: "14/07/2023", - inStock: false, - qty: 304, - orderId: "10-6007287", - country: "Romania", - }, - { - companyName: "Gutkowski Inc", - productName: "Small Fresh Bacon", - sellDate: "10/01/2023", - inStock: true, - qty: 375, - orderId: "25-1164132", - country: "Afghanistan", - }, - { - companyName: "Koepp and Sons", - productName: "Small Fresh Fish", - sellDate: "30/03/2023", - inStock: false, - qty: 365, - orderId: "75-7975820", - country: "Germany", - }, - { - companyName: "Zboncak and Sons", - productName: "Small Fresh Fish", - sellDate: "17/08/2023", - inStock: false, - qty: 308, - orderId: "59-6251875", - country: "Tajikistan", - }, - { - companyName: "Mills Group", - productName: "Rustic Soft Ball", - sellDate: "30/09/2023", - inStock: false, - qty: 191, - orderId: "67-7521441", - country: "Puerto Rico", - }, - { - companyName: "Zboncak and Sons", - productName: "Awesome Wooden Hat", - sellDate: "18/03/2023", - inStock: false, - qty: 208, - orderId: "19-4264192", - country: "Bolivia", - }, - { - companyName: "Rath LLC", - productName: "Rustic Soft Ball", - sellDate: "14/06/2023", - inStock: true, - qty: 191, - orderId: "78-5742060", - country: "Benin", - }, - { - companyName: "Upton - Reichert", - productName: "Tasty Frozen Table", - sellDate: "27/02/2023", - inStock: false, - qty: 45, - orderId: "26-6191298", - country: "Tunisia", - }, - { - companyName: "Carroll Group", - productName: "Rustic Soft Ball", - sellDate: "12/12/2022", - inStock: true, - qty: 385, - orderId: "13-7828353", - country: "French Southern Territories", - }, - { - companyName: "Reichel Group", - productName: "Small Frozen Tuna", - sellDate: "12/12/2022", - inStock: true, - qty: 117, - orderId: "67-9643738", - country: "Mongolia", - }, - { - companyName: "Kozey Inc", - productName: "Rustic Soft Ball", - sellDate: "24/03/2023", - inStock: false, - qty: 335, - orderId: "78-1331653", - country: "Angola", - }, - { - companyName: "Brown LLC", - productName: "Small Rubber Shoes", - sellDate: "13/06/2023", - inStock: true, - qty: 305, - orderId: "63-2315723", - country: "French Southern Territories", - }, - { - companyName: "Weber Inc", - productName: "Rustic Cotton Ball", - sellDate: "07/09/2023", - inStock: true, - qty: 409, - orderId: "53-6782557", - country: "Indonesia", - }, - { - companyName: "OReilly LLC", - productName: "Tasty Frozen Table", - sellDate: "18/05/2023", - inStock: true, - qty: 318, - orderId: "91-7787675", - country: "Mayotte", - }, - { - companyName: "Weber Inc", - productName: "Sleek Wooden Bacon", - sellDate: "20/04/2023", - inStock: false, - qty: 234, - orderId: "41-3560672", - country: "Switzerland", - }, - { - companyName: "Hodkiewicz Inc", - productName: "Tasty Frozen Table", - sellDate: "19/10/2023", - inStock: true, - qty: 136, - orderId: "48-6028776", - country: "Peru", - }, - { - companyName: "Lesch and Sons", - productName: "Rustic Cotton Ball", - sellDate: "29/09/2023", - inStock: false, - qty: 187, - orderId: "84-3770456", - country: "Central African Republic", - }, - { - companyName: "Pouros - Brakus", - productName: "Small Frozen Tuna", - sellDate: "29/01/2023", - inStock: false, - qty: 350, - orderId: "08-4844950", - country: "Isle of Man", - }, - { - companyName: "Batz - Rice", - productName: "Small Rubber Shoes", - sellDate: "06/11/2023", - inStock: false, - qty: 252, - orderId: "88-4899852", - country: "Burundi", - }, - { - companyName: "Kub Inc", - productName: "Small Fresh Fish", - sellDate: "05/09/2023", - inStock: true, - qty: 306, - orderId: "06-5022461", - country: "Mauritius", - }, - { - companyName: "Hills and Sons", - productName: "Small Frozen Tuna", - sellDate: "07/11/2023", - inStock: false, - qty: 435, - orderId: "99-5539911", - country: "Somalia", - }, - { - companyName: "Shanahan - Boyle", - productName: "Small Frozen Tuna", - sellDate: "19/06/2023", - inStock: true, - qty: 171, - orderId: "82-8162453", - country: "Virgin Islands, U.S.", - }, - { - companyName: "Luettgen Inc", - productName: "Awesome Wooden Hat", - sellDate: "30/09/2023", - inStock: false, - qty: 6, - orderId: "02-8118250", - country: "Colombia", - }, - { - companyName: "Hegmann Inc", - productName: "Small Rubber Shoes", - sellDate: "16/02/2023", - inStock: true, - qty: 278, - orderId: "07-9773343", - country: "Central African Republic", - }, - { - companyName: "Kub Inc", - productName: "Small Frozen Tuna", - sellDate: "08/08/2023", - inStock: false, - qty: 264, - orderId: "66-4470479", - country: "Norfolk Island", - }, - { - companyName: "Kub Inc", - productName: "Tasty Frozen Table", - sellDate: "06/06/2023", - inStock: true, - qty: 494, - orderId: "13-1175339", - country: "Liechtenstein", - }, - { - companyName: "Hahn - Welch", - productName: "Small Frozen Tuna", - sellDate: "12/06/2023", - inStock: false, - qty: 485, - orderId: "32-9127309", - country: "Bahrain", - }, - { - companyName: "Nader - Fritsch", - productName: "Small Frozen Tuna", - sellDate: "08/04/2023", - inStock: true, - qty: 332, - orderId: "41-3774568", - country: "Montserrat", - }, - { - companyName: "Crona and Sons", - productName: "Small Fresh Bacon", - sellDate: "21/06/2023", - inStock: true, - qty: 104, - orderId: "48-9995090", - country: "Syrian Arab Republic", - }, - { - companyName: "Lind Group", - productName: "Rustic Cotton Ball", - sellDate: "17/08/2023", - inStock: false, - qty: 51, - orderId: "68-9599400", - country: "Czech Republic", - }, - { - companyName: "Labadie LLC", - productName: "Small Fresh Bacon", - sellDate: "20/04/2023", - inStock: true, - qty: 155, - orderId: "52-4334332", - country: "Croatia", - }, - { - companyName: "Doyle Group", - productName: "Sleek Wooden Bacon", - sellDate: "23/07/2023", - inStock: false, - qty: 465, - orderId: "63-8894526", - country: "Indonesia", - }, -]; - -const countries = data.reduce((acc, curr) => { - if (acc.includes(curr.country)) { - return acc; - } - return [...acc, curr.country]; -}, []); - - -/* end:skip-in-preview */ - -// Get the DOM element with the ID 'example1' where the Handsontable will be rendered -const app = document.getElementById("example1"); - -// Define configuration options for the Handsontable -const hotOptions = { - data, - height: 464, - colWidths: [140, 165, 100, 100, 100, 110, 178], - colHeaders: [ - "Company name", - "Product name", - "Sell date", - "In stock", - "Qty", - "Order ID", - "Country", - ], - columns: [ - { data: "companyName", type: "text" }, - { data: "productName", type: "text" }, - { - data: "sellDate", - type: "date", - dateFormat: "DD/MM/YYYY", - allowInvalid: false, - }, - { - data: "inStock", - type: "checkbox", - className: "htCenter", - }, - { data: "qty", type: "numeric" }, - { - data: "orderId", - type: "text", - }, - { - data: "country", - type: "dropdown", - source: countries, - }, - ], - dropdownMenu: true, - hiddenColumns: { - indicators: true, - }, - contextMenu: true, - navigableHeaders: true, // New accessibility feature - tabNavigation: true, // New accessibility feature - autoWrapRow: true, - autoWrapCol: true, - multiColumnSorting: true, - filters: true, - rowHeaders: true, - manualRowMove: true, - licenseKey: "non-commercial-and-evaluation", -}; - -// Initialize the Handsontable instance with the specified configuration options -let hotInstance = new Handsontable(app, hotOptions); - -// Helper function to set up checkbox event handling -const setupCheckbox = (element, callback) => - element.addEventListener("click", (clickEvent) => callback(element.checked)); - -// Set up event listeners for various checkboxes to update Handsontable settings. -// This allows us to change the Handsontable settings from the UI, showcasing -// the flexibility of Handsontable in configuring according to your needs. - -// Checkbox: Enable/Disable Tab Navigation -setupCheckbox(document.querySelector("#enable-tab-navigation"), (checked) => { - hotOptions.tabNavigation = checked; - hotInstance.updateSettings({ - tabNavigation: hotOptions.tabNavigation, - }); - console.log( - `Updated setting: tabNavigation to`, - hotInstance.getSettings().tabNavigation - ); -}); - -// Checkbox: Enable/Disable Header Navigation -setupCheckbox( - document.querySelector("#enable-header-navigation"), - (checked) => { - hotOptions.navigableHeaders = checked; - hotInstance.updateSettings({ - navigableHeaders: hotOptions.navigableHeaders, - }); - console.log( - `Updated setting: navigableHeaders to`, - hotInstance.getSettings().navigableHeaders - ); - } -); - -// Checkbox: Enable/Disable Cell Virtualization -setupCheckbox( - document.querySelector("#enable-cell-virtualization"), - (checked) => { - hotInstance.destroy(); - hotInstance = new Handsontable(document.getElementById("example1"), { - ...hotOptions, - renderAllRows: !checked, - renderAllColumns: !checked, - }); - console.log("Updated virtualization settings:", { - renderAllRows: hotInstance.getSettings().renderAllRows, - renderAllColumns: hotInstance.getSettings().renderAllColumns, -}); - } -); - -// Checkbox: Enable/Disable Cell Enter Editing -setupCheckbox( - document.querySelector("#enable-cell-enter-editing"), - (checked) => { - hotOptions.enterBeginsEditing = checked; - hotInstance.updateSettings({ - enterBeginsEditing: hotOptions.enterBeginsEditing, - }); - console.log( - `Updated setting: enable-cell-enter-editing to`, - hotInstance.getSettings().enterBeginsEditing - ); - } -); - -// Checkbox: Enable/Disable Arrow Navigation for First/Last Row -setupCheckbox( - document.querySelector("#enable-arrow-rl-first-last-column"), - (checked) => { - hotOptions.autoWrapRow = checked; - hotInstance.updateSettings({ - autoWrapRow: hotOptions.autoWrapRow, - }); - console.log( - `Updated setting: autoWrapRow to`, - hotInstance.getSettings().autoWrapRow - ); - } -); - -// Checkbox: Enable/Disable Arrow Navigation for First/Last Column -setupCheckbox( - document.querySelector("#enable-arrow-td-first-last-column"), - (checked) => { - hotOptions.autoWrapCol = checked; - hotInstance.updateSettings({ - autoWrapCol: hotOptions.autoWrapCol, - }); - console.log( - `Updated setting: autoWrapCol to`, - hotInstance.getSettings().autoWrapCol - ); - } -); - -// Checkbox: Enable/Disable Enter Key Focus for Editing -setupCheckbox( - document.querySelector("#enable-enter-focus-editing"), - (checked) => { - hotOptions.enterMoves = checked ? { col: 0, row: 1 } : { col: 0, row: 0 }; - hotInstance.updateSettings({ - enterMoves: hotOptions.enterMoves, - }); - console.log( - `Updated setting: enterMoves to`, - hotInstance.getSettings().enterMoves - ); - } -); -``` - -::: - -::: - -::: only-for react - -::: example #example2 :react --css 1 --js 2 - -```css -.checkbox-container { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.checkbox-group { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.checkbox-group > div { - display: flex; -} - -.checkbox-group > div > label { - display: flex; - gap: 0.2rem; -} - -.external-link { - margin-left: 0.5rem; - position: relative; - top: 2px; - color: black; -} - -.external-link:hover { - color: #0000ee; -} - -.placeholder-input { - max-width: 20rem; - padding: 0.5rem 0.75rem; - font-size: 0.875rem; - line-height: 1.25rem; - color: black; - border: 1px solid #e4e4e7; - border-radius: 6px; -} - -.option-label { - align-items: flex-start; -} - -/* - We want the focus to be around input and label, in order to achieve this, - we remove focus from the input and add it to the label (wrapper in this case) - we then use the :focus-within pseudo class plus native focus styles - https://css-tricks.com/copy-the-browsers-native-focus-styles/ -*/ -.option-label:focus-within { - outline: 5px auto Highlight; - outline: 5px auto -webkit-focus-ring-color; -} - -.option-label > input:focus { - outline: none; -} - -/* fixes dark theme conflicting with text color */ -html.theme-dark .option-label { - color: #e5ebf1 !important; -} - -#example2 { - gap: 1rem; - display: flex; - flex-direction: column; -} -``` - -```jsx -/* start:skip-in-preview */ -import { HotTable, HotColumn } from "@handsontable/react"; -import { useState } from "react"; - -import { registerAllModules } from "handsontable/registry"; -import "handsontable/dist/handsontable.full.min.css"; - -const data = [ - { - companyName: "Hodkiewicz - Hintz", - productName: "Rustic Soft Ball", - sellDate: "05/07/2023", - inStock: false, - qty: 82, - orderId: "16-3974628", - country: "United Kingdom", - }, - { - companyName: "Rath LLC", - productName: "Small Frozen Tuna", - sellDate: "31/05/2023", - inStock: false, - qty: 459, - orderId: "77-7839351", - country: "Costa Rica", - }, - { - companyName: "Reichert LLC", - productName: "Rustic Soft Ball", - sellDate: "16/03/2023", - inStock: false, - qty: 318, - orderId: "75-6343150", - country: "United States of America", - }, - { - companyName: "Kozey Inc", - productName: "Sleek Wooden Bacon", - sellDate: "24/04/2023", - inStock: true, - qty: 177, - orderId: "56-3608689", - country: "Pitcairn Islands", - }, - { - companyName: "Nader - Fritsch", - productName: "Awesome Wooden Hat", - sellDate: "29/04/2023", - inStock: true, - qty: 51, - orderId: "58-1204318", - country: "Argentina", - }, - { - companyName: "Gerhold - Rowe", - productName: "Tasty Frozen Table", - sellDate: "27/03/2023", - inStock: false, - qty: 439, - orderId: "62-6066132", - country: "Senegal", - }, - { - companyName: "Rath LLC", - productName: "Awesome Wooden Hat", - sellDate: "24/11/2022", - inStock: false, - qty: 493, - orderId: "76-7785471", - country: "Cyprus", - }, - { - companyName: "Kozey Inc", - productName: "Rustic Soft Ball", - sellDate: "11/08/2023", - inStock: false, - qty: 225, - orderId: "34-3551159", - country: "Saint Martin", - }, - { - companyName: "Hodkiewicz - Hintz", - productName: "Awesome Wooden Hat", - sellDate: "07/02/2023", - inStock: false, - qty: 261, - orderId: "77-1112514", - country: "Chile", - }, - { - companyName: "Hegmann Inc", - productName: "Tasty Frozen Table", - sellDate: "06/05/2023", - inStock: false, - qty: 439, - orderId: "12-3252385", - country: "Switzerland", - }, - { - companyName: "Weber Inc", - productName: "Awesome Wooden Hat", - sellDate: "22/04/2023", - inStock: true, - qty: 235, - orderId: "71-7639998", - country: "Brazil", - }, - { - companyName: "Jacobi - Kutch", - productName: "Sleek Wooden Bacon", - sellDate: "13/12/2022", - inStock: true, - qty: 163, - orderId: "68-1588829", - country: "Burkina Faso", - }, - { - companyName: "Jenkins LLC", - productName: "Small Rubber Shoes", - sellDate: "26/03/2023", - inStock: true, - qty: 8, - orderId: "61-6324553", - country: "Virgin Islands, U.S.", - }, - { - companyName: "Koepp and Sons", - productName: "Sleek Wooden Bacon", - sellDate: "04/05/2023", - inStock: true, - qty: 355, - orderId: "74-6985005", - country: "Mozambique", - }, - { - companyName: "Doyle Group", - productName: "Awesome Wooden Hat", - sellDate: "01/08/2023", - inStock: false, - qty: 186, - orderId: "84-4370131", - country: "Cocos (Keeling) Islands", - }, - { - companyName: "Rempel - Durgan", - productName: "Tasty Frozen Table", - sellDate: "30/09/2023", - inStock: false, - qty: 284, - orderId: "13-6461825", - country: "Monaco", - }, - { - companyName: "Lesch - Jakubowski", - productName: "Small Fresh Bacon", - sellDate: "26/09/2023", - inStock: true, - qty: 492, - orderId: "13-9465439", - country: "Iran", - }, - { - companyName: "Jacobi - Kutch", - productName: "Rustic Cotton Ball", - sellDate: "04/05/2023", - inStock: true, - qty: 300, - orderId: "76-5194058", - country: "Indonesia", - }, - { - companyName: "Gerhold - Rowe", - productName: "Rustic Cotton Ball", - sellDate: "07/07/2023", - inStock: true, - qty: 493, - orderId: "61-8600792", - country: "Norfolk Island", - }, - { - companyName: "Johnston - Wisozk", - productName: "Small Fresh Fish", - sellDate: "14/07/2023", - inStock: false, - qty: 304, - orderId: "10-6007287", - country: "Romania", - }, - { - companyName: "Gutkowski Inc", - productName: "Small Fresh Bacon", - sellDate: "10/01/2023", - inStock: true, - qty: 375, - orderId: "25-1164132", - country: "Afghanistan", - }, - { - companyName: "Koepp and Sons", - productName: "Small Fresh Fish", - sellDate: "30/03/2023", - inStock: false, - qty: 365, - orderId: "75-7975820", - country: "Germany", - }, - { - companyName: "Zboncak and Sons", - productName: "Small Fresh Fish", - sellDate: "17/08/2023", - inStock: false, - qty: 308, - orderId: "59-6251875", - country: "Tajikistan", - }, - { - companyName: "Mills Group", - productName: "Rustic Soft Ball", - sellDate: "30/09/2023", - inStock: false, - qty: 191, - orderId: "67-7521441", - country: "Puerto Rico", - }, - { - companyName: "Zboncak and Sons", - productName: "Awesome Wooden Hat", - sellDate: "18/03/2023", - inStock: false, - qty: 208, - orderId: "19-4264192", - country: "Bolivia", - }, - { - companyName: "Rath LLC", - productName: "Rustic Soft Ball", - sellDate: "14/06/2023", - inStock: true, - qty: 191, - orderId: "78-5742060", - country: "Benin", - }, - { - companyName: "Upton - Reichert", - productName: "Tasty Frozen Table", - sellDate: "27/02/2023", - inStock: false, - qty: 45, - orderId: "26-6191298", - country: "Tunisia", - }, - { - companyName: "Carroll Group", - productName: "Rustic Soft Ball", - sellDate: "12/12/2022", - inStock: true, - qty: 385, - orderId: "13-7828353", - country: "French Southern Territories", - }, - { - companyName: "Reichel Group", - productName: "Small Frozen Tuna", - sellDate: "12/12/2022", - inStock: true, - qty: 117, - orderId: "67-9643738", - country: "Mongolia", - }, - { - companyName: "Kozey Inc", - productName: "Rustic Soft Ball", - sellDate: "24/03/2023", - inStock: false, - qty: 335, - orderId: "78-1331653", - country: "Angola", - }, - { - companyName: "Brown LLC", - productName: "Small Rubber Shoes", - sellDate: "13/06/2023", - inStock: true, - qty: 305, - orderId: "63-2315723", - country: "French Southern Territories", - }, - { - companyName: "Weber Inc", - productName: "Rustic Cotton Ball", - sellDate: "07/09/2023", - inStock: true, - qty: 409, - orderId: "53-6782557", - country: "Indonesia", - }, - { - companyName: "OReilly LLC", - productName: "Tasty Frozen Table", - sellDate: "18/05/2023", - inStock: true, - qty: 318, - orderId: "91-7787675", - country: "Mayotte", - }, - { - companyName: "Weber Inc", - productName: "Sleek Wooden Bacon", - sellDate: "20/04/2023", - inStock: false, - qty: 234, - orderId: "41-3560672", - country: "Switzerland", - }, - { - companyName: "Hodkiewicz Inc", - productName: "Tasty Frozen Table", - sellDate: "19/10/2023", - inStock: true, - qty: 136, - orderId: "48-6028776", - country: "Peru", - }, - { - companyName: "Lesch and Sons", - productName: "Rustic Cotton Ball", - sellDate: "29/09/2023", - inStock: false, - qty: 187, - orderId: "84-3770456", - country: "Central African Republic", - }, - { - companyName: "Pouros - Brakus", - productName: "Small Frozen Tuna", - sellDate: "29/01/2023", - inStock: false, - qty: 350, - orderId: "08-4844950", - country: "Isle of Man", - }, - { - companyName: "Batz - Rice", - productName: "Small Rubber Shoes", - sellDate: "06/11/2023", - inStock: false, - qty: 252, - orderId: "88-4899852", - country: "Burundi", - }, - { - companyName: "Kub Inc", - productName: "Small Fresh Fish", - sellDate: "05/09/2023", - inStock: true, - qty: 306, - orderId: "06-5022461", - country: "Mauritius", - }, - { - companyName: "Hills and Sons", - productName: "Small Frozen Tuna", - sellDate: "07/11/2023", - inStock: false, - qty: 435, - orderId: "99-5539911", - country: "Somalia", - }, - { - companyName: "Shanahan - Boyle", - productName: "Small Frozen Tuna", - sellDate: "19/06/2023", - inStock: true, - qty: 171, - orderId: "82-8162453", - country: "Virgin Islands, U.S.", - }, - { - companyName: "Luettgen Inc", - productName: "Awesome Wooden Hat", - sellDate: "30/09/2023", - inStock: false, - qty: 6, - orderId: "02-8118250", - country: "Colombia", - }, - { - companyName: "Hegmann Inc", - productName: "Small Rubber Shoes", - sellDate: "16/02/2023", - inStock: true, - qty: 278, - orderId: "07-9773343", - country: "Central African Republic", - }, - { - companyName: "Kub Inc", - productName: "Small Frozen Tuna", - sellDate: "08/08/2023", - inStock: false, - qty: 264, - orderId: "66-4470479", - country: "Norfolk Island", - }, - { - companyName: "Kub Inc", - productName: "Tasty Frozen Table", - sellDate: "06/06/2023", - inStock: true, - qty: 494, - orderId: "13-1175339", - country: "Liechtenstein", - }, - { - companyName: "Hahn - Welch", - productName: "Small Frozen Tuna", - sellDate: "12/06/2023", - inStock: false, - qty: 485, - orderId: "32-9127309", - country: "Bahrain", - }, - { - companyName: "Nader - Fritsch", - productName: "Small Frozen Tuna", - sellDate: "08/04/2023", - inStock: true, - qty: 332, - orderId: "41-3774568", - country: "Montserrat", - }, - { - companyName: "Crona and Sons", - productName: "Small Fresh Bacon", - sellDate: "21/06/2023", - inStock: true, - qty: 104, - orderId: "48-9995090", - country: "Syrian Arab Republic", - }, - { - companyName: "Lind Group", - productName: "Rustic Cotton Ball", - sellDate: "17/08/2023", - inStock: false, - qty: 51, - orderId: "68-9599400", - country: "Czech Republic", - }, - { - companyName: "Labadie LLC", - productName: "Small Fresh Bacon", - sellDate: "20/04/2023", - inStock: true, - qty: 155, - orderId: "52-4334332", - country: "Croatia", - }, - { - companyName: "Doyle Group", - productName: "Sleek Wooden Bacon", - sellDate: "23/07/2023", - inStock: false, - qty: 465, - orderId: "63-8894526", - country: "Indonesia", - }, -]; - -const countries = data.reduce((acc, curr) => { - if (acc.includes(curr.country)) { - return acc; - } - return [...acc, curr.country]; -}, []); -/* end:skip-in-preview */ - -// Handsontable options -const hotOptions = { - data, - height: 464, - colWidths: [140, 165, 100, 100, 100, 110, 178], - colHeaders: [ - "Company name", - "Product name", - "Sell date", - "In stock", - "Qty", - "Order ID", - "Country", - ], - dropdownMenu: true, - hiddenColumns: { - indicators: true, - }, - multiColumnSorting: true, - filters: true, - rowHeaders: true, - manualRowMove: true, - licenseKey: "non-commercial-and-evaluation", -}; - -function App() { - const [toggleableOptions, setToggleableOptions] = useState({ - tabNavigation: true, - navigableHeaders: true, - renderAllRows: false, - renderAllColumns: false, - enterBeginsEditing: true, - autoWrapRow: true, - autoWrapCol: true, - enterMoves: { col: 0, row: 1 }, - }); - - return ( - <> - {/* DemoOptions component for changing Handsontable options */} - - - - - {/* Handsontable component with dynamic options */} - - {/* Define HotColumns for the data */} - - - - - - - - - - - ); -} - -// Demo Options allows you to change the Handsontable options -// This allows us to change the Handsontable settings from the UI, showcasing -// the flexibility of Handsontable in configuring according to your needs. -function DemoOptions({ - tabNavigation, - navigableHeaders, - renderAllRows, - renderAllColumns, - enterBeginsEditing, - autoWrapRow, - autoWrapCol, - enterMoves, - changeToggleOptions, -}) { - // on checkbox change, update handsontable option - const handleCheckboxChange = (checkboxName) => { - switch (checkboxName) { - case "enable-tab-navigation": - changeToggleOptions((existing) => ({ - ...existing, - tabNavigation: !tabNavigation, - })); - break; - case "enable-header-navigation": - changeToggleOptions((existing) => ({ - ...existing, - navigableHeaders: !navigableHeaders, - })); - break; - case "enable-cell-virtualization": - changeToggleOptions((existing) => ({ - ...existing, - renderAllRows: !renderAllRows, - renderAllColumns: !renderAllColumns, - })); - break; - case "enable-cell-enter-editing": - changeToggleOptions((existing) => ({ - ...existing, - enterBeginsEditing: !enterBeginsEditing, - })); - break; - case "enable-arrow-rl-first-last-column": - changeToggleOptions((existing) => ({ - ...existing, - autoWrapRow: !autoWrapRow, - })); - break; - case "enable-arrow-td-first-last-column": - changeToggleOptions((existing) => ({ - ...existing, - autoWrapCol: !autoWrapCol, - })); - break; - case "enable-enter-focus-editing": - changeToggleOptions((existing) => ({ - ...existing, - enterMoves: - enterMoves.row !== 1 ? { col: 0, row: 1 } : { col: 0, row: 0 }, - })); - break; - default: - break; - } - }; - - return ( - <> -
-
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
-
-
- - - - -
-
- - - - -
-
- - - - -
-
-
- - ); -} - -ReactDOM.render(, document.getElementById("example2")); -``` - -::: - -::: - -## Disabling DOM virtualization for improved accessibility - -By default, Handsontable uses DOM virtualization to display only the [rows](@/guides/rows/row-virtualization.md) -and [columns](@/guides/columns/column-virtualization.md) that are currently visible on the screen, -plus a few extra cells outside the visible area to ensure a seamless scrolling experience. - -However, assistive technologies rely on the elements within the DOM appearing in the correct order. -Otherwise, they require the use of [additional ARIA attributes](https://www.w3.org/WAI/ARIA/apg/practices/grid-and-table-properties), -such as `row-colindex` or `aria-rowindex`, to understand the grid's structure and accurately announce (read) it to the user. - -We already use ARIA attributes to describe data sorting, hidden columns or rows, and merged cells. -Unfortunately, our tests have discovered scenarios where screen readers either announce incorrect indices or omit the ARIA attributes altogether. -To address this issue, we recommend disabling DOM virtualization, which entails loading all grid elements into the browser. -This action creates a complete [Accessibility tree](https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree) that can be easily parsed -and interpreted by assistive technology. - -::: only-for javascript - -```js -const hot = new Handsontable(container, { - // disable column virtualization - renderAllColumns: true, - // disable row virtualization - renderAllRows: true, -}); -``` - -::: - -::: only-for react - -```js - -``` - -::: - -## High-contrast theme - -The recommended [minimum contrast ratio](https://www.w3.org/WAI/WCAG21/quickref/#contrast-minimum) for text against images or backgrounds is 4.5:1. To achieve this level of contrast with Handsontable's default theme, you can: - -- Override the grid's CSS with your own styles. -- Use third-party software, such as the [High Contrast](https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph) extension for Chrome, supported by Google. - -## Requirements for developers - -When you customize Handsontable, it's you who's responsible for ensuring the accessibility of your solution. Especially when you create a [custom cell type](@/guides/cell-types/cell-type.md) or a [custom plugin](@/guides/tools-and-building/custom-plugins.md), remember to make them accessible to everyone. - -Our recommendations for custom development: - -- Test your code against WCAG 2.1 requirements frequently. -- Use proper color contrast, font size, and semantic HTML. -- If needed, implement additional WAI-ARIA attributes. -- Avoid flashing or blinking content. -- Test your customizations with real users who have different types of disabilities. If you don’t have access to real users, try [Funkify](https://www.funkify.org/), a disability simulator, which can help you look at your application from the perspective of users with different disabilities. - -::: tip - -The quality of custom Handsontable modifications can impact your application's accessibility level. Make sure to actively check how your changes influence the overall accessibility of your application, using tools like [Lighthouse](https://developers.google.com/web/tools/lighthouse). - -::: - -## Maintaining accessibility - -We make sure our data grid remains accessible by taking the following measures: - -- We check Handsontable's accessibility score with a range of accessibility testing tools. -- We use automated visual regression testing. -- We manually test all of Handsontable's features with the most popular screen readers. -- We use automated unit and end-to-end tests. - -## Known limitations - -- When [frozen rows](@/guides/rows/row-freezing.md), [frozen columns](@/guides/columns/column-freezing.md), or both, are enabled, some screen readers may incorrectly read the total number of rows and columns. -- When you select a cell that's part of a frozen row, frozen column, or both, NVDA and JAWS might incorrectly announce that cell's column header name. -- Dynamic ARIA attributes are sometimes omitted by screen readers. -- The `aria-rowcount` attribute is intentionally set to `-1`, as most screen readers either ignore or misinterpret it. This configuration ensures accuracy with screen readers such as VoiceOver. We plan to revise this approach once screen readers consistently handle the `aria-rowcount` attribute correctly. - -## API reference - -For the list of [options](@/guides/getting-started/configuration-options.md), methods, and [Handsontable hooks](@/guides/getting-started/events-and-hooks.md) related to accessibility, see the following API reference pages: - -- [`autoWrapCol`](@/api/options.md#autowrapcol) -- [`autoWrapRow`](@/api/options.md#autowraprow) -- [`tabNavigation`](@/api/options.md#tabnavigation) -- [`enterBeginsEditing`](@/api/options.md#enterbeginsediting) -- [`enterMoves`](@/api/options.md#entermoves) -- [`navigableHeaders`](@/api/options.md#navigableheaders) -- [`renderAllColumns`](@/api/options.md#renderallcolumns) -- [`renderAllRows`](@/api/options.md#renderallrows) -- [`viewportColumnRenderingOffset`](@/api/options.md#viewportcolumnrenderingoffset) -- [`viewportRowRenderingOffset`](@/api/options.md#viewportrowrenderingoffset) - -## Troubleshooting - -Try the following links if you didn't find what you need: - -- [View related topics](https://github.com/handsontable/handsontable/labels/Accessibility) on GitHub -- [Report an issue](https://github.com/handsontable/handsontable/issues/new/choose) on GitHub -- [Ask a question](https://stackoverflow.com/questions/tagged/handsontable) on Stack Overflow -- [Start a discussion](https://forum.handsontable.com/c/getting-help/questions) on Handsontable's forum -- [Contact our technical support](https://handsontable.com/contact?category=technical_support) to get help diff --git a/docs/content/guides/accessibility/accessibility/accessibility.md b/docs/content/guides/accessibility/accessibility/accessibility.md new file mode 100644 index 00000000000..559979ed03f --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/accessibility.md @@ -0,0 +1,233 @@ +--- +id: o4qhm1bg +title: Accessibility +metaTitle: Accessibility - JavaScript Data Grid | Handsontable +description: Learn about Handsontable's accessibility features. +permalink: /accessibility +canonicalUrl: /accessibility +tags: + - accessibility + - a11y + - aria + - jaws + - nvda + - voiceover + - wcag + - section 508 + - ada + - compliance + - vpat +react: + id: x82phf34 + metaTitle: Accessibility - React Data Grid | Handsontable +searchCategory: Guides +--- + +# Accessibility + +Handsontable is designed to be accessible, aligning with global standards. We prioritize inclusivity, ensuring web applications are usable by people with disabilities. + +[[toc]] + +## Overview + +Accessibility features of Handsontable include: + +- Keyboard navigation that lets you use the grid without a mouse. +- Support for the most popular screen readers. +- Flexible API to configure keyboard shortcuts and navigation methods. + +## Conformance with standards + +Most global standards and regulations are created in accordance with WCAG (Web Content Accessibility Guidelines). +Handsontable meets requirements outlined in the [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/) guidelines, +which makes it compatible with most local standards, such as: + +|
Region
| Standards | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| USA | [Section 508 of the US Rehabilitation Act](https://www.section508.gov/)
[Americans with Disabilities Act (ADA)](https://www.ada.gov/resources/web-guidance/) | +| Europe | [European Accessibility Act (EAA)](https://ec.europa.eu/social/main.jsp?catId=1202)
[Web Accessibility Directive (WAD)](https://eur-lex.europa.eu/legal-content/EN/LSU/?uri=CELEX:32016L2102) | +| Canada | [Standard on Web Accessibility](https://www.tbs-sct.canada.ca/pol/doc-eng.aspx?id=23601) | + +## Keyboard navigation + +Handsontable doesn't require a mouse to navigate across the grid's elements. This is an important feature for those users with temporary or permanent motor impairments for whom following the mouse cursor is difficult. Keyboard navigation is also a great way to improve productivity, which is why many users choose the keyboard over the mouse regardless of their accessibility needs. + +Our experience with hundreds of implementations shows that Handsontable tends to be used either as a **spreadsheet application** or a **data grid component**. While at first the difference seems subtle, it significantly impacts user expectations regarding navigation. + +In a typical spreadsheet application (think of Microsoft Excel or Google Sheets), you can't move the focus onto headers. This makes it difficult to sort or filter data without knowing complex [keyboard shortcuts](@/guides/navigation/keyboard-shortcuts/keyboard-shortcuts.md). Additionally, opening a [column menu](@/guides/columns/column-menu/column-menu.md) is not trivial. +Handsontable offers flexibility in this regard, allowing users to switch between data grid and spreadsheet "modes". To do that switch, you can use a combination of two options: [`navigableHeaders`](@/api/options.md#navigableheaders) to enable or disable moving focus onto headers, and [`tabNavigation`](@/api/options.md#tabnavigation) to decide if the **Tab** key can be used to navigate across cells and headers. + +The following table provides more details about these two scenarios: + +| Aspect | Data grid mode | Spreadsheet mode (default) | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Configuration | [`navigableHeaders: true`](@/api/options.md#navigableheaders)
[`tabNavigation: false`](@/api/options.md#tabnavigation) | [`navigableHeaders: false`](@/api/options.md#navigableheaders)
[`tabNavigation: true`](@/api/options.md#tabnavigation) | +| Primary navigation method | Arrow keys | **Tab** / **Shift**+**Tab** | +| Navigable headers | Yes | No | +| Navigation | Use the arrow keys navigate across the grid. Use simple shortcuts such as **Enter** or **Space** to open menus or interact with headers, cells, or cell editors.

You can't use the **Tab** key for navigation. | Use **Tab** / **Shift**+**Tab** to navigate across the grid.
This behavior is similar to Excel or Google Sheets.

To open menus, use more complex shortcuts. | +| Focus behavior | One tab stop. The grid is included in the page tab sequence only once. | Multiple tab stops. All tabbable elements of the grid, such as cells, are included in the page tab sequence. | + +## Navigation shortcuts + +Handsontable provides a wide range of [keyboard shortcuts](@/guides/navigation/keyboard-shortcuts/keyboard-shortcuts.md), but some of them are particularly important for users who navigate the grid with the keyboard only. For example, actions triggered while navigating across headers involve simple key combinations, making them intuitive and useful. For more complex scenarios, you can [customize the shortcuts keys](@/guides/navigation/custom-shortcuts/custom-shortcuts.md) through the API. + +| Windows | macOS | Action | Focused element | +| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------- | --------------- | +| **Shift**+**Alt**+**↓** | **Shift**+**Option**+**↓** | Open a column menu | Any cell | +| **Ctrl**+**Enter** | **Cmd**+**Enter** | Open a column menu | Column header | +| **Enter** | **Enter** | Sort data | Column header | +| **Alt**+**A** | **Option**+**A** | Clear filters | Any cell | +| **Ctrl**+**Space** | **Ctrl**+**Space*** | Select a column | Any cell | +| **Shift**+**Space** | **Shift**+**Space** | Select a row | Any cell | +| **Ctrl**+**Shift**+**\\**
**Shift**+**F10** | **Cmd**+**Shift**+**\\**
**Shift**+**F10** | Open a context menu | Any cell | + +*To use this shortcut, disable the default macOS behavior for the **Ctrl**+**Space** key combination, under **System Settings** > **Keyboard** > **Keyboard Shortcuts** > **Input Sources**. + +## Support for screen readers + +Although semantic HTML doesn't need any additional attributes to be properly interpreted by assistive technologies, some of Handsontable's complex features are not fully covered by the HTML specification. That's why Handsontable provides support for screen readers with ARIA attributes (Accessible Rich Internet Applications) applied to its HTML markup. + +Each new version of Handsontable is thoroughly tested for accessibility with the following screen readers: + +- NVDA (Windows) +- JAWS (Windows) +- VoiceOver (macOS) + +## Accessible data grid demo + +Check out the interactive demo below to see how various Handsontable settings impact its accessibility level and affect the user experience. + +::: only-for javascript angular vue + +::: example #example1 --html 1 --css 2 --js 3 + +@[code](@/content/guides/accessibility/accessibility/example1.html) + +@[code](@/content/guides/accessibility/accessibility/example1.css) + +@[code](@/content/guides/accessibility/accessibility/example1.js) + +::: + +::: + +::: only-for react + +::: example #example2 :react --css 1 --js 2 + +@[code](@/content/guides/accessibility/accessibility/example2.css) + +@[code](@/content/guides/accessibility/accessibility/example2.jsx) + +::: + +::: + +## Disabling DOM virtualization for improved accessibility + +By default, Handsontable uses DOM virtualization to display only the [rows](@/guides/rows/row-virtualization/row-virtualization.md) +and [columns](@/guides/columns/column-virtualization/column-virtualization.md) that are currently visible on the screen, +plus a few extra cells outside the visible area to ensure a seamless scrolling experience. + +However, assistive technologies rely on the elements within the DOM appearing in the correct order. +Otherwise, they require the use of [additional ARIA attributes](https://www.w3.org/WAI/ARIA/apg/practices/grid-and-table-properties), +such as `row-colindex` or `aria-rowindex`, to understand the grid's structure and accurately announce (read) it to the user. + +We already use ARIA attributes to describe data sorting, hidden columns or rows, and merged cells. +Unfortunately, our tests have discovered scenarios where screen readers either announce incorrect indices or omit the ARIA attributes altogether. +To address this issue, we recommend disabling DOM virtualization, which entails loading all grid elements into the browser. +This action creates a complete [Accessibility tree](https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree) that can be easily parsed +and interpreted by assistive technology. + +::: only-for javascript + +```js +const hot = new Handsontable(container, { + // disable column virtualization + renderAllColumns: true, + // disable row virtualization + renderAllRows: true, +}); +``` + +::: + +::: only-for react + +```js + +``` + +::: + +## High-contrast theme + +The recommended [minimum contrast ratio](https://www.w3.org/WAI/WCAG21/quickref/#contrast-minimum) for text against images or backgrounds is 4.5:1. To achieve this level of contrast with Handsontable's default theme, you can: + +- Override the grid's CSS with your own styles. +- Use third-party software, such as the [High Contrast](https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph) extension for Chrome, supported by Google. + +## Requirements for developers + +When you customize Handsontable, it's you who's responsible for ensuring the accessibility of your solution. Especially when you create a [custom cell type](@/guides/cell-types/cell-type/cell-type.md) or a [custom plugin](@/guides/tools-and-building/custom-plugins/custom-plugins.md), remember to make them accessible to everyone. + +Our recommendations for custom development: + +- Test your code against WCAG 2.1 requirements frequently. +- Use proper color contrast, font size, and semantic HTML. +- If needed, implement additional WAI-ARIA attributes. +- Avoid flashing or blinking content. +- Test your customizations with real users who have different types of disabilities. If you don’t have access to real users, try [Funkify](https://www.funkify.org/), a disability simulator, which can help you look at your application from the perspective of users with different disabilities. + +::: tip + +The quality of custom Handsontable modifications can impact your application's accessibility level. Make sure to actively check how your changes influence the overall accessibility of your application, using tools like [Lighthouse](https://developers.google.com/web/tools/lighthouse). + +::: + +## Maintaining accessibility + +We make sure our data grid remains accessible by taking the following measures: + +- We check Handsontable's accessibility score with a range of accessibility testing tools. +- We use automated visual regression testing. +- We manually test all of Handsontable's features with the most popular screen readers. +- We use automated unit and end-to-end tests. + +## Known limitations + +- When [frozen rows](@/guides/rows/row-freezing/row-freezing.md), [frozen columns](@/guides/columns/column-freezing/column-freezing.md), or both, are enabled, some screen readers may incorrectly read the total number of rows and columns. +- When you select a cell that's part of a frozen row, frozen column, or both, NVDA and JAWS might incorrectly announce that cell's column header name. +- Dynamic ARIA attributes are sometimes omitted by screen readers. +- The `aria-rowcount` attribute is intentionally set to `-1`, as most screen readers either ignore or misinterpret it. This configuration ensures accuracy with screen readers such as VoiceOver. We plan to revise this approach once screen readers consistently handle the `aria-rowcount` attribute correctly. + +## API reference + +For the list of [options](@/guides/getting-started/configuration-options/configuration-options.md), methods, and [Handsontable hooks](@/guides/getting-started/events-and-hooks/events-and-hooks.md) related to accessibility, see the following API reference pages: + +- [`autoWrapCol`](@/api/options.md#autowrapcol) +- [`autoWrapRow`](@/api/options.md#autowraprow) +- [`tabNavigation`](@/api/options.md#tabnavigation) +- [`enterBeginsEditing`](@/api/options.md#enterbeginsediting) +- [`enterMoves`](@/api/options.md#entermoves) +- [`navigableHeaders`](@/api/options.md#navigableheaders) +- [`renderAllColumns`](@/api/options.md#renderallcolumns) +- [`renderAllRows`](@/api/options.md#renderallrows) +- [`viewportColumnRenderingOffset`](@/api/options.md#viewportcolumnrenderingoffset) +- [`viewportRowRenderingOffset`](@/api/options.md#viewportrowrenderingoffset) + +## Troubleshooting + +Try the following links if you didn't find what you need: + +- [View related topics](https://github.com/handsontable/handsontable/labels/Accessibility) on GitHub +- [Report an issue](https://github.com/handsontable/handsontable/issues/new/choose) on GitHub +- [Ask a question](https://stackoverflow.com/questions/tagged/handsontable) on Stack Overflow +- [Start a discussion](https://forum.handsontable.com/c/getting-help/questions) on Handsontable's forum +- [Contact our technical support](https://handsontable.com/contact?category=technical_support) to get help diff --git a/docs/content/guides/accessibility/accessibility/example1.css b/docs/content/guides/accessibility/accessibility/example1.css new file mode 100644 index 00000000000..b2dbae050c9 --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/example1.css @@ -0,0 +1,71 @@ +.checkbox-container { + padding-bottom: 1rem; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.checkbox-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.checkbox-group > div { + display: flex; +} + +.checkbox-group > div > label { + display: flex; + gap: 0.2rem; +} + +.external-link { + margin-left: 0.5rem; + position: relative; + top: 2px; + color: black; +} + +.external-link:hover { + color: #0000ee; +} + +.placeholder-input { + max-width: 20rem; + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + line-height: 1.25rem; + color: black; + border: 1px solid #e4e4e7; + border-radius: 6px; +} + +.option-label { + align-items: flex-start; +} + +/* + We want the focus to be around input and label, in order to achieve this, + we remove focus from the input and add it to the label (wrapper in this case) + we then use the :focus-within pseudo class plus native focus styles + https://css-tricks.com/copy-the-browsers-native-focus-styles/ +*/ +.option-label:focus-within { + outline: 5px auto Highlight; + outline: 5px auto -webkit-focus-ring-color; +} + +.option-label > input:focus { + outline: none; +} + +/* fixes dark theme conflicting with text color */ +html.theme-dark .option-label { + color: #e5ebf1 !important; +} + +.example-container { + gap: 1rem; + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/docs/content/guides/accessibility/accessibility/example1.html b/docs/content/guides/accessibility/accessibility/example1.html new file mode 100644 index 00000000000..91a973dbb53 --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/example1.html @@ -0,0 +1,327 @@ +
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+ + + +
+ + +
\ No newline at end of file diff --git a/docs/content/guides/accessibility/accessibility/example1.js b/docs/content/guides/accessibility/accessibility/example1.js new file mode 100644 index 00000000000..74ee7270ad7 --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/example1.js @@ -0,0 +1,645 @@ +/* start:skip-in-preview */ +const data = [ + { + companyName: "Hodkiewicz - Hintz", + productName: "Rustic Soft Ball", + sellDate: "05/07/2023", + inStock: false, + qty: 82, + orderId: "16-3974628", + country: "United Kingdom", + }, + { + companyName: "Rath LLC", + productName: "Small Frozen Tuna", + sellDate: "31/05/2023", + inStock: false, + qty: 459, + orderId: "77-7839351", + country: "Costa Rica", + }, + { + companyName: "Reichert LLC", + productName: "Rustic Soft Ball", + sellDate: "16/03/2023", + inStock: false, + qty: 318, + orderId: "75-6343150", + country: "United States of America", + }, + { + companyName: "Kozey Inc", + productName: "Sleek Wooden Bacon", + sellDate: "24/04/2023", + inStock: true, + qty: 177, + orderId: "56-3608689", + country: "Pitcairn Islands", + }, + { + companyName: "Nader - Fritsch", + productName: "Awesome Wooden Hat", + sellDate: "29/04/2023", + inStock: true, + qty: 51, + orderId: "58-1204318", + country: "Argentina", + }, + { + companyName: "Gerhold - Rowe", + productName: "Tasty Frozen Table", + sellDate: "27/03/2023", + inStock: false, + qty: 439, + orderId: "62-6066132", + country: "Senegal", + }, + { + companyName: "Rath LLC", + productName: "Awesome Wooden Hat", + sellDate: "24/11/2022", + inStock: false, + qty: 493, + orderId: "76-7785471", + country: "Cyprus", + }, + { + companyName: "Kozey Inc", + productName: "Rustic Soft Ball", + sellDate: "11/08/2023", + inStock: false, + qty: 225, + orderId: "34-3551159", + country: "Saint Martin", + }, + { + companyName: "Hodkiewicz - Hintz", + productName: "Awesome Wooden Hat", + sellDate: "07/02/2023", + inStock: false, + qty: 261, + orderId: "77-1112514", + country: "Chile", + }, + { + companyName: "Hegmann Inc", + productName: "Tasty Frozen Table", + sellDate: "06/05/2023", + inStock: false, + qty: 439, + orderId: "12-3252385", + country: "Switzerland", + }, + { + companyName: "Weber Inc", + productName: "Awesome Wooden Hat", + sellDate: "22/04/2023", + inStock: true, + qty: 235, + orderId: "71-7639998", + country: "Brazil", + }, + { + companyName: "Jacobi - Kutch", + productName: "Sleek Wooden Bacon", + sellDate: "13/12/2022", + inStock: true, + qty: 163, + orderId: "68-1588829", + country: "Burkina Faso", + }, + { + companyName: "Jenkins LLC", + productName: "Small Rubber Shoes", + sellDate: "26/03/2023", + inStock: true, + qty: 8, + orderId: "61-6324553", + country: "Virgin Islands, U.S.", + }, + { + companyName: "Koepp and Sons", + productName: "Sleek Wooden Bacon", + sellDate: "04/05/2023", + inStock: true, + qty: 355, + orderId: "74-6985005", + country: "Mozambique", + }, + { + companyName: "Doyle Group", + productName: "Awesome Wooden Hat", + sellDate: "01/08/2023", + inStock: false, + qty: 186, + orderId: "84-4370131", + country: "Cocos (Keeling) Islands", + }, + { + companyName: "Rempel - Durgan", + productName: "Tasty Frozen Table", + sellDate: "30/09/2023", + inStock: false, + qty: 284, + orderId: "13-6461825", + country: "Monaco", + }, + { + companyName: "Lesch - Jakubowski", + productName: "Small Fresh Bacon", + sellDate: "26/09/2023", + inStock: true, + qty: 492, + orderId: "13-9465439", + country: "Iran", + }, + { + companyName: "Jacobi - Kutch", + productName: "Rustic Cotton Ball", + sellDate: "04/05/2023", + inStock: true, + qty: 300, + orderId: "76-5194058", + country: "Indonesia", + }, + { + companyName: "Gerhold - Rowe", + productName: "Rustic Cotton Ball", + sellDate: "07/07/2023", + inStock: true, + qty: 493, + orderId: "61-8600792", + country: "Norfolk Island", + }, + { + companyName: "Johnston - Wisozk", + productName: "Small Fresh Fish", + sellDate: "14/07/2023", + inStock: false, + qty: 304, + orderId: "10-6007287", + country: "Romania", + }, + { + companyName: "Gutkowski Inc", + productName: "Small Fresh Bacon", + sellDate: "10/01/2023", + inStock: true, + qty: 375, + orderId: "25-1164132", + country: "Afghanistan", + }, + { + companyName: "Koepp and Sons", + productName: "Small Fresh Fish", + sellDate: "30/03/2023", + inStock: false, + qty: 365, + orderId: "75-7975820", + country: "Germany", + }, + { + companyName: "Zboncak and Sons", + productName: "Small Fresh Fish", + sellDate: "17/08/2023", + inStock: false, + qty: 308, + orderId: "59-6251875", + country: "Tajikistan", + }, + { + companyName: "Mills Group", + productName: "Rustic Soft Ball", + sellDate: "30/09/2023", + inStock: false, + qty: 191, + orderId: "67-7521441", + country: "Puerto Rico", + }, + { + companyName: "Zboncak and Sons", + productName: "Awesome Wooden Hat", + sellDate: "18/03/2023", + inStock: false, + qty: 208, + orderId: "19-4264192", + country: "Bolivia", + }, + { + companyName: "Rath LLC", + productName: "Rustic Soft Ball", + sellDate: "14/06/2023", + inStock: true, + qty: 191, + orderId: "78-5742060", + country: "Benin", + }, + { + companyName: "Upton - Reichert", + productName: "Tasty Frozen Table", + sellDate: "27/02/2023", + inStock: false, + qty: 45, + orderId: "26-6191298", + country: "Tunisia", + }, + { + companyName: "Carroll Group", + productName: "Rustic Soft Ball", + sellDate: "12/12/2022", + inStock: true, + qty: 385, + orderId: "13-7828353", + country: "French Southern Territories", + }, + { + companyName: "Reichel Group", + productName: "Small Frozen Tuna", + sellDate: "12/12/2022", + inStock: true, + qty: 117, + orderId: "67-9643738", + country: "Mongolia", + }, + { + companyName: "Kozey Inc", + productName: "Rustic Soft Ball", + sellDate: "24/03/2023", + inStock: false, + qty: 335, + orderId: "78-1331653", + country: "Angola", + }, + { + companyName: "Brown LLC", + productName: "Small Rubber Shoes", + sellDate: "13/06/2023", + inStock: true, + qty: 305, + orderId: "63-2315723", + country: "French Southern Territories", + }, + { + companyName: "Weber Inc", + productName: "Rustic Cotton Ball", + sellDate: "07/09/2023", + inStock: true, + qty: 409, + orderId: "53-6782557", + country: "Indonesia", + }, + { + companyName: "OReilly LLC", + productName: "Tasty Frozen Table", + sellDate: "18/05/2023", + inStock: true, + qty: 318, + orderId: "91-7787675", + country: "Mayotte", + }, + { + companyName: "Weber Inc", + productName: "Sleek Wooden Bacon", + sellDate: "20/04/2023", + inStock: false, + qty: 234, + orderId: "41-3560672", + country: "Switzerland", + }, + { + companyName: "Hodkiewicz Inc", + productName: "Tasty Frozen Table", + sellDate: "19/10/2023", + inStock: true, + qty: 136, + orderId: "48-6028776", + country: "Peru", + }, + { + companyName: "Lesch and Sons", + productName: "Rustic Cotton Ball", + sellDate: "29/09/2023", + inStock: false, + qty: 187, + orderId: "84-3770456", + country: "Central African Republic", + }, + { + companyName: "Pouros - Brakus", + productName: "Small Frozen Tuna", + sellDate: "29/01/2023", + inStock: false, + qty: 350, + orderId: "08-4844950", + country: "Isle of Man", + }, + { + companyName: "Batz - Rice", + productName: "Small Rubber Shoes", + sellDate: "06/11/2023", + inStock: false, + qty: 252, + orderId: "88-4899852", + country: "Burundi", + }, + { + companyName: "Kub Inc", + productName: "Small Fresh Fish", + sellDate: "05/09/2023", + inStock: true, + qty: 306, + orderId: "06-5022461", + country: "Mauritius", + }, + { + companyName: "Hills and Sons", + productName: "Small Frozen Tuna", + sellDate: "07/11/2023", + inStock: false, + qty: 435, + orderId: "99-5539911", + country: "Somalia", + }, + { + companyName: "Shanahan - Boyle", + productName: "Small Frozen Tuna", + sellDate: "19/06/2023", + inStock: true, + qty: 171, + orderId: "82-8162453", + country: "Virgin Islands, U.S.", + }, + { + companyName: "Luettgen Inc", + productName: "Awesome Wooden Hat", + sellDate: "30/09/2023", + inStock: false, + qty: 6, + orderId: "02-8118250", + country: "Colombia", + }, + { + companyName: "Hegmann Inc", + productName: "Small Rubber Shoes", + sellDate: "16/02/2023", + inStock: true, + qty: 278, + orderId: "07-9773343", + country: "Central African Republic", + }, + { + companyName: "Kub Inc", + productName: "Small Frozen Tuna", + sellDate: "08/08/2023", + inStock: false, + qty: 264, + orderId: "66-4470479", + country: "Norfolk Island", + }, + { + companyName: "Kub Inc", + productName: "Tasty Frozen Table", + sellDate: "06/06/2023", + inStock: true, + qty: 494, + orderId: "13-1175339", + country: "Liechtenstein", + }, + { + companyName: "Hahn - Welch", + productName: "Small Frozen Tuna", + sellDate: "12/06/2023", + inStock: false, + qty: 485, + orderId: "32-9127309", + country: "Bahrain", + }, + { + companyName: "Nader - Fritsch", + productName: "Small Frozen Tuna", + sellDate: "08/04/2023", + inStock: true, + qty: 332, + orderId: "41-3774568", + country: "Montserrat", + }, + { + companyName: "Crona and Sons", + productName: "Small Fresh Bacon", + sellDate: "21/06/2023", + inStock: true, + qty: 104, + orderId: "48-9995090", + country: "Syrian Arab Republic", + }, + { + companyName: "Lind Group", + productName: "Rustic Cotton Ball", + sellDate: "17/08/2023", + inStock: false, + qty: 51, + orderId: "68-9599400", + country: "Czech Republic", + }, + { + companyName: "Labadie LLC", + productName: "Small Fresh Bacon", + sellDate: "20/04/2023", + inStock: true, + qty: 155, + orderId: "52-4334332", + country: "Croatia", + }, + { + companyName: "Doyle Group", + productName: "Sleek Wooden Bacon", + sellDate: "23/07/2023", + inStock: false, + qty: 465, + orderId: "63-8894526", + country: "Indonesia", + }, +]; + +const countries = data.reduce((acc, curr) => { + if (acc.includes(curr.country)) { + return acc; + } + return [...acc, curr.country]; +}, []); + + +/* end:skip-in-preview */ + +// Get the DOM element with the ID 'example1' where the Handsontable will be rendered +const app = document.getElementById("example1"); + +// Define configuration options for the Handsontable +const hotOptions = { + data, + height: 464, + colWidths: [140, 165, 100, 100, 100, 110, 178], + colHeaders: [ + "Company name", + "Product name", + "Sell date", + "In stock", + "Qty", + "Order ID", + "Country", + ], + columns: [ + { data: "companyName", type: "text" }, + { data: "productName", type: "text" }, + { + data: "sellDate", + type: "date", + dateFormat: "DD/MM/YYYY", + allowInvalid: false, + }, + { + data: "inStock", + type: "checkbox", + className: "htCenter", + }, + { data: "qty", type: "numeric" }, + { + data: "orderId", + type: "text", + }, + { + data: "country", + type: "dropdown", + source: countries, + }, + ], + dropdownMenu: true, + hiddenColumns: { + indicators: true, + }, + contextMenu: true, + navigableHeaders: true, // New accessibility feature + tabNavigation: true, // New accessibility feature + autoWrapRow: true, + autoWrapCol: true, + multiColumnSorting: true, + filters: true, + rowHeaders: true, + manualRowMove: true, + licenseKey: "non-commercial-and-evaluation", +}; + +// Initialize the Handsontable instance with the specified configuration options +let hotInstance = new Handsontable(app, hotOptions); + +// Helper function to set up checkbox event handling +const setupCheckbox = (element, callback) => + element.addEventListener("click", (clickEvent) => callback(element.checked)); + +// Set up event listeners for various checkboxes to update Handsontable settings. +// This allows us to change the Handsontable settings from the UI, showcasing +// the flexibility of Handsontable in configuring according to your needs. + +// Checkbox: Enable/Disable Tab Navigation +setupCheckbox(document.querySelector("#enable-tab-navigation"), (checked) => { + hotOptions.tabNavigation = checked; + hotInstance.updateSettings({ + tabNavigation: hotOptions.tabNavigation, + }); + console.log( + `Updated setting: tabNavigation to`, + hotInstance.getSettings().tabNavigation + ); +}); + +// Checkbox: Enable/Disable Header Navigation +setupCheckbox( + document.querySelector("#enable-header-navigation"), + (checked) => { + hotOptions.navigableHeaders = checked; + hotInstance.updateSettings({ + navigableHeaders: hotOptions.navigableHeaders, + }); + console.log( + `Updated setting: navigableHeaders to`, + hotInstance.getSettings().navigableHeaders + ); + } +); + +// Checkbox: Enable/Disable Cell Virtualization +setupCheckbox( + document.querySelector("#enable-cell-virtualization"), + (checked) => { + hotInstance.destroy(); + hotInstance = new Handsontable(document.getElementById("example1"), { + ...hotOptions, + renderAllRows: !checked, + renderAllColumns: !checked, + }); + console.log("Updated virtualization settings:", { + renderAllRows: hotInstance.getSettings().renderAllRows, + renderAllColumns: hotInstance.getSettings().renderAllColumns, +}); + } +); + +// Checkbox: Enable/Disable Cell Enter Editing +setupCheckbox( + document.querySelector("#enable-cell-enter-editing"), + (checked) => { + hotOptions.enterBeginsEditing = checked; + hotInstance.updateSettings({ + enterBeginsEditing: hotOptions.enterBeginsEditing, + }); + console.log( + `Updated setting: enable-cell-enter-editing to`, + hotInstance.getSettings().enterBeginsEditing + ); + } +); + +// Checkbox: Enable/Disable Arrow Navigation for First/Last Row +setupCheckbox( + document.querySelector("#enable-arrow-rl-first-last-column"), + (checked) => { + hotOptions.autoWrapRow = checked; + hotInstance.updateSettings({ + autoWrapRow: hotOptions.autoWrapRow, + }); + console.log( + `Updated setting: autoWrapRow to`, + hotInstance.getSettings().autoWrapRow + ); + } +); + +// Checkbox: Enable/Disable Arrow Navigation for First/Last Column +setupCheckbox( + document.querySelector("#enable-arrow-td-first-last-column"), + (checked) => { + hotOptions.autoWrapCol = checked; + hotInstance.updateSettings({ + autoWrapCol: hotOptions.autoWrapCol, + }); + console.log( + `Updated setting: autoWrapCol to`, + hotInstance.getSettings().autoWrapCol + ); + } +); + +// Checkbox: Enable/Disable Enter Key Focus for Editing +setupCheckbox( + document.querySelector("#enable-enter-focus-editing"), + (checked) => { + hotOptions.enterMoves = checked ? { col: 0, row: 1 } : { col: 0, row: 0 }; + hotInstance.updateSettings({ + enterMoves: hotOptions.enterMoves, + }); + console.log( + `Updated setting: enterMoves to`, + hotInstance.getSettings().enterMoves + ); + } +); \ No newline at end of file diff --git a/docs/content/guides/accessibility/accessibility/example2.css b/docs/content/guides/accessibility/accessibility/example2.css new file mode 100644 index 00000000000..931a8ef13d5 --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/example2.css @@ -0,0 +1,70 @@ +.checkbox-container { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.checkbox-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.checkbox-group > div { + display: flex; +} + +.checkbox-group > div > label { + display: flex; + gap: 0.2rem; +} + +.external-link { + margin-left: 0.5rem; + position: relative; + top: 2px; + color: black; +} + +.external-link:hover { + color: #0000ee; +} + +.placeholder-input { + max-width: 20rem; + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + line-height: 1.25rem; + color: black; + border: 1px solid #e4e4e7; + border-radius: 6px; +} + +.option-label { + align-items: flex-start; +} + +/* + We want the focus to be around input and label, in order to achieve this, + we remove focus from the input and add it to the label (wrapper in this case) + we then use the :focus-within pseudo class plus native focus styles + https://css-tricks.com/copy-the-browsers-native-focus-styles/ +*/ +.option-label:focus-within { + outline: 5px auto Highlight; + outline: 5px auto -webkit-focus-ring-color; +} + +.option-label > input:focus { + outline: none; +} + +/* fixes dark theme conflicting with text color */ +html.theme-dark .option-label { + color: #e5ebf1 !important; +} + +#example2 { + gap: 1rem; + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/docs/content/guides/accessibility/accessibility/example2.jsx b/docs/content/guides/accessibility/accessibility/example2.jsx new file mode 100644 index 00000000000..a5f0a9b57c2 --- /dev/null +++ b/docs/content/guides/accessibility/accessibility/example2.jsx @@ -0,0 +1,967 @@ +/* start:skip-in-preview */ +import { HotTable, HotColumn } from "@handsontable/react"; +import { useState } from "react"; + +import { registerAllModules } from "handsontable/registry"; +import "handsontable/dist/handsontable.full.min.css"; + +const data = [ + { + companyName: "Hodkiewicz - Hintz", + productName: "Rustic Soft Ball", + sellDate: "05/07/2023", + inStock: false, + qty: 82, + orderId: "16-3974628", + country: "United Kingdom", + }, + { + companyName: "Rath LLC", + productName: "Small Frozen Tuna", + sellDate: "31/05/2023", + inStock: false, + qty: 459, + orderId: "77-7839351", + country: "Costa Rica", + }, + { + companyName: "Reichert LLC", + productName: "Rustic Soft Ball", + sellDate: "16/03/2023", + inStock: false, + qty: 318, + orderId: "75-6343150", + country: "United States of America", + }, + { + companyName: "Kozey Inc", + productName: "Sleek Wooden Bacon", + sellDate: "24/04/2023", + inStock: true, + qty: 177, + orderId: "56-3608689", + country: "Pitcairn Islands", + }, + { + companyName: "Nader - Fritsch", + productName: "Awesome Wooden Hat", + sellDate: "29/04/2023", + inStock: true, + qty: 51, + orderId: "58-1204318", + country: "Argentina", + }, + { + companyName: "Gerhold - Rowe", + productName: "Tasty Frozen Table", + sellDate: "27/03/2023", + inStock: false, + qty: 439, + orderId: "62-6066132", + country: "Senegal", + }, + { + companyName: "Rath LLC", + productName: "Awesome Wooden Hat", + sellDate: "24/11/2022", + inStock: false, + qty: 493, + orderId: "76-7785471", + country: "Cyprus", + }, + { + companyName: "Kozey Inc", + productName: "Rustic Soft Ball", + sellDate: "11/08/2023", + inStock: false, + qty: 225, + orderId: "34-3551159", + country: "Saint Martin", + }, + { + companyName: "Hodkiewicz - Hintz", + productName: "Awesome Wooden Hat", + sellDate: "07/02/2023", + inStock: false, + qty: 261, + orderId: "77-1112514", + country: "Chile", + }, + { + companyName: "Hegmann Inc", + productName: "Tasty Frozen Table", + sellDate: "06/05/2023", + inStock: false, + qty: 439, + orderId: "12-3252385", + country: "Switzerland", + }, + { + companyName: "Weber Inc", + productName: "Awesome Wooden Hat", + sellDate: "22/04/2023", + inStock: true, + qty: 235, + orderId: "71-7639998", + country: "Brazil", + }, + { + companyName: "Jacobi - Kutch", + productName: "Sleek Wooden Bacon", + sellDate: "13/12/2022", + inStock: true, + qty: 163, + orderId: "68-1588829", + country: "Burkina Faso", + }, + { + companyName: "Jenkins LLC", + productName: "Small Rubber Shoes", + sellDate: "26/03/2023", + inStock: true, + qty: 8, + orderId: "61-6324553", + country: "Virgin Islands, U.S.", + }, + { + companyName: "Koepp and Sons", + productName: "Sleek Wooden Bacon", + sellDate: "04/05/2023", + inStock: true, + qty: 355, + orderId: "74-6985005", + country: "Mozambique", + }, + { + companyName: "Doyle Group", + productName: "Awesome Wooden Hat", + sellDate: "01/08/2023", + inStock: false, + qty: 186, + orderId: "84-4370131", + country: "Cocos (Keeling) Islands", + }, + { + companyName: "Rempel - Durgan", + productName: "Tasty Frozen Table", + sellDate: "30/09/2023", + inStock: false, + qty: 284, + orderId: "13-6461825", + country: "Monaco", + }, + { + companyName: "Lesch - Jakubowski", + productName: "Small Fresh Bacon", + sellDate: "26/09/2023", + inStock: true, + qty: 492, + orderId: "13-9465439", + country: "Iran", + }, + { + companyName: "Jacobi - Kutch", + productName: "Rustic Cotton Ball", + sellDate: "04/05/2023", + inStock: true, + qty: 300, + orderId: "76-5194058", + country: "Indonesia", + }, + { + companyName: "Gerhold - Rowe", + productName: "Rustic Cotton Ball", + sellDate: "07/07/2023", + inStock: true, + qty: 493, + orderId: "61-8600792", + country: "Norfolk Island", + }, + { + companyName: "Johnston - Wisozk", + productName: "Small Fresh Fish", + sellDate: "14/07/2023", + inStock: false, + qty: 304, + orderId: "10-6007287", + country: "Romania", + }, + { + companyName: "Gutkowski Inc", + productName: "Small Fresh Bacon", + sellDate: "10/01/2023", + inStock: true, + qty: 375, + orderId: "25-1164132", + country: "Afghanistan", + }, + { + companyName: "Koepp and Sons", + productName: "Small Fresh Fish", + sellDate: "30/03/2023", + inStock: false, + qty: 365, + orderId: "75-7975820", + country: "Germany", + }, + { + companyName: "Zboncak and Sons", + productName: "Small Fresh Fish", + sellDate: "17/08/2023", + inStock: false, + qty: 308, + orderId: "59-6251875", + country: "Tajikistan", + }, + { + companyName: "Mills Group", + productName: "Rustic Soft Ball", + sellDate: "30/09/2023", + inStock: false, + qty: 191, + orderId: "67-7521441", + country: "Puerto Rico", + }, + { + companyName: "Zboncak and Sons", + productName: "Awesome Wooden Hat", + sellDate: "18/03/2023", + inStock: false, + qty: 208, + orderId: "19-4264192", + country: "Bolivia", + }, + { + companyName: "Rath LLC", + productName: "Rustic Soft Ball", + sellDate: "14/06/2023", + inStock: true, + qty: 191, + orderId: "78-5742060", + country: "Benin", + }, + { + companyName: "Upton - Reichert", + productName: "Tasty Frozen Table", + sellDate: "27/02/2023", + inStock: false, + qty: 45, + orderId: "26-6191298", + country: "Tunisia", + }, + { + companyName: "Carroll Group", + productName: "Rustic Soft Ball", + sellDate: "12/12/2022", + inStock: true, + qty: 385, + orderId: "13-7828353", + country: "French Southern Territories", + }, + { + companyName: "Reichel Group", + productName: "Small Frozen Tuna", + sellDate: "12/12/2022", + inStock: true, + qty: 117, + orderId: "67-9643738", + country: "Mongolia", + }, + { + companyName: "Kozey Inc", + productName: "Rustic Soft Ball", + sellDate: "24/03/2023", + inStock: false, + qty: 335, + orderId: "78-1331653", + country: "Angola", + }, + { + companyName: "Brown LLC", + productName: "Small Rubber Shoes", + sellDate: "13/06/2023", + inStock: true, + qty: 305, + orderId: "63-2315723", + country: "French Southern Territories", + }, + { + companyName: "Weber Inc", + productName: "Rustic Cotton Ball", + sellDate: "07/09/2023", + inStock: true, + qty: 409, + orderId: "53-6782557", + country: "Indonesia", + }, + { + companyName: "OReilly LLC", + productName: "Tasty Frozen Table", + sellDate: "18/05/2023", + inStock: true, + qty: 318, + orderId: "91-7787675", + country: "Mayotte", + }, + { + companyName: "Weber Inc", + productName: "Sleek Wooden Bacon", + sellDate: "20/04/2023", + inStock: false, + qty: 234, + orderId: "41-3560672", + country: "Switzerland", + }, + { + companyName: "Hodkiewicz Inc", + productName: "Tasty Frozen Table", + sellDate: "19/10/2023", + inStock: true, + qty: 136, + orderId: "48-6028776", + country: "Peru", + }, + { + companyName: "Lesch and Sons", + productName: "Rustic Cotton Ball", + sellDate: "29/09/2023", + inStock: false, + qty: 187, + orderId: "84-3770456", + country: "Central African Republic", + }, + { + companyName: "Pouros - Brakus", + productName: "Small Frozen Tuna", + sellDate: "29/01/2023", + inStock: false, + qty: 350, + orderId: "08-4844950", + country: "Isle of Man", + }, + { + companyName: "Batz - Rice", + productName: "Small Rubber Shoes", + sellDate: "06/11/2023", + inStock: false, + qty: 252, + orderId: "88-4899852", + country: "Burundi", + }, + { + companyName: "Kub Inc", + productName: "Small Fresh Fish", + sellDate: "05/09/2023", + inStock: true, + qty: 306, + orderId: "06-5022461", + country: "Mauritius", + }, + { + companyName: "Hills and Sons", + productName: "Small Frozen Tuna", + sellDate: "07/11/2023", + inStock: false, + qty: 435, + orderId: "99-5539911", + country: "Somalia", + }, + { + companyName: "Shanahan - Boyle", + productName: "Small Frozen Tuna", + sellDate: "19/06/2023", + inStock: true, + qty: 171, + orderId: "82-8162453", + country: "Virgin Islands, U.S.", + }, + { + companyName: "Luettgen Inc", + productName: "Awesome Wooden Hat", + sellDate: "30/09/2023", + inStock: false, + qty: 6, + orderId: "02-8118250", + country: "Colombia", + }, + { + companyName: "Hegmann Inc", + productName: "Small Rubber Shoes", + sellDate: "16/02/2023", + inStock: true, + qty: 278, + orderId: "07-9773343", + country: "Central African Republic", + }, + { + companyName: "Kub Inc", + productName: "Small Frozen Tuna", + sellDate: "08/08/2023", + inStock: false, + qty: 264, + orderId: "66-4470479", + country: "Norfolk Island", + }, + { + companyName: "Kub Inc", + productName: "Tasty Frozen Table", + sellDate: "06/06/2023", + inStock: true, + qty: 494, + orderId: "13-1175339", + country: "Liechtenstein", + }, + { + companyName: "Hahn - Welch", + productName: "Small Frozen Tuna", + sellDate: "12/06/2023", + inStock: false, + qty: 485, + orderId: "32-9127309", + country: "Bahrain", + }, + { + companyName: "Nader - Fritsch", + productName: "Small Frozen Tuna", + sellDate: "08/04/2023", + inStock: true, + qty: 332, + orderId: "41-3774568", + country: "Montserrat", + }, + { + companyName: "Crona and Sons", + productName: "Small Fresh Bacon", + sellDate: "21/06/2023", + inStock: true, + qty: 104, + orderId: "48-9995090", + country: "Syrian Arab Republic", + }, + { + companyName: "Lind Group", + productName: "Rustic Cotton Ball", + sellDate: "17/08/2023", + inStock: false, + qty: 51, + orderId: "68-9599400", + country: "Czech Republic", + }, + { + companyName: "Labadie LLC", + productName: "Small Fresh Bacon", + sellDate: "20/04/2023", + inStock: true, + qty: 155, + orderId: "52-4334332", + country: "Croatia", + }, + { + companyName: "Doyle Group", + productName: "Sleek Wooden Bacon", + sellDate: "23/07/2023", + inStock: false, + qty: 465, + orderId: "63-8894526", + country: "Indonesia", + }, +]; + +const countries = data.reduce((acc, curr) => { + if (acc.includes(curr.country)) { + return acc; + } + return [...acc, curr.country]; +}, []); +/* end:skip-in-preview */ + +// Handsontable options +const hotOptions = { + data, + height: 464, + colWidths: [140, 165, 100, 100, 100, 110, 178], + colHeaders: [ + "Company name", + "Product name", + "Sell date", + "In stock", + "Qty", + "Order ID", + "Country", + ], + dropdownMenu: true, + hiddenColumns: { + indicators: true, + }, + multiColumnSorting: true, + filters: true, + rowHeaders: true, + manualRowMove: true, + licenseKey: "non-commercial-and-evaluation", +}; + +function App() { + const [toggleableOptions, setToggleableOptions] = useState({ + tabNavigation: true, + navigableHeaders: true, + renderAllRows: false, + renderAllColumns: false, + enterBeginsEditing: true, + autoWrapRow: true, + autoWrapCol: true, + enterMoves: { col: 0, row: 1 }, + }); + + return ( + <> + {/* DemoOptions component for changing Handsontable options */} + + + + + {/* Handsontable component with dynamic options */} + + {/* Define HotColumns for the data */} + + + + + + + + + + + ); +} + +// Demo Options allows you to change the Handsontable options +// This allows us to change the Handsontable settings from the UI, showcasing +// the flexibility of Handsontable in configuring according to your needs. +function DemoOptions({ + tabNavigation, + navigableHeaders, + renderAllRows, + renderAllColumns, + enterBeginsEditing, + autoWrapRow, + autoWrapCol, + enterMoves, + changeToggleOptions, +}) { + // on checkbox change, update handsontable option + const handleCheckboxChange = (checkboxName) => { + switch (checkboxName) { + case "enable-tab-navigation": + changeToggleOptions((existing) => ({ + ...existing, + tabNavigation: !tabNavigation, + })); + break; + case "enable-header-navigation": + changeToggleOptions((existing) => ({ + ...existing, + navigableHeaders: !navigableHeaders, + })); + break; + case "enable-cell-virtualization": + changeToggleOptions((existing) => ({ + ...existing, + renderAllRows: !renderAllRows, + renderAllColumns: !renderAllColumns, + })); + break; + case "enable-cell-enter-editing": + changeToggleOptions((existing) => ({ + ...existing, + enterBeginsEditing: !enterBeginsEditing, + })); + break; + case "enable-arrow-rl-first-last-column": + changeToggleOptions((existing) => ({ + ...existing, + autoWrapRow: !autoWrapRow, + })); + break; + case "enable-arrow-td-first-last-column": + changeToggleOptions((existing) => ({ + ...existing, + autoWrapCol: !autoWrapCol, + })); + break; + case "enable-enter-focus-editing": + changeToggleOptions((existing) => ({ + ...existing, + enterMoves: + enterMoves.row !== 1 ? { col: 0, row: 1 } : { col: 0, row: 0 }, + })); + break; + default: + break; + } + }; + + return ( + <> +
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+ + ); +} + +ReactDOM.render(, document.getElementById("example2")); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu.md b/docs/content/guides/accessories-and-menus/context-menu/context-menu.md similarity index 71% rename from docs/content/guides/accessories-and-menus/context-menu.md rename to docs/content/guides/accessories-and-menus/context-menu/context-menu.md index b18aa17ca13..90398df354f 100644 --- a/docs/content/guides/accessories-and-menus/context-menu.md +++ b/docs/content/guides/accessories-and-menus/context-menu/context-menu.md @@ -68,40 +68,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/context-menu/example1.jsx) ::: @@ -187,40 +154,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/context-menu/example2.jsx) ::: @@ -236,56 +170,7 @@ To see the context menu, right-click on a cell: ::: example #example4 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { ContextMenu } from 'handsontable/plugins/contextMenu'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( -
- -
- ) -} - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example4')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/context-menu/example4.jsx) ::: @@ -406,106 +291,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - Custom option'; // Name can contain HTML - }, - hidden() { // `hidden` can be a boolean or a function - // Hide the option when the first column was clicked - return this.getSelectedLast()[1] == 0; // `this` === hot - }, - callback(key, selection, clickEvent) { // Callback for specific option - setTimeout(() => { - alert('Hello world!'); // Fire alert after menu close (with timeout) - }, 0); - } - }, - colors: { // Own custom option - name: 'Colors...', - submenu: { - // Custom option with submenu of items - items: [{ - // Key must be in the form 'parent_key:child_key' - key: 'colors:red', - name: 'Red', - callback(key, selection, clickEvent) { - setTimeout(() => { - alert('You clicked red!'); - }, 0); - } - }, - { key: 'colors:green', name: 'Green' }, - { key: 'colors:blue', name: 'Blue' } - ] - } - }, - credits: { // Own custom property - // Custom rendered element in the context menu - renderer(hot, wrapper, row, col, prop, itemValue) { - const elem = document.createElement('marquee'); - - elem.style.cssText = 'background: lightgray;'; - elem.textContent = 'Brought to you by...'; - - return elem; - }, - disableSelection: true, // Prevent mouseoever from highlighting the item for selection - isCommand: false // Prevent clicks from executing command and closing the menu - } - } - }} - /> - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/context-menu/example3.jsx) ::: @@ -526,14 +312,14 @@ ReactDOM.render(, document.getElementById('example3')); ### Related guides -- [Adding comments via the context menu](@/guides/cell-features/comments.md#add-comments-via-the-context-menu) -- [Clipboard: Context menu](@/guides/cell-features/clipboard.md#context-menu) -- [Icon pack](@/guides/accessories-and-menus/icon-pack.md) +- [Adding comments via the context menu](@/guides/cell-features/comments/comments.md#add-comments-via-the-context-menu) +- [Clipboard: Context menu](@/guides/cell-features/clipboard/clipboard.md#context-menu) +- [Icon pack](@/guides/accessories-and-menus/icon-pack/icon-pack.md) ::: only-for javascript -- [Custom context menu in React](@/react/guides/accessories-and-menus/context-menu.md) -- [Custom context menu in Angular](@/guides/integrate-with-angular/angular-custom-context-menu-example.md) -- [Custom context menu in Vue 2](@/guides/integrate-with-vue/vue-custom-context-menu-example.md) -- [Custom context menu in Vue 3](@/guides/integrate-with-vue3/vue3-custom-context-menu-example.md) +- [Custom context menu in React](@/react/guides/accessories-and-menus/context-menu/context-menu.md) +- [Custom context menu in Angular](@/guides/integrate-with-angular/angular-custom-context-menu-example/angular-custom-context-menu-example.md) +- [Custom context menu in Vue 2](@/guides/integrate-with-vue/vue-custom-context-menu-example/vue-custom-context-menu-example.md) +- [Custom context menu in Vue 3](@/guides/integrate-with-vue3/vue3-custom-context-menu-example/vue3-custom-context-menu-example.md) ::: ### Related blog articles diff --git a/docs/content/guides/accessories-and-menus/context-menu/example1.js b/docs/content/guides/accessories-and-menus/context-menu/example1.js new file mode 100644 index 00000000000..fca04ff1171 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example1.js @@ -0,0 +1,21 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'], + ['2017', 10, 11, 12, 13, 15, 16], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ['2021', 10, 11, 12, 13, 15, 16] + ], + rowHeaders: true, + colHeaders: true, + contextMenu: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example1.jsx b/docs/content/guides/accessories-and-menus/context-menu/example1.jsx new file mode 100644 index 00000000000..25cd58ed368 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example1.jsx @@ -0,0 +1,32 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example2.js b/docs/content/guides/accessories-and-menus/context-menu/example2.js new file mode 100644 index 00000000000..4befc87d450 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example2.js @@ -0,0 +1,21 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'], + ['2017', 10, 11, 12, 13, 15, 16], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ['2021', 10, 11, 12, 13, 15, 16] + ], + rowHeaders: true, + colHeaders: true, + contextMenu: ['row_above', 'row_below', 'remove_row', 'clear_column'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example2.jsx b/docs/content/guides/accessories-and-menus/context-menu/example2.jsx new file mode 100644 index 00000000000..bea3a24d73e --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example2.jsx @@ -0,0 +1,32 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example3.js b/docs/content/guides/accessories-and-menus/context-menu/example3.js new file mode 100644 index 00000000000..f9863e3c6d7 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example3.js @@ -0,0 +1,88 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'], + ['2017', 10, 11, 12, 13, 15, 16], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ['2021', 10, 11, 12, 13, 15, 16] + ], + rowHeaders: true, + colHeaders: true, + licenseKey: 'non-commercial-and-evaluation', + height: 'auto', + contextMenu: { + callback(key, selection, clickEvent) { + // Common callback for all options + console.log(key, selection, clickEvent); + }, + items: { + row_above: { + disabled() { // `disabled` can be a boolean or a function + // Disable option when first row was clicked + return this.getSelectedLast()[0] === 0; // `this` === hot + } + }, + // A separator line can also be added like this: + // 'sp1': { name: '---------' } + // and the key has to be unique + sp1: '---------', + row_below: { + name: 'Click to add row below' // Set custom text for predefined option + }, + about: { // Own custom option + name() { // `name` can be a string or a function + return 'Custom option'; // Name can contain HTML + }, + hidden() { // `hidden` can be a boolean or a function + // Hide the option when the first column was clicked + return this.getSelectedLast()[1] == 0; // `this` === hot + }, + callback(key, selection, clickEvent) { // Callback for specific option + setTimeout(() => { + alert('Hello world!'); // Fire alert after menu close (with timeout) + }, 0); + } + }, + colors: { // Own custom option + name: 'Colors...', + submenu: { + // Custom option with submenu of items + items: [ + { + // Key must be in the form 'parent_key:child_key' + key: 'colors:red', + name: 'Red', + callback(key, selection, clickEvent) { + setTimeout(() => { + alert('You clicked red!'); + }, 0); + } + }, + { key: 'colors:green', name: 'Green' }, + { key: 'colors:blue', name: 'Blue' } + ] + } + }, + credits: { // Own custom property + // Custom rendered element in the context menu + renderer(hot, wrapper, row, col, prop, itemValue) { + const elem = document.createElement('marquee'); + + elem.style.cssText = 'background: lightgray;'; + elem.textContent = 'Brought to you by...'; + + return elem; + }, + disableSelection: true, // Prevent mouseoever from highlighting the item for selection + isCommand: false // Prevent clicks from executing command and closing the menu + } + } + }, + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example3.jsx b/docs/content/guides/accessories-and-menus/context-menu/example3.jsx new file mode 100644 index 00000000000..746e5420dea --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example3.jsx @@ -0,0 +1,98 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + Custom option'; // Name can contain HTML + }, + hidden() { // `hidden` can be a boolean or a function + // Hide the option when the first column was clicked + return this.getSelectedLast()[1] == 0; // `this` === hot + }, + callback(key, selection, clickEvent) { // Callback for specific option + setTimeout(() => { + alert('Hello world!'); // Fire alert after menu close (with timeout) + }, 0); + } + }, + colors: { // Own custom option + name: 'Colors...', + submenu: { + // Custom option with submenu of items + items: [{ + // Key must be in the form 'parent_key:child_key' + key: 'colors:red', + name: 'Red', + callback(key, selection, clickEvent) { + setTimeout(() => { + alert('You clicked red!'); + }, 0); + } + }, + { key: 'colors:green', name: 'Green' }, + { key: 'colors:blue', name: 'Blue' } + ] + } + }, + credits: { // Own custom property + // Custom rendered element in the context menu + renderer(hot, wrapper, row, col, prop, itemValue) { + const elem = document.createElement('marquee'); + + elem.style.cssText = 'background: lightgray;'; + elem.textContent = 'Brought to you by...'; + + return elem; + }, + disableSelection: true, // Prevent mouseoever from highlighting the item for selection + isCommand: false // Prevent clicks from executing command and closing the menu + } + } + }} + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/context-menu/example4.jsx b/docs/content/guides/accessories-and-menus/context-menu/example4.jsx new file mode 100644 index 00000000000..0e624e8012a --- /dev/null +++ b/docs/content/guides/accessories-and-menus/context-menu/example4.jsx @@ -0,0 +1,48 @@ +import { HotTable } from '@handsontable/react'; +import { ContextMenu } from 'handsontable/plugins/contextMenu'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( +
+ +
+ ) +} + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example4')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example1.html b/docs/content/guides/accessories-and-menus/export-to-csv/example1.html new file mode 100644 index 00000000000..3acaf0a59a5 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example1.html @@ -0,0 +1,5 @@ +
+ +
+ +
\ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example1.js b/docs/content/guides/accessories-and-menus/export-to-csv/example1.js new file mode 100644 index 00000000000..06e14d69c27 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example1.js @@ -0,0 +1,41 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const button = document.querySelector('#export-file'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7'], + ], + colHeaders: true, + rowHeaders: true, + hiddenRows: { rows: [1, 3, 5], indicators: true }, + hiddenColumns: { columns: [1, 3, 5], indicators: true }, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +const exportPlugin = hot.getPlugin('exportFile'); + +button.addEventListener('click', () => { + exportPlugin.downloadFile('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + fileExtension: 'csv', + filename: 'Handsontable-CSV-file_[YYYY]-[MM]-[DD]', + mimeType: 'text/csv', + rowDelimiter: '\r\n', + rowHeaders: true + }); +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example1.jsx b/docs/content/guides/accessories-and-menus/export-to-csv/example1.jsx new file mode 100644 index 00000000000..e6195a85d60 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example1.jsx @@ -0,0 +1,65 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let buttonClickCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + const exportPlugin = hot.getPlugin('exportFile'); + buttonClickCallback = () => { + exportPlugin.downloadFile('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + fileExtension: 'csv', + filename: 'Handsontable-CSV-file_[YYYY]-[MM]-[DD]', + mimeType: 'text/csv', + rowDelimiter: '\r\n', + rowHeaders: true + }); + }; + }); + + return ( + <> + +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example2.html b/docs/content/guides/accessories-and-menus/export-to-csv/example2.html new file mode 100644 index 00000000000..75a01f62fc1 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example2.html @@ -0,0 +1,5 @@ +
+ +
+ +
\ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example2.js b/docs/content/guides/accessories-and-menus/export-to-csv/example2.js new file mode 100644 index 00000000000..272667ee2f9 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example2.js @@ -0,0 +1,41 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const button = document.querySelector('#export-blob'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7'], + ], + colHeaders: true, + rowHeaders: true, + hiddenRows: { rows: [1, 3, 5], indicators: true }, + hiddenColumns: { columns: [1, 3, 5], indicators: true }, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +const exportPlugin = hot.getPlugin('exportFile'); + +button.addEventListener('click', () => { + const exportedBlob = exportPlugin.exportAsBlob('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + mimeType: 'text/csv', + rowDelimiter: '\r\n', + rowHeaders: true + }); + + console.log(exportedBlob); +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example2.jsx b/docs/content/guides/accessories-and-menus/export-to-csv/example2.jsx new file mode 100644 index 00000000000..51cc5c150c9 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example2.jsx @@ -0,0 +1,65 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let buttonClickCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + const exportPlugin = hot.getPlugin('exportFile'); + + buttonClickCallback = () => { + const exportedBlob = exportPlugin.exportAsBlob('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + mimeType: 'text/csv', + rowDelimiter: '\r\n', + rowHeaders: true + }); + + console.log(exportedBlob); + }; + }); + + return ( + <> + +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example3.html b/docs/content/guides/accessories-and-menus/export-to-csv/example3.html new file mode 100644 index 00000000000..a7eead61179 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example3.html @@ -0,0 +1,5 @@ +
+ +
+ +
\ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example3.js b/docs/content/guides/accessories-and-menus/export-to-csv/example3.js new file mode 100644 index 00000000000..b89fb2956fb --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example3.js @@ -0,0 +1,40 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const button = document.querySelector('#export-string'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7'], + ], + colHeaders: true, + rowHeaders: true, + hiddenRows: { rows: [1, 3, 5], indicators: true }, + hiddenColumns: { columns: [1, 3, 5], indicators: true }, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +const exportPlugin = hot.getPlugin('exportFile'); + +button.addEventListener('click', () => { + const exportedString = exportPlugin.exportAsString('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + rowDelimiter: '\r\n', + rowHeaders: true + }); + + console.log(exportedString); +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv/example3.jsx b/docs/content/guides/accessories-and-menus/export-to-csv/example3.jsx new file mode 100644 index 00000000000..ba557af24cd --- /dev/null +++ b/docs/content/guides/accessories-and-menus/export-to-csv/example3.jsx @@ -0,0 +1,64 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let buttonClickCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + const exportPlugin = hot.getPlugin('exportFile'); + + buttonClickCallback = () => { + const exportedString = exportPlugin.exportAsString('csv', { + bom: false, + columnDelimiter: ',', + columnHeaders: false, + exportHiddenColumns: true, + exportHiddenRows: true, + rowDelimiter: '\r\n', + rowHeaders: true + }); + + console.log(exportedString); + }; + }); + + return ( + <> + +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/export-to-csv.md b/docs/content/guides/accessories-and-menus/export-to-csv/export-to-csv.md similarity index 61% rename from docs/content/guides/accessories-and-menus/export-to-csv.md rename to docs/content/guides/accessories-and-menus/export-to-csv/export-to-csv.md index d884ccd26be..3695df886d8 100644 --- a/docs/content/guides/accessories-and-menus/export-to-csv.md +++ b/docs/content/guides/accessories-and-menus/export-to-csv/export-to-csv.md @@ -89,73 +89,7 @@ button.addEventListener('click', () => { ::: example #example1 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let buttonClickCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - const exportPlugin = hot.getPlugin('exportFile'); - buttonClickCallback = () => { - exportPlugin.downloadFile('csv', { - bom: false, - columnDelimiter: ',', - columnHeaders: false, - exportHiddenColumns: true, - exportHiddenRows: true, - fileExtension: 'csv', - filename: 'Handsontable-CSV-file_[YYYY]-[MM]-[DD]', - mimeType: 'text/csv', - rowDelimiter: '\r\n', - rowHeaders: true - }); - }; - }); - - return ( - <> - -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/export-to-csv/example1.jsx) ::: @@ -229,73 +163,7 @@ button.addEventListener('click', () => { ::: example #example2 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let buttonClickCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - const exportPlugin = hot.getPlugin('exportFile'); - - buttonClickCallback = () => { - const exportedBlob = exportPlugin.exportAsBlob('csv', { - bom: false, - columnDelimiter: ',', - columnHeaders: false, - exportHiddenColumns: true, - exportHiddenRows: true, - mimeType: 'text/csv', - rowDelimiter: '\r\n', - rowHeaders: true - }); - - console.log(exportedBlob); - }; - }); - - return ( - <> - -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/export-to-csv/example2.jsx) ::: @@ -367,72 +235,7 @@ button.addEventListener('click', () => { ::: example #example3 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let buttonClickCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - const exportPlugin = hot.getPlugin('exportFile'); - - buttonClickCallback = () => { - const exportedString = exportPlugin.exportAsString('csv', { - bom: false, - columnDelimiter: ',', - columnHeaders: false, - exportHiddenColumns: true, - exportHiddenRows: true, - rowDelimiter: '\r\n', - rowHeaders: true - }); - - console.log(exportedString); - }; - }); - - return ( - <> - -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/export-to-csv/example3.jsx) ::: @@ -446,7 +249,7 @@ ReactDOM.render(, document.getElementById('example3')); To use the Handsontable API, you'll need access to the Handsontable instance. You can do that by utilizing a reference to the `HotTable` component, and reading its `hotInstance` property. -For more information, see the [Instance methods](@/guides/getting-started/react-methods.md) page. +For more information, see the [Instance methods](@/guides/getting-started/react-methods/react-methods.md) page. ::: diff --git a/docs/content/guides/accessories-and-menus/icon-pack.md b/docs/content/guides/accessories-and-menus/icon-pack/icon-pack.md similarity index 100% rename from docs/content/guides/accessories-and-menus/icon-pack.md rename to docs/content/guides/accessories-and-menus/icon-pack/icon-pack.md diff --git a/docs/content/guides/accessories-and-menus/undo-redo/example.javascript b/docs/content/guides/accessories-and-menus/undo-redo/example.javascript new file mode 100644 index 00000000000..a62259624e0 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/undo-redo/example.javascript @@ -0,0 +1,24 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ['A6', 'B6', 'C6', 'D6', 'E6'], + ['A7', 'B7', 'C7', 'D7', 'E7'], + ['A8', 'B8', 'C8', 'D8', 'E8'], + ['A9', 'B9', 'C9', 'D9', 'E9'], + ], + rowHeaders: true, + colHeaders: true, + stretchH: 'all', + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/undo-redo/example.jsx b/docs/content/guides/accessories-and-menus/undo-redo/example.jsx new file mode 100644 index 00000000000..a6815e92b47 --- /dev/null +++ b/docs/content/guides/accessories-and-menus/undo-redo/example.jsx @@ -0,0 +1,35 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/accessories-and-menus/undo-redo.md b/docs/content/guides/accessories-and-menus/undo-redo/undo-redo.md similarity index 77% rename from docs/content/guides/accessories-and-menus/undo-redo.md rename to docs/content/guides/accessories-and-menus/undo-redo/undo-redo.md index dd52e13ec3c..65f97c01bbd 100644 --- a/docs/content/guides/accessories-and-menus/undo-redo.md +++ b/docs/content/guides/accessories-and-menus/undo-redo/undo-redo.md @@ -77,43 +77,7 @@ const hot = new Handsontable(container, { ::: example #example :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/accessories-and-menus/undo-redo/example.jsx) ::: @@ -124,10 +88,10 @@ ReactDOM.render(, document.getElementById('example')); Not all user-triggered actions are recorded in the undo-and-redo history. Here's the list of all unsupported features: -- [Rows sorting](@/guides/rows/rows-sorting.md) -- [Column filter](@/guides/columns/column-filter.md) -- [Column moving](@/guides/columns/column-moving.md) -- [Row moving](@/guides/rows/row-moving.md) +- [Rows sorting](@/guides/rows/rows-sorting/rows-sorting.md) +- [Column filter](@/guides/columns/column-filter/column-filter.md) +- [Column moving](@/guides/columns/column-moving/column-moving.md) +- [Row moving](@/guides/rows/row-moving/row-moving.md) ## Related keyboard shortcuts diff --git a/docs/content/guides/cell-features/autofill-values.md b/docs/content/guides/cell-features/autofill-values/autofill-values.md similarity index 63% rename from docs/content/guides/cell-features/autofill-values.md rename to docs/content/guides/cell-features/autofill-values/autofill-values.md index 0bea625d765..3afa7959c9b 100644 --- a/docs/content/guides/cell-features/autofill-values.md +++ b/docs/content/guides/cell-features/autofill-values/autofill-values.md @@ -69,42 +69,7 @@ hot.loadData(data); ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const data = [ - ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], - ['2017', 10, 11, 12, 13], - ['2018', 20, 11, 14, 13], - ['2019', 30, 15, 12, 13], - ['2020', '', '', '', ''], - ['2021', '', '', '', ''] - ]; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/autofill-values/example1.jsx) ::: @@ -155,45 +120,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const data = [ - ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], - ['2017', 10, 11, 12, 13], - ['2018', 20, 11, 14, 13], - ['2019', 30, 15, 12, 13], - ['2020', '', '', '', ''], - ['2021', '', '', '', ''] - ]; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/autofill-values/example2.jsx) ::: diff --git a/docs/content/guides/cell-features/autofill-values/example1.js b/docs/content/guides/cell-features/autofill-values/example1.js new file mode 100644 index 00000000000..020344b4712 --- /dev/null +++ b/docs/content/guides/cell-features/autofill-values/example1.js @@ -0,0 +1,25 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', 10, 11, 12, 13], + ['2018', 20, 11, 14, 13], + ['2019', 30, 15, 12, 13], + ['2020', '', '', '', ''], + ['2021', '', '', '', ''] +]; + +const hot = new Handsontable(container, { + rowHeaders: true, + colHeaders: true, + fillHandle: true, // possible values: true, false, "horizontal", "vertical", + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +// or, use `updateData()` to replace `data` without resetting states +hot.loadData(data); \ No newline at end of file diff --git a/docs/content/guides/cell-features/autofill-values/example1.jsx b/docs/content/guides/cell-features/autofill-values/example1.jsx new file mode 100644 index 00000000000..e435ffa1c29 --- /dev/null +++ b/docs/content/guides/cell-features/autofill-values/example1.jsx @@ -0,0 +1,34 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', 10, 11, 12, 13], + ['2018', 20, 11, 14, 13], + ['2019', 30, 15, 12, 13], + ['2020', '', '', '', ''], + ['2021', '', '', '', ''] + ]; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/autofill-values/example2.js b/docs/content/guides/cell-features/autofill-values/example2.js new file mode 100644 index 00000000000..e9f29fe8c6c --- /dev/null +++ b/docs/content/guides/cell-features/autofill-values/example2.js @@ -0,0 +1,26 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', 10, 11, 12, 13], + ['2018', 20, 11, 14, 13], + ['2019', 30, 15, 12, 13], + ['2020', '', '', '', ''], + ['2021', '', '', '', ''] +]; + +const hot = new Handsontable(container, { + data: data, + rowHeaders: true, + colHeaders: true, + fillHandle: { + direction: 'vertical', + autoInsertRow: true + }, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/autofill-values/example2.jsx b/docs/content/guides/cell-features/autofill-values/example2.jsx new file mode 100644 index 00000000000..6c9cd744bea --- /dev/null +++ b/docs/content/guides/cell-features/autofill-values/example2.jsx @@ -0,0 +1,37 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', 10, 11, 12, 13], + ['2018', 20, 11, 14, 13], + ['2019', 30, 15, 12, 13], + ['2020', '', '', '', ''], + ['2021', '', '', '', ''] + ]; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard.md b/docs/content/guides/cell-features/clipboard/clipboard.md similarity index 73% rename from docs/content/guides/cell-features/clipboard.md rename to docs/content/guides/cell-features/clipboard/clipboard.md index 62948f856fd..bb79b012cf1 100644 --- a/docs/content/guides/cell-features/clipboard.md +++ b/docs/content/guides/cell-features/clipboard/clipboard.md @@ -50,7 +50,7 @@ When the context menu is enabled, it includes default items, including copy & cu - Copy - as a predefined key `copy` - Cut - as a predefined key `cut` -You can use them in the same way as the rest of the predefined items in the [context menu](@/guides/accessories-and-menus/context-menu.md#context-menu-with-specific-options). These operations are executed by `document.execCommand()`. +You can use them in the same way as the rest of the predefined items in the [context menu](@/guides/accessories-and-menus/context-menu/context-menu.md#context-menu-with-specific-options). These operations are executed by `document.execCommand()`. ::: only-for javascript @@ -87,39 +87,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/clipboard/example1.jsx) ::: @@ -133,7 +101,7 @@ ReactDOM.render(, document.getElementById('example1')); To use the Handsontable API, you'll need access to the Handsontable instance. You can do that by utilizing a reference to the `HotTable` component, and reading its `hotInstance` property. -For more information, see the [Instance methods](@/guides/getting-started/react-methods.md) page. +For more information, see the [Instance methods](@/guides/getting-started/react-methods/react-methods.md) page. ::: @@ -214,81 +182,7 @@ cutBtn.addEventListener('click', function() { ::: example #example3 :react -```jsx -import { useEffect, useRef } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - const copyBtnClickCallback = function() { - document.execCommand('copy'); - }; - const cutBtnClickCallback = function() { - document.execCommand('cut'); - }; - let copyBtnMousedownCallback; - let cutBtnMousedownCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - copyBtnMousedownCallback = function() { - hot.selectCell(1, 1); - }; - cutBtnMousedownCallback = function() { - hot.selectCell(1, 1); - }; - }); - - return ( - <> - -
- - -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/clipboard/example3.jsx) ::: @@ -309,7 +203,7 @@ Examples of how to use them are provided in their descriptions. ### Copy with headers -You can let the end user copy the contents of column headers, by enabling additional [context menu](@/guides/accessories-and-menus/context-menu.md) items: +You can let the end user copy the contents of column headers, by enabling additional [context menu](@/guides/accessories-and-menus/context-menu/context-menu.md) items: | Context menu item | Copied area | | ------------------------- | ------------------------------------------------------------------------- | @@ -363,49 +257,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/clipboard/example2.jsx) ::: @@ -435,7 +287,7 @@ To copy column headers programmatically, call the [`copyPaste.copy()`](@/api/cop To use the Handsontable API, you'll need access to the Handsontable instance. You can do that by utilizing a reference to the `HotTable` component, and reading its `hotInstance` property. -For more information, see the [Instance methods](@/guides/getting-started/react-methods.md) page. +For more information, see the [Instance methods](@/guides/getting-started/react-methods/react-methods.md) page. ::: diff --git a/docs/content/guides/cell-features/clipboard/example1.js b/docs/content/guides/cell-features/clipboard/example1.js new file mode 100644 index 00000000000..890defb9b74 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example1.js @@ -0,0 +1,20 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ], + rowHeaders: true, + colHeaders: true, + contextMenu: ['copy', 'cut'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example1.jsx b/docs/content/guides/cell-features/clipboard/example1.jsx new file mode 100644 index 00000000000..38ec9e49ac3 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example1.jsx @@ -0,0 +1,31 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example2.js b/docs/content/guides/cell-features/clipboard/example2.js new file mode 100644 index 00000000000..19910dfaf81 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example2.js @@ -0,0 +1,29 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5', 'J5'], + ], + contextMenu: true, + copyPaste: { + copyColumnHeaders: true, + copyColumnGroupHeaders: true, + copyColumnHeadersOnly: true, + }, + colHeaders: true, + rowHeaders: true, + height: 'auto', + nestedHeaders: [ + ['A', { label: 'B', colspan: 2 }, { label: 'C', colspan: 2 }, { label: 'D', colspan: 2 }, { label: 'E', colspan: 2 }, 'F'], + ['G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'] + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example2.jsx b/docs/content/guides/cell-features/clipboard/example2.jsx new file mode 100644 index 00000000000..d86eeaa9055 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example2.jsx @@ -0,0 +1,41 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example3.html b/docs/content/guides/cell-features/clipboard/example3.html new file mode 100644 index 00000000000..bb795dbb6d7 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example3.html @@ -0,0 +1,5 @@ +
+
+ + +
\ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example3.js b/docs/content/guides/cell-features/clipboard/example3.js new file mode 100644 index 00000000000..3fa4b92a88d --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example3.js @@ -0,0 +1,39 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const copyBtn = document.querySelector('#copy'); +const cutBtn = document.querySelector('#cut'); + +const hot = new Handsontable(container, { + rowHeaders: true, + colHeaders: true, + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ], + outsideClickDeselects: false, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +copyBtn.addEventListener('mousedown', function() { + hot.selectCell(1, 1); +}); + +copyBtn.addEventListener('click', function() { + document.execCommand('copy'); +}); + +cutBtn.addEventListener('mousedown', function() { + hot.selectCell(1, 1); +}); + +cutBtn.addEventListener('click', function() { + document.execCommand('cut'); +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/clipboard/example3.jsx b/docs/content/guides/cell-features/clipboard/example3.jsx new file mode 100644 index 00000000000..e0aa3e01b29 --- /dev/null +++ b/docs/content/guides/cell-features/clipboard/example3.jsx @@ -0,0 +1,73 @@ +import { useEffect, useRef } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + const copyBtnClickCallback = function() { + document.execCommand('copy'); + }; + const cutBtnClickCallback = function() { + document.execCommand('cut'); + }; + let copyBtnMousedownCallback; + let cutBtnMousedownCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + copyBtnMousedownCallback = function() { + hot.selectCell(1, 1); + }; + cutBtnMousedownCallback = function() { + hot.selectCell(1, 1); + }; + }); + + return ( + <> + +
+ + +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments.md b/docs/content/guides/cell-features/comments/comments.md similarity index 61% rename from docs/content/guides/cell-features/comments.md rename to docs/content/guides/cell-features/comments/comments.md index fd17159c273..47693b8503d 100644 --- a/docs/content/guides/cell-features/comments.md +++ b/docs/content/guides/cell-features/comments/comments.md @@ -55,7 +55,7 @@ const hot = new Handsontable(container, { ## Add comments via the context menu -After you've enabled the plugin, the [Context Menu](@/guides/accessories-and-menus/context-menu.md) gains a few new items: +After you've enabled the plugin, the [Context Menu](@/guides/accessories-and-menus/context-menu/context-menu.md) gains a few new items: - Add/Edit comment - Delete comment @@ -130,45 +130,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/comments/example1.jsx) ::: @@ -217,43 +179,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/comments/example2.jsx) ::: @@ -303,44 +229,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/comments/example3.jsx) ::: @@ -391,45 +280,7 @@ const hot = new Handsontable(container, { ::: example #example4 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example4')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/comments/example4.jsx) ::: diff --git a/docs/content/guides/cell-features/comments/example1.js b/docs/content/guides/cell-features/comments/example1.js new file mode 100644 index 00000000000..2eb34036bf3 --- /dev/null +++ b/docs/content/guides/cell-features/comments/example1.js @@ -0,0 +1,26 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'], + ['2017', 10, 11, 12, 13, 15, 16], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ['2021', 10, 11, 12, 13, 15, 16] + ], + rowHeaders: true, + colHeaders: true, + contextMenu: true, + comments: true, + cell: [ + { row: 1, col: 1, comment: { value: 'Some comment' } }, + { row: 2, col: 2, comment: { value: 'More comments' } } + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example1.jsx b/docs/content/guides/cell-features/comments/example1.jsx new file mode 100644 index 00000000000..cd7eedbcf61 --- /dev/null +++ b/docs/content/guides/cell-features/comments/example1.jsx @@ -0,0 +1,37 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example2.js b/docs/content/guides/cell-features/comments/example2.js new file mode 100644 index 00000000000..69dbb2cc5ca --- /dev/null +++ b/docs/content/guides/cell-features/comments/example2.js @@ -0,0 +1,24 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Toyota', 'Honda', 'Ford'], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ], + rowHeaders: true, + colHeaders: true, + contextMenu: true, + comments: true, + cell: [ + { row: 0, col: 1, comment: { value: 'A read-only comment.', readOnly: true } }, + { row: 0, col: 3, comment: { value: 'You can edit this comment' } } + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example2.jsx b/docs/content/guides/cell-features/comments/example2.jsx new file mode 100644 index 00000000000..ca8d4be89e2 --- /dev/null +++ b/docs/content/guides/cell-features/comments/example2.jsx @@ -0,0 +1,35 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example3.js b/docs/content/guides/cell-features/comments/example3.js new file mode 100644 index 00000000000..8d1e7182606 --- /dev/null +++ b/docs/content/guides/cell-features/comments/example3.js @@ -0,0 +1,25 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'], + ['2017', 10, 11, 12, 13, 15, 16], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ], + rowHeaders: true, + colHeaders: true, + contextMenu: true, + comments: true, + cell: [ + { row: 1, col: 1, comment: { value: 'Some comment' } }, + // add the `style` parameter + { row: 2, col: 2, comment: { value: 'Comment 200x50 px', style: { width: 200, height: 50 } } } + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example3.jsx b/docs/content/guides/cell-features/comments/example3.jsx new file mode 100644 index 00000000000..4625485cc8c --- /dev/null +++ b/docs/content/guides/cell-features/comments/example3.jsx @@ -0,0 +1,36 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example4.js b/docs/content/guides/cell-features/comments/example4.js new file mode 100644 index 00000000000..4b7f9cf3213 --- /dev/null +++ b/docs/content/guides/cell-features/comments/example4.js @@ -0,0 +1,26 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example4'); +const hot = new Handsontable(container, { + data: [ + ['', 'Tesla', 'Toyota', 'Honda', 'Ford'], + ['2018', 10, 11, 12, 13, 15, 16], + ['2019', 10, 11, 12, 13, 15, 16], + ['2020', 10, 11, 12, 13, 15, 16], + ], + rowHeaders: true, + colHeaders: true, + contextMenu: true, + comments: { + // on mouseover, wait 2 seconds before the comment box displays + displayDelay: 2000, + }, + cell: [ + { row: 1, col: 1, comment: { value: 'Some comment' } }, + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/comments/example4.jsx b/docs/content/guides/cell-features/comments/example4.jsx new file mode 100644 index 00000000000..21c8643f90c --- /dev/null +++ b/docs/content/guides/cell-features/comments/example4.jsx @@ -0,0 +1,37 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example4')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/conditional-formatting.md b/docs/content/guides/cell-features/conditional-formatting/conditional-formatting.md similarity index 62% rename from docs/content/guides/cell-features/conditional-formatting.md rename to docs/content/guides/cell-features/conditional-formatting/conditional-formatting.md index 55f1ccb8dba..a62e81ed0b2 100644 --- a/docs/content/guides/cell-features/conditional-formatting.md +++ b/docs/content/guides/cell-features/conditional-formatting/conditional-formatting.md @@ -34,11 +34,7 @@ This demo shows how to use the cell type renderer feature to make some condition ::: example #example1 --css 1 --js 2 -```css -.make-me-red { - color: #FF5A12; -} -``` +@[code](@/content/guides/cell-features/conditional-formatting/example1.css) ```js import Handsontable from 'handsontable'; @@ -132,95 +128,7 @@ const hot = new Handsontable(container, { } ``` -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import { registerRenderer, textRenderer } from 'handsontable/renderers'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const data = [ - ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], - ['2017', -5, '', 12, 13], - ['2018', '', -11, 14, 13], - ['2019', '', 15, -12, 'readOnly'] - ]; - - function firstRowRenderer(instance, td, row, col, prop, value, cellProperties) { - textRenderer.apply(this, arguments); - td.style.fontWeight = 'bold'; - td.style.color = 'green'; - td.style.background = '#CEC'; - } - - function negativeValueRenderer(instance, td, row, col, prop, value, cellProperties) { - textRenderer.apply(this, arguments); - - // if row contains negative number - if (parseInt(value, 10) < 0) { - // add class "negative" - td.className = 'make-me-red'; - } - - if (!value || value === '') { - td.style.background = '#EEE'; - - } else { - if (value === 'Nissan') { - td.style.fontStyle = 'italic'; - } - - td.style.background = ''; - } - } - // maps function to a lookup string - registerRenderer('negativeValueRenderer', negativeValueRenderer); - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/conditional-formatting/example1.jsx) ::: @@ -230,7 +138,7 @@ ReactDOM.render(, document.getElementById('example1')); ### Related guides -- [Formatting cells](@/guides/cell-features/formatting-cells.md) +- [Formatting cells](@/guides/cell-features/formatting-cells/formatting-cells.md) ### Related API reference diff --git a/docs/content/guides/cell-features/conditional-formatting/example1.css b/docs/content/guides/cell-features/conditional-formatting/example1.css new file mode 100644 index 00000000000..c5465984805 --- /dev/null +++ b/docs/content/guides/cell-features/conditional-formatting/example1.css @@ -0,0 +1,3 @@ +.make-me-red { + color: #FF5A12; +} \ No newline at end of file diff --git a/docs/content/guides/cell-features/conditional-formatting/example1.js b/docs/content/guides/cell-features/conditional-formatting/example1.js new file mode 100644 index 00000000000..445fc825530 --- /dev/null +++ b/docs/content/guides/cell-features/conditional-formatting/example1.js @@ -0,0 +1,75 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', -5, '', 12, 13], + ['2018', '', -11, 14, 13], + ['2019', '', 15, -12, 'readOnly'] +]; + +function firstRowRenderer(instance, td, row, col, prop, value, cellProperties) { + Handsontable.renderers.TextRenderer.apply(this, arguments); + td.style.fontWeight = 'bold'; + td.style.color = 'green'; + td.style.background = '#CEC'; +} + +function negativeValueRenderer(instance, td, row, col, prop, value, cellProperties) { + Handsontable.renderers.TextRenderer.apply(this, arguments); + + // if the row contains a negative number + if (parseInt(value, 10) < 0) { + // add class 'make-me-red' + td.className = 'make-me-red'; + } + + if (!value || value === '') { + td.style.background = '#EEE'; + + } else { + if (value === 'Nissan') { + td.style.fontStyle = 'italic'; + } + + td.style.background = ''; + } +} +// maps function to a lookup string +Handsontable.renderers.registerRenderer('negativeValueRenderer', negativeValueRenderer); + +const hot = new Handsontable(container, { + data: data, + licenseKey: 'non-commercial-and-evaluation', + height: 'auto', + afterSelection(row, col, row2, col2) { + const meta = this.getCellMeta(row2, col2); + + if (meta.readOnly) { + this.updateSettings({fillHandle: false}); + + } else { + this.updateSettings({fillHandle: true}); + } + }, + cells(row, col) { + const cellProperties = {}; + const data = this.instance.getData(); + + if (row === 0 || data[row] && data[row][col] === 'readOnly') { + cellProperties.readOnly = true; // make cell read-only if it is first row or the text reads 'readOnly' + } + + if (row === 0) { + cellProperties.renderer = firstRowRenderer; // uses function directly + + } else { + cellProperties.renderer = 'negativeValueRenderer'; // uses lookup map + } + + return cellProperties; + }, + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/conditional-formatting/example1.jsx b/docs/content/guides/cell-features/conditional-formatting/example1.jsx new file mode 100644 index 00000000000..1f02fe2c3c7 --- /dev/null +++ b/docs/content/guides/cell-features/conditional-formatting/example1.jsx @@ -0,0 +1,87 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import { registerRenderer, textRenderer } from 'handsontable/renderers'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const data = [ + ['', 'Tesla', 'Nissan', 'Toyota', 'Honda'], + ['2017', -5, '', 12, 13], + ['2018', '', -11, 14, 13], + ['2019', '', 15, -12, 'readOnly'] + ]; + + function firstRowRenderer(instance, td, row, col, prop, value, cellProperties) { + textRenderer.apply(this, arguments); + td.style.fontWeight = 'bold'; + td.style.color = 'green'; + td.style.background = '#CEC'; + } + + function negativeValueRenderer(instance, td, row, col, prop, value, cellProperties) { + textRenderer.apply(this, arguments); + + // if row contains negative number + if (parseInt(value, 10) < 0) { + // add class "negative" + td.className = 'make-me-red'; + } + + if (!value || value === '') { + td.style.background = '#EEE'; + + } else { + if (value === 'Nissan') { + td.style.fontStyle = 'italic'; + } + + td.style.background = ''; + } + } + // maps function to a lookup string + registerRenderer('negativeValueRenderer', negativeValueRenderer); + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells.md b/docs/content/guides/cell-features/disabled-cells/disabled-cells.md similarity index 58% rename from docs/content/guides/cell-features/disabled-cells.md rename to docs/content/guides/cell-features/disabled-cells/disabled-cells.md index fc52ca899a4..67aa97ca26b 100644 --- a/docs/content/guides/cell-features/disabled-cells.md +++ b/docs/content/guides/cell-features/disabled-cells/disabled-cells.md @@ -36,7 +36,7 @@ Disabling a cell makes the cell read-only or non-editable. Both have similar out ## Read-only grid -You can make the entire grid read-only by setting [`readOnly`](@/api/options.md#readonly) to `true` as a [top-level grid option](@/guides/getting-started/configuration-options.md#set-grid-options). +You can make the entire grid read-only by setting [`readOnly`](@/api/options.md#readonly) to `true` as a [top-level grid option](@/guides/getting-started/configuration-options/configuration-options.md#set-grid-options). ::: only-for javascript @@ -72,37 +72,7 @@ const hot = new Handsontable(container, { ::: example #exampleReadOnlyGrid :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleReadOnlyGrid')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.jsx) ::: @@ -161,51 +131,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/disabled-cells/example1.jsx) ::: @@ -259,56 +185,7 @@ hot.updateSettings({ ::: example #example2 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - hot.updateSettings({ - cells(row, col) { - const cellProperties = {}; - - if (hot.getData()[row][col] === 'Nissan') { - cellProperties.readOnly = true; - } - - return cellProperties; - } - }); - }); - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/disabled-cells/example2.jsx) ::: @@ -372,54 +249,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/disabled-cells/example3.jsx) ::: @@ -476,59 +306,7 @@ hot.updateSettings({ ::: example #example4 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - hot.updateSettings({ - cells(row, col, prop) { - const cellProperties = {}; - - if (hot.getDataAtRowProp(row, prop) === 'Nissan') { - cellProperties.editor = false; - - } else { - cellProperties.editor = 'text'; - } - - return cellProperties; - } - }); - }); - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example4')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/disabled-cells/example4.jsx) ::: diff --git a/docs/content/guides/cell-features/disabled-cells/example1.js b/docs/content/guides/cell-features/disabled-cells/example1.js new file mode 100644 index 00000000000..7a3352ae6a8 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example1.js @@ -0,0 +1,32 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + { car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' }, + { car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' }, + { car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' }, + { car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' } + ], + height: 'auto', + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { + data: 'car', + readOnly: true + }, + { + data: 'year' + }, + { + data: 'chassis' + }, + { + data: 'bumper' + } + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example1.jsx b/docs/content/guides/cell-features/disabled-cells/example1.jsx new file mode 100644 index 00000000000..551a38e5f74 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example1.jsx @@ -0,0 +1,43 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example2.js b/docs/content/guides/cell-features/disabled-cells/example2.js new file mode 100644 index 00000000000..acc936dc52f --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example2.js @@ -0,0 +1,29 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + { car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' }, + { car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' }, + { car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' }, + { car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' } + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +hot.updateSettings({ + cells(row, col) { + const cellProperties = {}; + + if (hot.getData()[row][col] === 'Nissan') { + cellProperties.readOnly = true; + } + + return cellProperties; + } +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example2.jsx b/docs/content/guides/cell-features/disabled-cells/example2.jsx new file mode 100644 index 00000000000..f9a06e1d081 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example2.jsx @@ -0,0 +1,48 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + hot.updateSettings({ + cells(row, col) { + const cellProperties = {}; + + if (hot.getData()[row][col] === 'Nissan') { + cellProperties.readOnly = true; + } + + return cellProperties; + } + }); + }); + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example3.js b/docs/content/guides/cell-features/disabled-cells/example3.js new file mode 100644 index 00000000000..a5ddc214e76 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example3.js @@ -0,0 +1,35 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + { car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' }, + { car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' }, + { car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' }, + { car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' } + ], + height: 'auto', + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { + data: 'car', + editor: false + }, + { + data: 'year', + editor: 'numeric' + }, + { + data: 'chassis', + editor: 'text' + }, + { + data: 'bumper', + editor: 'text' + } + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example3.jsx b/docs/content/guides/cell-features/disabled-cells/example3.jsx new file mode 100644 index 00000000000..23bb185bc5d --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example3.jsx @@ -0,0 +1,46 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example4.js b/docs/content/guides/cell-features/disabled-cells/example4.js new file mode 100644 index 00000000000..f33e802a623 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example4.js @@ -0,0 +1,32 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example4'); +const hot = new Handsontable(container, { + data: [ + { car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' }, + { car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' }, + { car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' }, + { car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' } + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +hot.updateSettings({ + cells(row, col, prop) { + const cellProperties = {}; + + if (hot.getDataAtRowProp(row, prop) === 'Nissan') { + cellProperties.editor = false; + + } else { + cellProperties.editor = 'text'; + } + + return cellProperties; + } +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/example4.jsx b/docs/content/guides/cell-features/disabled-cells/example4.jsx new file mode 100644 index 00000000000..8866805642b --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/example4.jsx @@ -0,0 +1,51 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + hot.updateSettings({ + cells(row, col, prop) { + const cellProperties = {}; + + if (hot.getDataAtRowProp(row, prop) === 'Nissan') { + cellProperties.editor = false; + + } else { + cellProperties.editor = 'text'; + } + + return cellProperties; + } + }); + }); + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example4')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.js b/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.js new file mode 100644 index 00000000000..90298a64eb3 --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.js @@ -0,0 +1,19 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleReadOnlyGrid'); +const hot = new Handsontable(container, { + data: [ + { car: 'Tesla', year: 2017, chassis: 'black', bumper: 'black' }, + { car: 'Nissan', year: 2018, chassis: 'blue', bumper: 'blue' }, + { car: 'Chrysler', year: 2019, chassis: 'yellow', bumper: 'black' }, + { car: 'Volvo', year: 2020, chassis: 'white', bumper: 'gray' } + ], + height: 'auto', + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + licenseKey: 'non-commercial-and-evaluation', + // make the entire grid read-only + readOnly: true, + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.jsx b/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.jsx new file mode 100644 index 00000000000..866f58fbf0d --- /dev/null +++ b/docs/content/guides/cell-features/disabled-cells/exampleReadOnlyGrid.jsx @@ -0,0 +1,29 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleReadOnlyGrid')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example1.css b/docs/content/guides/cell-features/formatting-cells/example1.css new file mode 100644 index 00000000000..189318c2b47 --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example1.css @@ -0,0 +1,8 @@ +td.custom-cell { + color: #fff; + background-color: #37bc6c; +} +.custom-table thead th:nth-child(even), +.custom-table tbody tr:nth-child(odd) th { + background-color: #d7f1e1; +} \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example1.javascript b/docs/content/guides/cell-features/formatting-cells/example1.javascript new file mode 100644 index 00000000000..1c86a2ba619 --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example1.javascript @@ -0,0 +1,28 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ], + rowHeaders: true, + colHeaders: true, + stretchH: 'all', + className: 'custom-table', + cell: [ + { + row: 0, + col: 0, + className: 'custom-cell', + }, + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example1.jsx b/docs/content/guides/cell-features/formatting-cells/example1.jsx new file mode 100644 index 00000000000..4f5e0f80ed2 --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example1.jsx @@ -0,0 +1,39 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example2.javascript b/docs/content/guides/cell-features/formatting-cells/example2.javascript new file mode 100644 index 00000000000..5518e0ac282 --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example2.javascript @@ -0,0 +1,35 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +Handsontable.renderers.registerRenderer('customStylesRenderer', (hotInstance, TD, ...rest) => { + Handsontable.renderers.TextRenderer(hotInstance, TD, ...rest); + + TD.style.fontWeight = 'bold'; + TD.style.color = 'green'; + TD.style.background = '#d7f1e1'; +}); + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ], + rowHeaders: true, + colHeaders: true, + stretchH: 'all', + cell: [ + { + row: 0, + col: 0, + renderer: 'customStylesRenderer', + }, + ], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example2.jsx b/docs/content/guides/cell-features/formatting-cells/example2.jsx new file mode 100644 index 00000000000..1f566d4c55d --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example2.jsx @@ -0,0 +1,47 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import { textRenderer, registerRenderer } from 'handsontable/renderers'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + registerRenderer('customStylesRenderer', (hotInstance, TD, ...rest) => { + textRenderer(hotInstance, TD, ...rest); + + TD.style.fontWeight = 'bold'; + TD.style.color = 'green'; + TD.style.background = '#d7f1e1'; + }); + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example3.js b/docs/content/guides/cell-features/formatting-cells/example3.js new file mode 100644 index 00000000000..d02afd35f69 --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example3.js @@ -0,0 +1,62 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5'], + ], + rowHeaders: true, + colHeaders: true, + autoWrapRow: true, + autoWrapCol: true, + stretchH: 'all', + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + customBorders: [ + { + range: { + from: { + row: 1, + col: 1, + }, + to: { + row: 3, + col: 4, + }, + }, + top: { + width: 2, + color: '#5292F7', + }, + bottom: { + width: 2, + color: 'red', + }, + start: { + width: 2, + color: 'orange', + }, + end: { + width: 2, + color: 'magenta', + }, + }, + { + row: 2, + col: 2, + start: { + width: 2, + color: 'red', + }, + end: { + width: 1, + color: 'green', + }, + }, + ], +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells/example3.jsx b/docs/content/guides/cell-features/formatting-cells/example3.jsx new file mode 100644 index 00000000000..335100bb34c --- /dev/null +++ b/docs/content/guides/cell-features/formatting-cells/example3.jsx @@ -0,0 +1,73 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/formatting-cells.md b/docs/content/guides/cell-features/formatting-cells/formatting-cells.md similarity index 59% rename from docs/content/guides/cell-features/formatting-cells.md rename to docs/content/guides/cell-features/formatting-cells/formatting-cells.md index 02eee0ce012..51c74ea7eb6 100644 --- a/docs/content/guides/cell-features/formatting-cells.md +++ b/docs/content/guides/cell-features/formatting-cells/formatting-cells.md @@ -32,16 +32,7 @@ In this example, we add a custom class `custom-cell` to the cell in the top left ::: example #example1 --css 1 --js 2 -```css -td.custom-cell { - color: #fff; - background-color: #37bc6c; -} -.custom-table thead th:nth-child(even), -.custom-table tbody tr:nth-child(odd) th { - background-color: #d7f1e1; -} -``` +@[code](@/content/guides/cell-features/formatting-cells/example1.css) ```javascript import Handsontable from 'handsontable'; @@ -93,47 +84,7 @@ td.custom-cell { } ``` -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/formatting-cells/example1.jsx) ::: @@ -193,55 +144,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import { textRenderer, registerRenderer } from 'handsontable/renderers'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - registerRenderer('customStylesRenderer', (hotInstance, TD, ...rest) => { - textRenderer(hotInstance, TD, ...rest); - - TD.style.fontWeight = 'bold'; - TD.style.color = 'green'; - TD.style.background = '#d7f1e1'; - }); - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/formatting-cells/example2.jsx) ::: @@ -253,7 +156,7 @@ To enable the custom borders feature, set the [`customBorders`](@/api/options.md array with a pre-defined setup. For the list of available settings and methods, visit the [API reference](@/api/customBorders.md). In the names of the API properties, the words `start` and `end` refer to the starting and ending edges of the -[layout direction](@/guides/internationalization/layout-direction.md). +[layout direction](@/guides/internationalization/layout-direction/layout-direction.md). ::: only-for javascript @@ -332,81 +235,7 @@ const hot = Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/formatting-cells/example3.jsx) ::: @@ -416,7 +245,7 @@ ReactDOM.render(, document.getElementById('example3')); ### Related guides -- [Conditional formatting](@/guides/cell-features/conditional-formatting.md) +- [Conditional formatting](@/guides/cell-features/conditional-formatting/conditional-formatting.md) ### Related API reference diff --git a/docs/content/guides/cell-features/merge-cells/example1.js b/docs/content/guides/cell-features/merge-cells/example1.js new file mode 100644 index 00000000000..41274ff249b --- /dev/null +++ b/docs/content/guides/cell-features/merge-cells/example1.js @@ -0,0 +1,28 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data, + height: 320, + colWidths: 47, + rowHeaders: true, + colHeaders: true, + contextMenu: true, + mergeCells: [ + { row: 1, col: 1, rowspan: 3, colspan: 3 }, + { row: 3, col: 4, rowspan: 2, colspan: 2 }, + { row: 5, col: 6, rowspan: 3, colspan: 3 } + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/merge-cells/example1.jsx b/docs/content/guides/cell-features/merge-cells/example1.jsx new file mode 100644 index 00000000000..a9628a831a3 --- /dev/null +++ b/docs/content/guides/cell-features/merge-cells/example1.jsx @@ -0,0 +1,39 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/merge-cells.md b/docs/content/guides/cell-features/merge-cells/merge-cells.md similarity index 74% rename from docs/content/guides/cell-features/merge-cells.md rename to docs/content/guides/cell-features/merge-cells/merge-cells.md index 604482ed7ee..524253df2a3 100644 --- a/docs/content/guides/cell-features/merge-cells.md +++ b/docs/content/guides/cell-features/merge-cells/merge-cells.md @@ -86,47 +86,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// generate an array of arrays with dummy data -const data = new Array(100) // number of rows - .fill() - .map((_, row) => new Array(50) // number of columns - .fill() - .map((_, column) => `${row}, ${column}`) - ); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/merge-cells/example1.jsx) ::: diff --git a/docs/content/guides/cell-features/selection/example1.html b/docs/content/guides/cell-features/selection/example1.html new file mode 100644 index 00000000000..436d1107885 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example1.html @@ -0,0 +1,8 @@ +
+
+ +
\ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example1.js b/docs/content/guides/cell-features/selection/example1.js new file mode 100644 index 00000000000..1aa80f665e5 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example1.js @@ -0,0 +1,37 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const selectOption = document.querySelector('#selectOption'); +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'I7'], + ['A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8', 'I8'], + ['A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9', 'I9'], + ], + width: 'auto', + height: 'auto', + colWidths: 100, + rowHeights: 23, + rowHeaders: true, + colHeaders: true, + selectionMode: 'multiple', // 'single', 'range' or 'multiple', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +selectOption.addEventListener('change', event => { + const value = event.target.value; + const first = value.split(' ')[0].toLowerCase(); + + hot.updateSettings({ + selectionMode: first + }); +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example1.jsx b/docs/content/guides/cell-features/selection/example1.jsx new file mode 100644 index 00000000000..591f83d4937 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example1.jsx @@ -0,0 +1,70 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let selectOptionChangeCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + selectOptionChangeCallback = event => { + const value = event.target.value; + const first = value.split(' ')[0].toLowerCase(); + + hot.updateSettings({ + selectionMode: first + }); + }; + }); + + return ( + <> + +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example2.html b/docs/content/guides/cell-features/selection/example2.html new file mode 100644 index 00000000000..63f76c26db4 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example2.html @@ -0,0 +1,5 @@ +
+Here you will see the log +
+ +
\ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example2.js b/docs/content/guides/cell-features/selection/example2.js new file mode 100644 index 00000000000..6feef7abe09 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example2.js @@ -0,0 +1,43 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const output = document.querySelector('#output'); +const getButton = document.querySelector('#getButton'); +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'I7'], + ['A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8', 'I8'], + ['A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9', 'I9'], + ], + width: 'auto', + height: 'auto', + colWidths: 100, + rowHeights: 23, + rowHeaders: true, + colHeaders: true, + outsideClickDeselects: false, + selectionMode: 'multiple', // 'single', 'range' or 'multiple', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +getButton.addEventListener('click', event => { + const selected = hot.getSelected() || []; + const data = []; + + for (let i = 0; i < selected.length; i += 1) { + const item = selected[i]; + + data.push(hot.getData(...item)); + } + + output.innerText = JSON.stringify(data); +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example2.jsx b/docs/content/guides/cell-features/selection/example2.jsx new file mode 100644 index 00000000000..97863b09256 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example2.jsx @@ -0,0 +1,74 @@ +import { useRef, useEffect, useState } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + const [output, setOutput] = useState(''); + + let getButtonClickCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + getButtonClickCallback = event => { + const selected = hot.getSelected() || []; + const data = []; + + for (let i = 0; i < selected.length; i += 1) { + const item = selected[i]; + + data.push(hot.getData(...item)); + } + + setOutput(JSON.stringify(data)); + }; + }); + + return ( + <> + + {output} +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example3.css b/docs/content/guides/cell-features/selection/example3.css new file mode 100644 index 00000000000..e8830611301 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example3.css @@ -0,0 +1,3 @@ +.c-red { + color: red; +} \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example3.html b/docs/content/guides/cell-features/selection/example3.html new file mode 100644 index 00000000000..2b22c461a7e --- /dev/null +++ b/docs/content/guides/cell-features/selection/example3.html @@ -0,0 +1,5 @@ +
+ +
+ +
\ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example3.js b/docs/content/guides/cell-features/selection/example3.js new file mode 100644 index 00000000000..1cdf63cfb35 --- /dev/null +++ b/docs/content/guides/cell-features/selection/example3.js @@ -0,0 +1,54 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const button = document.querySelector('#set-data-action'); +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5'], + ['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6'], + ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'I7'], + ['A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8', 'I8'], + ['A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9', 'I9'], + ], + width: 'auto', + height: 272, + colWidths: 100, + rowHeights: 23, + rowHeaders: true, + colHeaders: true, + outsideClickDeselects: false, + selectionMode: 'multiple', // 'single', 'range' or 'multiple', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +button.addEventListener('click', event => { + const selected = hot.getSelected() || []; + const target = event.target.id; + + hot.suspendRender(); + + for (let index = 0; index < selected.length; index += 1) { + const [row1, column1, row2, column2] = selected[index]; + const startRow = Math.max(Math.min(row1, row2), 0); + const endRow = Math.max(row1, row2); + const startCol = Math.max(Math.min(column1, column2), 0); + const endCol = Math.max(column1, column2); + + for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) { + for (let columnIndex = startCol; columnIndex <= endCol; columnIndex += 1) { + hot.setDataAtCell(rowIndex, columnIndex, 'data changed'); + hot.setCellMeta(rowIndex, columnIndex, 'className', 'c-red'); + } + } + } + + hot.render(); + hot.resumeRender(); +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection/example3.jsx b/docs/content/guides/cell-features/selection/example3.jsx new file mode 100644 index 00000000000..88ff596600c --- /dev/null +++ b/docs/content/guides/cell-features/selection/example3.jsx @@ -0,0 +1,79 @@ +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let buttonClickCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + buttonClickCallback = event => { + const selected = hot.getSelected() || []; + const target = event.target.id; + + hot.suspendRender(); + + for (let index = 0; index < selected.length; index += 1) { + const [row1, column1, row2, column2] = selected[index]; + const startRow = Math.max(Math.min(row1, row2), 0); + const endRow = Math.max(row1, row2); + const startCol = Math.max(Math.min(column1, column2), 0); + const endCol = Math.max(column1, column2); + + for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) { + for (let columnIndex = startCol; columnIndex <= endCol; columnIndex += 1) { + hot.setDataAtCell(rowIndex, columnIndex, 'data changed'); + hot.setCellMeta(rowIndex, columnIndex, 'className', 'c-red'); + } + } + } + + hot.render(); + hot.resumeRender(); + }; + }); + + return ( + <> + +
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/selection.md b/docs/content/guides/cell-features/selection/selection.md similarity index 66% rename from docs/content/guides/cell-features/selection.md rename to docs/content/guides/cell-features/selection/selection.md index 61b68596f31..7166b4375a7 100644 --- a/docs/content/guides/cell-features/selection.md +++ b/docs/content/guides/cell-features/selection/selection.md @@ -102,78 +102,7 @@ selectOption.addEventListener('change', event => { ::: example #example1 :react -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let selectOptionChangeCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - selectOptionChangeCallback = event => { - const value = event.target.value; - const first = value.split(' ')[0].toLowerCase(); - - hot.updateSettings({ - selectionMode: first - }); - }; - }); - - return ( - <> - -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/selection/example1.jsx) ::: @@ -248,82 +177,7 @@ getButton.addEventListener('click', event => { ::: example #example2 :react -```jsx -import { useRef, useEffect, useState } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - const [output, setOutput] = useState(''); - - let getButtonClickCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - getButtonClickCallback = event => { - const selected = hot.getSelected() || []; - const data = []; - - for (let i = 0; i < selected.length; i += 1) { - const item = selected[i]; - - data.push(hot.getData(...item)); - } - - setOutput(JSON.stringify(data)); - }; - }); - - return ( - <> - - {output} -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/selection/example2.jsx) ::: @@ -344,11 +198,7 @@ You may want to delete, format, or otherwise change the selected cells. For exam ``` -```css -.c-red { - color: red; -} -``` +@[code](@/content/guides/cell-features/selection/example3.css) ```js import Handsontable from 'handsontable'; import 'handsontable/dist/handsontable.full.min.css'; @@ -419,87 +269,7 @@ button.addEventListener('click', event => { color: red; } ``` -```jsx -import { useRef, useEffect } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let buttonClickCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - buttonClickCallback = event => { - const selected = hot.getSelected() || []; - const target = event.target.id; - - hot.suspendRender(); - - for (let index = 0; index < selected.length; index += 1) { - const [row1, column1, row2, column2] = selected[index]; - const startRow = Math.max(Math.min(row1, row2), 0); - const endRow = Math.max(row1, row2); - const startCol = Math.max(Math.min(column1, column2), 0); - const endCol = Math.max(column1, column2); - - for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) { - for (let columnIndex = startCol; columnIndex <= endCol; columnIndex += 1) { - hot.setDataAtCell(rowIndex, columnIndex, 'data changed'); - hot.setCellMeta(rowIndex, columnIndex, 'className', 'c-red'); - } - } - } - - hot.render(); - hot.resumeRender(); - }; - }); - - return ( - <> - -
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/selection/example3.jsx) ::: @@ -555,7 +325,7 @@ To jump across a horizontal edge: | **Delete** | **Delete** | Clear the contents of the selected cells | ✓ | ✓ | | **Backspace** | **Backspace** | Clear the contents of the selected cells | ✓ | ✓ | -* This action depends on your [layout direction](@/guides/internationalization/layout-direction.md).
+* This action depends on your [layout direction](@/guides/internationalization/layout-direction/layout-direction.md).
** In case of multiple selection layers, only the last selection layer gets extended. ## Related API reference diff --git a/docs/content/guides/cell-features/text-alignment/example1.js b/docs/content/guides/cell-features/text-alignment/example1.js new file mode 100644 index 00000000000..5351ef8532e --- /dev/null +++ b/docs/content/guides/cell-features/text-alignment/example1.js @@ -0,0 +1,36 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(18) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data, + colWidths: 100, + height: 320, + rowHeaders: true, + colHeaders: true, + contextMenu: true, + licenseKey: 'non-commercial-and-evaluation', + mergeCells: [ + { row: 1, col: 1, rowspan: 3, colspan: 3 }, + { row: 3, col: 4, rowspan: 2, colspan: 2 } + ], + className: 'htCenter', + cell: [ + { row: 0, col: 0, className: 'htRight' }, + { row: 1, col: 1, className: 'htLeft htMiddle' }, + { row: 3, col: 4, className: 'htLeft htBottom' } + ], + afterSetCellMeta(row, col, key, val) { + console.log('cell meta changed', row, col, key, val); + }, + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-features/text-alignment/example1.jsx b/docs/content/guides/cell-features/text-alignment/example1.jsx new file mode 100644 index 00000000000..a7f2b9b7108 --- /dev/null +++ b/docs/content/guides/cell-features/text-alignment/example1.jsx @@ -0,0 +1,47 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(18) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-features/text-alignment.md b/docs/content/guides/cell-features/text-alignment/text-alignment.md similarity index 67% rename from docs/content/guides/cell-features/text-alignment.md rename to docs/content/guides/cell-features/text-alignment/text-alignment.md index 725027ffe0b..b23e4ac2a81 100644 --- a/docs/content/guides/cell-features/text-alignment.md +++ b/docs/content/guides/cell-features/text-alignment/text-alignment.md @@ -101,55 +101,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// generate an array of arrays with dummy data -const data = new Array(100) // number of rows - .fill() - .map((_, row) => new Array(18) // number of columns - .fill() - .map((_, column) => `${row}, ${column}`) - ); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-features/text-alignment/example1.jsx) ::: diff --git a/docs/content/guides/cell-functions/cell-editor.md b/docs/content/guides/cell-functions/cell-editor/cell-editor.md similarity index 90% rename from docs/content/guides/cell-functions/cell-editor.md rename to docs/content/guides/cell-functions/cell-editor/cell-editor.md index 884005aa926..4e773430000 100644 --- a/docs/content/guides/cell-functions/cell-editor.md +++ b/docs/content/guides/cell-functions/cell-editor/cell-editor.md @@ -41,142 +41,7 @@ Note that in case of React 16 and older, it wouldn't work out of the box because ::: example #example1 :react --tab preview -```jsx -import React from 'react'; -import { HotTable, HotColumn, BaseEditorComponent } from '@handsontable/react'; -import 'handsontable/dist/handsontable.full.min.css'; - -// an editor component -class EditorComponent extends BaseEditorComponent { - constructor(props) { - super(props); - - this.mainElementRef = React.createRef(); - this.containerStyle = { - display: 'none', - position: 'absolute', - left: 0, - top: 0, - background: '#fff', - border: '1px solid #000', - padding: '15px', - zIndex: 999 - }; - this.state = { - value: '' - }; - } - - setValue(value, callback) { - this.setState((state, props) => { - return { value: value }; - }, callback); - } - - getValue() { - return this.state.value; - } - - open() { - this.mainElementRef.current.style.display = 'block'; - } - - close() { - this.mainElementRef.current.style.display = 'none'; - } - - prepare(row, col, prop, td, originalValue, cellProperties) { - // We'll need to call the `prepare` method from - // the `BaseEditorComponent` class, as it provides - // the component with the information needed to use the editor - // (hotInstance, row, col, prop, TD, originalValue, cellProperties) - super.prepare(row, col, prop, td, originalValue, cellProperties); - - const tdPosition = td.getBoundingClientRect(); - - // As the `prepare` method is triggered after selecting - // any cell, we're updating the styles for the editor element, - // so it shows up in the correct position. - this.mainElementRef.current.style.left = tdPosition.left + window.pageXOffset + 'px'; - this.mainElementRef.current.style.top = tdPosition.top + window.pageYOffset + 'px'; - } - - setLowerCase() { - this.setState( - (state, props) => { - return { value: this.state.value.toString().toLowerCase() }; - }, - () => { - this.finishEditing(); - } - ); - } - - setUpperCase() { - this.setState( - (state, props) => { - return { value: this.state.value.toString().toUpperCase() }; - }, - () => { - this.finishEditing(); - } - ); - } - - stopMousedownPropagation(e) { - e.stopPropagation(); - } - - render() { - return ( -
- - -
- ); - } -} - -const data = [ - ['Obrien Fischer'], - ['Alexandria Gordon'], - ['John Stafford'], - ['Regina Waters'], - ['Kay Bentley'], - ['Emerson Drake'], - ['Dean Stapleton'] -]; - -export const ExampleComponent = () => { - return ( - - - {/* add the `hot-editor` attribute to mark the component as a Handsontable editor */} - - - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-editor/example1.jsx) ::: @@ -188,51 +53,7 @@ The following example implements the `@handsontable/react` component with a cust ::: example #example2 :react --no-edit -```jsx -import { HotTable } from '@handsontable/react'; -import { TextEditor } from 'handsontable/editors/textEditor'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -class CustomEditor extends TextEditor { - createElements() { - super.createElements(); - - this.TEXTAREA = document.createElement('input'); - this.TEXTAREA.setAttribute('placeholder', 'Custom placeholder'); - this.TEXTAREA.setAttribute('data-hot-input', true); - this.textareaStyle = this.TEXTAREA.style; - this.TEXTAREA_PARENT.innerText = ''; - this.TEXTAREA_PARENT.appendChild(this.TEXTAREA); - } -} - -export const ExampleComponent = () => { - return ( - - ); -} - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-editor/example2.jsx) ::: @@ -289,7 +110,7 @@ We will discuss each of those tasks in detail. ::: -When user selects a cell [`EditorManager`](@/api/baseEditor.md) finds the editor class assigned to this cell, examining the value of the [`editor`](@/api/options.md#editor) configuration option. You can define the [`editor`](@/api/options.md#editor) configuration option globally (for all cells in table), per column (for all cells in column) or for each cell individually. For more details, see the [Configuration options](@/guides/getting-started/configuration-options.md#cascading-configuration) guide. +When user selects a cell [`EditorManager`](@/api/baseEditor.md) finds the editor class assigned to this cell, examining the value of the [`editor`](@/api/options.md#editor) configuration option. You can define the [`editor`](@/api/options.md#editor) configuration option globally (for all cells in table), per column (for all cells in column) or for each cell individually. For more details, see the [Configuration options](@/guides/getting-started/configuration-options/configuration-options.md#cascading-configuration) guide. The value of the [`editor`](@/api/options.md#editor) configuration option can be either a string representing an editor (such as 'text', 'autocomplete', 'checkbox' etc.), or an editor class. [`EditorManager`](@/api/baseEditor.md) will then get an instance of editor class and the first very important thing to remember is: **there is always one instance of certain editor class in a single table**, in other words each editor class object **is a singleton** within a single table, which means that its constructor will be invoked only once per table. If you have 3 tables on a page, each table will have its own instance of editor class. This has some important implications that you have to consider creating your own editor. @@ -345,8 +166,8 @@ When editor is opened the [`EditorManager`](@/api/baseEditor.md) waits for user - Pressing **Shift**+**Enter** (saves changes and moves selection one cell up) - Pressing **Ctrl**/**Cmd**+**Enter** or **Alt**/**Option**+**Enter** (adds a new line inside the cell) - Pressing **Escape** (aborts changes) -- Pressing **Tab** (saves changes and moves one cell to the right or to the left, depending on your [layout direction](@/guides/internationalization/layout-direction.md#elements-affected-by-layout-direction)) -- Pressing **Shift**+**Tab** (saves changes and moves one cell to the left or to the right, depending on your [layout direction](@/guides/internationalization/layout-direction.md#elements-affected-by-layout-direction)) +- Pressing **Tab** (saves changes and moves one cell to the right or to the left, depending on your [layout direction](@/guides/internationalization/layout-direction/layout-direction.md#elements-affected-by-layout-direction)) +- Pressing **Shift**+**Tab** (saves changes and moves one cell to the left or to the right, depending on your [layout direction](@/guides/internationalization/layout-direction/layout-direction.md#elements-affected-by-layout-direction)) - Pressing **Page Up**, **Page Down** (saves changes and moves one screen up/down) If any of those events is triggered, [`EditorManager`](@/api/baseEditor.md) calls editor's [`finishEditing()`](@/api/baseEditor.md#finishediting) method, which should try to save changes (unless ESC key has been pressed) and close the editor. @@ -1393,17 +1214,17 @@ const hot = new Handsontable(container, { | **Ctrl**+**Enter** | **Ctrl**/**Cmd**+**Enter** | Insert a line break | ✗ | ✓ | | **Escape** | **Escape** | Cancel the cell entry and exit the editing mode | ✓ | ✓ | -* This action depends on your [layout direction](@/guides/internationalization/layout-direction.md). +* This action depends on your [layout direction](@/guides/internationalization/layout-direction/layout-direction.md). ::: only-for javascript ## Related articles ### Related guides -- [Custom editor in React](@/react/guides/cell-functions/cell-editor.md) -- [Custom editor in Angular](@/guides/integrate-with-angular/angular-custom-editor-example.md) -- [Custom editor in Vue 2](@/guides/integrate-with-vue/vue-custom-editor-example.md) -- [Custom editor in Vue 3](@/guides/integrate-with-vue3/vue3-custom-editor-example.md) +- [Custom editor in React](@/react/guides/cell-functions/cell-editor/cell-editor.md) +- [Custom editor in Angular](@/guides/integrate-with-angular/angular-custom-editor-example/angular-custom-editor-example.md) +- [Custom editor in Vue 2](@/guides/integrate-with-vue/vue-custom-editor-example/vue-custom-editor-example.md) +- [Custom editor in Vue 3](@/guides/integrate-with-vue3/vue3-custom-editor-example/vue3-custom-editor-example.md) ### Related API reference diff --git a/docs/content/guides/cell-functions/cell-editor/example1.jsx b/docs/content/guides/cell-functions/cell-editor/example1.jsx new file mode 100644 index 00000000000..ffec36743c9 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-editor/example1.jsx @@ -0,0 +1,134 @@ +import React from 'react'; +import { HotTable, HotColumn, BaseEditorComponent } from '@handsontable/react'; +import 'handsontable/dist/handsontable.full.min.css'; + +// an editor component +class EditorComponent extends BaseEditorComponent { + constructor(props) { + super(props); + + this.mainElementRef = React.createRef(); + this.containerStyle = { + display: 'none', + position: 'absolute', + left: 0, + top: 0, + background: '#fff', + border: '1px solid #000', + padding: '15px', + zIndex: 999 + }; + this.state = { + value: '' + }; + } + + setValue(value, callback) { + this.setState((state, props) => { + return { value: value }; + }, callback); + } + + getValue() { + return this.state.value; + } + + open() { + this.mainElementRef.current.style.display = 'block'; + } + + close() { + this.mainElementRef.current.style.display = 'none'; + } + + prepare(row, col, prop, td, originalValue, cellProperties) { + // We'll need to call the `prepare` method from + // the `BaseEditorComponent` class, as it provides + // the component with the information needed to use the editor + // (hotInstance, row, col, prop, TD, originalValue, cellProperties) + super.prepare(row, col, prop, td, originalValue, cellProperties); + + const tdPosition = td.getBoundingClientRect(); + + // As the `prepare` method is triggered after selecting + // any cell, we're updating the styles for the editor element, + // so it shows up in the correct position. + this.mainElementRef.current.style.left = tdPosition.left + window.pageXOffset + 'px'; + this.mainElementRef.current.style.top = tdPosition.top + window.pageYOffset + 'px'; + } + + setLowerCase() { + this.setState( + (state, props) => { + return { value: this.state.value.toString().toLowerCase() }; + }, + () => { + this.finishEditing(); + } + ); + } + + setUpperCase() { + this.setState( + (state, props) => { + return { value: this.state.value.toString().toUpperCase() }; + }, + () => { + this.finishEditing(); + } + ); + } + + stopMousedownPropagation(e) { + e.stopPropagation(); + } + + render() { + return ( +
+ + +
+ ); + } +} + +const data = [ + ['Obrien Fischer'], + ['Alexandria Gordon'], + ['John Stafford'], + ['Regina Waters'], + ['Kay Bentley'], + ['Emerson Drake'], + ['Dean Stapleton'] +]; + +export const ExampleComponent = () => { + return ( + + + {/* add the `hot-editor` attribute to mark the component as a Handsontable editor */} + + + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-editor/example2.jsx b/docs/content/guides/cell-functions/cell-editor/example2.jsx new file mode 100644 index 00000000000..2e9f8cc988d --- /dev/null +++ b/docs/content/guides/cell-functions/cell-editor/example2.jsx @@ -0,0 +1,43 @@ +import { HotTable } from '@handsontable/react'; +import { TextEditor } from 'handsontable/editors/textEditor'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +class CustomEditor extends TextEditor { + createElements() { + super.createElements(); + + this.TEXTAREA = document.createElement('input'); + this.TEXTAREA.setAttribute('placeholder', 'Custom placeholder'); + this.TEXTAREA.setAttribute('data-hot-input', true); + this.textareaStyle = this.TEXTAREA.style; + this.TEXTAREA_PARENT.innerText = ''; + this.TEXTAREA_PARENT.appendChild(this.TEXTAREA); + } +} + +export const ExampleComponent = () => { + return ( + + ); +} + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-function.md b/docs/content/guides/cell-functions/cell-function/cell-function.md similarity index 92% rename from docs/content/guides/cell-functions/cell-function.md rename to docs/content/guides/cell-functions/cell-function/cell-function.md index 59fd5dc3d39..bd9ff0df9e9 100644 --- a/docs/content/guides/cell-functions/cell-function.md +++ b/docs/content/guides/cell-functions/cell-function/cell-function.md @@ -35,7 +35,7 @@ Handsontable does not display the values stored in the data source directly. Ins ## Editor -Cell editors are the most complex cell functions. We have prepared a separate page [custom cell editor](@/guides/cell-functions/cell-editor.md) explaining how cell edit works and how to write your own cell editor. +Cell editors are the most complex cell functions. We have prepared a separate page [custom cell editor](@/guides/cell-functions/cell-editor/cell-editor.md) explaining how cell edit works and how to write your own cell editor. ## Validator @@ -45,7 +45,7 @@ Contrary to `renderer` and `editor` functions, the `validator` function doesn't ## Cell type -Manually defining those functions for cells or columns would be tedious, so to simplify the configuration, Handsontable introduced [cell types](@/guides/cell-types/cell-type.md). +Manually defining those functions for cells or columns would be tedious, so to simplify the configuration, Handsontable introduced [cell types](@/guides/cell-types/cell-type/cell-type.md). ## Cell functions getters @@ -55,7 +55,7 @@ Manually defining those functions for cells or columns would be tedious, so to s To use the Handsontable API, you'll need access to the Handsontable instance. You can do that by utilizing a reference to the `HotTable` component, and reading its `hotInstance` property. -For more information, see the [Instance methods](@/guides/getting-started/react-methods.md) page. +For more information, see the [Instance methods](@/guides/getting-started/react-methods/react-methods.md) page. ::: @@ -143,10 +143,10 @@ export const ExampleComponent = () => { ### Related guides -- [Cell editor](@/guides/cell-functions/cell-editor.md) -- [Cell renderer](@/guides/cell-functions/cell-renderer.md) -- [Cell validator](@/guides/cell-functions/cell-validator.md) -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell editor](@/guides/cell-functions/cell-editor/cell-editor.md) +- [Cell renderer](@/guides/cell-functions/cell-renderer/cell-renderer.md) +- [Cell validator](@/guides/cell-functions/cell-validator/cell-validator.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-functions/cell-renderer.md b/docs/content/guides/cell-functions/cell-renderer/cell-renderer.md similarity index 63% rename from docs/content/guides/cell-functions/cell-renderer.md rename to docs/content/guides/cell-functions/cell-renderer/cell-renderer.md index 1920d80ea53..356a6596aaa 100644 --- a/docs/content/guides/cell-functions/cell-renderer.md +++ b/docs/content/guides/cell-functions/cell-renderer/cell-renderer.md @@ -44,7 +44,7 @@ It gives users a convenient way for defining which renderer should be used when A renderer is a function that determines how a cell looks. -Set together, a renderer, [editor](@/guides/cell-functions/cell-editor.md) and [validator](@/guides/cell-functions/cell-validator.md) form a [cell type](@/guides/cell-types/cell-type.md). +Set together, a renderer, [editor](@/guides/cell-functions/cell-editor/cell-editor.md) and [validator](@/guides/cell-functions/cell-validator/cell-validator.md) form a [cell type](@/guides/cell-types/cell-type/cell-type.md). ## Declare a custom renderer as a component @@ -63,135 +63,18 @@ Be sure to turn those options off in your Handsontable configuration, as keeping ::: example #example1 :react --tab preview -```jsx -import { HotTable, HotColumn } from '@handsontable/react'; -import 'handsontable/dist/handsontable.full.min.css'; - -// your renderer component -const RendererComponent = (props) => { - // the available renderer-related props are: - // - `row` (row index) - // - `col` (column index) - // - `prop` (column property name) - // - `TD` (the HTML cell element) - // - `cellProperties` (the `cellProperties` object for the edited cell) - return ( - <> - - Row: {props.row}, column: {props.col}, - {" "} - value: {props.value} - - ); -} - -const hotData = [ - ['A1', 'B1', 'C1', 'D1', 'E1'], - ['A2', 'B2', 'C2', 'D2', 'E2'], - ['A3', 'B3', 'C3', 'D3', 'E3'], - ['A4', 'B4', 'C4', 'D4', 'E4'], - ['A5', 'B5', 'C5', 'D5', 'E5'], - ['A6', 'B6', 'C6', 'D6', 'E6'], - ['A7', 'B7', 'C7', 'D7', 'E7'], - ['A8', 'B8', 'C8', 'D8', 'E8'], - ['A9', 'B9', 'C9', 'D9', 'E9'], -]; - -export const ExampleComponent = () => { - return ( - - - {/* add the `hot-renderer` attribute to mark the component as a Handsontable renderer */} - - - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-renderer/example1.jsx) ::: ## Use the renderer component within React's Context -In this example, React's `Context` passes information available in the main app component to the renderer. In this case, we're using just the renderer, but the same principle works with [editors](@/guides/cell-functions/cell-editor.md) as well. +In this example, React's `Context` passes information available in the main app component to the renderer. In this case, we're using just the renderer, but the same principle works with [editors](@/guides/cell-functions/cell-editor/cell-editor.md) as well. ::: example #example2 :react --css 1 --js 2 --tab preview -```css -.handsontable td.dark { - background: #000; - color: #fff; -} -``` -```jsx -import React, { useState, useContext } from 'react'; -import { HotTable, HotColumn } from '@handsontable/react'; -import 'handsontable/dist/handsontable.full.min.css'; - -// a component -const HighlightContext = React.createContext(); - -// a renderer component -function CustomRenderer(props) { - const darkMode = useContext(HighlightContext); - - if (darkMode) { - props.TD.className = 'dark'; - } else { - props.TD.className = ''; - } - - return
{props.value}
; -} - -export const ExampleComponent = () => { - const [darkMode, setDarkMode] = useState(false); - - const toggleDarkMode = (event) => { - setDarkMode(event.target.checked); - }; - - return ( - -
- -
- - - {/* add the `hot-renderer` attribute to mark the component as a Handsontable renderer */} - - - -
- ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-renderer/example2.css) +@[code](@/content/guides/cell-functions/cell-renderer/example2.jsx) ::: @@ -203,56 +86,7 @@ The following example implements `@handsontable/react` with a custom renderer ad ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { textRenderer } from 'handsontable/renderers/textRenderer'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - { - event.preventDefault(); - }); - - td.innerText = ''; - td.appendChild(img); - - return td; - } - } - ]} - colHeaders={true} - rowHeights={55} - height="auto" - autoWrapRow={true} - autoWrapCol={true} - licenseKey="non-commercial-and-evaluation" - /> - ); -} - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-renderer/example3.jsx) ::: @@ -535,79 +369,7 @@ function coverRenderer(instance, td, row, col, prop, value, cellProperties) { ::: example #example4 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const data = [{ - title: 'Professional JavaScript for Web Developers', - description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', - comments: 'I would rate it ★★★★☆', - cover: '{{$basePath}}/img/examples/professional-javascript-developers-nicholas-zakas.jpg' - }, - { - title: 'JavaScript: The Good Parts', - description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', - comments: 'This is the book about JavaScript', - cover: '{{$basePath}}/img/examples/javascript-the-good-parts.jpg' - }, - { - title: 'JavaScript: The Definitive Guide', - description: 'JavaScript: The Definitive Guide provides a thorough description of the core JavaScript language and both the legacy and standard DOMs implemented in web browsers.', - comments: 'I\'ve never actually read it, but the comments are highly positive.', - cover: '{{$basePath}}/img/examples/javascript-the-definitive-guide.jpg' - } - ]; - - function safeHtmlRenderer(instance, td, row, col, prop, value, cellProperties) { - // WARNING: Be sure you only allow certain HTML tags to avoid XSS threats. - // Sanitize the "value" before passing it to the innerHTML property. - td.innerHTML = value; - } - - function coverRenderer(instance, td, row, col, prop, value, cellProperties) { - const img = document.createElement('img'); - - img.src = value; - - img.addEventListener('mousedown', event => { - event.preventDefault(); - }); - - td.innerText = ''; - td.appendChild(img); - - return td; - } - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example4')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-renderer/example4.jsx) ::: @@ -696,80 +458,7 @@ exampleContainer.addEventListener('mouseup', event => { ::: example #example5 :react -```jsx -import { useEffect, useRef } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import { textRenderer } from 'handsontable/renderers/textRenderer'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const hotRef = useRef(null); - - let isChecked = false; - - function customRenderer(instance, td) { - textRenderer.apply(this, arguments); - - if (isChecked) { - td.style.backgroundColor = 'yellow'; - } else { - td.style.backgroundColor = 'white'; - } - } - - const exampleContainerMousedownCallback = event => { - if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { - event.stopPropagation(); - } - }; - let exampleContainerMouseupCallback; - - useEffect(() => { - const hot = hotRef.current.hotInstance; - - exampleContainerMouseupCallback = event => { - if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { - isChecked = !event.target.checked; - hot.render(); - } - }; - }); - - return ( -
exampleContainerMouseupCallback(...args)}> - Bold and Beautiful'; - - case 1: - return `Some checkbox`; - } - }} - autoWrapRow={true} - autoWrapCol={true} - licenseKey="non-commercial-and-evaluation" - /> -
- ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example5')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-renderer/example5.jsx) ::: @@ -794,7 +483,7 @@ This is because Handsontable: - Calls `renderer` functions multiple times per cell - this can lead to having multiple copies of the same event listener attached to a cell - Reuses table cell nodes during table scrolling and adding/removing new rows/columns - this can lead to having event listeners attached to the wrong cell -Before deciding to attach an event listener in cell renderer make sure, that there is no [Handsontable event](@/guides/getting-started/events-and-hooks.md) that suits your needs. Using _Handsontable events_ system is the safest way to respond to user actions. +Before deciding to attach an event listener in cell renderer make sure, that there is no [Handsontable event](@/guides/getting-started/events-and-hooks/events-and-hooks.md) that suits your needs. Using _Handsontable events_ system is the safest way to respond to user actions. If you did't find a suitable _Handsontable event_ put the cell content into a wrapping `
`, attach the event listener to the wrapper and then put it into the table cell. @@ -808,10 +497,10 @@ Cell renderers are called separately for every displayed cell, during every tabl ### Related guides -- [Custom renderer in React](@/react/guides/cell-functions/cell-renderer.md) -- [Custom renderer in Angular](@/guides/integrate-with-angular/angular-custom-renderer-example.md) -- [Custom renderer in Vue 2](@/guides/integrate-with-vue/vue-custom-renderer-example.md) -- [Custom renderer in Vue 3](@/guides/integrate-with-vue3/vue3-custom-renderer-example.md) +- [Custom renderer in React](@/react/guides/cell-functions/cell-renderer/cell-renderer.md) +- [Custom renderer in Angular](@/guides/integrate-with-angular/angular-custom-renderer-example/angular-custom-renderer-example.md) +- [Custom renderer in Vue 2](@/guides/integrate-with-vue/vue-custom-renderer-example/vue-custom-renderer-example.md) +- [Custom renderer in Vue 3](@/guides/integrate-with-vue3/vue3-custom-renderer-example/vue3-custom-renderer-example.md) ### Related API reference diff --git a/docs/content/guides/cell-functions/cell-renderer/example1.jsx b/docs/content/guides/cell-functions/cell-renderer/example1.jsx new file mode 100644 index 00000000000..8d2781ea08b --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example1.jsx @@ -0,0 +1,50 @@ +import { HotTable, HotColumn } from '@handsontable/react'; +import 'handsontable/dist/handsontable.full.min.css'; + +// your renderer component +const RendererComponent = (props) => { + // the available renderer-related props are: + // - `row` (row index) + // - `col` (column index) + // - `prop` (column property name) + // - `TD` (the HTML cell element) + // - `cellProperties` (the `cellProperties` object for the edited cell) + return ( + <> + + Row: {props.row}, column: {props.col}, + {" "} + value: {props.value} + + ); +} + +const hotData = [ + ['A1', 'B1', 'C1', 'D1', 'E1'], + ['A2', 'B2', 'C2', 'D2', 'E2'], + ['A3', 'B3', 'C3', 'D3', 'E3'], + ['A4', 'B4', 'C4', 'D4', 'E4'], + ['A5', 'B5', 'C5', 'D5', 'E5'], + ['A6', 'B6', 'C6', 'D6', 'E6'], + ['A7', 'B7', 'C7', 'D7', 'E7'], + ['A8', 'B8', 'C8', 'D8', 'E8'], + ['A9', 'B9', 'C9', 'D9', 'E9'], +]; + +export const ExampleComponent = () => { + return ( + + + {/* add the `hot-renderer` attribute to mark the component as a Handsontable renderer */} + + + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example2.css b/docs/content/guides/cell-functions/cell-renderer/example2.css new file mode 100644 index 00000000000..69cb8e49547 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example2.css @@ -0,0 +1,4 @@ +.handsontable td.dark { + background: #000; + color: #fff; +} \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example2.jsx b/docs/content/guides/cell-functions/cell-renderer/example2.jsx new file mode 100644 index 00000000000..f1cc5f80dff --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example2.jsx @@ -0,0 +1,60 @@ +import React, { useState, useContext } from 'react'; +import { HotTable, HotColumn } from '@handsontable/react'; +import 'handsontable/dist/handsontable.full.min.css'; + +// a component +const HighlightContext = React.createContext(); + +// a renderer component +function CustomRenderer(props) { + const darkMode = useContext(HighlightContext); + + if (darkMode) { + props.TD.className = 'dark'; + } else { + props.TD.className = ''; + } + + return
{props.value}
; +} + +export const ExampleComponent = () => { + const [darkMode, setDarkMode] = useState(false); + + const toggleDarkMode = (event) => { + setDarkMode(event.target.checked); + }; + + return ( + +
+ +
+ + + {/* add the `hot-renderer` attribute to mark the component as a Handsontable renderer */} + + + +
+ ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example3.jsx b/docs/content/guides/cell-functions/cell-renderer/example3.jsx new file mode 100644 index 00000000000..71dfb44a194 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example3.jsx @@ -0,0 +1,48 @@ +import { HotTable } from '@handsontable/react'; +import { textRenderer } from 'handsontable/renderers/textRenderer'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + { + event.preventDefault(); + }); + + td.innerText = ''; + td.appendChild(img); + + return td; + } + } + ]} + colHeaders={true} + rowHeights={55} + height="auto" + autoWrapRow={true} + autoWrapCol={true} + licenseKey="non-commercial-and-evaluation" + /> + ); +} + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example4.js b/docs/content/guides/cell-functions/cell-renderer/example4.js new file mode 100644 index 00000000000..b3853ec1f88 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example4.js @@ -0,0 +1,61 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const data = [ + { + title: 'Professional JavaScript for Web Developers', + description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', + comments: 'I would rate it ★★★★☆', + cover: '{{$basePath}}/img/examples/professional-javascript-developers-nicholas-zakas.jpg' + }, + { + title: 'JavaScript: The Good Parts', + description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', + comments: 'This is the book about JavaScript', + cover: '{{$basePath}}/img/examples/javascript-the-good-parts.jpg' + }, + { + title: 'JavaScript: The Definitive Guide', + description: 'JavaScript: The Definitive Guide provides a thorough description of the core JavaScript language and both the legacy and standard DOMs implemented in web browsers.', + comments: 'I\'ve never actually read it, but the comments are highly positive.', + cover: '{{$basePath}}/img/examples/javascript-the-definitive-guide.jpg' + } +]; + +const container = document.querySelector('#example4'); +const hot = new Handsontable(container, { + data, + colWidths: [200, 200, 200, 80], + colHeaders: ['Title', 'Description', 'Comments', 'Cover'], + height: 'auto', + columns: [ + { data: 'title', renderer: 'html' }, + { data: 'description', renderer: 'html' }, + { data: 'comments', renderer: safeHtmlRenderer }, + { data: 'cover', renderer: coverRenderer } + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +function safeHtmlRenderer(instance, td, row, col, prop, value, cellProperties) { + // WARNING: Be sure you only allow certain HTML tags to avoid XSS threats. + // Sanitize the "value" before passing it to the innerHTML property. + td.innerHTML = value; +} + +function coverRenderer(instance, td, row, col, prop, value, cellProperties) { + const img = document.createElement('img'); + + img.src = value; + + img.addEventListener('mousedown', event => { + event.preventDefault(); + }); + + td.innerText = ''; + td.appendChild(img); + + return td; +} \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example4.jsx b/docs/content/guides/cell-functions/cell-renderer/example4.jsx new file mode 100644 index 00000000000..44eba7c085f --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example4.jsx @@ -0,0 +1,71 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const data = [{ + title: 'Professional JavaScript for Web Developers', + description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', + comments: 'I would rate it ★★★★☆', + cover: '{{$basePath}}/img/examples/professional-javascript-developers-nicholas-zakas.jpg' + }, + { + title: 'JavaScript: The Good Parts', + description: 'This book provides a developer-level introduction along with more advanced and useful features of JavaScript.', + comments: 'This is the book about JavaScript', + cover: '{{$basePath}}/img/examples/javascript-the-good-parts.jpg' + }, + { + title: 'JavaScript: The Definitive Guide', + description: 'JavaScript: The Definitive Guide provides a thorough description of the core JavaScript language and both the legacy and standard DOMs implemented in web browsers.', + comments: 'I\'ve never actually read it, but the comments are highly positive.', + cover: '{{$basePath}}/img/examples/javascript-the-definitive-guide.jpg' + } + ]; + + function safeHtmlRenderer(instance, td, row, col, prop, value, cellProperties) { + // WARNING: Be sure you only allow certain HTML tags to avoid XSS threats. + // Sanitize the "value" before passing it to the innerHTML property. + td.innerHTML = value; + } + + function coverRenderer(instance, td, row, col, prop, value, cellProperties) { + const img = document.createElement('img'); + + img.src = value; + + img.addEventListener('mousedown', event => { + event.preventDefault(); + }); + + td.innerText = ''; + td.appendChild(img); + + return td; + } + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example4')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example5.html b/docs/content/guides/cell-functions/cell-renderer/example5.html new file mode 100644 index 00000000000..96790310e56 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example5.html @@ -0,0 +1,3 @@ +
+
+
\ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example5.js b/docs/content/guides/cell-functions/cell-renderer/example5.js new file mode 100644 index 00000000000..4560f9d9361 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example5.js @@ -0,0 +1,49 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +let isChecked = false; +const exampleContainer = document.querySelector('#exampleContainer5'); +const container = document.querySelector('#example5'); + +function customRenderer(instance, td) { + Handsontable.renderers.TextRenderer.apply(this, arguments); + + if (isChecked) { + td.style.backgroundColor = 'yellow'; + } else { + td.style.backgroundColor = 'white'; + } +} + +const hot = new Handsontable(container, { + height: 'auto', + columns: [ + {}, + { renderer: customRenderer } + ], + colHeaders(col) { + switch (col) { + case 0: + return 'Bold and Beautiful'; + + case 1: + return `Some checkbox`; + } + }, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); + +exampleContainer.addEventListener('mousedown', event => { + if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { + event.stopPropagation(); + } +}); + +exampleContainer.addEventListener('mouseup', event => { + if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { + isChecked = !event.target.checked; + hot.render(); + } +}); \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-renderer/example5.jsx b/docs/content/guides/cell-functions/cell-renderer/example5.jsx new file mode 100644 index 00000000000..1753fded483 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-renderer/example5.jsx @@ -0,0 +1,72 @@ +import { useEffect, useRef } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import { textRenderer } from 'handsontable/renderers/textRenderer'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const hotRef = useRef(null); + + let isChecked = false; + + function customRenderer(instance, td) { + textRenderer.apply(this, arguments); + + if (isChecked) { + td.style.backgroundColor = 'yellow'; + } else { + td.style.backgroundColor = 'white'; + } + } + + const exampleContainerMousedownCallback = event => { + if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { + event.stopPropagation(); + } + }; + let exampleContainerMouseupCallback; + + useEffect(() => { + const hot = hotRef.current.hotInstance; + + exampleContainerMouseupCallback = event => { + if (event.target.nodeName == 'INPUT' && event.target.className == 'checker') { + isChecked = !event.target.checked; + hot.render(); + } + }; + }); + + return ( +
exampleContainerMouseupCallback(...args)}> + Bold and Beautiful'; + + case 1: + return `Some checkbox`; + } + }} + autoWrapRow={true} + autoWrapCol={true} + licenseKey="non-commercial-and-evaluation" + /> +
+ ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example5')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-validator.md b/docs/content/guides/cell-functions/cell-validator/cell-validator.md similarity index 73% rename from docs/content/guides/cell-functions/cell-validator.md rename to docs/content/guides/cell-functions/cell-validator/cell-validator.md index 751804dd29b..a5d2dfefc9b 100644 --- a/docs/content/guides/cell-functions/cell-validator.md +++ b/docs/content/guides/cell-functions/cell-validator/cell-validator.md @@ -250,91 +250,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { useState } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const [output, setOutput] = useState(''); - - const ipValidatorRegexp = /^(?:\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|null)$/; - const emailValidator = (value, callback) => { - setTimeout(() => { - if (/.+@.+/.test(value)) { - callback(true); - - } else { - callback(false); - } - }, 1000); - }; - - return ( - <> - = 0; i--) { - // gently don't accept the word "foo" (remove the change at index i) - if (changes[i][3] === 'foo') { - changes.splice(i, 1); - } - // if any of pasted cells contains the word "nuke", reject the whole paste - else if (changes[i][3] === 'nuke') { - return false; - } - // capitalise first letter in column 1 and 2 - else if ((changes[i][1] === 'name.first' || changes[i][1] === 'name.last')) { - if (changes[i][3] !== null) { - changes[i][3] = changes[i][3].charAt(0).toUpperCase() + changes[i][3].slice(1); - } - } - } - }} - afterChange={function(changes, source) { - if (source !== 'loadData' && source !== 'updateData') { - setOutput(JSON.stringify(changes)); - } - }} - colHeaders={['ID', 'First name', 'Last name', 'IP', 'E-mail']} - height="auto" - autoWrapRow={true} - autoWrapCol={true} - licenseKey="non-commercial-and-evaluation" - columns={[ - { data: 'id', type: 'numeric' }, - { data: 'name.first' }, - { data: 'name.last' }, - { data: 'ip', validator: ipValidatorRegexp, allowInvalid: true }, - { data: 'email', validator: emailValidator, allowInvalid: false } - ]} - /> - {output} - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-functions/cell-validator/example1.jsx) ::: diff --git a/docs/content/guides/cell-functions/cell-validator/example1.html b/docs/content/guides/cell-functions/cell-validator/example1.html new file mode 100644 index 00000000000..f2344a19d13 --- /dev/null +++ b/docs/content/guides/cell-functions/cell-validator/example1.html @@ -0,0 +1,2 @@ +
+Here you will see the log \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-validator/example1.js b/docs/content/guides/cell-functions/cell-validator/example1.js new file mode 100644 index 00000000000..a4ee3e0b12a --- /dev/null +++ b/docs/content/guides/cell-functions/cell-validator/example1.js @@ -0,0 +1,69 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const output = document.querySelector('#output'); + +const ipValidatorRegexp = /^(?:\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|null)$/; + +const emailValidator = (value, callback) => { + setTimeout(() => { + if (/.+@.+/.test(value)) { + callback(true); + + } else { + callback(false); + } + }, 1000); +}; + +const hot = new Handsontable(container, { + data: [ + { id: 1, name: { first: 'Joe', last: 'Fabiano' }, ip: '0.0.0.1', email: 'Joe.Fabiano@ex.com' }, + { id: 2, name: { first: 'Fred', last: 'Wecler' }, ip: '0.0.0.1', email: 'Fred.Wecler@ex.com' }, + { id: 3, name: { first: 'Steve', last: 'Wilson' }, ip: '0.0.0.1', email: 'Steve.Wilson@ex.com' }, + { id: 4, name: { first: 'Maria', last: 'Fernandez' }, ip: '0.0.0.1', email: 'M.Fernandez@ex.com' }, + { id: 5, name: { first: 'Pierre', last: 'Barbault' }, ip: '0.0.0.1', email: 'Pierre.Barbault@ex.com' }, + { id: 6, name: { first: 'Nancy', last: 'Moore' }, ip: '0.0.0.1', email: 'Nancy.Moore@ex.com' }, + { id: 7, name: { first: 'Barbara', last: 'MacDonald' }, ip: '0.0.0.1', email: 'B.MacDonald@ex.com' }, + { id: 8, name: { first: 'Wilma', last: 'Williams' }, ip: '0.0.0.1', email: 'Wilma.Williams@ex.com' }, + { id: 9, name: { first: 'Sasha', last: 'Silver' }, ip: '0.0.0.1', email: 'Sasha.Silver@ex.com' }, + { id: 10, name: { first: 'Don', last: 'Pérignon' }, ip: '0.0.0.1', email: 'Don.Pérignon@ex.com' }, + { id: 11, name: { first: 'Aaron', last: 'Kinley' }, ip: '0.0.0.1', email: 'Aaron.Kinley@ex.com' } + ], + beforeChange(changes, source) { + for (let i = changes.length - 1; i >= 0; i--) { + // gently don't accept the word "foo" (remove the change at index i) + if (changes[i][3] === 'foo') { + changes.splice(i, 1); + } + // if any of pasted cells contains the word "nuke", reject the whole paste + else if (changes[i][3] === 'nuke') { + return false; + } + // capitalise first letter in column 1 and 2 + else if ((changes[i][1] === 'name.first' || changes[i][1] === 'name.last')) { + if(changes[i][3] !== null){ + changes[i][3] = changes[i][3].charAt(0).toUpperCase() + changes[i][3].slice(1); + } + } + } + }, + afterChange(changes, source) { + if (source !== 'loadData') { + output.innerText = JSON.stringify(changes); + } + }, + colHeaders: ['ID', 'First name', 'Last name', 'IP', 'E-mail'], + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { data: 'id', type: 'numeric' }, + { data: 'name.first' }, + { data: 'name.last' }, + { data: 'ip', validator: ipValidatorRegexp, allowInvalid: true }, + { data: 'email', validator: emailValidator, allowInvalid: false } + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-functions/cell-validator/example1.jsx b/docs/content/guides/cell-functions/cell-validator/example1.jsx new file mode 100644 index 00000000000..cdeae851e0e --- /dev/null +++ b/docs/content/guides/cell-functions/cell-validator/example1.jsx @@ -0,0 +1,83 @@ +import { useState } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const [output, setOutput] = useState(''); + + const ipValidatorRegexp = /^(?:\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|null)$/; + const emailValidator = (value, callback) => { + setTimeout(() => { + if (/.+@.+/.test(value)) { + callback(true); + + } else { + callback(false); + } + }, 1000); + }; + + return ( + <> + = 0; i--) { + // gently don't accept the word "foo" (remove the change at index i) + if (changes[i][3] === 'foo') { + changes.splice(i, 1); + } + // if any of pasted cells contains the word "nuke", reject the whole paste + else if (changes[i][3] === 'nuke') { + return false; + } + // capitalise first letter in column 1 and 2 + else if ((changes[i][1] === 'name.first' || changes[i][1] === 'name.last')) { + if (changes[i][3] !== null) { + changes[i][3] = changes[i][3].charAt(0).toUpperCase() + changes[i][3].slice(1); + } + } + } + }} + afterChange={function(changes, source) { + if (source !== 'loadData' && source !== 'updateData') { + setOutput(JSON.stringify(changes)); + } + }} + colHeaders={['ID', 'First name', 'Last name', 'IP', 'E-mail']} + height="auto" + autoWrapRow={true} + autoWrapCol={true} + licenseKey="non-commercial-and-evaluation" + columns={[ + { data: 'id', type: 'numeric' }, + { data: 'name.first' }, + { data: 'name.last' }, + { data: 'ip', validator: ipValidatorRegexp, allowInvalid: true }, + { data: 'email', validator: emailValidator, allowInvalid: false } + ]} + /> + {output} + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type.md b/docs/content/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md similarity index 61% rename from docs/content/guides/cell-types/autocomplete-cell-type.md rename to docs/content/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md index da4599673bf..472e69d43c2 100644 --- a/docs/content/guides/cell-types/autocomplete-cell-type.md +++ b/docs/content/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md @@ -82,58 +82,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const colors = ['yellow', 'red', 'orange and another color', 'green', - 'blue', 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan' - ]; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/autocomplete-cell-type/example1.jsx) ::: @@ -208,61 +157,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const colors = ['yellow', 'red', 'orange', 'green', 'blue', - 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan' - ]; - const cars = ['BMW', 'Chrysler', 'Nissan', 'Suzuki', 'Toyota', 'Volvo']; - - return ( - (allowInvalid true)', 'Year', 'Chassis color
(allowInvalid false)', - 'Bumper color
(allowInvalid true)']} - columns={[{ - type: 'autocomplete', - source: cars, - strict: true - // allowInvalid: true // true is default - }, - {}, - { - type: 'autocomplete', - source: colors, - strict: true, - allowInvalid: false - }, - { - type: 'autocomplete', - source: colors, - strict: true, - allowInvalid: true //true is default - } - ]} - /> - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/autocomplete-cell-type/example2.jsx) ::: @@ -318,48 +213,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - response.json()) - .then(response => process(response.data)); - }, - strict: true - }, - {}, // Year is a default text column - {}, // Chassis color is a default text column - {} // Bumper color is a default text column - ]} - /> - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/autocomplete-cell-type/example3.jsx) ::: @@ -369,9 +223,9 @@ ReactDOM.render(, document.getElementById('example3')); ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) -- [Dropdown cell type](@/guides/cell-types/dropdown-cell-type.md) -- [Select cell type](@/guides/cell-types/select-cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) +- [Dropdown cell type](@/guides/cell-types/dropdown-cell-type/dropdown-cell-type.md) +- [Select cell type](@/guides/cell-types/select-cell-type/select-cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example1.js b/docs/content/guides/cell-types/autocomplete-cell-type/example1.js new file mode 100644 index 00000000000..44ecd8ccffe --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example1.js @@ -0,0 +1,39 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const colors = ['yellow', 'red', 'orange and another color', 'green', + 'blue', 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan']; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['BMW', 2017, 'black', 'black'], + ['Nissan', 2018, 'blue', 'blue'], + ['Chrysler', 2019, 'yellow', 'black'], + ['Volvo', 2020, 'white', 'gray'] + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + columns: [ + { + type: 'autocomplete', + source: ['BMW', 'Chrysler', 'Nissan', 'Suzuki', 'Toyota', 'Volvo'], + strict: false + }, + { type: 'numeric' }, + { + type: 'autocomplete', + source: colors, + strict: false, + visibleRows: 4 + }, + { + type: 'autocomplete', + source: colors, + strict: false, + trimDropdown: false + } + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example1.jsx b/docs/content/guides/cell-types/autocomplete-cell-type/example1.jsx new file mode 100644 index 00000000000..42696897a2a --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example1.jsx @@ -0,0 +1,50 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const colors = ['yellow', 'red', 'orange and another color', 'green', + 'blue', 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan' + ]; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example2.js b/docs/content/guides/cell-types/autocomplete-cell-type/example2.js new file mode 100644 index 00000000000..5495d34af79 --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example2.js @@ -0,0 +1,42 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const colors = ['yellow', 'red', 'orange', 'green', 'blue', + 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan']; +const cars = ['BMW', 'Chrysler', 'Nissan', 'Suzuki', 'Toyota', 'Volvo']; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['BMW', 2017, 'black', 'black'], + ['Nissan', 2018, 'blue', 'blue'], + ['Chrysler', 2019, 'yellow', 'black'], + ['Volvo', 2020, 'white', 'gray'] + ], + colHeaders: ['Car
(allowInvalid true)', 'Year', + 'Chassis color
(allowInvalid false)', 'Bumper color
(allowInvalid true)'], + columns: [ + { + type: 'autocomplete', + source: cars, + strict: true + // allowInvalid: true // true is default + }, + {}, + { + type: 'autocomplete', + source: colors, + strict: true, + allowInvalid: false + }, + { + type: 'autocomplete', + source: colors, + strict: true, + allowInvalid: true //true is default + } + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example2.jsx b/docs/content/guides/cell-types/autocomplete-cell-type/example2.jsx new file mode 100644 index 00000000000..f235ab4a716 --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example2.jsx @@ -0,0 +1,53 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const colors = ['yellow', 'red', 'orange', 'green', 'blue', + 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan' + ]; + const cars = ['BMW', 'Chrysler', 'Nissan', 'Suzuki', 'Toyota', 'Volvo']; + + return ( + (allowInvalid true)', 'Year', 'Chassis color
(allowInvalid false)', + 'Bumper color
(allowInvalid true)']} + columns={[{ + type: 'autocomplete', + source: cars, + strict: true + // allowInvalid: true // true is default + }, + {}, + { + type: 'autocomplete', + source: colors, + strict: true, + allowInvalid: false + }, + { + type: 'autocomplete', + source: colors, + strict: true, + allowInvalid: true //true is default + } + ]} + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example3.js b/docs/content/guides/cell-types/autocomplete-cell-type/example3.js new file mode 100644 index 00000000000..1773ce1c1e8 --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example3.js @@ -0,0 +1,31 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['BMW', 2017, 'black', 'black'], + ['Nissan', 2018, 'blue', 'blue'], + ['Chrysler', 2019, 'yellow', 'black'], + ['Volvo', 2020, 'white', 'gray'] + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { + type: 'autocomplete', + source(query, process) { + fetch('{{$basePath}}/scripts/json/autocomplete.json') + .then(response => response.json()) + .then(response => process(response.data)); + }, + strict: true + }, + {}, // Year is a default text column + {}, // Chassis color is a default text column + {} // Bumper color is a default text column + ], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/autocomplete-cell-type/example3.jsx b/docs/content/guides/cell-types/autocomplete-cell-type/example3.jsx new file mode 100644 index 00000000000..ca8be43e5ad --- /dev/null +++ b/docs/content/guides/cell-types/autocomplete-cell-type/example3.jsx @@ -0,0 +1,40 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + response.json()) + .then(response => process(response.data)); + }, + strict: true + }, + {}, // Year is a default text column + {}, // Chassis color is a default text column + {} // Bumper color is a default text column + ]} + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/cell-type.md b/docs/content/guides/cell-types/cell-type/cell-type.md similarity index 75% rename from docs/content/guides/cell-types/cell-type.md rename to docs/content/guides/cell-types/cell-type/cell-type.md index e15d3055ed0..5a07790e350 100644 --- a/docs/content/guides/cell-types/cell-type.md +++ b/docs/content/guides/cell-types/cell-type/cell-type.md @@ -85,15 +85,15 @@ columns={[{ Handsontable comes with nine types: -- ["autocomplete"](@/guides/cell-types/autocomplete-cell-type.md) or `Handsontable.cellTypes.autocomplete` -- ["checkbox"](@/guides/cell-types/checkbox-cell-type.md) or `Handsontable.cellTypes.checkbox` -- ["date"](@/guides/cell-types/date-cell-type.md) or `Handsontable.cellTypes.date` -- ["dropdown"](@/guides/cell-types/dropdown-cell-type.md) or `Handsontable.cellTypes.dropdown` -- ["handsontable"](@/guides/cell-types/handsontable-cell-type.md) or `Handsontable.cellTypes.handsontable` -- ["numeric"](@/guides/cell-types/numeric-cell-type.md) or `Handsontable.cellTypes.numeric` -- ["password"](@/guides/cell-types/password-cell-type.md) or `Handsontable.cellTypes.password` -- ["select"](@/guides/cell-types/select-cell-type.md) or `Handsontable.cellTypes.select` -- ["time"](@/guides/cell-types/time-cell-type.md) or `Handsontable.cellTypes.time` +- ["autocomplete"](@/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md) or `Handsontable.cellTypes.autocomplete` +- ["checkbox"](@/guides/cell-types/checkbox-cell-type/checkbox-cell-type.md) or `Handsontable.cellTypes.checkbox` +- ["date"](@/guides/cell-types/date-cell-type/date-cell-type.md) or `Handsontable.cellTypes.date` +- ["dropdown"](@/guides/cell-types/dropdown-cell-type/dropdown-cell-type.md) or `Handsontable.cellTypes.dropdown` +- ["handsontable"](@/guides/cell-types/handsontable-cell-type/handsontable-cell-type.md) or `Handsontable.cellTypes.handsontable` +- ["numeric"](@/guides/cell-types/numeric-cell-type/numeric-cell-type.md) or `Handsontable.cellTypes.numeric` +- ["password"](@/guides/cell-types/password-cell-type/password-cell-type.md) or `Handsontable.cellTypes.password` +- ["select"](@/guides/cell-types/select-cell-type/select-cell-type.md) or `Handsontable.cellTypes.select` +- ["time"](@/guides/cell-types/time-cell-type/time-cell-type.md) or `Handsontable.cellTypes.time` - "text" or `Handsontable.cellTypes.text` The `text` cell type is the default type. @@ -360,7 +360,7 @@ const hot = new Handsontable(container, { ::: -Using [cascade configuration](@/guides/getting-started/configuration-options.md#cascading-configuration) we define a table with two columns, with [`validator`](@/api/options.md#validator) set to `customValidator` function. The s[`type`](@/api/options.md#type) of the first column is set to `password`. The `Password` cell type does not define a validator function: +Using [cascade configuration](@/guides/getting-started/configuration-options/configuration-options.md#cascading-configuration) we define a table with two columns, with [`validator`](@/api/options.md#validator) set to `customValidator` function. The s[`type`](@/api/options.md#type) of the first column is set to `password`. The `Password` cell type does not define a validator function: ```js { @@ -484,65 +484,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import { textRenderer } from 'handsontable/renderers/textRenderer'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const colors = ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white']; - const yellowRenderer = function(instance, td, row, col, prop, value, cellProperties) { - textRenderer.apply(this, arguments); - td.style.backgroundColor = 'yellow'; - }; - const greenRenderer = function(instance, td, row, col, prop, value, cellProperties) { - textRenderer.apply(this, arguments); - - td.style.backgroundColor = 'green'; - }; - const cells = function(instance, td, row, col, prop, value, cellProperties) { - if (row === 0 && col === 0) { - this.renderer = greenRenderer; - } - }; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/cell-type/example1.jsx) ::: @@ -619,64 +561,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - underneath', 'type:text', 'type:numeric', 'type:checkbox', 'type:dropdown', 'type:password']} - /> - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/cell-type/example2.jsx) ::: @@ -688,10 +573,10 @@ Empty cells may be treated differently in different contexts, for example, the [ ### Related guides -- [Cell function](@/guides/cell-functions/cell-function.md) -- [Cell editor](@/guides/cell-functions/cell-editor.md) -- [Cell renderer](@/guides/cell-functions/cell-renderer.md) -- [Cell validator](@/guides/cell-functions/cell-validator.md) +- [Cell function](@/guides/cell-functions/cell-function/cell-function.md) +- [Cell editor](@/guides/cell-functions/cell-editor/cell-editor.md) +- [Cell renderer](@/guides/cell-functions/cell-renderer/cell-renderer.md) +- [Cell validator](@/guides/cell-functions/cell-validator/cell-validator.md) ### Related API reference diff --git a/docs/content/guides/cell-types/cell-type/example1.js b/docs/content/guides/cell-types/cell-type/example1.js new file mode 100644 index 00000000000..dcfca54f65b --- /dev/null +++ b/docs/content/guides/cell-types/cell-type/example1.js @@ -0,0 +1,46 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const colors = ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white']; + +const yellowRenderer = function(instance, td, row, col, prop, value, cellProperties) { + Handsontable.renderers.TextRenderer.apply(this, arguments); + td.style.backgroundColor = 'yellow'; +}; + +const greenRenderer = function(instance, td, row, col, prop, value, cellProperties) { + Handsontable.renderers.TextRenderer.apply(this, arguments); + + td.style.backgroundColor = 'green'; +}; + +const hot = new Handsontable(container, { + data: [ + { id: 1, name: 'Ted', isActive: true, color: 'orange', date: '2015-01-01' }, + { id: 2, name: 'John', isActive: false, color: 'black', date: null }, + { id: 3, name: 'Al', isActive: true, color: 'red', date: null }, + { id: 4, name: 'Ben', isActive: false, color: 'blue', date: null }, + ], + colHeaders: true, + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { data: 'id', type: 'text' }, + // 'text' is default, you don't actually need to declare it + { data: 'name', renderer: yellowRenderer }, + // use default 'text' cell type but overwrite its renderer with yellowRenderer + { data: 'isActive', type: 'checkbox' }, + { data: 'date', type: 'date', dateFormat: 'YYYY-MM-DD' }, + { data: 'color', type: 'autocomplete', source: colors } + ], + cell: [ + { row: 1, col: 0, renderer: greenRenderer } + ], + cells(row, col, prop) { + if (row === 0 && col === 0) { + this.renderer = greenRenderer; + } + }, + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/cell-type/example1.jsx b/docs/content/guides/cell-types/cell-type/example1.jsx new file mode 100644 index 00000000000..bf5daa80f43 --- /dev/null +++ b/docs/content/guides/cell-types/cell-type/example1.jsx @@ -0,0 +1,57 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import { textRenderer } from 'handsontable/renderers/textRenderer'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const colors = ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white']; + const yellowRenderer = function(instance, td, row, col, prop, value, cellProperties) { + textRenderer.apply(this, arguments); + td.style.backgroundColor = 'yellow'; + }; + const greenRenderer = function(instance, td, row, col, prop, value, cellProperties) { + textRenderer.apply(this, arguments); + + td.style.backgroundColor = 'green'; + }; + const cells = function(instance, td, row, col, prop, value, cellProperties) { + if (row === 0 && col === 0) { + this.renderer = greenRenderer; + } + }; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/cell-type/example2.js b/docs/content/guides/cell-types/cell-type/example2.js new file mode 100644 index 00000000000..6a4a578f04f --- /dev/null +++ b/docs/content/guides/cell-types/cell-type/example2.js @@ -0,0 +1,45 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['empty string', '', '', '', '', ''], + ['null', null, null, null, null, null], + ['undefined', undefined, undefined, undefined, undefined, undefined], + ['non-empty value', 'non-empty text', 13000, true, 'orange', 'password'], + ], + columnSorting: { + sortEmptyCells: true + }, + columns: [ + { + columnSorting: { + indicator: false, + headerAction: false, + compareFunctionFactory: function compareFunctionFactory() { + return function comparator() { + return 0; // Don't sort the first visual column. + }; + } + }, + readOnly: true, + }, + {}, + { + type: 'numeric', + numericFormat: { + pattern: '$0,0.00', + culture: 'en-US' // this is the default culture, set up for USD + }, + }, + { type: 'checkbox' }, + { type: 'dropdown', source: ['yellow', 'red', 'orange'] }, + { type: 'password' }, + ], + preventOverflow: 'horizontal', + colHeaders: ['value
underneath', 'type:text', 'type:numeric', 'type:checkbox', 'type:dropdown', 'type:password'], + autoWrapRow: true, + autoWrapCol: true +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/cell-type/example2.jsx b/docs/content/guides/cell-types/cell-type/example2.jsx new file mode 100644 index 00000000000..6fe4415110b --- /dev/null +++ b/docs/content/guides/cell-types/cell-type/example2.jsx @@ -0,0 +1,56 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + underneath', 'type:text', 'type:numeric', 'type:checkbox', 'type:dropdown', 'type:password']} + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type.md b/docs/content/guides/cell-types/checkbox-cell-type/checkbox-cell-type.md similarity index 61% rename from docs/content/guides/cell-types/checkbox-cell-type.md rename to docs/content/guides/cell-types/checkbox-cell-type/checkbox-cell-type.md index 3ef64feb7f5..043a1ca4484 100644 --- a/docs/content/guides/cell-types/checkbox-cell-type.md +++ b/docs/content/guides/cell-types/checkbox-cell-type/checkbox-cell-type.md @@ -77,49 +77,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/checkbox-cell-type/example1.jsx) ::: @@ -177,51 +135,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/checkbox-cell-type/example2.jsx) ::: @@ -287,59 +201,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/checkbox-cell-type/example3.jsx) ::: @@ -358,7 +220,7 @@ ReactDOM.render(, document.getElementById('example3')); ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example1.js b/docs/content/guides/cell-types/checkbox-cell-type/example1.js new file mode 100644 index 00000000000..358d6bea120 --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example1.js @@ -0,0 +1,31 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + { car: 'Mercedes A 160', year: 2017, available: true, comesInBlack: 'yes' }, + { car: 'Citroen C4 Coupe', year: 2018, available: false, comesInBlack: 'yes' }, + { car: 'Audi A4 Avant', year: 2019, available: true, comesInBlack: 'no' }, + { car: 'Opel Astra', year: 2020, available: false, comesInBlack: 'yes' }, + { car: 'BMW 320i Coupe', year: 2021, available: false, comesInBlack: 'no' } + ], + colHeaders: ['Car model', 'Year of manufacture', 'Available'], + height: 'auto', + columns: [ + { + data: 'car' + }, + { + data: 'year', + type: 'numeric' + }, + { + data: 'available', + type: 'checkbox' + } + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example1.jsx b/docs/content/guides/cell-types/checkbox-cell-type/example1.jsx new file mode 100644 index 00000000000..9aa0c43f840 --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example1.jsx @@ -0,0 +1,41 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example2.js b/docs/content/guides/cell-types/checkbox-cell-type/example2.js new file mode 100644 index 00000000000..8c9d41acbbe --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example2.js @@ -0,0 +1,33 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + { car: 'Mercedes A 160', year: 2017, available: true, comesInBlack: 'yes' }, + { car: 'Citroen C4 Coupe', year: 2018, available: false, comesInBlack: 'yes' }, + { car: 'Audi A4 Avant', year: 2019, available: true, comesInBlack: 'no' }, + { car: 'Opel Astra', year: 2020, available: false, comesInBlack: 'yes' }, + { car: 'BMW 320i Coupe', year: 2021, available: false, comesInBlack: 'no' } + ], + colHeaders: ['Car model', 'Year of manufacture', 'Comes in black'], + height: 'auto', + columns: [ + { + data: 'car' + }, + { + data: 'year', + type: 'numeric' + }, + { + data: 'comesInBlack', + type: 'checkbox', + checkedTemplate: 'yes', + uncheckedTemplate: 'no' + } + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example2.jsx b/docs/content/guides/cell-types/checkbox-cell-type/example2.jsx new file mode 100644 index 00000000000..1486489ecf2 --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example2.jsx @@ -0,0 +1,43 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example3.js b/docs/content/guides/cell-types/checkbox-cell-type/example3.js new file mode 100644 index 00000000000..db832c78e6f --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example3.js @@ -0,0 +1,41 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + { car: 'Mercedes A 160', year: 2017, available: true, comesInBlack: 'yes' }, + { car: 'Citroen C4 Coupe', year: 2018, available: false, comesInBlack: 'yes' }, + { car: 'Audi A4 Avant', year: 2019, available: true, comesInBlack: 'no' }, + { car: 'Opel Astra', year: 2020, available: false, comesInBlack: 'yes' }, + { car: 'BMW 320i Coupe', year: 2021, available: false, comesInBlack: 'no' } + ], + colHeaders: ['Car model', 'Accepted', 'Comes in black'], + height: 'auto', + columns: [ + { + data: 'car' + }, + { + data: 'available', + type: 'checkbox', + label: { + position: 'after', + property: 'car' // Read value from row object + }, + }, + { + data: 'comesInBlack', + type: 'checkbox', + checkedTemplate: 'yes', + uncheckedTemplate: 'no', + label: { + position: 'before', + value: 'In black? ' + }, + }, + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/checkbox-cell-type/example3.jsx b/docs/content/guides/cell-types/checkbox-cell-type/example3.jsx new file mode 100644 index 00000000000..0deaff35533 --- /dev/null +++ b/docs/content/guides/cell-types/checkbox-cell-type/example3.jsx @@ -0,0 +1,51 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/date-cell-type.md b/docs/content/guides/cell-types/date-cell-type/date-cell-type.md similarity index 73% rename from docs/content/guides/cell-types/date-cell-type.md rename to docs/content/guides/cell-types/date-cell-type/date-cell-type.md index 67b57a0b4a0..db50f0efb30 100644 --- a/docs/content/guides/cell-types/date-cell-type.md +++ b/docs/content/guides/cell-types/date-cell-type/date-cell-type.md @@ -161,68 +161,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/date-cell-type/example1.jsx) ::: @@ -232,7 +171,7 @@ ReactDOM.render(, document.getElementById('example1')); ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/date-cell-type/example1.js b/docs/content/guides/cell-types/date-cell-type/example1.js new file mode 100644 index 00000000000..0fc4854f6b4 --- /dev/null +++ b/docs/content/guides/cell-types/date-cell-type/example1.js @@ -0,0 +1,49 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['Mercedes', 'A 160', '01/14/2021', 6999.95], + ['Citroen', 'C4 Coupe', '12/01/2022', 8330], + ['Audi', 'A4 Avant', '11/19/2023', 33900], + ['Opel', 'Astra', '02/02/2021', 7000], + ['BMW', '320i Coupe', '07/24/2022', 30500] + ], + colHeaders: ['Car', 'Model', 'Registration date', 'Price'], + height: 'auto', + columns: [ + { + type: 'text', + }, + { + // 2nd cell is simple text, no special options here + }, + { + type: 'date', + dateFormat: 'MM/DD/YYYY', + correctFormat: true, + defaultDate: '01/01/1900', + // datePicker additional options + // (see https://github.com/dbushell/Pikaday#configuration) + datePickerConfig: { + // First day of the week (0: Sunday, 1: Monday, etc) + firstDay: 0, + showWeekNumber: true, + disableDayFn(date) { + // Disable Sunday and Saturday + return date.getDay() === 0 || date.getDay() === 6; + } + } + }, + { + type: 'numeric', + numericFormat: { + pattern: '$ 0,0.00' + } + } + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/date-cell-type/example1.jsx b/docs/content/guides/cell-types/date-cell-type/example1.jsx new file mode 100644 index 00000000000..8d01d7af0d5 --- /dev/null +++ b/docs/content/guides/cell-types/date-cell-type/example1.jsx @@ -0,0 +1,60 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/dropdown-cell-type.md b/docs/content/guides/cell-types/dropdown-cell-type/dropdown-cell-type.md similarity index 69% rename from docs/content/guides/cell-types/dropdown-cell-type.md rename to docs/content/guides/cell-types/dropdown-cell-type/dropdown-cell-type.md index c665da5c177..60dd0285663 100644 --- a/docs/content/guides/cell-types/dropdown-cell-type.md +++ b/docs/content/guides/cell-types/dropdown-cell-type/dropdown-cell-type.md @@ -23,7 +23,7 @@ The dropdown cell type is based on an autocomplete cell type and can also be sea ## Usage -This example shows the usage of the dropdown feature. Dropdown is based on [Autocomplete](@/guides/cell-types/autocomplete-cell-type.md) cell type. All options used by `autocomplete` cell type apply to `dropdown` as well. +This example shows the usage of the dropdown feature. Dropdown is based on [Autocomplete](@/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md) cell type. All options used by `autocomplete` cell type apply to `dropdown` as well. ::: only-for javascript @@ -80,47 +80,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/dropdown-cell-type/example1.jsx) ::: @@ -130,9 +90,9 @@ ReactDOM.render(, document.getElementById('example1')); ### Related guides -- [Autocomplete cell type](@/guides/cell-types/autocomplete-cell-type.md) -- [Cell type](@/guides/cell-types/cell-type.md) -- [Select cell type](@/guides/cell-types/select-cell-type.md) +- [Autocomplete cell type](@/guides/cell-types/autocomplete-cell-type/autocomplete-cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) +- [Select cell type](@/guides/cell-types/select-cell-type/select-cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/dropdown-cell-type/example1.js b/docs/content/guides/cell-types/dropdown-cell-type/example1.js new file mode 100644 index 00000000000..47ab162c8f5 --- /dev/null +++ b/docs/content/guides/cell-types/dropdown-cell-type/example1.js @@ -0,0 +1,28 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['Tesla', 2017, 'black', 'black'], + ['Nissan', 2018, 'blue', 'blue'], + ['Chrysler', 2019, 'yellow', 'black'], + ['Volvo', 2020, 'white', 'gray'] + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + columns: [ + {}, + { type: 'numeric' }, + { + type: 'dropdown', + source: ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white'] + }, + { + type: 'dropdown', + source: ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white'] + } + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/dropdown-cell-type/example1.jsx b/docs/content/guides/cell-types/dropdown-cell-type/example1.jsx new file mode 100644 index 00000000000..2a5014ae63b --- /dev/null +++ b/docs/content/guides/cell-types/dropdown-cell-type/example1.jsx @@ -0,0 +1,39 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/handsontable-cell-type/example1.js b/docs/content/guides/cell-types/handsontable-cell-type/example1.js new file mode 100644 index 00000000000..5f00af3b76d --- /dev/null +++ b/docs/content/guides/cell-types/handsontable-cell-type/example1.js @@ -0,0 +1,58 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const colorData = [['yellow'], ['red'], ['orange'], ['green'], ['blue'], ['gray'], ['black'], ['white']]; +const manufacturerData = [ + { name: 'BMW', country: 'Germany', owner: 'Bayerische Motoren Werke AG' }, + { name: 'Chrysler', country: 'USA', owner: 'Chrysler Group LLC' }, + { name: 'Nissan', country: 'Japan', owner: 'Nissan Motor Company Ltd' }, + { name: 'Suzuki', country: 'Japan', owner: 'Suzuki Motor Corporation' }, + { name: 'Toyota', country: 'Japan', owner: 'Toyota Motor Corporation' }, + { name: 'Volvo', country: 'Sweden', owner: 'Zhejiang Geely Holding Group' } +]; + +const hot = new Handsontable(container, { + licenseKey: 'non-commercial-and-evaluation', + data: [ + ['Tesla', 2017, 'black', 'black'], + ['Nissan', 2018, 'blue', 'blue'], + ['Chrysler', 2019, 'yellow', 'black'], + ['Volvo', 2020, 'white', 'gray'] + ], + colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'], + columns: [ + { + type: 'handsontable', + handsontable: { + colHeaders: ['Marque', 'Country', 'Parent company'], + autoColumnSize: true, + data: manufacturerData, + getValue() { + const selection = this.getSelectedLast(); + + // Get the manufacturer name of the clicked row and ignore header + // coordinates (negative values) + return this.getSourceDataAtRow(Math.max(selection[0], 0)).name; + }, + } + }, + { type: 'numeric' }, + { + type: 'handsontable', + handsontable: { + colHeaders: false, + data: colorData + } + }, + { + type: 'handsontable', + handsontable: { + colHeaders: false, + data: colorData + } + }, + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/handsontable-cell-type/example1.jsx b/docs/content/guides/cell-types/handsontable-cell-type/example1.jsx new file mode 100644 index 00000000000..68daed55e8f --- /dev/null +++ b/docs/content/guides/cell-types/handsontable-cell-type/example1.jsx @@ -0,0 +1,77 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + const colorData = [ + ['yellow'], + ['red'], + ['orange'], + ['green'], + ['blue'], + ['gray'], + ['black'], + ['white'] + ]; + const manufacturerData = [ + { name: 'BMW', country: 'Germany', owner: 'Bayerische Motoren Werke AG' }, + { name: 'Chrysler', country: 'USA', owner: 'Chrysler Group LLC' }, + { name: 'Nissan', country: 'Japan', owner: 'Nissan Motor Company Ltd' }, + { name: 'Suzuki', country: 'Japan', owner: 'Suzuki Motor Corporation' }, + { name: 'Toyota', country: 'Japan', owner: 'Toyota Motor Corporation' }, + { name: 'Volvo', country: 'Sweden', owner: 'Zhejiang Geely Holding Group' } + ]; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/handsontable-cell-type.md b/docs/content/guides/cell-types/handsontable-cell-type/handsontable-cell-type.md similarity index 67% rename from docs/content/guides/cell-types/handsontable-cell-type.md rename to docs/content/guides/cell-types/handsontable-cell-type/handsontable-cell-type.md index 6dcd5ebc47e..2d2ec4b93ad 100644 --- a/docs/content/guides/cell-types/handsontable-cell-type.md +++ b/docs/content/guides/cell-types/handsontable-cell-type/handsontable-cell-type.md @@ -112,85 +112,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - const colorData = [ - ['yellow'], - ['red'], - ['orange'], - ['green'], - ['blue'], - ['gray'], - ['black'], - ['white'] - ]; - const manufacturerData = [ - { name: 'BMW', country: 'Germany', owner: 'Bayerische Motoren Werke AG' }, - { name: 'Chrysler', country: 'USA', owner: 'Chrysler Group LLC' }, - { name: 'Nissan', country: 'Japan', owner: 'Nissan Motor Company Ltd' }, - { name: 'Suzuki', country: 'Japan', owner: 'Suzuki Motor Corporation' }, - { name: 'Toyota', country: 'Japan', owner: 'Toyota Motor Corporation' }, - { name: 'Volvo', country: 'Sweden', owner: 'Zhejiang Geely Holding Group' } - ]; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/handsontable-cell-type/example1.jsx) ::: @@ -200,7 +122,7 @@ ReactDOM.render(, document.getElementById('example1')); ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/numeric-cell-type/example1.js b/docs/content/guides/cell-types/numeric-cell-type/example1.js new file mode 100644 index 00000000000..e07dcddf49a --- /dev/null +++ b/docs/content/guides/cell-types/numeric-cell-type/example1.js @@ -0,0 +1,52 @@ +import Handsontable from 'handsontable'; +import numbro from 'numbro'; +import deDE from 'numbro/languages/de-DE'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register the languages you need +numbro.registerLanguage(deDE); + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + { car: 'Mercedes A 160', year: 2017, price_usd: 7000, price_eur: 7000 }, + { car: 'Citroen C4 Coupe', year: 2018, price_usd: 8330, price_eur: 8330 }, + { car: 'Audi A4 Avant', year: 2019, price_usd: 33900, price_eur: 33900 }, + { car: 'Opel Astra', year: 2020, price_usd: 5000, price_eur: 5000 }, + { car: 'BMW 320i Coupe', year: 2021, price_usd: 30500, price_eur: 30500 }, + ], + colHeaders: ['Car', 'Year', 'Price ($)', 'Price (€)'], + columnSorting: true, + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { + data: 'car', + // 1st column is simple text, no special options here + }, + { + data: 'year', + type: 'numeric', + }, + { + data: 'price_usd', + type: 'numeric', + numericFormat: { + pattern: '$0,0.00', + culture: 'en-US', // this is the default culture, set up for USD + }, + allowEmpty: false, + }, + { + data: 'price_eur', + type: 'numeric', + numericFormat: { + pattern: '0,0.00 $', + culture: 'de-DE', // use this for EUR (German), + // more cultures available on http://numbrojs.com/languages.html + }, + }, + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/numeric-cell-type/example1.jsx b/docs/content/guides/cell-types/numeric-cell-type/example1.jsx new file mode 100644 index 00000000000..b8b1107ecc0 --- /dev/null +++ b/docs/content/guides/cell-types/numeric-cell-type/example1.jsx @@ -0,0 +1,63 @@ +import { HotTable } from '@handsontable/react'; +import numbro from 'numbro'; +import deDE from 'numbro/languages/de-DE'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// register the languages you need +numbro.registerLanguage(deDE); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/numeric-cell-type/example3.js b/docs/content/guides/cell-types/numeric-cell-type/example3.js new file mode 100644 index 00000000000..fd6a5e0d565 --- /dev/null +++ b/docs/content/guides/cell-types/numeric-cell-type/example3.js @@ -0,0 +1,68 @@ +import Handsontable from 'handsontable'; +import numbro from 'numbro'; +import jaJP from 'numbro/languages/ja-JP'; +import trTR from 'numbro/languages/tr-TR'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register the languages you need +numbro.registerLanguage(jaJP); +numbro.registerLanguage(trTR); + +// define formats +const formatJP = { + pattern: '0,0.00 $', + culture: 'ja-JP', +}; + +const formatTR = { + pattern: '0,0.00 $', + culture: 'tr-TR', +}; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + { + productName: 'Product A', + JP_price: 1450.32, + TR_price: 202.14, + }, + { + productName: 'Product B', + JP_price: 2430.22, + TR_price: 338.86, + }, + { + productName: 'Product C', + JP_price: 3120.10, + TR_price: 435.20, + }, + ], + columns: [ + { + data: 'productName', + type: 'text', + width: '120', + }, + { + data: 'JP_price', + type: 'numeric', + width: '120', + numericFormat: formatJP, + }, + { + data: 'TR_price', + type: 'numeric', + width: '120', + numericFormat: formatTR, + }, + ], + autoRowSize: false, + autoColumnSize: false, + columnSorting: true, + colHeaders: ['Product name', 'Price in Japan', 'Price in Turkey'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/numeric-cell-type/example3.jsx b/docs/content/guides/cell-types/numeric-cell-type/example3.jsx new file mode 100644 index 00000000000..786d9e66673 --- /dev/null +++ b/docs/content/guides/cell-types/numeric-cell-type/example3.jsx @@ -0,0 +1,64 @@ +import { HotTable, HotColumn } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import numbro from 'numbro'; +import jaJP from 'numbro/languages/ja-JP'; +import trTR from 'numbro/languages/tr-TR'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// register the languages you need +numbro.registerLanguage(jaJP); +numbro.registerLanguage(trTR); + +// define formats +const formatJP = { + pattern: '0,0.00 $', + culture: 'ja-JP', +}; + +const formatTR = { + pattern: '0,0.00 $', + culture: 'tr-TR', +}; + +export const ExampleComponent = () => { + return ( + + + + + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/numeric-cell-type.md b/docs/content/guides/cell-types/numeric-cell-type/numeric-cell-type.md similarity index 66% rename from docs/content/guides/cell-types/numeric-cell-type.md rename to docs/content/guides/cell-types/numeric-cell-type/numeric-cell-type.md index 80abb4499c0..95725b345ea 100644 --- a/docs/content/guides/cell-types/numeric-cell-type.md +++ b/docs/content/guides/cell-types/numeric-cell-type/numeric-cell-type.md @@ -96,71 +96,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react-numbro -```jsx -import { HotTable } from '@handsontable/react'; -import numbro from 'numbro'; -import deDE from 'numbro/languages/de-DE'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// register the languages you need -numbro.registerLanguage(deDE); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/numeric-cell-type/example1.jsx) ::: @@ -225,7 +161,7 @@ bigger numbers won't be calculated precisely, due to JavaScript's limitations. ## Format numbers -To format the look of numeric values in [cell renderers](@/guides/cell-functions/cell-renderer.md), +To format the look of numeric values in [cell renderers](@/guides/cell-functions/cell-renderer/cell-renderer.md), use the [`numericFormat`](@/api/options.md#numericformat) option. In the following demo, columns **Price in Japan** and **Price in Turkey** use two different @@ -314,79 +250,14 @@ const hot = new Handsontable(container, { ::: example #example3 :react-numbro -```jsx -import { HotTable, HotColumn } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import numbro from 'numbro'; -import jaJP from 'numbro/languages/ja-JP'; -import trTR from 'numbro/languages/tr-TR'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// register the languages you need -numbro.registerLanguage(jaJP); -numbro.registerLanguage(trTR); - -// define formats -const formatJP = { - pattern: '0,0.00 $', - culture: 'ja-JP', -}; - -const formatTR = { - pattern: '0,0.00 $', - culture: 'tr-TR', -}; - -export const ExampleComponent = () => { - return ( - - - - - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/numeric-cell-type/example3.jsx) ::: ::: Mind that the [`numericFormat`](@/api/options.md#numericformat) option doesn't change the way -numbers are presented or parsed by the [cell editor](@/guides/cell-functions/cell-editor.md). When +numbers are presented or parsed by the [cell editor](@/guides/cell-functions/cell-editor/cell-editor.md). When you edit a numeric cell: - Regardless of the [`numericFormat`](@/api/options.md#numericformat) configuration, the number @@ -402,7 +273,7 @@ you edit a numeric cell: ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/password-cell-type/example1.js b/docs/content/guides/cell-types/password-cell-type/example1.js new file mode 100644 index 00000000000..9268d010320 --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example1.js @@ -0,0 +1,22 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + { id: 1, name: { first: 'Chris', last: 'Right' }, password: 'plainTextPassword' }, + { id: 2, name: { first: 'John', last: 'Honest' }, password: 'txt' }, + { id: 3, name: { first: 'Greg', last: 'Well' }, password: 'longer' } + ], + colHeaders: ['ID', 'First name', 'Last name', 'Password'], + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { data: 'id' }, + { data: 'name.first' }, + { data: 'name.last' }, + { data: 'password', type: 'password' } + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type/example1.jsx b/docs/content/guides/cell-types/password-cell-type/example1.jsx new file mode 100644 index 00000000000..1b393f3daa0 --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example1.jsx @@ -0,0 +1,33 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type/example2.js b/docs/content/guides/cell-types/password-cell-type/example2.js new file mode 100644 index 00000000000..dd9f03f061e --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example2.js @@ -0,0 +1,22 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + { id: 1, name: { first: 'Chris', last: 'Right' }, password: 'plainTextPassword' }, + { id: 2, name: { first: 'John', last: 'Honest' }, password: 'txt' }, + { id: 3, name: { first: 'Greg', last: 'Well' }, password: 'longer' } + ], + colHeaders: ['ID', 'First name', 'Last name', 'Password'], + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { data: 'id' }, + { data: 'name.first' }, + { data: 'name.last' }, + { data: 'password', type: 'password', hashLength: 10 } + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type/example2.jsx b/docs/content/guides/cell-types/password-cell-type/example2.jsx new file mode 100644 index 00000000000..c2445f7f53b --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example2.jsx @@ -0,0 +1,33 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type/example3.js b/docs/content/guides/cell-types/password-cell-type/example3.js new file mode 100644 index 00000000000..caa5ba236e6 --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example3.js @@ -0,0 +1,22 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + { id: 1, name: { first: 'Chris', last: 'Right' }, password: 'plainTextPassword' }, + { id: 2, name: { first: 'John', last: 'Honest' }, password: 'txt' }, + { id: 3, name: { first: 'Greg', last: 'Well' }, password: 'longer' } + ], + colHeaders: ['ID', 'First name', 'Last name', 'Password'], + height: 'auto', + licenseKey: 'non-commercial-and-evaluation', + columns: [ + { data: 'id' }, + { data: 'name.first' }, + { data: 'name.last' }, + { data: 'password', type: 'password', hashSymbol: '■' } + ], + autoWrapRow: true, + autoWrapCol: true, +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type/example3.jsx b/docs/content/guides/cell-types/password-cell-type/example3.jsx new file mode 100644 index 00000000000..5617e4032d4 --- /dev/null +++ b/docs/content/guides/cell-types/password-cell-type/example3.jsx @@ -0,0 +1,33 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/password-cell-type.md b/docs/content/guides/cell-types/password-cell-type/password-cell-type.md similarity index 58% rename from docs/content/guides/cell-types/password-cell-type.md rename to docs/content/guides/cell-types/password-cell-type/password-cell-type.md index 253d8c1a326..e65db028051 100644 --- a/docs/content/guides/cell-types/password-cell-type.md +++ b/docs/content/guides/cell-types/password-cell-type/password-cell-type.md @@ -58,41 +58,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/password-cell-type/example1.jsx) ::: @@ -139,41 +105,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/password-cell-type/example2.jsx) ::: @@ -220,41 +152,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/cell-types/password-cell-type/example3.jsx) ::: @@ -264,7 +162,7 @@ ReactDOM.render(, document.getElementById('example3')); ### Related guides -- [Cell type](@/guides/cell-types/cell-type.md) +- [Cell type](@/guides/cell-types/cell-type/cell-type.md) ### Related API reference diff --git a/docs/content/guides/cell-types/select-cell-type/example1.js b/docs/content/guides/cell-types/select-cell-type/example1.js new file mode 100644 index 00000000000..5d4c4503860 --- /dev/null +++ b/docs/content/guides/cell-types/select-cell-type/example1.js @@ -0,0 +1,24 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['2017', 'Honda', 10], + ['2018', 'Toyota', 20], + ['2019', 'Nissan', 30] + ], + colWidths: [50, 70, 50], + colHeaders: true, + columns: [ + {}, + { + type: 'select', + selectOptions: ['Kia', 'Nissan', 'Toyota', 'Honda'] + }, + {} + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/cell-types/select-cell-type/example1.jsx b/docs/content/guides/cell-types/select-cell-type/example1.jsx new file mode 100644 index 00000000000..a5c10d52e97 --- /dev/null +++ b/docs/content/guides/cell-types/select-cell-type/example1.jsx @@ -0,0 +1,35 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/cell-types/select-cell-type.md b/docs/content/guides/cell-types/select-cell-type/select-cell-type.md similarity index 68% rename from docs/content/guides/cell-types/select-cell-type.md rename to docs/content/guides/cell-types/select-cell-type/select-cell-type.md index df431613472..ac30dffefc8 100644 --- a/docs/content/guides/cell-types/select-cell-type.md +++ b/docs/content/guides/cell-types/select-cell-type/select-cell-type.md @@ -19,11 +19,11 @@ Use the select cell type to collect user input with an HTML ` - - - - - - - -
-
- -
- - - ); -}; - -ReactDOM.render(, document.getElementById('exampleQuickFilter')); -``` +@[code](@/content/guides/columns/column-filter/exampleQuickFilter.jsx) ```css .controlsQuickFilter { @@ -1916,22 +1111,7 @@ const handsontableInstance = new Handsontable(container, { }); ``` -```css -.handsontable.customFilterButtonExample1 .changeType { - background: #e2e2e2; - border-radius: 100%; - width: 16px; - color: #0075ff; - height: 16px; - padding: 3px 2px; - border: none; -} - -.handsontable.customFilterButtonExample1 .changeType::before { - content: '▼ '; - zoom: 0.9; -} -``` +@[code](@/content/guides/columns/column-filter/exampleCustomFilterButton.css) ::: @@ -1941,120 +1121,7 @@ const handsontableInstance = new Handsontable(container, { ::: example #exampleCustomFilterButton :react --js 1 --css 2 -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const App = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleCustomFilterButton')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-filter/exampleCustomFilterButton.jsx) ```css .handsontable.customFilterButtonExample1 .changeType { @@ -2192,17 +1259,7 @@ const handsontableInstance = new Handsontable(container, { }); ``` -```css -/* hide the column menu button by default */ -.customFilterButtonExample2 .changeType { - visibility: hidden; -} - -/* show the column menu button on hover */ -.customFilterButtonExample2 th .relative:hover .changeType { - visibility: visible; -} -``` +@[code](@/content/guides/columns/column-filter/exampleCustomFilterButton2.css) ::: @@ -2212,120 +1269,7 @@ const handsontableInstance = new Handsontable(container, { ::: example #exampleCustomFilterButton2 :react --js 1 --css 2 -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const App = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleCustomFilterButton2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-filter/exampleCustomFilterButton2.jsx) ```css /* hide the column menu button by default */ @@ -2359,7 +1303,7 @@ column menu's width for better user experience. You can achieve this with by sty You can exclude any number of top or bottom rows from filtering. -In the following demo, the first and the last row are [frozen](@/guides/rows/row-freezing.md), and +In the following demo, the first and the last row are [frozen](@/guides/rows/row-freezing/row-freezing.md), and filtering doesn't affect them. ::: only-for javascript @@ -2564,207 +1508,7 @@ const handsontableInstance = new Handsontable(container, { ::: example #exampleExcludeRowsFromFiltering :react -```jsx -// you need `useRef` to call Handsontable's instance methods -import { useRef } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const App = () => { - const hotTableComponentRef = useRef(null); - const exclude = () => { - let hotInstance = hotTableComponentRef.current.hotInstance; - let filtersRowsMap = hotInstance.getPlugin('filters').filtersRowsMap; - - filtersRowsMap.setValueAtIndex(0, false); - filtersRowsMap.setValueAtIndex(filtersRowsMap.indexedValues.length - 1, false); - }; - - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleExcludeRowsFromFiltering')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.jsx) ::: @@ -2919,133 +1663,7 @@ const handsontableInstance = new Handsontable(container, { ::: example #exampleServerSideFilter :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const App = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleServerSideFilter')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-filter/exampleServerSideFilter.jsx) ::: @@ -3054,7 +1672,7 @@ ReactDOM.render(, document.getElementById('exampleServerSideFilter')); ## Control filtering programmatically You can control filtering at the grid's runtime by using Handsontable's -[hooks](@/guides/getting-started/events-and-hooks.md) and [API methods](@/api/filters.md#methods). +[hooks](@/guides/getting-started/events-and-hooks/events-and-hooks.md) and [API methods](@/api/filters.md#methods). This allows you to enable or disable filtering based on specific conditions. For example, you may create a user interface outside of the grid to manage Handsontable's filtering behavior. @@ -3324,163 +1942,7 @@ document.querySelector('.clearAllFilters').addEventListener('click', function () ::: example #exampleFilterThroughAPI1 :react -```jsx -import { useRef } from 'react'; -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const App = () => { - const hotTableComponentRef = useRef(null); - const filterBelow200 = () => { - // get the `Filters` plugin, so you can use its API - const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); - - // clear any existing filters - filters.clearConditions(); - // filter data by the 'Price' column (column at index 2) - // to display only items that are less than ('lt') $200 - filters.addCondition(2, 'lt', [200]); - filters.filter(); - }; - - const filterAbove200 = () => { - // get the `Filters` plugin, so you can use its API - const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); - - filters.clearConditions(); - // display only items that are more than ('gt') $200 - filters.addCondition(2, 'gt', [200]); - filters.filter(); - }; - - const clearAllFilters = () => { - // get the `Filters` plugin, so you can use its API - const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); - - // clear all filters - filters.clearConditions(); - filters.filter(); - }; - - return ( - <> - -
- -
-
- -
-
- -
- - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('exampleFilterThroughAPI1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-filter/exampleFilterThroughAPI1.jsx) ::: @@ -3489,11 +1951,11 @@ ReactDOM.render(, document.getElementById('exampleFilterThroughAPI1')); ## Import the filtering module You can reduce the size of your bundle by importing and registering only the -[modules](@/guides/tools-and-building/modules.md) that you need. +[modules](@/guides/tools-and-building/modules/modules.md) that you need. To use filtering, you need only the following modules: -- The [base module](@/guides/tools-and-building/modules.md#import-the-base-module) +- The [base module](@/guides/tools-and-building/modules/modules.md#import-the-base-module) - The [`Filters`](@/api/filters.md) module - The [`DropdownMenu`](@/api/dropdownMenu.md) module @@ -3528,8 +1990,8 @@ At the moment, filtering comes with the following limitations: ## API reference -For the list of [options](@/guides/getting-started/configuration-options.md), methods, and -[Handsontable hooks](@/guides/getting-started/events-and-hooks.md) related to filtering, see the +For the list of [options](@/guides/getting-started/configuration-options/configuration-options.md), methods, and +[Handsontable hooks](@/guides/getting-started/events-and-hooks/events-and-hooks.md) related to filtering, see the following API reference pages: - [`Filters`](@/api/filters.md) diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton.css b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.css new file mode 100644 index 00000000000..44ed9fbf1a7 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.css @@ -0,0 +1,14 @@ +.handsontable.customFilterButtonExample1 .changeType { + background: #e2e2e2; + border-radius: 100%; + width: 16px; + color: #0075ff; + height: 16px; + padding: 3px 2px; + border: none; +} + +.handsontable.customFilterButtonExample1 .changeType::before { + content: '▼ '; + zoom: 0.9; +} \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton.html b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.html new file mode 100644 index 00000000000..061a29e7da0 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton.js b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.js new file mode 100644 index 00000000000..87a75d49f7a --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.js @@ -0,0 +1,101 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleCustomFilterButton'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + // to differentiate this example's CSS from other examples on this page + className: 'customFilterButtonExample1', + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton.jsx b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.jsx new file mode 100644 index 00000000000..c652054e5eb --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton.jsx @@ -0,0 +1,112 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleCustomFilterButton')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.css b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.css new file mode 100644 index 00000000000..9cb07872706 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.css @@ -0,0 +1,9 @@ +/* hide the column menu button by default */ +.customFilterButtonExample2 .changeType { + visibility: hidden; +} + +/* show the column menu button on hover */ +.customFilterButtonExample2 th .relative:hover .changeType { + visibility: visible; +} \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.html b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.html new file mode 100644 index 00000000000..230c76ff55e --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.js b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.js new file mode 100644 index 00000000000..e7bec3312b1 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.js @@ -0,0 +1,101 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleCustomFilterButton2'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + // to differentiate this example's CSS from other examples on this page + className: 'customFilterButtonExample2', + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.jsx b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.jsx new file mode 100644 index 00000000000..e20f0c34856 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleCustomFilterButton2.jsx @@ -0,0 +1,112 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleCustomFilterButton2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.html b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.html new file mode 100644 index 00000000000..af1173eca4b --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.js b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.js new file mode 100644 index 00000000000..d8222113c82 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.js @@ -0,0 +1,136 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleEnableFilterInColumns'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering for all columns + filters: true, + // enable the column menu for all columns + // but display only the 'Filter by value' list and the 'OK' and 'Cancel' buttons + dropdownMenu: { + items: { + filter_by_value: { + // hide the 'Filter by value' list from all columns but the first one + hidden() { + return this.getSelectedRangeLast().to.col > 0; + }, + }, + filter_action_bar: { + // hide the 'OK' and 'Cancel' buttons from all columns but the first one + hidden() { + return this.getSelectedRangeLast().to.col > 0; + }, + }, + clear_column: { + // hide the 'Clear column' menu item from the first column + hidden() { + return this.getSelectedRangeLast().to.col < 1; + }, + }, + }, + }, + // `afterGetColHeader()` is a Handsontable hook + // it's fired after Handsontable appends information about a column header to the table header + afterGetColHeader(col, TH) { + // remove the column menu button from the 'Brand', 'Price', and 'Date' columns + if (col > 1) { + const button = TH.querySelector('.changeType'); + + if (!button) { + return; + } + + button.parentElement.removeChild(button); + } + }, + height: 'auto', + example: 'exampleEnableFilterInColumns', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.jsx b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.jsx new file mode 100644 index 00000000000..35ee71d4ad6 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleEnableFilterInColumns.jsx @@ -0,0 +1,148 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + // remove the column menu button from the 'Brand', 'Price', and 'Date' columns + const removeColumnMenuButton = (col, TH) => { + if (col > 1) { + const button = TH.querySelector('.changeType'); + + if (!button) { + return; + } + + button.parentElement.removeChild(button); + } + }; + + return ( + 0; + }, + }, + filter_action_bar: { + // hide the 'OK' and 'Cancel' buttons from all columns but the first one + hidden() { + return this.getSelectedRangeLast().to.col > 0; + }, + }, + clear_column: { + // hide the 'Clear column' menu item from the first column + hidden() { + return this.getSelectedRangeLast().to.col < 1; + }, + }, + }, + }} + // `afterGetColHeader()` is a Handsontable hook + // it's fired after Handsontable appends information about a column header to the table header + afterGetColHeader={removeColumnMenuButton} + height="auto" + autoWrapRow={true} + autoWrapCol={true} + licenseKey="non-commercial-and-evaluation" + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleEnableFilterInColumns')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.html b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.html new file mode 100644 index 00000000000..af8e48d0a7c --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.js b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.js new file mode 100644 index 00000000000..4f8eb45c5d3 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.js @@ -0,0 +1,183 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleExcludeRowsFromFiltering'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: 11, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: 0, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: 1, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '13:23 AM', + inStock: 3, + }, + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: 5, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: 22, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: 13, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: 0, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '13:23 AM', + inStock: 14, + }, + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: 16, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: 18, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: 3, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: 0, + }, + { + brand: 'Vinte', + model: 'ML Road Frame-W', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: 2, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + className: 'htRight', + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'numeric', + data: 'inStock', + className: 'htCenter', + }, + ], + height: 200, + colWidths: [120, 150, 120, 140, 120, 120], + fixedRowsTop: 1, + fixedRowsBottom: 1, + minSpareRows: 1, + colHeaders: true, + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + afterFilter() { + let filtersRowsMap = this.getPlugin('filters').filtersRowsMap; + + filtersRowsMap.setValueAtIndex(0, false); + filtersRowsMap.setValueAtIndex(filtersRowsMap.indexedValues.length - 1, false); + }, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.jsx b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.jsx new file mode 100644 index 00000000000..832f6ca4d3d --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleExcludeRowsFromFiltering.jsx @@ -0,0 +1,199 @@ +// you need `useRef` to call Handsontable's instance methods +import { useRef } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + const hotTableComponentRef = useRef(null); + const exclude = () => { + let hotInstance = hotTableComponentRef.current.hotInstance; + let filtersRowsMap = hotInstance.getPlugin('filters').filtersRowsMap; + + filtersRowsMap.setValueAtIndex(0, false); + filtersRowsMap.setValueAtIndex(filtersRowsMap.indexedValues.length - 1, false); + }; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleExcludeRowsFromFiltering')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.html b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.html new file mode 100644 index 00000000000..aa5301ea938 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.js b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.js new file mode 100644 index 00000000000..0011c94af0f --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.js @@ -0,0 +1,100 @@ +// to import filtering as an individual module, see the 'Import the filtering module' section of this page +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleFilterBasicDemo'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.jsx b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.jsx new file mode 100644 index 00000000000..830298789b4 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterBasicDemo.jsx @@ -0,0 +1,111 @@ +// to import filtering as an individual module, see the 'Import the filtering module' section of this page +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleFilterBasicDemo')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.html b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.html new file mode 100644 index 00000000000..7c6fde32ea0 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.js b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.js new file mode 100644 index 00000000000..0d3aa456067 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.js @@ -0,0 +1,120 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleFilterDifferentTypes'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + model: 'Racing Socks', + size: 'S', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + color: 'Black', + }, + { + model: 'HL Mountain Shirt', + size: 'XS', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + color: 'White', + }, + { + model: 'Cycling Cap', + size: 'L', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + color: 'Green', + }, + { + model: 'Ski Jacket', + size: 'M', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + color: 'Blue', + }, + { + model: 'HL Goggles', + size: 'XL', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + color: 'Black', + }, + ], + columns: [ + { + title: 'Model', + // set the type of the 'Model' column + type: 'text', // 'text' is the default type, so you can omit this line + data: 'model', + }, + { + title: 'Price', + // set the type of the 'Price' column + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Sold on', + // set the type of the 'Date' column + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + // set the type of the 'Time' column + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + // set the type of the 'In stock' column + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + { + title: 'Size', + // set the type of the 'Size' column + type: 'dropdown', + data: 'size', + source: ['XS', 'S', 'M', 'L', 'XL'], + className: 'htCenter', + }, + { + title: 'Color', + // set the type of the 'Size' column + type: 'autocomplete', + data: 'color', + source: ['White', 'Black', 'Yellow', 'Blue', 'Green'], + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + height: 142, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.jsx b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.jsx new file mode 100644 index 00000000000..f68e39a8fd6 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterDifferentTypes.jsx @@ -0,0 +1,131 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleFilterDifferentTypes')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.html b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.html new file mode 100644 index 00000000000..b666fcd8bb0 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.js b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.js new file mode 100644 index 00000000000..617f783b8a0 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.js @@ -0,0 +1,110 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleFilterOnInitialization'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + // `afterInit()` is a Handsontable hook: it's fired after the Handsontable instance is initiated + afterInit() { + const handsontableInstance = this; + // get the `Filters` plugin, so you can use its API + const filters = handsontableInstance.getPlugin('Filters'); + + // filter data by the 'Price' column (column at index 2) + // to display only items that are less than ('lt') $200 + filters.addCondition(2, 'lt', [200]); + filters.filter(); + }, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.jsx b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.jsx new file mode 100644 index 00000000000..602408fa71b --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterOnInitialization.jsx @@ -0,0 +1,126 @@ +// you need `useRef` to call Handsontable's instance methods +import { useRef, useEffect } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + const hotTableComponentRef = useRef(null); + + useEffect(() => { + const handsontableInstance = hotTableComponentRef.current.hotInstance; + // get the `Filters` plugin, so you can use its API + const filters = handsontableInstance.getPlugin('Filters'); + + // filter data by the 'Price' column (column at index 2) + // to display only items that are less than ('lt') $200 + filters.addCondition(2, 'lt', [200]); + filters.filter(); + }, []); + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleFilterOnInitialization')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.html b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.html new file mode 100644 index 00000000000..82b4ee3bccd --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.html @@ -0,0 +1,11 @@ +
+ +
+ +
+
+ +
+
+ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.js b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.js new file mode 100644 index 00000000000..4274e013b96 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.js @@ -0,0 +1,123 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleFilterThroughAPI1'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); + +// get the `Filters` plugin, so you can use its API +const filters = handsontableInstance.getPlugin('Filters'); + +document.querySelector('.filterBelow200').addEventListener('click', function () { + // clear any existing filters + filters.clearConditions(); + // filter data by the 'Price' column (column at index 2) + // to display only items that are less than ('lt') $200 + filters.addCondition(2, 'lt', [200]); + filters.filter(); +}); + +document.querySelector('.filterAbove200').addEventListener('click', function () { + filters.clearConditions(); + // display only items that are more than ('gt') $200 + filters.addCondition(2, 'gt', [200]); + filters.filter(); +}); + +document.querySelector('.clearAllFilters').addEventListener('click', function () { + filters.clearConditions(); + filters.filter(); +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.jsx b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.jsx new file mode 100644 index 00000000000..d6eda0644d5 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleFilterThroughAPI1.jsx @@ -0,0 +1,155 @@ +import { useRef } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + const hotTableComponentRef = useRef(null); + const filterBelow200 = () => { + // get the `Filters` plugin, so you can use its API + const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); + + // clear any existing filters + filters.clearConditions(); + // filter data by the 'Price' column (column at index 2) + // to display only items that are less than ('lt') $200 + filters.addCondition(2, 'lt', [200]); + filters.filter(); + }; + + const filterAbove200 = () => { + // get the `Filters` plugin, so you can use its API + const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); + + filters.clearConditions(); + // display only items that are more than ('gt') $200 + filters.addCondition(2, 'gt', [200]); + filters.filter(); + }; + + const clearAllFilters = () => { + // get the `Filters` plugin, so you can use its API + const filters = hotTableComponentRef.current.hotInstance.getPlugin('filters'); + + // clear all filters + filters.clearConditions(); + filters.filter(); + }; + + return ( + <> + +
+ +
+
+ +
+
+ +
+ + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleFilterThroughAPI1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleQuickFilter.css b/docs/content/guides/columns/column-filter/exampleQuickFilter.css new file mode 100644 index 00000000000..6d2a8a40efd --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleQuickFilter.css @@ -0,0 +1,14 @@ +.controlsQuickFilter { + margin: 0; + padding: 0; +} + +.selectColumn { + color: white; + mix-blend-mode: exclusion; +} + +#filterField { + margin: 20px 0px; + padding: 2px 5px; +} \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleQuickFilter.html b/docs/content/guides/columns/column-filter/exampleQuickFilter.html new file mode 100644 index 00000000000..0fbb0ac37bf --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleQuickFilter.html @@ -0,0 +1,16 @@ +
+ + +
+
+ +
+
+
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleQuickFilter.js b/docs/content/guides/columns/column-filter/exampleQuickFilter.js new file mode 100644 index 00000000000..9fe81bb5d96 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleQuickFilter.js @@ -0,0 +1,110 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleQuickFilter'); +const filterField = document.querySelector('#filterField'); + +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: '11/10/2023', + sellTime: '01:23', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: '03/05/2023', + sellTime: '11:27', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: '27/03/2023', + sellTime: '03:17', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: '28/08/2023', + sellTime: '08:01', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: '02/10/2023', + sellTime: '13:23', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + colHeaders: true, + height: 'auto', + filters: true, + className: 'exampleQuickFilter', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); + +// add a filter input listener +filterField.addEventListener('keyup', function (event) { + const filters = handsontableInstance.getPlugin('filters'); + const columnSelector = document.getElementById('columns'); + const columnValue = columnSelector.value; + + filters.removeConditions(columnValue); + filters.addCondition(columnValue, 'contains', [event.target.value]); + filters.filter(); + + handsontableInstance.render(); +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleQuickFilter.jsx b/docs/content/guides/columns/column-filter/exampleQuickFilter.jsx new file mode 100644 index 00000000000..f51fa498e86 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleQuickFilter.jsx @@ -0,0 +1,146 @@ +// you need `useRef` to call Handsontable's instance methods +import { useEffect, useRef } from 'react'; +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + const hotTableComponentRef = useRef(null); + + useEffect(() => { + const handsontableInstance = hotTableComponentRef.current.hotInstance; + const filterField = document.querySelector('#filterField'); + + filterField.addEventListener('keyup', function (event) { + const filtersPlugin = handsontableInstance.getPlugin('filters'); + const columnSelector = document.getElementById('columns'); + const columnValue = columnSelector.value; + + filtersPlugin.removeConditions(columnValue); + filtersPlugin.addCondition(columnValue, 'contains', [event.target.value]); + filtersPlugin.filter(); + + handsontableInstance.render(); + }); + }, []); + + return ( + <> +
+ + +
+
+ +
+ + + ); +}; + +ReactDOM.render(, document.getElementById('exampleQuickFilter')); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleServerSideFilter.html b/docs/content/guides/columns/column-filter/exampleServerSideFilter.html new file mode 100644 index 00000000000..4400f4705dd --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleServerSideFilter.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleServerSideFilter.js b/docs/content/guides/columns/column-filter/exampleServerSideFilter.js new file mode 100644 index 00000000000..64184c71356 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleServerSideFilter.js @@ -0,0 +1,114 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleServerSideFilter'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu + dropdownMenu: true, + height: 'auto', + // `beforeFilter()` is a Handsontable hook + // it's fired after Handsontable gathers information about the filters, but before the filters are applied + beforeFilter: function (conditionsStack) { + // gather information about the filters + console.log('The amount of filters: ' + conditionsStack.length); + console.log('The last changed column index: ' + conditionsStack[0].column); + console.log( + 'The amount of filters added to this column: ' + conditionsStack[0].conditions.length + ); + // the list of filter conditions + console.log(conditionsStack[0].conditions); + + // return `false` to disable filtering on the client side + return false; + }, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleServerSideFilter.jsx b/docs/content/guides/columns/column-filter/exampleServerSideFilter.jsx new file mode 100644 index 00000000000..dd17830e635 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleServerSideFilter.jsx @@ -0,0 +1,125 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleServerSideFilter')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.html b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.html new file mode 100644 index 00000000000..2414a7a7492 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.js b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.js new file mode 100644 index 00000000000..bd3d3f13668 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.js @@ -0,0 +1,99 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#exampleShowFilterItemsOnly'); +const handsontableInstance = new Handsontable(container, { + data: [ + { + brand: 'Jetpulse', + model: 'Racing Socks', + price: 30, + sellDate: 'Oct 11, 2023', + sellTime: '01:23 AM', + inStock: false, + }, + { + brand: 'Gigabox', + model: 'HL Mountain Frame', + price: 1890.9, + sellDate: 'May 3, 2023', + sellTime: '11:27 AM', + inStock: false, + }, + { + brand: 'Camido', + model: 'Cycling Cap', + price: 130.1, + sellDate: 'Mar 27, 2023', + sellTime: '03:17 AM', + inStock: true, + }, + { + brand: 'Chatterpoint', + model: 'Road Tire Tube', + price: 59, + sellDate: 'Aug 28, 2023', + sellTime: '08:01 AM', + inStock: true, + }, + { + brand: 'Eidel', + model: 'HL Road Tire', + price: 279.99, + sellDate: 'Oct 2, 2023', + sellTime: '01:23 AM', + inStock: true, + }, + ], + columns: [ + { + title: 'Brand', + type: 'text', + data: 'brand', + }, + { + title: 'Model', + type: 'text', + data: 'model', + }, + { + title: 'Price', + type: 'numeric', + data: 'price', + numericFormat: { + pattern: '$ 0,0.00', + culture: 'en-US', + }, + }, + { + title: 'Date', + type: 'date', + data: 'sellDate', + dateFormat: 'MMM D, YYYY', + correctFormat: true, + className: 'htRight', + }, + { + title: 'Time', + type: 'time', + data: 'sellTime', + timeFormat: 'hh:mm A', + correctFormat: true, + className: 'htRight', + }, + { + title: 'In stock', + type: 'checkbox', + data: 'inStock', + className: 'htCenter', + }, + ], + // enable filtering + filters: true, + // enable the column menu, but display only the filter menu items + dropdownMenu: ['filter_by_condition', 'filter_by_value', 'filter_action_bar'], + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.jsx b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.jsx new file mode 100644 index 00000000000..6e718d83da8 --- /dev/null +++ b/docs/content/guides/columns/column-filter/exampleShowFilterItemsOnly.jsx @@ -0,0 +1,123 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const App = () => { + // remove the column menu button from the 'Brand', 'Price', and 'Date' columns + const removeColumnMenuButton = (col, TH) => { + if (col == 0 || col == 2 || col == 4) { + const button = TH.querySelector('.changeType'); + + if (!button) { + return; + } + + button.parentElement.removeChild(button); + } + }; + + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('exampleShowFilterItemsOnly')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-freezing.md b/docs/content/guides/columns/column-freezing/column-freezing.md similarity index 60% rename from docs/content/guides/columns/column-freezing.md rename to docs/content/guides/columns/column-freezing/column-freezing.md index 0ebdabc3647..b9ddd1d42ef 100644 --- a/docs/content/guides/columns/column-freezing.md +++ b/docs/content/guides/columns/column-freezing/column-freezing.md @@ -34,7 +34,7 @@ You can freeze columns during initialization and by the user. To freeze columns at initialization, use the [`fixedColumnsStart`](@/api/options.md#fixedcolumnsstart) option. Then, configure the container of your grid with the following CSS attributes: `width` and `overflow: hidden`. -If your [layout direction](@/guides/internationalization/layout-direction.md) is `ltr`, columns get frozen from the left side of the table. If your layout direction is `rtl`, columns get frozen from the right side of the table. +If your [layout direction](@/guides/internationalization/layout-direction/layout-direction.md) is `ltr`, columns get frozen from the left side of the table. If your layout direction is `rtl`, columns get frozen from the right side of the table. ::: only-for javascript @@ -75,43 +75,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// generate an array of arrays with dummy data -const data = new Array(100) // number of rows - .fill() - .map((_, row) => new Array(50) // number of columns - .fill() - .map((_, column) => `${row}, ${column}`) - ); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-freezing/example1.jsx) ::: @@ -119,7 +83,7 @@ ReactDOM.render(, document.getElementById('example1')); ## User-triggered freeze -To enable manual column freezing, set [`manualColumnFreeze`](@/api/options.md#manualcolumnfreeze) to `true`. This lets you freeze and unfreeze columns by using the grid's [context menu](@/guides/accessories-and-menus/context-menu.md). +To enable manual column freezing, set [`manualColumnFreeze`](@/api/options.md#manualcolumnfreeze) to `true`. This lets you freeze and unfreeze columns by using the grid's [context menu](@/guides/accessories-and-menus/context-menu/context-menu.md). Mind that when you unfreeze a frozen column, it doesn't go back to the original position. @@ -164,45 +128,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -// generate an array of arrays with dummy data -const data = new Array(100) // number of rows - .fill() - .map((_, row) => new Array(50) // number of columns - .fill() - .map((_, column) => `${row}, ${column}`) - ); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-freezing/example2.jsx) ::: diff --git a/docs/content/guides/columns/column-freezing/example1.js b/docs/content/guides/columns/column-freezing/example1.js new file mode 100644 index 00000000000..b0d12f28522 --- /dev/null +++ b/docs/content/guides/columns/column-freezing/example1.js @@ -0,0 +1,24 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data, + colWidths: 100, + width: '100%', + height: 320, + rowHeaders: true, + colHeaders: true, + fixedColumnsStart: 1, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-freezing/example1.jsx b/docs/content/guides/columns/column-freezing/example1.jsx new file mode 100644 index 00000000000..eef84992260 --- /dev/null +++ b/docs/content/guides/columns/column-freezing/example1.jsx @@ -0,0 +1,35 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-freezing/example2.js b/docs/content/guides/columns/column-freezing/example2.js new file mode 100644 index 00000000000..abd150c4887 --- /dev/null +++ b/docs/content/guides/columns/column-freezing/example2.js @@ -0,0 +1,26 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data, + colWidths: 100, + width: '100%', + height: 320, + rowHeaders: true, + colHeaders: true, + fixedColumnsStart: 2, + contextMenu: true, + manualColumnFreeze: true, + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-freezing/example2.jsx b/docs/content/guides/columns/column-freezing/example2.jsx new file mode 100644 index 00000000000..aaf0082473b --- /dev/null +++ b/docs/content/guides/columns/column-freezing/example2.jsx @@ -0,0 +1,37 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +// generate an array of arrays with dummy data +const data = new Array(100) // number of rows + .fill() + .map((_, row) => new Array(50) // number of columns + .fill() + .map((_, column) => `${row}, ${column}`) + ); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-groups.md b/docs/content/guides/columns/column-groups/column-groups.md similarity index 71% rename from docs/content/guides/columns/column-groups.md rename to docs/content/guides/columns/column-groups/column-groups.md index f2c4f95bd83..d1270ac0dc3 100644 --- a/docs/content/guides/columns/column-groups.md +++ b/docs/content/guides/columns/column-groups/column-groups.md @@ -99,44 +99,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-groups/example1.jsx) ::: @@ -229,51 +192,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-groups/example2.jsx) ::: diff --git a/docs/content/guides/columns/column-groups/example1.js b/docs/content/guides/columns/column-groups/example1.js new file mode 100644 index 00000000000..6217a2bb752 --- /dev/null +++ b/docs/content/guides/columns/column-groups/example1.js @@ -0,0 +1,25 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5', 'J5'], + ], + colHeaders: true, + rowHeaders: true, + height: 'auto', + nestedHeaders: [ + ['A', { label: 'B', colspan: 8 }, 'C'], + ['D', { label: 'E', colspan: 4 }, { label: 'F', colspan: 4 }, 'G'], + ['H', { label: 'I', colspan: 2 }, { label: 'J', colspan: 2 }, { label: 'K', colspan: 2 }, { label: 'L', colspan: 2 }, 'M'], + ['N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'], + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-groups/example1.jsx b/docs/content/guides/columns/column-groups/example1.jsx new file mode 100644 index 00000000000..25593688372 --- /dev/null +++ b/docs/content/guides/columns/column-groups/example1.jsx @@ -0,0 +1,36 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-groups/example2.js b/docs/content/guides/columns/column-groups/example2.js new file mode 100644 index 00000000000..f351d25777a --- /dev/null +++ b/docs/content/guides/columns/column-groups/example2.js @@ -0,0 +1,32 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3'], + ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4'], + ['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5', 'J5'], + ], + colHeaders: true, + rowHeaders: true, + colWidths: 60, + height: 'auto', + nestedHeaders: [ + ['A', { label: 'B', colspan: 8 }, 'C'], + ['D', { label: 'E', colspan: 4 }, { label: 'F', colspan: 4 }, 'G'], + ['H', { label: 'I', colspan: 2 }, { label: 'J', colspan: 2 }, { label: 'K', colspan: 2 }, { label: 'L', colspan: 2 }, 'M'], + ['N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'], + ], + collapsibleColumns: [ + { row: -4, col: 1, collapsible: true }, + { row: -3, col: 1, collapsible: true }, + { row: -2, col: 1, collapsible: true }, + { row: -2, col: 3, collapsible: true }, + ], + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation', +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-groups/example2.jsx b/docs/content/guides/columns/column-groups/example2.jsx new file mode 100644 index 00000000000..853011b250f --- /dev/null +++ b/docs/content/guides/columns/column-groups/example2.jsx @@ -0,0 +1,43 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-header.md b/docs/content/guides/columns/column-header/column-header.md similarity index 63% rename from docs/content/guides/columns/column-header.md rename to docs/content/guides/columns/column-header/column-header.md index 17f8b321dc1..6270e5bb052 100644 --- a/docs/content/guides/columns/column-header.md +++ b/docs/content/guides/columns/column-header/column-header.md @@ -59,36 +59,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-header/example1.jsx) ::: @@ -129,36 +100,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example2')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-header/example2.jsx) ::: @@ -201,38 +143,7 @@ const hot = new Handsontable(container, { ::: example #example3 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - { - return 'Col ' + (index + 1); - }} - rowHeaders={true} - height="auto" - autoWrapRow={true} - autoWrapCol={true} - licenseKey="non-commercial-and-evaluation" - /> - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example3')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-header/example3.jsx) ::: @@ -240,13 +151,13 @@ ReactDOM.render(, document.getElementById('example3')); ## Nested headers -More complex data structures can be displayed with multiple headers, each representing a different category of data. To learn more about nested headers, see the [column groups](@/guides/columns/column-groups.md) page. +More complex data structures can be displayed with multiple headers, each representing a different category of data. To learn more about nested headers, see the [column groups](@/guides/columns/column-groups/column-groups.md) page. ## Related articles ### Related guides -- [Column groups](@/guides/columns/column-groups.md) +- [Column groups](@/guides/columns/column-groups/column-groups.md) ### Related API reference diff --git a/docs/content/guides/columns/column-header/example1.js b/docs/content/guides/columns/column-header/example1.js new file mode 100644 index 00000000000..a05a062247d --- /dev/null +++ b/docs/content/guides/columns/column-header/example1.js @@ -0,0 +1,17 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example1'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1', 'K1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2', 'K2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3', 'K3'], + ], + colHeaders: true, + rowHeaders: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-header/example1.jsx b/docs/content/guides/columns/column-header/example1.jsx new file mode 100644 index 00000000000..97496648122 --- /dev/null +++ b/docs/content/guides/columns/column-header/example1.jsx @@ -0,0 +1,28 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example1')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-header/example2.js b/docs/content/guides/columns/column-header/example2.js new file mode 100644 index 00000000000..b30efc0d91f --- /dev/null +++ b/docs/content/guides/columns/column-header/example2.js @@ -0,0 +1,17 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example2'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3'], + ], + colHeaders: ['ID', 'Full name', 'Position','Country', 'City', 'Address', 'Zip code', 'Mobile', 'E-mail'], + rowHeaders: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-header/example2.jsx b/docs/content/guides/columns/column-header/example2.jsx new file mode 100644 index 00000000000..0e875baf20e --- /dev/null +++ b/docs/content/guides/columns/column-header/example2.jsx @@ -0,0 +1,28 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example2')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-header/example3.js b/docs/content/guides/columns/column-header/example3.js new file mode 100644 index 00000000000..bd7f3c78e87 --- /dev/null +++ b/docs/content/guides/columns/column-header/example3.js @@ -0,0 +1,19 @@ +import Handsontable from 'handsontable'; +import 'handsontable/dist/handsontable.full.min.css'; + +const container = document.querySelector('#example3'); +const hot = new Handsontable(container, { + data: [ + ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1', 'K1'], + ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2', 'K2'], + ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3', 'K3'], + ], + colHeaders(index) { + return 'Col ' + (index + 1); + }, + rowHeaders: true, + height: 'auto', + autoWrapRow: true, + autoWrapCol: true, + licenseKey: 'non-commercial-and-evaluation' +}); \ No newline at end of file diff --git a/docs/content/guides/columns/column-header/example3.jsx b/docs/content/guides/columns/column-header/example3.jsx new file mode 100644 index 00000000000..eaec88620cd --- /dev/null +++ b/docs/content/guides/columns/column-header/example3.jsx @@ -0,0 +1,30 @@ +import { HotTable } from '@handsontable/react'; +import { registerAllModules } from 'handsontable/registry'; +import 'handsontable/dist/handsontable.full.min.css'; + +// register Handsontable's modules +registerAllModules(); + +export const ExampleComponent = () => { + return ( + { + return 'Col ' + (index + 1); + }} + rowHeaders={true} + height="auto" + autoWrapRow={true} + autoWrapCol={true} + licenseKey="non-commercial-and-evaluation" + /> + ); +}; + +/* start:skip-in-preview */ +ReactDOM.render(, document.getElementById('example3')); +/* end:skip-in-preview */ \ No newline at end of file diff --git a/docs/content/guides/columns/column-hiding.md b/docs/content/guides/columns/column-hiding/column-hiding.md similarity index 61% rename from docs/content/guides/columns/column-hiding.md rename to docs/content/guides/columns/column-hiding/column-hiding.md index 4eb31cfc445..e103fc19dc4 100644 --- a/docs/content/guides/columns/column-hiding.md +++ b/docs/content/guides/columns/column-hiding/column-hiding.md @@ -74,44 +74,7 @@ const hot = new Handsontable(container, { ::: example #example1 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( - - ); -}; - -/* start:skip-in-preview */ -ReactDOM.render(, document.getElementById('example1')); -/* end:skip-in-preview */ -``` +@[code](@/content/guides/columns/column-hiding/example1.jsx) ::: @@ -170,43 +133,7 @@ const hot = new Handsontable(container, { ::: example #example2 :react -```jsx -import { HotTable } from '@handsontable/react'; -import { registerAllModules } from 'handsontable/registry'; -import 'handsontable/dist/handsontable.full.min.css'; - -// register Handsontable's modules -registerAllModules(); - -export const ExampleComponent = () => { - return ( -