From bda8d8ff41ad4cc9715ad9c6e7c5c3f972d08ee9 Mon Sep 17 00:00:00 2001 From: pipeline Date: Wed, 17 Jan 2024 02:21:52 +0000 Subject: [PATCH] v24.1.46 is released --- controls/barcodegenerator/CHANGELOG.md | 2 +- controls/base/CHANGELOG.md | 8 + controls/base/releasenotes/README.md | 183 ------------------ controls/buttons/CHANGELOG.md | 16 ++ controls/buttons/package.json | 2 +- controls/buttons/spec/switch.spec.ts | 42 +++- controls/buttons/src/check-box/check-box.ts | 2 +- .../floating-action-button.ts | 4 + .../buttons/src/radio-button/radio-button.ts | 2 +- controls/buttons/src/switch/switch.ts | 26 ++- .../styles/button/_bootstrap4-definition.scss | 24 +-- controls/buttons/styles/button/_mixin.scss | 60 ++---- controls/calendars/CHANGELOG.md | 2 +- controls/charts/package.json | 2 +- .../src/accumulation-chart/accumulation.ts | 6 +- .../accumulation-chart/renderer/dataLabel.ts | 8 +- .../src/accumulation-chart/renderer/legend.ts | 4 +- .../charts/src/bullet-chart/bullet-chart.ts | 8 +- .../charts/src/bullet-chart/legend/legend.ts | 2 +- controls/charts/src/chart/axis/axis.ts | 6 +- .../charts/src/chart/axis/cartesian-panel.ts | 4 +- .../src/chart/axis/multi-level-labels.ts | 8 +- .../src/chart/axis/polar-radar-panel.ts | 2 +- controls/charts/src/chart/chart.ts | 4 +- controls/charts/src/chart/legend/legend.ts | 4 +- .../src/chart/user-interaction/crosshair.ts | 2 +- controls/charts/src/chart3d/axis/axis.ts | 6 +- controls/charts/src/chart3d/chart3D.ts | 4 +- controls/charts/src/chart3d/legend/legend.ts | 4 +- controls/charts/src/common/legend/legend.ts | 11 +- controls/charts/src/common/utils/helper.ts | 20 +- .../charts/src/stock-chart/legend/legend.ts | 2 +- .../stock-chart/renderer/cartesian-chart.ts | 1 + .../stock-chart/renderer/range-selector.ts | 1 + controls/diagrams/CHANGELOG.md | 11 +- controls/diagrams/package.json | 2 +- .../layout/complex-hierarchical.spec.ts | 2 +- .../diagrams/spec/overview/overview.spec.ts | 161 +++++++++++---- controls/diagrams/src/diagram/diagram.ts | 32 ++- .../diagram/interaction/command-manager.ts | 2 +- .../src/diagram/interaction/event-handlers.ts | 13 ++ .../diagram/interaction/line-distribution.ts | 29 ++- .../src/diagram/interaction/scroller.ts | 8 + controls/diagrams/src/overview/overview.ts | 16 +- controls/documenteditor/CHANGELOG.md | 11 ++ controls/documenteditor/package.json | 2 +- .../src/document-editor/document-editor.ts | 8 +- .../implementation/editor/editor-helper.ts | 9 + .../implementation/editor/editor.ts | 14 +- .../implementation/viewer/layout.ts | 10 +- .../implementation/writer/sfdt-export.ts | 18 +- .../icons/_bootstrap-dark.scss | 5 + .../document-editor/icons/_bootstrap.scss | 5 + .../document-editor/icons/_bootstrap4.scss | 5 + .../document-editor/icons/_bootstrap5.scss | 5 + .../document-editor/icons/_fabric-dark.scss | 5 + .../styles/document-editor/icons/_fabric.scss | 5 + .../styles/document-editor/icons/_fluent.scss | 5 + .../document-editor/icons/_fusionnew.scss | 5 + .../icons/_highcontrast-light.scss | 5 + .../document-editor/icons/_highcontrast.scss | 5 + .../document-editor/icons/_material-dark.scss | 5 + .../document-editor/icons/_material.scss | 5 + .../document-editor/icons/_material3.scss | 5 + .../document-editor/icons/_tailwind-dark.scss | 5 + .../document-editor/icons/_tailwind.scss | 5 + controls/drawings/CHANGELOG.md | 2 +- controls/dropdowns/CHANGELOG.md | 18 ++ controls/dropdowns/package.json | 2 +- .../drop-down-list/drop-down-list.spec.ts | 36 +++- .../multi-select/checkbox-selectall.spec.ts | 16 +- .../multi-select/checkbox-selection.spec.ts | 16 +- .../spec/multi-select/multi-select.spec.ts | 42 ++-- .../src/drop-down-list/drop-down-list.ts | 11 +- controls/dropdowns/src/mention/mention.ts | 8 +- .../src/multi-select/multi-select.ts | 2 +- .../styles/drop-down-list/_layout.scss | 5 + controls/ej2/package.json | 2 +- controls/excelexport/CHANGELOG.md | 2 +- controls/filemanager/CHANGELOG.md | 2 +- controls/gantt/CHANGELOG.md | 23 ++- controls/gantt/package.json | 2 +- .../gantt/spec/action/taskbaredit.spec.ts | 2 +- controls/gantt/spec/base/gantt.spec.ts | 117 +++++++++++ controls/gantt/spec/base/taskbar.spec.ts | 2 +- .../gantt/src/gantt/actions/dialog-edit.ts | 20 +- controls/gantt/src/gantt/actions/edit.ts | 23 ++- .../gantt/src/gantt/actions/pdf-export.ts | 7 +- .../gantt/src/gantt/actions/taskbar-edit.ts | 9 +- .../gantt/src/gantt/base/task-processor.ts | 71 ++++--- controls/gantt/src/gantt/base/tree-grid.ts | 4 + .../gantt/src/gantt/export/export-helper.ts | 5 +- .../gantt/src/gantt/export/pdf-taskbar.ts | 52 ++++- controls/gantt/styles/gantt/_layout.scss | 2 +- controls/grids/CHANGELOG.md | 9 + controls/grids/package.json | 2 +- .../spec/grid/actions/infinite-scroll.spec.ts | 2 + .../grids/src/grid/actions/infinite-scroll.ts | 13 +- controls/grids/src/grid/base/grid.ts | 5 +- .../src/grid/common/checkbox-filter-base.ts | 2 + .../grids/src/grid/renderer/row-renderer.ts | 2 +- controls/grids/styles/grid/_layout.scss | 19 +- controls/heatmap/CHANGELOG.md | 2 +- controls/imageeditor/CHANGELOG.md | 12 ++ controls/imageeditor/package.json | 3 +- .../imageeditor/spec/image-editor.spec.ts | 20 +- .../src/image-editor/action/selection.ts | 8 +- .../src/image-editor/action/shape.ts | 18 +- .../src/image-editor/base/image-editor.ts | 1 + controls/inputs/CHANGELOG.md | 8 + controls/inputs/package.json | 2 +- controls/inputs/spec/rating.spec.ts | 22 +++ controls/inputs/src/common/signature-base.ts | 1 - controls/inputs/src/rating/rating.ts | 7 + .../data-form/_material3-definition.scss | 2 +- controls/kanban/CHANGELOG.md | 8 + controls/kanban/package.json | 2 +- controls/kanban/src/kanban/actions/drag.ts | 7 +- controls/lineargauge/CHANGELOG.md | 2 +- controls/maps/CHANGELOG.md | 2 +- controls/navigations/CHANGELOG.md | 20 ++ controls/navigations/spec/stepper.spec.ts | 59 +++--- .../spec/treeview/treeview.spec.ts | 113 ++++++++++- controls/navigations/src/common/menu-base.ts | 18 ++ controls/navigations/src/stepper/stepper.ts | 33 ++-- controls/navigations/src/treeview/treeview.ts | 53 ++++- .../stepper/_bootstrap-dark-definition.scss | 1 + .../styles/stepper/_bootstrap-definition.scss | 1 + .../stepper/_bootstrap4-definition.scss | 1 + .../stepper/_bootstrap5-definition.scss | 1 + .../stepper/_fabric-dark-definition.scss | 1 + .../styles/stepper/_fabric-definition.scss | 1 + .../styles/stepper/_fluent-definition.scss | 1 + .../styles/stepper/_fusionnew-definition.scss | 1 + .../stepper/_highcontrast-definition.scss | 1 + .../_highcontrast-light-definition.scss | 1 + .../stepper/_material-dark-definition.scss | 1 + .../styles/stepper/_material-definition.scss | 1 + .../styles/stepper/_material3-definition.scss | 1 + .../styles/stepper/_tailwind-definition.scss | 1 + .../navigations/styles/stepper/_theme.scss | 12 +- .../toolbar/_bootstrap5-definition.scss | 6 +- .../styles/toolbar/_fluent-definition.scss | 4 +- .../styles/toolbar/_tailwind-definition.scss | 4 +- controls/pdf/CHANGELOG.md | 2 +- controls/pdfexport/CHANGELOG.md | 2 +- controls/pdfviewer/CHANGELOG.md | 13 ++ controls/pdfviewer/package.json | 2 +- .../src/pdfviewer/annotation/annotation.ts | 15 +- .../annotation/sticky-notes-annotation.ts | 12 +- .../annotation/text-markup-annotation.ts | 11 +- .../src/pdfviewer/base/pdfviewer-base.ts | 2 +- .../pdfviewer/src/pdfviewer/base/signature.ts | 1 + .../src/pdfviewer/drawing/drawing.ts | 1 + .../pdfviewer/src/pdfviewer/drawing/tools.ts | 5 +- controls/pdfviewer/src/pdfviewer/pdfviewer.ts | 1 + controls/pivotview/CHANGELOG.md | 9 + controls/pivotview/package.json | 2 +- controls/pivotview/spec/base/grouping.spec.ts | 104 +++++++++- controls/pivotview/src/base/engine.ts | 10 +- controls/pivotview/src/base/util.ts | 5 + .../src/common/actions/event-base.ts | 2 +- .../pivotview/src/common/popups/grouping.ts | 4 +- .../src/pivotchart/base/pivotchart.ts | 4 +- .../src/pivotfieldlist/base/field-list.ts | 7 +- .../pivotfieldlist/renderer/tree-renderer.ts | 3 +- .../pivotview/src/pivotview/base/pivotview.ts | 16 +- .../src/pivotview/renderer/render.ts | 5 +- controls/popups/CHANGELOG.md | 9 +- controls/querybuilder/CHANGELOG.md | 16 ++ controls/querybuilder/package.json | 2 +- .../querybuilder/spec/query-builder.spec.ts | 22 +++ .../query-builder/query-builder-model.d.ts | 2 +- .../src/query-builder/query-builder.d.ts | 3 +- .../src/query-builder/query-builder.ts | 46 +++-- controls/ribbon/CHANGELOG.md | 2 +- controls/ribbon/README.md | 13 +- controls/ribbon/package.json | 2 +- .../ribbon/_bootstrap-dark-definition.scss | 2 + .../styles/ribbon/_bootstrap-definition.scss | 2 + .../styles/ribbon/_bootstrap4-definition.scss | 2 + .../styles/ribbon/_bootstrap5-definition.scss | 2 + .../ribbon/_fabric-dark-definition.scss | 2 + .../styles/ribbon/_fabric-definition.scss | 2 + .../styles/ribbon/_fluent-definition.scss | 2 + .../styles/ribbon/_fusionnew-definition.scss | 2 + .../ribbon/_highcontrast-definition.scss | 2 + .../_highcontrast-light-definition.scss | 2 + controls/ribbon/styles/ribbon/_layout.scss | 8 + .../ribbon/_material-dark-definition.scss | 2 + .../styles/ribbon/_material-definition.scss | 2 + .../styles/ribbon/_material3-definition.scss | 2 + .../styles/ribbon/_tailwind-definition.scss | 2 + controls/richtexteditor/CHANGELOG.md | 10 + controls/richtexteditor/package.json | 2 +- .../spec/cr-issues/rich-text-editor.spec.ts | 28 +++ .../plugin/msword-cleanup.spec.ts | 2 +- .../plugin/selection-commands.spec.ts | 4 +- .../actions/paste-clean-up.spec.ts | 12 +- .../rich-text-editor/actions/toolbar.spec.ts | 29 +++ .../base/rich-text-editor.spec.ts | 2 +- .../renderer/link-module.spec.ts | 12 +- .../renderer/toolbar-renderer.spec.ts | 31 ++- .../src/editor-manager/plugin/link.ts | 3 + .../src/editor-manager/plugin/nodecutter.ts | 2 +- .../rich-text-editor/actions/html-editor.ts | 4 +- .../actions/paste-clean-up.ts | 2 +- .../src/rich-text-editor/actions/toolbar.ts | 2 +- .../src/rich-text-editor/base/interface.ts | 1 + .../rich-text-editor/base/rich-text-editor.ts | 13 ++ .../renderer/toolbar-renderer.ts | 2 +- controls/splitbuttons/package.json | 2 +- .../styles/button-group/_layout.scss | 2 +- .../styles/button-group/_theme.scss | 26 +-- controls/spreadsheet/README.md | 2 - controls/spreadsheet/package.json | 2 +- .../spreadsheet/integrations/formula.spec.ts | 9 + .../spreadsheet/src/calculate/base/parser.ts | 2 +- .../spreadsheet/actions/data-validation.ts | 35 ++-- .../src/spreadsheet/actions/selection.ts | 2 +- .../spreadsheet/integrations/formula-bar.ts | 8 +- .../src/workbook/base/workbook-model.d.ts | 2 +- .../spreadsheet/src/workbook/base/workbook.ts | 10 +- .../spreadsheet/src/workbook/common/event.ts | 2 + .../src/workbook/common/interface.ts | 26 ++- .../spreadsheet/src/workbook/common/util.ts | 34 +++- .../src/workbook/integrations/data-bind.ts | 92 ++++----- .../workbook/integrations/number-format.ts | 82 ++++---- .../src/workbook/integrations/save.ts | 69 +++++-- controls/svgbase/CHANGELOG.md | 7 + controls/svgbase/package.json | 2 +- controls/svgbase/src/tooltip/tooltip.ts | 8 +- controls/treegrid/CHANGELOG.md | 8 + controls/treegrid/package.json | 2 +- .../src/treegrid/actions/context-menu.ts | 19 +- 235 files changed, 2071 insertions(+), 859 deletions(-) delete mode 100644 controls/base/releasenotes/README.md diff --git a/controls/barcodegenerator/CHANGELOG.md b/controls/barcodegenerator/CHANGELOG.md index 54e66a0eed..f48db75be4 100644 --- a/controls/barcodegenerator/CHANGELOG.md +++ b/controls/barcodegenerator/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### Barcode diff --git a/controls/base/CHANGELOG.md b/controls/base/CHANGELOG.md index f204e654cb..27b00a4aca 100644 --- a/controls/base/CHANGELOG.md +++ b/controls/base/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Common + +#### Bug Fixes + +- `#I531468` - The issue with "rendering the html template string" has been resolved. + ## 23.2.6 (2023-11-28) ### Common diff --git a/controls/base/releasenotes/README.md b/controls/base/releasenotes/README.md deleted file mode 100644 index 08421ac829..0000000000 --- a/controls/base/releasenotes/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# Release Notes Guidelines - -This section contains guidelines on naming files, sections and other document elements. - -> **If there is no changes in product, you don't need to mention that in Release Notes.** - -## Encoding Format - -All Release Notes files should be saved in **Encoding in UTF-8 (Without BOM)** format. You can use Notepad++ to verify the encoding. - -![Encoding.png](https://bitbucket.org/repo/j57Gz9/images/2199960455-Encoding.png) - -## Release Notes Folder Hierarchy - -* Platform [Folder] -* ----ReleaseNotes [Folder] -* --------v13.3.x.x [Folder] -* ------------Control1.md -* ------------Control2.md -* ------------Control3.md -* --------v13.4.x.x [Folder] -* ------------Control1.md -* ------------Control2.md -* ------------Control3.md - -### How to write Release Notes? - -* Each release markdown files should reside under corresponding version folder in their platform. -* Each product release notes should be created in separate file name. -* File name should be same as the product name. - -> **NOTE**: Please do not add any Front Matter information in Release Notes files. - -## Markdown File Structure - -Each markdown file should have following items. - -* Control Name -* Features -* Bug fixes -* Braking Changes -* Known Issues - -> Do not add any front matter(triple dashed line) in this markdown. - -### Control Name - -Control Name should be with prefix `##`. This will be rendered as `H2` in html file. - -#### Syntax - -``` -## -``` - -#### Example - -``` -## ejAccrodion -``` - -### Features - -* Each features should be written in unordered list. -* Feature header should have id in the following format `-features`. All characters in **id should be written in lower case.** - -#### Syntax - -``` -### Features -{:#-features} - -* \#1 - Feature Info -* \#2 - Feature Info -* \#3 - Feature Info -``` - -#### Example - -``` -### Features -{:#ejaccordion-features} - -* \#140303 - Accordion provides option to add new items dynamically by using the `addItem` method -* \#140303 - Accordion provides option to add new items dynamically by using the `addItem` method -* \#140303, \#140304 - Accordion provides option to add new items dynamically by using the `addItem` method -``` - -> **NOTE:** -> * In markdown `#` used to represent headers. -> * By default it will be converted as HTML headers. -> * To display the `#` in html, please use escape sequences [See above example]. - -### Bug Fixes - -* Each bug fix should be written in unordered list. -* Bug fixes header should have id in the following format `-bug-fixes`. All characters in **id should be written in lower case.** - -#### Syntax - -``` -### Bug fixes -{:#-bug-fixes} - -* \#1 - Bug Fix -* \#2 - Bug Fix -* \#3 - Bug Fix -``` - -#### Example - -``` -### Bug Fixes -{:#ejaccordion-bug-fixes} - -* \#140303 - Accordion provides option to add new items dynamically by using the `addItem` method -* \#140303 - Accordion provides option to add new items dynamically by using the `addItem` method -* \#140303, \#140304 - Accordion provides option to add new items dynamically by using the `addItem` method -``` - -> **NOTE:** -> * In markdown `#` used to represent headers. -> * By default it will be converted as HTML headers. -> * To display the `#` in html, please use escape sequences [See above example]. - -### Breaking Changes - -* Each breaking changes should be written in unordered list. -* Breaking changes header should have id in the following format `-breaking-changes`. All characters in **id should be written in lower case.** - -``` -### Breaking Changes -{:#-breaking-changes} - -* * Breaking Change 1 -* * Breaking Change 2 -* * Breaking Change 3 -``` - -#### Example - -``` -### Breaking Changes -{:#ejaccordion-breaking-changes} - -* Now, Circular series end angle will not be adjusted based on the start angle, so the output will be like semi-circle instead of full circle. In order to render the complete circular series with customized start angle, you have to add the start angle value to end angle property now. This break will occur only if you have specified startAngle already -``` - -> **NOTE:** -> * In markdown `#` used to represent headers. -> * By default it will be converted as HTML headers. -> * To display the `#` in html, please use escape sequences [See above example]. - -## Incidents and Forums in Release notes - -We can represent the Incident ID with I and F for forums in release notes MD files - -#### Example - - -``` -## ChromelessWindow - -### Bug Fixes -{:#chromelesswindow-bug-fixes} - -* \#I336220 - When using `ShowDialog` on a `RibbonWindow`, a `NullReferenceException` will no longer occur. -* \#F166385 - The gap between the bottom of the window and the `TaskBar` is now properly maintained. - -``` - -This is published in the page : https://help.syncfusion.com/wpf/release-notes/v19.3.0.43?type=all#chromelesswindow - - -## Commit - -Same workflow for User Guide applicable to this repository. All the changes needs to be committed in `development` branch. - -## Preview Changes - -All the changes will be included with User Guide automation and published in Staging Documentation machine. - - \ No newline at end of file diff --git a/controls/buttons/CHANGELOG.md b/controls/buttons/CHANGELOG.md index a6903a4300..07441aeb6d 100644 --- a/controls/buttons/CHANGELOG.md +++ b/controls/buttons/CHANGELOG.md @@ -2,6 +2,22 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Checkbox + +#### Bug Fixes + +- `#I535107` - Need to set `HtmlAttributes` Id in input element checkbox has been resolved. + +## 24.1.45 (2024-01-09) + +### Switch + +#### Bug Fixes + +- `#I530742` - The issue with "Switch unintentionally changed when we touch the switch and drag vertically" has been resolved. + ## 24.1.41 (2023-12-18) ### RadioButton diff --git a/controls/buttons/package.json b/controls/buttons/package.json index 29afdaffb4..b08d3c056d 100644 --- a/controls/buttons/package.json +++ b/controls/buttons/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-buttons", - "version": "24.1.41", + "version": "24.1.45", "description": "A package of feature-rich Essential JS 2 components such as Button, CheckBox, RadioButton and Switch.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/buttons/spec/switch.spec.ts b/controls/buttons/spec/switch.spec.ts index 05b09bc5ce..f5fe7c95bf 100644 --- a/controls/buttons/spec/switch.spec.ts +++ b/controls/buttons/spec/switch.spec.ts @@ -122,14 +122,44 @@ describe('Switch', () => { }); it('Wrapper touch', () => { specSwitch = new Switch({}, '#specSwitch'); - const start: MouseEvent = document.createEvent('MouseEvent'); - start.initEvent('touchstart', true, true); + const start: TouchEvent = new TouchEvent('touchstart', { + touches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + targetTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + changedTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + bubbles: true, cancelable: true, composed: true, + }); element.parentElement.dispatchEvent(start); - const move: MouseEvent = document.createEvent('MouseEvent'); - move.initEvent('touchmove', true, true); + const move: TouchEvent = new TouchEvent('touchmove', { + touches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + targetTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + changedTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + bubbles: true, cancelable: true, composed: true, + }); element.parentElement.dispatchEvent(move); - const end: MouseEvent = document.createEvent('MouseEvent'); - end.initEvent('touchend', true, true); + const end: TouchEvent = new TouchEvent('touchend', { + touches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + targetTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + changedTouches: [ + new Touch({ identifier: 1, target: document.documentElement }), + ], + bubbles: true, cancelable: true, composed: true, + }); element.parentElement.dispatchEvent(end); expect(element.parentElement.children[1].classList.contains('e-switch-active')).toEqual(true); const up: MouseEvent = document.createEvent('MouseEvent'); diff --git a/controls/buttons/src/check-box/check-box.ts b/controls/buttons/src/check-box/check-box.ts index e2c7a4c985..9247b88f55 100644 --- a/controls/buttons/src/check-box/check-box.ts +++ b/controls/buttons/src/check-box/check-box.ts @@ -22,7 +22,7 @@ const RIPPLECHECK: string = 'e-ripple-check'; const RIPPLEINDETERMINATE: string = 'e-ripple-stop'; const RTL: string = 'e-rtl'; const WRAPPER: string = 'e-checkbox-wrapper'; -const containerAttr: string[] = ['title', 'class', 'style', 'disabled', 'readonly', 'name', 'value']; +const containerAttr: string[] = ['title', 'class', 'style', 'disabled', 'readonly', 'name', 'value', 'id']; /** * The CheckBox is a graphical user interface element that allows you to select one or more options from the choices. diff --git a/controls/buttons/src/floating-action-button/floating-action-button.ts b/controls/buttons/src/floating-action-button/floating-action-button.ts index 258be4dbdc..5e4b72b216 100644 --- a/controls/buttons/src/floating-action-button/floating-action-button.ts +++ b/controls/buttons/src/floating-action-button/floating-action-button.ts @@ -285,6 +285,10 @@ export class Fab extends Button implements INotifyPropertyChanged { this.checkTarget(); this.setPosition(); break; + /* REF - 861739 */ + case 'currencyCode': + this.refresh(); + break; } } } diff --git a/controls/buttons/src/radio-button/radio-button.ts b/controls/buttons/src/radio-button/radio-button.ts index 2caf4e1bf4..7c6c951931 100644 --- a/controls/buttons/src/radio-button/radio-button.ts +++ b/controls/buttons/src/radio-button/radio-button.ts @@ -16,7 +16,7 @@ const LABEL: string = 'e-label'; const RIPPLE: string = 'e-ripple-container'; const RTL: string = 'e-rtl'; const WRAPPER: string = 'e-radio-wrapper'; -const ATTRIBUTES: string[] = ['title', 'class', 'style', 'disabled', 'readonly', 'name', 'value']; +const ATTRIBUTES: string[] = ['title', 'class', 'style', 'disabled', 'readonly', 'name', 'value', 'id']; /** * The RadioButton is a graphical user interface element that allows you to select one option from the choices. diff --git a/controls/buttons/src/switch/switch.ts b/controls/buttons/src/switch/switch.ts index 85786f4593..4ce8848b0d 100644 --- a/controls/buttons/src/switch/switch.ts +++ b/controls/buttons/src/switch/switch.ts @@ -33,6 +33,8 @@ export class Switch extends Component implements INotifyProper private delegateKeyUpHandler: Function; private formElement: HTMLFormElement; private initialSwitchCheckedValue: boolean; + private bTouchY: number; + private bTouchX: number; /** * Triggers when Switch state has been changed by user interaction. @@ -427,19 +429,37 @@ export class Switch extends Component implements INotifyProper } } private switchMouseUp(e: MouseEventArgs): void { + let aTouchY: number = 0; let yDiff: number = 0; + let aTouchX: number = 0; let xDiff: number = 0; const target: Element = e.target as Element; if (e.type === 'touchmove') { e.preventDefault(); + aTouchX = e.changedTouches[0].clientX; + aTouchY = e.changedTouches[0].clientY; + xDiff = this.bTouchX - aTouchX; + yDiff = this.bTouchY - aTouchY; + if (Math.abs(xDiff) < Math.abs(yDiff)) { + this.isDrag = false; + this.rippleTouchHandler('mouseup'); + } else { + this.isDrag = true; + } } if (e.type === 'touchstart') { + this.bTouchX = e.changedTouches[0].clientX; + this.bTouchY = e.changedTouches[0].clientY; this.isDrag = true; this.rippleTouchHandler('mousedown'); } if (this.isDrag) { if ((e.type === 'mouseup' && target.className.indexOf('e-switch') < 0) || e.type === 'touchend') { - this.clickHandler(e); - this.rippleTouchHandler('mouseup'); - e.preventDefault(); + xDiff = this.bTouchX - e.changedTouches[0].clientX; + yDiff = this.bTouchY - e.changedTouches[0].clientY; + if (Math.abs(xDiff) >= Math.abs(yDiff)) { + this.clickHandler(e); + this.rippleTouchHandler('mouseup'); + e.preventDefault(); + } } } } diff --git a/controls/buttons/styles/button/_bootstrap4-definition.scss b/controls/buttons/styles/button/_bootstrap4-definition.scss index 8464b03627..76bc77f369 100644 --- a/controls/buttons/styles/button/_bootstrap4-definition.scss +++ b/controls/buttons/styles/button/_bootstrap4-definition.scss @@ -7,8 +7,8 @@ $btn-border-color: $gray-600 !default; $btn-hover-bgcolor: darken($gray-600, 7.5%) !default; $btn-hover-border-color: darken($gray-600, 10%) !default; $btn-hover-color: $white !default; -$btn-focus-bgcolor: $gray-600 !default; -$btn-focus-border-color: $gray-600 !default; +$btn-focus-bgcolor: darken($gray-600, 7%) !default; +$btn-focus-border-color: darken($gray-600, 5%) !default; $btn-focus-color: $white !default; $btn-active-border-color: darken($gray-600, 12.5%) !default; $btn-active-bgcolor: darken($gray-600, 10%) !default; @@ -53,9 +53,9 @@ $btn-primary-hover-bgcolor: darken($primary, 7.5%) !default; $btn-primary-active-bgcolor: darken($primary, 10%) !default; $btn-primary-border-color: $primary !default; $btn-primary-hover-border-color: darken($primary, 10%) !default; -$btn-primary-focus-border-color: $primary !default; +$btn-primary-focus-border-color: darken($primary, 5%) !default; $btn-primary-active-border-color: darken($primary, 12.5%) !default; -$btn-primary-focus-bgcolor: $primary !default; +$btn-primary-focus-bgcolor: darken($primary, 7%) !default; $btn-primary-disabled-bgcolor: rgba($btn-primary-bgcolor, .65) !default; $btn-primary-disabled-color: $white !default; $btn-primary-disabled-border-color: transparent !default; @@ -115,11 +115,11 @@ $btn-ripple-flat-danger-bgcolor: transparent !default; $btn-success-color: $white !default; $btn-success-bgcolor: $success !default; $btn-success-hover-bgcolor: darken($success, 7.5%) !default; -$btn-success-focus-bgcolor: $success !default; +$btn-success-focus-bgcolor: darken($success, 7%) !default; $btn-success-active-bgcolor: darken($success, 10%) !default; $btn-success-border-color: $success !default; $btn-success-hover-border-color: darken($success, 10%) !default; -$btn-success-focus-border-color: $success !default; +$btn-success-focus-border-color: darken($success, 5%) !default; $btn-success-active-border-color: darken($success, 12.5%) !default; $btn-success-disabled-bgcolor: rgba($btn-success-bgcolor, .65) !default; $btn-success-disabled-color: $white !default; @@ -135,11 +135,11 @@ $btn-warning-bgcolor: $warning !default; $btn-warning-color: $gray-900 !default; $btn-warning-hover-color: $gray-900 !default; $btn-warning-hover-bgcolor: darken($warning, 7.5%) !default; -$btn-warning-focus-bgcolor: $warning !default; +$btn-warning-focus-bgcolor: darken($warning, 7%) !default; $btn-warning-active-bgcolor: darken($warning, 10%) !default; $btn-warning-border-color: $warning !default; $btn-warning-hover-border-color: darken($warning, 10%) !default; -$btn-warning-focus-border-color: $warning !default; +$btn-warning-focus-border-color: darken($warning, 5%) !default; $btn-warning-focus-color: $gray-900 !default; $btn-warning-active-color: $gray-900 !default; $btn-warning-active-border-color: darken($warning, 12.5%) !default; @@ -153,12 +153,12 @@ $btn-ripple-warning-bgcolor: transparent !default; $btn-danger-color: $white !default; $btn-danger-bgcolor: $danger !default; $btn-danger-hover-bgcolor: darken($danger, 7.5%) !default; -$btn-danger-focus-bgcolor: $danger !default; +$btn-danger-focus-bgcolor: darken($danger, 7%) !default; $btn-danger-active-bgcolor: darken($danger, 10%) !default; $btn-danger-active-color: $white !default; $btn-danger-border-color: $danger !default; $btn-danger-hover-border-color: darken($danger, 10%) !default; -$btn-danger-focus-border-color: $danger !default; +$btn-danger-focus-border-color: darken($danger, 5%) !default; $btn-danger-active-border-color: darken($danger, 12.5%) !default; $btn-danger-disabled-bgcolor: rgba($btn-danger-bgcolor, .65) !default; $btn-danger-disabled-color: $white !default; @@ -171,11 +171,11 @@ $btn-ripple-danger-bgcolor: transparent !default; $btn-info-bgcolor: $info !default; $btn-info-color: $white !default; $btn-info-hover-bgcolor: darken($info, 7.5%) !default; -$btn-info-focus-bgcolor: $info !default; +$btn-info-focus-bgcolor: darken($info, 7%) !default; $btn-info-active-bgcolor: darken($info, 10%) !default; $btn-info-border-color: $info !default; $btn-info-hover-border-color: darken($info, 10%) !default; -$btn-info-focus-border-color: $info !default; +$btn-info-focus-border-color: darken($info, 5%) !default; $btn-info-active-border-color: darken($info, 12.5%) !default; $btn-info-disabled-bgcolor: rgba($btn-info-bgcolor, .65) !default; $btn-info-disabled-color: $white !default; diff --git a/controls/buttons/styles/button/_mixin.scss b/controls/buttons/styles/button/_mixin.scss index 1ebb0ffee5..4894abf0cd 100644 --- a/controls/buttons/styles/button/_mixin.scss +++ b/controls/buttons/styles/button/_mixin.scss @@ -34,11 +34,7 @@ outline: $btn-active-outline; outline-offset: $btn-active-outline-offset; } - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-active-bgcolor, 50%), $btn-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @else if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } @@ -70,11 +66,7 @@ @if $skin-name != 'FluentUI' { outline: $btn-active-outline; } - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-primary-active-bgcolor, 50%), $btn-primary-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } @@ -111,13 +103,11 @@ @if $skin-name == 'bootstrap4' { background: $btn-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-outline-active-bgcolor, 50%), $btn-outline-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else { background: $btn-outline-active-bgcolor; border-color: $btn-outline-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -146,8 +136,6 @@ @if $skin-name == 'bootstrap4' { background: $btn-primary-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-primary-active-bgcolor, 50%), $btn-outline-primary-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else if ($skin-name == 'bootstrap5') { background: $btn-primary-bgcolor; @@ -155,7 +143,7 @@ @else { background: $btn-primary-active-bgcolor; border-color: $btn-outline-primary-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -189,13 +177,11 @@ @if $skin-name == 'bootstrap4' { background: $btn-success-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-success-active-bgcolor, 50%), $btn-success-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else { background: $btn-success-active-bgcolor; border-color: $btn-success-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -229,8 +215,6 @@ @if $skin-name == 'bootstrap4' { background: $btn-info-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-info-active-bgcolor, 50%), $btn-info-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else if ($skin-name == 'bootstrap5') { background: $btn-info-bgcolor; @@ -238,7 +222,7 @@ @else { background: $btn-info-active-bgcolor; border-color: $btn-info-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -272,13 +256,11 @@ @if $skin-name == 'bootstrap4' { background: $btn-warning-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-warning-active-bgcolor, 50%), $btn-warning-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else { background: $btn-warning-active-bgcolor; border-color: $btn-warning-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -312,13 +294,11 @@ @if $skin-name == 'bootstrap4' { background: $btn-danger-bgcolor; border-color: transparent; - $color-rgba: rgba(mix(lighten($btn-danger-active-bgcolor, 50%), $btn-danger-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; } @else { background: $btn-danger-active-bgcolor; border-color: $btn-danger-active-border-color; - @if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-outline-active-box-shadow; } } @@ -430,11 +410,7 @@ background: $btn-success-active-bgcolor; border-color: $btn-success-active-border-color; color: $btn-success-active-color; - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-success-active-bgcolor, 50%), $btn-success-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } @@ -467,11 +443,7 @@ @else { border-color: $btn-info-active-border-color; } - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-info-active-bgcolor, 50%), $btn-info-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } @@ -504,11 +476,7 @@ border-color: $btn-warning-active-border-color; } color: $btn-warning-active-color; - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-warning-active-bgcolor, 50%), $btn-warning-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } @@ -541,11 +509,7 @@ border-color: $btn-danger-active-border-color; } color: $btn-danger-active-color; - @if $skin-name == 'bootstrap4' { - $color-rgba: rgba(mix(lighten($btn-danger-active-bgcolor, 50%), $btn-danger-active-border-color, 15%), .5); - box-shadow: 0 0 0 .25em $color-rgba; - } - @else if $skin-name != 'bootstrap5' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' { box-shadow: $btn-active-box-shadow; } } diff --git a/controls/calendars/CHANGELOG.md b/controls/calendars/CHANGELOG.md index ae1877770b..1c9a954d15 100644 --- a/controls/calendars/CHANGELOG.md +++ b/controls/calendars/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### TimePicker diff --git a/controls/charts/package.json b/controls/charts/package.json index 7bb58a0913..7bd23ca061 100644 --- a/controls/charts/package.json +++ b/controls/charts/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-charts", - "version": "24.1.44", + "version": "24.1.45", "description": "Feature-rich chart control with built-in support for over 25 chart types, technical indictors, trendline, zooming, tooltip, selection, crosshair and trackball.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/charts/src/accumulation-chart/accumulation.ts b/controls/charts/src/accumulation-chart/accumulation.ts index 5335bb816f..88bb9056f9 100644 --- a/controls/charts/src/accumulation-chart/accumulation.ts +++ b/controls/charts/src/accumulation-chart/accumulation.ts @@ -1654,7 +1654,7 @@ export class AccumulationChart extends Component implements INotify let maxWidth: number = 0; let titleWidth: number = 0; if (this.title) { - this.titleCollection = getTitle(this.title, this.titleStyle, this.initialClipRect.width, this.themeStyle.chartTitleFont); + this.titleCollection = getTitle(this.title, this.titleStyle, this.initialClipRect.width, this.enableRtl, this.themeStyle.chartTitleFont); } titleHeight = this.title ? measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont).height * this.titleCollection.length : titleHeight; if (this.subTitle) { @@ -1662,7 +1662,7 @@ export class AccumulationChart extends Component implements INotify titleWidth = measureText(titleText, this.titleStyle, this.themeStyle.chartSubTitleFont).width; maxWidth = titleWidth > maxWidth ? titleWidth : maxWidth; } - this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, maxWidth, this.themeStyle.chartTitleFont); + this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, maxWidth, this.enableRtl, this.themeStyle.chartTitleFont); subTitleHeight = (measureText(this.subTitle, this.subTitleStyle, this.themeStyle.chartSubTitleFont).height * this.subTitleCollection.length); } subtractRect( @@ -1944,7 +1944,7 @@ export class AccumulationChart extends Component implements INotify for (let i: number = 0; i < collectionLength; i++) { const labelSize: Size = measureText(labelCollection[i as number], this.centerLabel.textStyle, this.themeStyle.chartTitleFont); if (labelSize.width > maxwidth) { - labelCollection.splice(i, 1, ...(textWrap(labelCollection[i as number], maxwidth, this.centerLabel.textStyle, null, null, this.themeStyle.chartTitleFont))); + labelCollection.splice(i, 1, ...(textWrap(labelCollection[i as number], maxwidth, this.centerLabel.textStyle, this.enableRtl, null, null, this.themeStyle.chartTitleFont))); } } if (centerLabelSize.height * (labelCollection.length) > maxwidth) { diff --git a/controls/charts/src/accumulation-chart/renderer/dataLabel.ts b/controls/charts/src/accumulation-chart/renderer/dataLabel.ts index de9e22df1c..43f7833f42 100644 --- a/controls/charts/src/accumulation-chart/renderer/dataLabel.ts +++ b/controls/charts/src/accumulation-chart/renderer/dataLabel.ts @@ -132,10 +132,10 @@ export class AccumulationDataLabel extends AccumulationBase { point.labelCollection = point.label.split('
'); } else if (dataLabel.textWrap === 'Normal' && dataLabel.textOverflow === 'Ellipsis') { - point.labelCollection[0] = textTrim(maxWidth, point.label, point.argsData.font, this.accumulation.themeStyle.datalabelFont); + point.labelCollection[0] = textTrim(maxWidth, point.label, point.argsData.font, this.accumulation.enableRtl, this.accumulation.themeStyle.datalabelFont); } else if (dataLabel.textWrap === 'Wrap' || dataLabel.textWrap === 'AnyWhere') { - point.labelCollection = textWrap(point.label, maxWidth, point.argsData.font, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont); + point.labelCollection = textWrap(point.label, maxWidth, point.argsData.font, this.accumulation.enableRtl, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont); } else { point.labelCollection[0] = point.label; @@ -318,10 +318,10 @@ export class AccumulationDataLabel extends AccumulationBase { } else if (size < point.labelRegion.width) { if (dataLabel.textWrap === 'Normal' && dataLabel.textOverflow === 'Ellipsis') { - point.labelCollection[0] = textTrim(size - (this.marginValue * 2), point.label, font, this.accumulation.themeStyle.datalabelFont); + point.labelCollection[0] = textTrim(size - (this.marginValue * 2), point.label, font, this.accumulation.enableRtl, this.accumulation.themeStyle.datalabelFont); } else if (dataLabel.textWrap === 'Wrap' || dataLabel.textWrap === 'AnyWhere') { - point.labelCollection = textWrap(point.label, size - (this.marginValue * 2), font, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont); + point.labelCollection = textWrap(point.label, size - (this.marginValue * 2), font, this.accumulation.enableRtl, dataLabel.textWrap === 'AnyWhere', dataLabel.textOverflow === 'Clip', this.accumulation.themeStyle.datalabelFont); } point.labelRegion.width = size; } diff --git a/controls/charts/src/accumulation-chart/renderer/legend.ts b/controls/charts/src/accumulation-chart/renderer/legend.ts index e2efb1b38e..7e8105aab9 100644 --- a/controls/charts/src/accumulation-chart/renderer/legend.ts +++ b/controls/charts/src/accumulation-chart/renderer/legend.ts @@ -262,7 +262,7 @@ export class AccumulationLegend extends BaseLegend { option.textCollection = textWrap( option.text, (legend.maximumLabelWidth ? Math.min(legend.maximumLabelWidth, (bounds.width - textPadding)) : - (bounds.width - textPadding)), legend.textStyle, null, null, this.chart.themeStyle.legendLabelFont + (bounds.width - textPadding)), legend.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.legendLabelFont ); } else { option.textCollection.push(option.text); @@ -376,7 +376,7 @@ export class AccumulationLegend extends BaseLegend { let availablewidth: number = this.getAvailWidth(legendOption.location.x, this.legendBounds.width); availablewidth = this.legend.maximumLabelWidth ? Math.min(this.legend.maximumLabelWidth, availablewidth) : availablewidth; if (this.legend.textOverflow === 'Ellipsis' && this.legend.textWrap === 'Normal') { - legendOption.text = textTrim(+availablewidth.toFixed(4), legendOption.text, this.legend.textStyle, this.chart.themeStyle.legendTitleFont); + legendOption.text = textTrim(+availablewidth.toFixed(4), legendOption.text, this.legend.textStyle, this.chart.enableRtl, this.chart.themeStyle.legendTitleFont); } } diff --git a/controls/charts/src/bullet-chart/bullet-chart.ts b/controls/charts/src/bullet-chart/bullet-chart.ts index a1f66096cb..43713a1950 100644 --- a/controls/charts/src/bullet-chart/bullet-chart.ts +++ b/controls/charts/src/bullet-chart/bullet-chart.ts @@ -631,11 +631,11 @@ export class BulletChart extends Component implements INotifyProper this.themeStyle = getBulletThemeColor(this.theme); if ((this.targetColor === null || this.targetColor === '#191919' || this.valueFill == null) && this.theme.indexOf('Fluent') > -1) { this.valueFill = !(this.valueFill) ? (this.theme === 'FluentDark' ? '#797775' : '#A19F9D') : this.valueFill; - this.targetColor = (!this.targetColor && this.targetColor != '#191919') ? (this.theme === 'FluentDark' ? '#797775' : '#A19F9D') : this.targetColor; + this.targetColor = (this.targetColor == '#191919') ? (this.theme === 'FluentDark' ? '#797775' : '#A19F9D') : this.targetColor; } if ((this.targetColor === null || this.targetColor === '#191919' || this.valueFill == null) && this.theme.indexOf('Material3') > -1) { this.valueFill = !(this.valueFill) ? (this.theme === 'Material3Dark' ? '#938F99' : '#79747E') : this.valueFill; - this.targetColor = (!this.targetColor && this.targetColor != '#191919') ? (this.theme === 'Material3Dark' ? '#938F99' : '#79747E') : this.targetColor; + this.targetColor = (this.targetColor == '#191919') ? (this.theme === 'Material3Dark' ? '#938F99' : '#79747E') : this.targetColor; } } @@ -870,7 +870,7 @@ export class BulletChart extends Component implements INotifyProper let maxTitlteHeight: number = 0; let maxVerticalTitlteHeight: number = padding; if (this.title) { - this.titleCollections = getTitle(this.title, this.titleStyle, this.titleStyle.maximumTitleWidth, this.themeStyle.titleFont); + this.titleCollections = getTitle(this.title, this.titleStyle, this.titleStyle.maximumTitleWidth, this.enableRtl, this.themeStyle.titleFont); titleHeight = (measureText(this.title, this.titleStyle, this.themeStyle.titleFont).height * this.titleCollections.length) + padding; for (const titleText of this.titleCollections) { titleSize = measureText(titleText, this.titleStyle, this.themeStyle.titleFont); @@ -878,7 +878,7 @@ export class BulletChart extends Component implements INotifyProper maxTitlteHeight = titleSize.height > maxTitlteHeight ? titleSize.height : maxTitlteHeight; } maxVerticalTitlteHeight += maxTitlteHeight; - this.subTitleCollections = getTitle(this.subtitle, this.subtitleStyle, this.titleStyle.maximumTitleWidth); + this.subTitleCollections = getTitle(this.subtitle, this.subtitleStyle, this.titleStyle.maximumTitleWidth, this.enableRtl); if (this.subtitle) { for (const subText of this.subTitleCollections) { titleSize = measureText(subText, this.subtitleStyle, this.themeStyle.subTitleFont); diff --git a/controls/charts/src/bullet-chart/legend/legend.ts b/controls/charts/src/bullet-chart/legend/legend.ts index 864b1fff1e..29de8fbb32 100644 --- a/controls/charts/src/bullet-chart/legend/legend.ts +++ b/controls/charts/src/bullet-chart/legend/legend.ts @@ -196,7 +196,7 @@ export class BulletChartLegend extends BaseLegend { } const availwidth: number = (this.legendBounds.x + this.legendBounds.width) - (bulletLegendOption.location.x + textPadding - this.legend.shapeWidth / 2); - bulletLegendOption.text = textTrim(+availwidth.toFixed(4), bulletLegendOption.text, this.legend.textStyle, this.chart.themeStyle.legendLabelFont); + bulletLegendOption.text = textTrim(+availwidth.toFixed(4), bulletLegendOption.text, this.legend.textStyle, this.chart.enableRtl, this.chart.themeStyle.legendLabelFont); } /** * To show the tooltip for the trimmed text in legend. diff --git a/controls/charts/src/chart/axis/axis.ts b/controls/charts/src/chart/axis/axis.ts index 2e08d39c2f..0f3a5a82f7 100644 --- a/controls/charts/src/chart/axis/axis.ts +++ b/controls/charts/src/chart/axis/axis.ts @@ -1079,7 +1079,7 @@ export class Axis extends ChildProperty { } if (this.rect.width || this.rect.height) { const length: number = isHorizontal ? this.rect.width : this.rect.height; - this.titleCollection = getTitle(this.title, this.titleStyle, length, chart.themeStyle.legendLabelFont); + this.titleCollection = getTitle(this.title, this.titleStyle, length, chart.enableRtl, chart.themeStyle.legendLabelFont); titleSize = (titleSize * this.titleCollection.length); } } @@ -1304,7 +1304,7 @@ export class Axis extends ChildProperty { for (let index: number = 0; index < label.text.length; index++) { result = textWrap( label.text[index as number], - this.rect.width / this.visibleLabels.length, this.labelStyle, null, null, chart.themeStyle.axisLabelFont); + this.rect.width / this.visibleLabels.length, this.labelStyle, chart.enableRtl, null, null, chart.themeStyle.axisLabelFont); if (result.length > 1) { for (let j: number = 0; j < result.length; j++) { str = result[j as number]; result1.push(str); @@ -1317,7 +1317,7 @@ export class Axis extends ChildProperty { } else { label.text = textWrap( label.text, - this.rect.width / this.visibleLabels.length, this.labelStyle, null, null, chart.themeStyle.axisLabelFont + this.rect.width / this.visibleLabels.length, this.labelStyle, chart.enableRtl, null, null, chart.themeStyle.axisLabelFont ); } // eslint-disable-next-line no-case-declarations diff --git a/controls/charts/src/chart/axis/cartesian-panel.ts b/controls/charts/src/chart/axis/cartesian-panel.ts index 5af556aa6b..4d0f9c59cb 100644 --- a/controls/charts/src/chart/axis/cartesian-panel.ts +++ b/controls/charts/src/chart/axis/cartesian-panel.ts @@ -1570,7 +1570,7 @@ export class CartesianAxisLayoutPanel { const rectPoint1: number = rotateAngle ? this.chart.availableSize.width - pointX : pointX; const rectPoint2: number = interSectPoint.y - axis.rect.y; const trimValue: number = Math.sqrt((rectPoint1 * rectPoint1) + (rectPoint2 * rectPoint2)); - options.text = textTrim(trimValue, label.text as string, label.labelStyle, chart.themeStyle.axisLabelFont); + options.text = textTrim(trimValue, label.text as string, label.labelStyle, chart.enableRtl, chart.themeStyle.axisLabelFont); } } } @@ -1753,7 +1753,7 @@ export class CartesianAxisLayoutPanel { */ private findAxisLabel(axis: Axis, label: string, width: number): string { return(axis.labelIntersectAction === 'Trim' ? - ((axis.angle % 360 === 0 && !axis.enableTrim) ? textTrim(width, label, axis.labelStyle, this.chart.themeStyle.axisLabelFont) : label) : label); + ((axis.angle % 360 === 0 && !axis.enableTrim) ? textTrim(width, label, axis.labelStyle, this.chart.enableRtl, this.chart.themeStyle.axisLabelFont) : label) : label); } /** diff --git a/controls/charts/src/chart/axis/multi-level-labels.ts b/controls/charts/src/chart/axis/multi-level-labels.ts index 25142286e1..d71450ee37 100644 --- a/controls/charts/src/chart/axis/multi-level-labels.ts +++ b/controls/charts/src/chart/axis/multi-level-labels.ts @@ -92,7 +92,7 @@ export class MultiLevelLabel { const len: number = axis.multiLevelLabels[index as number].categories.length; gap = ((i === 0 || i === len - 1) && axis.labelPlacement === 'OnTicks' && axis.edgeLabelPlacement === 'Shift') ? gap / 2 : gap; if ((labelSize.width > gap - padding) && gap > 0 && (multiLevel.overflow === 'Wrap') && !isVertical) { - height = (height * (textWrap(categoryLabel.text, gap - padding, multiLevel.textStyle, null, null, this.chart.themeStyle.axisLabelFont).length)); + height = (height * (textWrap(categoryLabel.text, gap - padding, multiLevel.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.axisLabelFont).length)); } multiLevelLabelsHeight[index as number] = !multiLevelLabelsHeight[index as number] ? height : ((multiLevelLabelsHeight[index as number] < height) ? height : multiLevelLabelsHeight[index as number]); @@ -217,7 +217,7 @@ export class MultiLevelLabel { } } options.text = (multiLevel.overflow === 'Wrap') ? - textWrap(argsData.text, gap, argsData.textStyle, null, null, this.chart.themeStyle.axisLabelFont) : textTrim(gap, argsData.text, argsData.textStyle, this.chart.themeStyle.axisLabelFont); + textWrap(argsData.text, gap, argsData.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.axisLabelFont) : textTrim(gap, argsData.text, argsData.textStyle, this.chart.enableRtl, this.chart.themeStyle.axisLabelFont); options.x = options.x - padding / 2; } textElement( @@ -392,10 +392,10 @@ export class MultiLevelLabel { textTrim( (categoryLabel.maximumTextWidth === null ? this.yAxisMultiLabelHeight[level as number] : categoryLabel.maximumTextWidth), - argsData.text, argsData.textStyle, this.chart.themeStyle.axisLabelFont) : options.text; + argsData.text, argsData.textStyle, this.chart.enableRtl, this.chart.themeStyle.axisLabelFont) : options.text; options.text = (multiLevel.overflow === 'Wrap') ? textWrap(argsData.text, (categoryLabel.maximumTextWidth === null ? this.yAxisMultiLabelHeight[level as number] : - categoryLabel.maximumTextWidth), argsData.textStyle, null, null, this.chart.themeStyle.axisLabelFont) : options.text; + categoryLabel.maximumTextWidth), argsData.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.axisLabelFont) : options.text; if (typeof options.text !== 'string' && options.text.length > 1) { options.y -= (padding * options.text.length / 2); } diff --git a/controls/charts/src/chart/axis/polar-radar-panel.ts b/controls/charts/src/chart/axis/polar-radar-panel.ts index ba55108d17..4479cba6d1 100644 --- a/controls/charts/src/chart/axis/polar-radar-panel.ts +++ b/controls/charts/src/chart/axis/polar-radar-panel.ts @@ -580,7 +580,7 @@ export class PolarRadarPanel extends LineBase { if (isIntersect) { const width: number = this.getAvailableSpaceToTrim(legendRect, labelRegions[i as number]); if (width > 0) { - labelText = textTrim(width, axis.visibleLabels[i as number].originalText, axis.labelStyle, this.chart.themeStyle.axisLabelFont); + labelText = textTrim(width, axis.visibleLabels[i as number].originalText, axis.labelStyle, this.chart.enableRtl, this.chart.themeStyle.axisLabelFont); isIntersect = false; } } diff --git a/controls/charts/src/chart/chart.ts b/controls/charts/src/chart/chart.ts index 668ff3d9df..b51089ef3c 100644 --- a/controls/charts/src/chart/chart.ts +++ b/controls/charts/src/chart/chart.ts @@ -2253,10 +2253,10 @@ export class Chart extends Component implements INotifyPropertyChan this.titleCollection = []; this.subTitleCollection = []; if (this.title) { - this.titleCollection = getTitle(this.title, this.titleStyle, width, this.themeStyle.chartTitleFont); + this.titleCollection = getTitle(this.title, this.titleStyle, width, this.enableRtl, this.themeStyle.chartTitleFont); titleHeight = (measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont).height * this.titleCollection.length) + padding; if (this.subTitle) { - this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, width, this.themeStyle.chartSubTitleFont); + this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, width, this.enableRtl, this.themeStyle.chartSubTitleFont); subTitleHeight = (measureText(this.subTitle, this.subTitleStyle, this.themeStyle.chartSubTitleFont).height * this.subTitleCollection.length) + padding; } diff --git a/controls/charts/src/chart/legend/legend.ts b/controls/charts/src/chart/legend/legend.ts index 2e46089601..34c461f39b 100644 --- a/controls/charts/src/chart/legend/legend.ts +++ b/controls/charts/src/chart/legend/legend.ts @@ -288,7 +288,7 @@ export class Legend extends BaseLegend { if (legendWidth > legend.maximumLabelWidth || legendWidth + rowWidth > legendBounds.width) { legendOption.textCollection = textWrap( legendOption.text, - (legend.maximumLabelWidth ? Math.min(legend.maximumLabelWidth, (legendBounds.width - textPadding)) : (legendBounds.width - textPadding)), legend.textStyle, null, null, this.chart.themeStyle.legendLabelFont + (legend.maximumLabelWidth ? Math.min(legend.maximumLabelWidth, (legendBounds.width - textPadding)) : (legendBounds.width - textPadding)), legend.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.legendLabelFont ); } else { legendOption.textCollection.push(legendOption.text); @@ -321,7 +321,7 @@ export class Legend extends BaseLegend { } availwidth = this.legend.maximumLabelWidth ? Math.min(this.legend.maximumLabelWidth, availwidth) : availwidth; if (this.legend.textOverflow === 'Ellipsis' && this.legend.textWrap === 'Normal') { - legendOption.text = textTrim(+availwidth.toFixed(4), legendOption.text, this.legend.textStyle, this.chart.themeStyle.legendLabelFont); + legendOption.text = textTrim(+availwidth.toFixed(4), legendOption.text, this.legend.textStyle, this.chart.enableRtl, this.chart.themeStyle.legendLabelFont); } } diff --git a/controls/charts/src/chart/user-interaction/crosshair.ts b/controls/charts/src/chart/user-interaction/crosshair.ts index e3b4e3b7f1..5c7992dfdf 100644 --- a/controls/charts/src/chart/user-interaction/crosshair.ts +++ b/controls/charts/src/chart/user-interaction/crosshair.ts @@ -284,7 +284,7 @@ export class Crosshair { null); } axisGroup.appendChild(pathElement); - options = new TextOption(this.elementID + '_axis_tooltip_text_' + k, 0, 0, 'start', text); + options = new TextOption(this.elementID + '_axis_tooltip_text_' + k, 0, 0, (chart.stockChart && chart.enableRtl) ? 'end' : 'start', text); const render: SvgRenderer | CanvasRenderer = chart.enableCanvas ? this.svgRenderer : chart.renderer; textElem = textElement( render, options, axis.crosshairTooltip.textStyle, diff --git a/controls/charts/src/chart3d/axis/axis.ts b/controls/charts/src/chart3d/axis/axis.ts index 5d71fa89d2..31b63d5820 100644 --- a/controls/charts/src/chart3d/axis/axis.ts +++ b/controls/charts/src/chart3d/axis/axis.ts @@ -714,7 +714,7 @@ export class Chart3DAxis extends ChildProperty { } if (this.rect.width || this.rect.height) { const length: number = isHorizontal ? this.rect.width : this.rect.height; - this.titleCollection = getTitle(this.title, this.titleStyle, length, chart.themeStyle.legendLabelFont); + this.titleCollection = getTitle(this.title, this.titleStyle, length, chart.enableRtl, chart.themeStyle.legendLabelFont); titleSize = (titleSize * this.titleCollection.length); } } @@ -848,7 +848,7 @@ export class Chart3DAxis extends ChildProperty { for (let index: number = 0; index < label.text.length; index++) { result = textWrap( label.text[index as number], - this.rect.width / this.visibleLabels.length, this.labelStyle, null, null, chart.themeStyle.axisLabelFont); + this.rect.width / this.visibleLabels.length, this.labelStyle, chart.enableRtl, null, null, chart.themeStyle.axisLabelFont); if (result.length > 1) { for (let j: number = 0; j < result.length; j++) { str = result[j as number]; result1.push(str); @@ -861,7 +861,7 @@ export class Chart3DAxis extends ChildProperty { } else { label.text = textWrap( label.text, - this.rect.width / this.visibleLabels.length, this.labelStyle, null, null, chart.themeStyle.axisLabelFont + this.rect.width / this.visibleLabels.length, this.labelStyle, chart.enableRtl, null, null, chart.themeStyle.axisLabelFont ); } // eslint-disable-next-line no-case-declarations diff --git a/controls/charts/src/chart3d/chart3D.ts b/controls/charts/src/chart3d/chart3D.ts index 07d56c0988..8b13f3c78c 100644 --- a/controls/charts/src/chart3d/chart3D.ts +++ b/controls/charts/src/chart3d/chart3D.ts @@ -1060,7 +1060,7 @@ export class Chart3D extends Component implements INotifyPropertyCh this.titleCollection = []; this.subTitleCollection = []; if (this.title) { - this.titleCollection = getTitle(this.title, this.titleStyle, (this.titleStyle.position === 'Left' || this.titleStyle.position === 'Right' ? height : width), this.themeStyle.chartTitleFont); + this.titleCollection = getTitle(this.title, this.titleStyle, (this.titleStyle.position === 'Left' || this.titleStyle.position === 'Right' ? height : width), this.enableRtl, this.themeStyle.chartTitleFont); titleHeight = (measureText(this.title, this.titleStyle, this.themeStyle.chartTitleFont).height * this.titleCollection.length) + padding; if (this.subTitle) { @@ -1069,7 +1069,7 @@ export class Chart3D extends Component implements INotifyPropertyCh titleWidth = measureText(titleText, this.titleStyle, this.themeStyle.chartSubTitleFont).width; maxWidth = titleWidth > maxWidth ? titleWidth : maxWidth; } - this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, maxWidth, this.themeStyle.chartSubTitleFont); + this.subTitleCollection = getTitle(this.subTitle, this.subTitleStyle, maxWidth, this.enableRtl, this.themeStyle.chartSubTitleFont); subTitleHeight = (measureText(this.subTitle, this.subTitleStyle, this.themeStyle.chartSubTitleFont).height * this.subTitleCollection.length) + padding; diff --git a/controls/charts/src/chart3d/legend/legend.ts b/controls/charts/src/chart3d/legend/legend.ts index 0c5b391146..a934ce5a8b 100644 --- a/controls/charts/src/chart3d/legend/legend.ts +++ b/controls/charts/src/chart3d/legend/legend.ts @@ -588,7 +588,7 @@ export class Legend3D extends BaseLegend { legendOption.textCollection = textWrap( legendOption.text, (legend.maximumLabelWidth ? Math.min(legend.maximumLabelWidth, (legendBounds.width - textPadding)) : - (legendBounds.width - textPadding)), legend.textStyle, null, null, this.chart.themeStyle.legendLabelFont + (legendBounds.width - textPadding)), legend.textStyle, this.chart.enableRtl, null, null, this.chart.themeStyle.legendLabelFont ); } else { legendOption.textCollection.push(legendOption.text); @@ -640,7 +640,7 @@ export class Legend3D extends BaseLegend { availwidth = this.legend.maximumLabelWidth ? Math.min(this.legend.maximumLabelWidth, availwidth) : availwidth; if (this.legend.textOverflow === 'Ellipsis' && this.legend.textWrap === 'Normal') { legendOption.text = textTrim(+availwidth.toFixed(4), legendOption.text, - this.legend.textStyle, this.chart.themeStyle.legendLabelFont); + this.legend.textStyle, this.chart.enableRtl, this.chart.themeStyle.legendLabelFont); } } diff --git a/controls/charts/src/common/legend/legend.ts b/controls/charts/src/common/legend/legend.ts index 025ca64a65..df6d5e4c36 100644 --- a/controls/charts/src/common/legend/legend.ts +++ b/controls/charts/src/common/legend/legend.ts @@ -1012,9 +1012,9 @@ export class BaseLegend { this.isTop = legend.titlePosition === 'Top'; const padding: number = legend.titleStyle.textOverflow === 'Trim' ? 2 * legend.padding : 0; if (this.isTop || this.isVertical) { - this.legendTitleCollections = getTitle(legend.title, legend.titleStyle, (legendBounds.width - padding), this.chart.themeStyle.legendTitleFont); + this.legendTitleCollections = getTitle(legend.title, legend.titleStyle, (legendBounds.width - padding), this.chart.enableRtl, this.chart.themeStyle.legendTitleFont); } else { - this.legendTitleCollections[0] = textTrim(legend.maximumTitleWidth, legend.title, legend.titleStyle, this.chart.themeStyle.legendTitleFont); + this.legendTitleCollections[0] = textTrim(legend.maximumTitleWidth, legend.title, legend.titleStyle, this.chart.enableRtl, this.chart.themeStyle.legendTitleFont); } const text: string = this.isTop ? legend.title : this.legendTitleCollections[0]; this.legendTitleSize = measureText(text, legend.titleStyle, this.chart.themeStyle.legendTitleFont); @@ -1225,7 +1225,8 @@ export class BaseLegend { textOptions.x = legendOption.location.x - (legend.shapeWidth / 2); } else if (this.isRtlEnable) { - textOptions.x = legendOption.location.x - (measureText(legendOption.text, legend.textStyle, this.chart.themeStyle.legendLabelFont).width + legend.shapeWidth / 2 + legend.shapePadding); + const textWidth: number = measureText(legendOption.text, legend.textStyle, this.chart.themeStyle.legendLabelFont).width; + textOptions.x = legendOption.location.x - ((legendOption.textCollection.length > 1 ? textWidth / legendOption.textCollection.length : textWidth) + legend.shapeWidth / 2 + legend.shapePadding); } else { textOptions.x = legendOption.location.x + (legend.shapeWidth / 2) + legend.shapePadding; @@ -1452,7 +1453,7 @@ export class BaseLegend { this.legendTranslateGroup.setAttribute('transform', translate); } if (!(this.chart as Chart).enableCanvas && (legend.enablePages || this.isBulletChartControl)) { - pagingText.textContent = !this.isRtlEnable ? (pageNumber) + '/' + this.totalPages : this.totalPages + '/' + pageNumber; + pagingText.textContent = (pageNumber) + '/' + this.totalPages; } this.currentPage = pageNumber; return size; @@ -1473,7 +1474,7 @@ export class BaseLegend { const isCanvas: boolean = this.isStockChartControl ? false : (this.chart as Chart).enableCanvas; const pageText: Element = (legend.enablePages || this.isBulletChartControl) ? document.getElementById(this.legendID + '_pagenumber') : null; - const page: number = (legend.enablePages || this.isBulletChartControl) ? parseInt(pageText.textContent.split('/')[!this.isRtlEnable ? 0 : 1], 10) : + const page: number = (legend.enablePages || this.isBulletChartControl) ? parseInt(pageText.textContent.split('/')[0], 10) : this.currentPage; if (pageUp && page > 1) { this.translatePage(isCanvas, pageText, (page - 2), (page - 1), legend); diff --git a/controls/charts/src/common/utils/helper.ts b/controls/charts/src/common/utils/helper.ts index fad5da319c..8cf22adb4e 100644 --- a/controls/charts/src/common/utils/helper.ts +++ b/controls/charts/src/common/utils/helper.ts @@ -966,7 +966,7 @@ export function triggerLabelRender( const isLineBreakLabels: boolean = argsData.text.indexOf('
') !== -1; const text: string | string[] = (axis.enableTrim) ? (isLineBreakLabels ? lineBreakLabelTrim(axis.maximumLabelWidth, argsData.text, axis.labelStyle, chart.themeStyle.axisLabelFont) : - textTrim(axis.maximumLabelWidth, argsData.text, axis.labelStyle, chart.themeStyle.axisLabelFont)) : argsData.text; + textTrim(axis.maximumLabelWidth, argsData.text, axis.labelStyle, chart.enableRtl, chart.themeStyle.axisLabelFont)) : argsData.text; axis.visibleLabels.push(new VisibleLabels(text, argsData.value, argsData.labelStyle, argsData.text)); } } @@ -1727,13 +1727,13 @@ export function calculateLegendShapes(location: ChartLocation, size: Size, shape return { renderOption: options }; } /** @private */ -export function textTrim(maxWidth: number, text: string, font: FontModel, themeFontStyle?: FontModel): string { +export function textTrim(maxWidth: number, text: string, font: FontModel, isRtlEnabled: boolean, themeFontStyle?: FontModel): string { let label: string = text; let size: number = measureText(text, font, themeFontStyle).width; if (size > maxWidth) { const textLength: number = text.length; for (let i: number = textLength - 1; i >= 0; --i) { - label = text.substring(0, i) + '...'; + label = isRtlEnabled ? '...' + text.substring(0, i) : text.substring(0, i) + '...'; size = measureText(label, font, themeFontStyle).width; if (size <= maxWidth) { return label; @@ -1981,14 +1981,14 @@ export function createSvg(chart: Chart | AccumulationChart | RangeNavigator | Ch * @param {FontModel} style style of the title * @param {number} width width of the title */ -export function getTitle(title: string, style: FontModel, width: number, themeFontStyle?: FontModel): string[] { +export function getTitle(title: string, style: FontModel, width: number, isRtlEnabled: boolean, themeFontStyle?: FontModel): string[] { let titleCollection: string[] = []; switch (style.textOverflow) { case 'Wrap': - titleCollection = textWrap(title, width, style, null, null, themeFontStyle); + titleCollection = textWrap(title, width, style, isRtlEnabled, null, null, themeFontStyle); break; case 'Trim': - titleCollection.push(textTrim(width, title, style, themeFontStyle)); + titleCollection.push(textTrim(width, title, style, isRtlEnabled, themeFontStyle)); break; default: titleCollection.push(title); @@ -2015,7 +2015,7 @@ export function titlePositionX(rect: Rect, titleStyle: FontModel): number { /** * Method to find new text and element size based on textOverflow. */ -export function textWrap(currentLabel: string, maximumWidth: number, font: FontModel, wrapAnyWhere ?: boolean, clip ?: boolean, themeFontStyle?: FontModel): string[] { +export function textWrap(currentLabel: string, maximumWidth: number, font: FontModel, isRtlEnabled : boolean, wrapAnyWhere ?: boolean, clip ?: boolean, themeFontStyle?: FontModel): string[] { if (wrapAnyWhere) { return (textWrapAnyWhere(currentLabel, maximumWidth, font, themeFontStyle)); } @@ -2030,15 +2030,15 @@ export function textWrap(currentLabel: string, maximumWidth: number, font: FontM label = label.concat((label === '' ? '' : ' ') + text); } else { if (label !== '') { - labelCollection.push(clip ? label : textTrim(maximumWidth, label, font, themeFontStyle)); + labelCollection.push(clip ? label : textTrim(maximumWidth, label, font, isRtlEnabled, themeFontStyle)); label = text; } else { - labelCollection.push(clip ? text : textTrim(maximumWidth, text, font, themeFontStyle)); + labelCollection.push(clip ? text : textTrim(maximumWidth, text, font, isRtlEnabled, themeFontStyle)); text = ''; } } if (label && i === len - 1) { - labelCollection.push(clip ? label : textTrim(maximumWidth, label, font, themeFontStyle)); + labelCollection.push(clip ? label : textTrim(maximumWidth, label, font, isRtlEnabled, themeFontStyle)); } } return labelCollection; diff --git a/controls/charts/src/stock-chart/legend/legend.ts b/controls/charts/src/stock-chart/legend/legend.ts index 8ee050ab52..d0f109544a 100644 --- a/controls/charts/src/stock-chart/legend/legend.ts +++ b/controls/charts/src/stock-chart/legend/legend.ts @@ -528,7 +528,7 @@ export class StockLegend extends BaseLegend { } const availwidth: number = (this.legendBounds.width + this.legendBounds.x) - (legendOptions.location.x + textPadding - this.itemPadding - this.legend.shapeWidth / 2); - legendOptions.text = textTrim(+availwidth.toFixed(4), legendOptions.text, this.legend.textStyle, this.chart.themeStyle.legendLabelFont); + legendOptions.text = textTrim(+availwidth.toFixed(4), legendOptions.text, this.legend.textStyle, this.chart.enableRtl, this.chart.themeStyle.legendLabelFont); } /** * @param index diff --git a/controls/charts/src/stock-chart/renderer/cartesian-chart.ts b/controls/charts/src/stock-chart/renderer/cartesian-chart.ts index 8e1da6e993..003debb38f 100644 --- a/controls/charts/src/stock-chart/renderer/cartesian-chart.ts +++ b/controls/charts/src/stock-chart/renderer/cartesian-chart.ts @@ -131,6 +131,7 @@ export class CartesianChart { annotations: stockChart.annotations, theme: stockChart.theme, legendSettings: { visible: false}, + enableRtl: stockChart.enableRtl, zoomComplete: (args: IZoomCompleteEventArgs) => { if (args.axis.valueType.indexOf('DateTime') !== -1 && stockChart.rangeNavigator) { this.stockChart.zoomChange = true; diff --git a/controls/charts/src/stock-chart/renderer/range-selector.ts b/controls/charts/src/stock-chart/renderer/range-selector.ts index 613fea53cd..6923b37147 100644 --- a/controls/charts/src/stock-chart/renderer/range-selector.ts +++ b/controls/charts/src/stock-chart/renderer/range-selector.ts @@ -58,6 +58,7 @@ export class RangeSelector { labelPosition:'Inside', dataSource: stockChart.dataSource, intervalType : stockChart.primaryXAxis.intervalType, + enableRtl: stockChart.enableRtl, changed: (args: IChangedEventArgs) => { const arg: IRangeChangeEventArgs = { name: 'rangeChange', diff --git a/controls/diagrams/CHANGELOG.md b/controls/diagrams/CHANGELOG.md index cade145863..43ef87b0dc 100644 --- a/controls/diagrams/CHANGELOG.md +++ b/controls/diagrams/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Diagram + +#### Bug Fixes + +- `#I527482` - Now, overview updated properly while zoom out and move nodes outside viewport. + ## 24.1.45 (2024-01-09) ### Diagram @@ -11,7 +19,8 @@ - `#F185764` - Now, removing bpmn text annotation dynamically is working properly. - `#I526870` - Resolved snap to lines issue when dragging shapes from palette to swimlane. - `#I526172` - The node now remains in the diagram when the ungroup action is performed. -- `#FB48313` - The background color is now visible on symbol hover after rapid expand and collapse in the palette +- `#FB48313` - The background color is now visible on symbol hover after rapid expand and collapse in the palette. +- `#I531978` - Now, connectors rendered properly with line routing and line distribution enabled during doLayout process. ## 24.1.43 (2023-12-27) diff --git a/controls/diagrams/package.json b/controls/diagrams/package.json index c8bc918bcc..15fbba4c5c 100644 --- a/controls/diagrams/package.json +++ b/controls/diagrams/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-diagrams", - "version": "24.1.43", + "version": "24.1.45", "description": "Feature-rich diagram control to create diagrams like flow charts, organizational charts, mind maps, and BPMN diagrams. Its rich feature set includes built-in shapes, editing, serializing, exporting, printing, overview, data binding, and automatic layouts.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/diagrams/spec/diagram/layout/complex-hierarchical.spec.ts b/controls/diagrams/spec/diagram/layout/complex-hierarchical.spec.ts index f2c42432b9..0a3965d94c 100644 --- a/controls/diagrams/spec/diagram/layout/complex-hierarchical.spec.ts +++ b/controls/diagrams/spec/diagram/layout/complex-hierarchical.spec.ts @@ -597,7 +597,7 @@ describe('Diagram Control', () => { expect(pathElement1.children[0].getAttribute("d") === "M762.75,11 L762.75,15.5 Q762.75,20,755.75,20 L743.5,20 Q736.5,20,736.5,13 L736.5,7 Q736.5,0,729.5,0 L7,0 Q0,0,0.11,7 L0.49,30.5 ").toBe(true); var connector11 = diagram.connectors[11].id; var pathElement11 = document.getElementById(connector11 + "_path_groupElement"); - expect(pathElement11.children[0].getAttribute("d") === "M5,0 L5,5 Q5,10,2.5,10 Q0,10,0,14.75 L0,19.5 ").toBe(true); + expect(pathElement11.children[0].getAttribute("d") === "M5,0 L5,5 Q5,10,2.5,10 Q0,10,0,14.75 L0,19.5 " || pathElement11.children[0].getAttribute("d") === "M5,0 L5,4.5 Q5,9,2.5,9 Q0,9,0,14.25 L0,19.5 ").toBe(true); var connector26 = diagram.connectors[26].id; var pathElement26 = document.getElementById(connector26 + "_path_groupElement"); expect(pathElement26.children[0].getAttribute("d") === "M0,10 L0,14.83 Q0,19.67,7,19.67 L28,19.67 Q35,19.67,35,12.67 L35,7 Q35,0,42,0 L118,0 Q125,0,125.12,7 L125.49,29.5 ").toBe(true); diff --git a/controls/diagrams/spec/overview/overview.spec.ts b/controls/diagrams/spec/overview/overview.spec.ts index 924d78ce72..6cbd90e871 100644 --- a/controls/diagrams/spec/overview/overview.spec.ts +++ b/controls/diagrams/spec/overview/overview.spec.ts @@ -119,10 +119,11 @@ describe('Overview', () => { mouseEvents.mouseDownEvent(overviewelement, 1236, 170); mouseEvents.mouseMoveEvent(overviewelement, 1186, 160); overview[mouseup]({ target: target, type: mouseDown }); - expect((diagram.scroller.currentZoom.toFixed(4) === '1.3889')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) === -233) && ( - (Math.round(diagram.scroller.verticalOffset) <= -45) || - (Math.round(diagram.scroller.verticalOffset) >= -47))).toBe(true); + console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ) + expect((diagram.scroller.currentZoom.toFixed(4) === '1.4493')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) === -243) && ( + (Math.round(diagram.scroller.verticalOffset) <= -48) || + (Math.round(diagram.scroller.verticalOffset) >= -49))).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer bottom right with y greater than X', (done: Function) => { @@ -135,9 +136,10 @@ describe('Overview', () => { mouseEvents.mouseDownEvent(overviewelement, 1236, 170); mouseEvents.mouseMoveEvent(overviewelement, 1226, 150); overview[mouseup]({ target: target, type: mouseDown }); - expect((diagram.scroller.currentZoom.toFixed(4) === '1.7077')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -287 && Math.round(diagram.scroller.horizontalOffset) <= -286) && - (Math.round(diagram.scroller.verticalOffset) >= -57 && Math.round(diagram.scroller.verticalOffset) <= -55 + console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ) + expect((diagram.scroller.currentZoom.toFixed(4) === '1.8695')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -314 && Math.round(diagram.scroller.horizontalOffset) <= -313) && + (Math.round(diagram.scroller.verticalOffset) >= -62 && Math.round(diagram.scroller.verticalOffset) <= -61 )).toBe(true); done(); }); @@ -151,10 +153,11 @@ describe('Overview', () => { mouseEvents.mouseDownEvent(overviewelement, 1186, 160); mouseEvents.mouseMoveEvent(overviewelement, 1236, 170); overview[mouseup]({ target: target, type: mouseDown }); - expect((diagram.scroller.currentZoom.toFixed(4) === '1.5319')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -226 && Math.round(diagram.scroller.horizontalOffset) <= -225) && - (Math.round(diagram.scroller.verticalOffset) <= -49 - && Math.round(diagram.scroller.verticalOffset) >= -51)).toBe(true); + console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ) + expect((diagram.scroller.currentZoom.toFixed(4) === '1.7063')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -252 && Math.round(diagram.scroller.horizontalOffset) <= -251) && + (Math.round(diagram.scroller.verticalOffset) <= -55 + && Math.round(diagram.scroller.verticalOffset) >= -56)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer bottom Left', (done: Function) => { @@ -168,11 +171,11 @@ describe('Overview', () => { mouseEvents.mouseMoveEvent(overviewelement, 1056, 160); overview[mouseup]({ target: target, type: mouseDown }); - - expect((diagram.scroller.currentZoom.toFixed(4) === '2.6824')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) <= -845 && - Math.round(diagram.scroller.horizontalOffset) >= -847) && (Math.round(diagram.scroller.verticalOffset) <= -86 && - Math.round(diagram.scroller.verticalOffset) >= -90)).toBe(true); + console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ) + expect((diagram.scroller.currentZoom.toFixed(4) === '3.4665')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) <= -1092 && + Math.round(diagram.scroller.horizontalOffset) >= -1093 ) && (Math.round(diagram.scroller.verticalOffset) <= -110 && + Math.round(diagram.scroller.verticalOffset) >= -113)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer bottom Left with y greater than X', (done: Function) => { @@ -185,9 +188,10 @@ describe('Overview', () => { mouseEvents.mouseDownEvent(overviewelement, 1006, 170); mouseEvents.mouseMoveEvent(overviewelement, 1016, 150); overview[mouseup]({ target: target, type: mouseDown }); - expect((diagram.scroller.currentZoom.toFixed(4) === '4.1946')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -1662 && Math.round(diagram.scroller.horizontalOffset) <= -1658) && - (Math.round(diagram.scroller.verticalOffset) >= -141 && Math.round(diagram.scroller.verticalOffset) <= -133)).toBe(true); + console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ) + expect((diagram.scroller.currentZoom.toFixed(4) === '6.8768')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -2722 && Math.round(diagram.scroller.horizontalOffset) <= -2721) && + (Math.round(diagram.scroller.verticalOffset) >= -221 && Math.round(diagram.scroller.verticalOffset) <= -216 )).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Left', (done: Function) => { @@ -199,9 +203,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1056, 160); mouseEvents.mouseMoveEvent(overviewelement, 1106, 170); - overview[mouseup]({ target: target, type: mouseDown }); expect((diagram.scroller.currentZoom.toFixed(4) === '4.1946')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -1662 && Math.round(diagram.scroller.horizontalOffset) <= -1658) && - (Math.round(diagram.scroller.verticalOffset) >= -139 && Math.round(diagram.scroller.verticalOffset) <= -132)).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) === '6.8768')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -2721 && Math.round(diagram.scroller.horizontalOffset) <= -2720 ) && + (Math.round(diagram.scroller.verticalOffset) >= -221 && Math.round(diagram.scroller.verticalOffset) <= -216 )).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Top left', (done: Function) => { @@ -213,9 +217,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1106, 170); mouseEvents.mouseMoveEvent(overviewelement, 1056, 160); - overview[mouseup]({ target: target, type: mouseDown }); expect((diagram.scroller.currentZoom.toFixed(3) === '1.929')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -440 && Math.round(diagram.scroller.horizontalOffset) <= -438) && - (Math.round(diagram.scroller.verticalOffset) >= 205) && Math.round(diagram.scroller.verticalOffset) <= 209).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) === '2.3750')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -541 && Math.round(diagram.scroller.horizontalOffset) <= -540) && + (Math.round(diagram.scroller.verticalOffset) >= 257) && Math.round(diagram.scroller.verticalOffset) <= 260).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Top left Y greater than x', (done: Function) => { @@ -227,9 +231,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1106, 170); mouseEvents.mouseMoveEvent(overviewelement, 1096, 140); - overview[mouseup]({ target: target, type: mouseDown }); expect(diagram.scroller.currentZoom.toFixed(3) == '1.389').toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) <= -147 && Math.round(diagram.scroller.horizontalOffset) >= -149) && - (Math.round(diagram.scroller.verticalOffset) >= 288) && (Math.round(diagram.scroller.verticalOffset) <= 291)).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect(diagram.scroller.currentZoom.toFixed(4) == '1.6393').toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) <= -174 && Math.round(diagram.scroller.horizontalOffset) >= -177) && + (Math.round(diagram.scroller.verticalOffset) >= 342) && (Math.round(diagram.scroller.verticalOffset) <= 344)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Top', (done: Function) => { @@ -241,9 +245,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1056, 170); mouseEvents.mouseMoveEvent(overviewelement, 1156, 190); - overview[mouseup]({ target: target, type: mouseDown }); expect((diagram.scroller.currentZoom.toFixed(4) == '1.7077')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -252 && Math.round(diagram.scroller.horizontalOffset) <= -250) && - (Math.round(diagram.scroller.verticalOffset) >= 239 && Math.round(diagram.scroller.verticalOffset) <= 243)).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) == '2.1866')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -321 && Math.round(diagram.scroller.horizontalOffset) <= -320) && + (Math.round(diagram.scroller.verticalOffset) >= 310 && Math.round(diagram.scroller.verticalOffset) <= 312)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Top right', (done: Function) => { @@ -255,9 +259,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1156, 190); mouseEvents.mouseMoveEvent(overviewelement, 1176, 170); - overview[mouseup]({ target: target, type: mouseDown }); expect((diagram.scroller.currentZoom.toFixed(4) === '1.4335')).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -211 && Math.round(diagram.scroller.horizontalOffset) <= -209) && - (Math.round(diagram.scroller.verticalOffset) >= 281 && Math.round(diagram.scroller.verticalOffset) <= 284)).toBe(true); done(); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) === '1.7998')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -265 && Math.round(diagram.scroller.horizontalOffset) <= -262) && + (Math.round(diagram.scroller.verticalOffset) >= 356 && Math.round(diagram.scroller.verticalOffset) <= 358)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Top right with Y greater than X', (done: Function) => { let overviewelement: HTMLElement = document.getElementById(overview.element.id); @@ -268,9 +272,9 @@ describe('Overview', () => { overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 1156, 190); mouseEvents.mouseMoveEvent(overviewelement, 1166, 170); - overview[mouseup]({ target: target, type: mouseDown }); expect(diagram.scroller.currentZoom.toFixed(4) === '1.2019').toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -177 && Math.round(diagram.scroller.horizontalOffset) <= -175) && - (Math.round(diagram.scroller.verticalOffset) >= 316 && Math.round(diagram.scroller.verticalOffset) <= 319)).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect(diagram.scroller.currentZoom.toFixed(4) === '1.4851').toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -218 && Math.round(diagram.scroller.horizontalOffset) <= -217) && + (Math.round(diagram.scroller.verticalOffset) >= 393 && Math.round(diagram.scroller.verticalOffset) <= 395)).toBe(true); done(); }); it('Click on the overview rect - Scale with resizer Right', (done: Function) => { @@ -283,9 +287,9 @@ describe('Overview', () => { mouseEvents.mouseDownEvent(overviewelement, 1176, 170); mouseEvents.mouseMoveEvent(overviewelement, 1226, 160); mouseEvents.mouseUpEvent(overviewelement, 1226, 160); - overview[mouseup]({ target: target, type: mouseDown }); expect((diagram.scroller.currentZoom === 0.8992806545147269)).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) >= -132 && Math.round(diagram.scroller.horizontalOffset) <= -131) && - (Math.round(diagram.scroller.verticalOffset) >= 300 && Math.round(diagram.scroller.verticalOffset) <= 302)).toBe(true); + overview[mouseup]({ target: target, type: mouseDown }); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) === '1.0717')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) >= -158 && Math.round(diagram.scroller.horizontalOffset) <= -157) && + (Math.round(diagram.scroller.verticalOffset) >= 358 && Math.round(diagram.scroller.verticalOffset) <= 360)).toBe(true); done(); }); it('Click on the overview rect - Pan', (done: Function) => { @@ -298,9 +302,9 @@ describe('Overview', () => { overview[mouseup]({ target: target, type: mouseDown }); overview[mouseDown]({ target: target, type: mouseDown }); mouseEvents.mouseDownEvent(overviewelement, 100, 650); - mouseEvents.mouseUpEvent(target, 100, 650); expect((diagram.scroller.currentZoom === 0.8992806545147269)).toBe(true); - expect((Math.round(diagram.scroller.horizontalOffset) === 22) && - (Math.round(diagram.scroller.verticalOffset) === -179)).toBe(true); + mouseEvents.mouseUpEvent(target, 100, 650); console.log('zoom '+ diagram.scroller.currentZoom.toFixed(4) + ' hOffset ' + Math.round(diagram.scroller.horizontalOffset) + ' vOffset ' + Math.round(diagram.scroller.verticalOffset) ); expect((diagram.scroller.currentZoom.toFixed(4) === '1.0717')).toBe(true); + expect((Math.round(diagram.scroller.horizontalOffset) === -40) && + (Math.round(diagram.scroller.verticalOffset) === -277)).toBe(true); done(); }); it('Click on the overview rect - Draw', (done: Function) => { @@ -1960,4 +1964,75 @@ describe('Overview', () => { done(); }); }); + + describe('863516-Overview is not synced with diagram content while zoom-out the diagram.', () => { + let diagram: Diagram; + let overview: Overview; + let ele: HTMLElement; + let ove: HTMLElement; + beforeAll((): void => { + ele = createElement('div', { id: 'diagramZoom', styles: "width:74%;height: 500px; float:left" }); + document.body.appendChild(ele); + ove = createElement('div', { id: 'overview', styles: "width:25%;height:200px;float:left; border-color:lightgray;border-style:solid;" }); + document.body.appendChild(ove); + + let nodes:NodeModel[] = [ + { id: 'node1', width: 100, height: 100, offsetX: -200, offsetY: 100,style:{fill:'red'} }, + { id: 'node2', width: 100, height: 100, offsetX: 600, offsetY: 100,style:{fill:'yellow'} }, + { id: 'node3', width: 100, height: 100, offsetX: 1400, offsetY: 100,style:{fill:'green'} }, + { id: 'node4', width: 100, height: 100, offsetX: -200, offsetY: 400,style:{fill:'pink'} }, + { id: 'node5', width: 100, height: 100, offsetX: 600, offsetY: 400,style:{fill:'orange'} }, + { id: 'node6', width: 100, height: 100, offsetX: 1400, offsetY: 400 ,style:{fill:'blue'} }, + { id: 'node7', width: 100, height: 100, offsetX: -200, offsetY: 800,style:{fill:'brown'} }, + { id: 'node8', width: 100, height: 100, offsetX: 600, offsetY: 800 ,style:{fill:'white'} }, + { id: 'node9', width: 100, height: 100, offsetX: 1400, offsetY: 800,style:{fill:'skyblue'} }, + + ]; + diagram = new Diagram({ + width: '100%', + height: '700px', + rulerSettings:{showRulers:true}, + pageSettings:{multiplePage:true,width:200,height:200,showPageBreaks:true}, + nodes:nodes, + getNodeDefaults: (obj: NodeModel, diagram: Diagram) => { + obj.height = 100; + return obj; + }, getConnectorDefaults: (connector: ConnectorModel, diagram: Diagram) => { + connector.targetDecorator.shape = 'None'; + connector.type = 'Orthogonal'; + return connector; + }, + }); + diagram.appendTo('#diagramZoom'); + + let overview: Overview = new Overview({ + sourceID: 'diagramZoom', + }); + overview.appendTo('#overview'); + + + }); + + afterAll((): void => { + overview.destroy(); + diagram.destroy(); + ele.remove(); + ove.remove(); + }); + + it('Zoom-out diagram and checking the overview rect size', (done: Function) => { + let zoomOptions:ZoomOptions = {zoomFactor:1,type:'ZoomOut',focusPoint:{x:0.5,y:0.5}}; + diagram.zoomTo(zoomOptions); + diagram.zoomTo(zoomOptions); + let overviewRect = document.getElementById('overview_canvasoverviewrect'); + let preHeight = overviewRect.getAttribute('height'); + diagram.select([diagram.nodes[8]]); + diagram.drag(diagram.selectedItems.nodes[0],0,300); + let curHeight = overviewRect.getAttribute('height'); + expect(preHeight !== curHeight); + done(); + }); + + }); + }); \ No newline at end of file diff --git a/controls/diagrams/src/diagram/diagram.ts b/controls/diagrams/src/diagram/diagram.ts index 3adef50841..0eaa38879c 100644 --- a/controls/diagrams/src/diagram/diagram.ts +++ b/controls/diagrams/src/diagram/diagram.ts @@ -5695,14 +5695,16 @@ export class Diagram extends Component implements INotifyPropertyCh const canEnableRouting: boolean = this.layout.enableRouting && this.layout.type === 'ComplexHierarchicalTree'; const viewPort: PointModel = { x: this.scroller.viewPortWidth, y: this.scroller.viewPortHeight }; if (this.layout.type !== 'None') { - if ((canEnableRouting && this.lineDistributionModule) || ((this.layout as Layout).connectionPointOrigin === 'DifferentPoint' && this.lineDistributionModule && canDoOverlap) || (this.layout.arrangement === 'Linear' && this.lineDistributionModule)) { - this.lineDistributionModule.initLineDistribution((this.layout as Layout), this); - } + //Bug 862601: Connectors are not rendered properly with lineRouting and lineDistribution enables during doLayout process. + //Removed initLineDistribution method call here and added it below after the complex hierarchical tree doLayout process. if (this.organizationalChartModule) { layout = this.organizationalChartModule.updateLayout( nodes, this.nameTable, this.layout as Layout, viewPort, this.dataSourceSettings.id, this.diagramActions); update = true; + if (this.canDistribute(canEnableRouting, canDoOverlap)) { + this.lineDistributionModule.initLineDistribution(this.layout as Layout, this); + } if (this.layoutAnimateModule && layout.rootNode && !this.diagramActions) { this.updateNodeExpand(layout.rootNode as Node, layout.rootNode.isExpanded); } @@ -5732,12 +5734,26 @@ export class Diagram extends Component implements INotifyPropertyCh this.symmetricalLayoutModule, this.nameTable, this.layout as Layout, viewPort); update = true; } else if (this.complexHierarchicalTreeModule) { + //Bug 862601: Connectors are not rendered properly with lineRouting and lineDistribution enables during doLayout process. + //As the initLineDistribution method call removed from above and added below doLayout, we need to set diagram value and clear obstacle collection + // of connectors. + if(this.canDistribute(canEnableRouting,canDoOverlap)){ + (this.lineDistributionModule as any).diagram = this; + const obstacleCollection: string = 'obstaclePointCollection'; + this.connectors.forEach((connector) => { + connector[`${obstacleCollection}`] = []; + }); + } const nodes: INode[] = this.complexHierarchicalTreeModule.getLayoutNodesCollection(this.nodes as INode[]); if (nodes.length > 0) { // eslint-disable-next-line max-len this.complexHierarchicalTreeModule.doLayout(nodes, this.nameTable, this.layout as Layout, viewPort, this.lineDistributionModule); } update = true; + //initLineDistribution method call after doLayout. + if(this.canDistribute(canEnableRouting,canDoOverlap)){ + this.lineDistributionModule.initLineDistribution((this.layout as Layout), this); + } } if (update) { this.preventDiagramUpdate = true; @@ -5809,6 +5825,14 @@ export class Diagram extends Component implements INotifyPropertyCh } return ((this.blazorActions & BlazorAction.expandNode) ? layout : isBlazor() ? null : true); } + //Checks if line distribution is enabled. + private canDistribute(canEnableRouting: boolean,canDoOverlap: boolean){ + if ((canEnableRouting && this.lineDistributionModule) || ((this.layout as Layout).connectionPointOrigin === 'DifferentPoint' && this.lineDistributionModule && canDoOverlap) || (this.layout.arrangement === 'Linear' && this.lineDistributionModule)) { + return true; + }else{ + return false; + } + }; /* tslint:enable */ /** * Serializes the diagram control as a string. @@ -10516,7 +10540,7 @@ export class Diagram extends Component implements INotifyPropertyCh (this.constraints & DiagramConstraints.LineRouting) && !(this.diagramActions & DiagramAction.ToolAction)) { this.lineRoutingModule.renderVirtualRegion(this, true); // EJ2-65876 - Exception occurs on line routing injection module - if (actualObject.sourceID !== actualObject.targetID && actualObject.segments.length>1){ + if (actualObject.sourceID !== actualObject.targetID && actualObject.segments.length>1 && this.layout.type !== 'ComplexHierarchicalTree'){ //EJ2-69573 - Excecption occurs when calling doLayout method with the lineRouting module this.lineRoutingModule.refreshConnectorSegments(this, actualObject, false); } diff --git a/controls/diagrams/src/diagram/interaction/command-manager.ts b/controls/diagrams/src/diagram/interaction/command-manager.ts index 2777e1fa78..1a0e594a6e 100644 --- a/controls/diagrams/src/diagram/interaction/command-manager.ts +++ b/controls/diagrams/src/diagram/interaction/command-manager.ts @@ -5842,7 +5842,7 @@ Remove terinal segment in initial } // EJ2-65876 - Exception occurs on line routing injection module //Bug 850195: Exception occurs due to line routing constraints enabled - if(connector.sourceID && connector.targetID && connector.sourceID != connector.targetID){ + if(connector.sourceID && connector.targetID && connector.sourceID != connector.targetID && this.diagram.layout.type !== 'ComplexHierarchicalTree'){ //EJ2-69573 - Excecption occurs when calling doLayout method with the lineRouting module let sourceNode:NodeModel= this.diagram.getObject(connector.sourceID); let targetNode:NodeModel = this.diagram.getObject(connector.targetID); diff --git a/controls/diagrams/src/diagram/interaction/event-handlers.ts b/controls/diagrams/src/diagram/interaction/event-handlers.ts index 0344b6e7a6..54c140960a 100644 --- a/controls/diagrams/src/diagram/interaction/event-handlers.ts +++ b/controls/diagrams/src/diagram/interaction/event-handlers.ts @@ -727,7 +727,20 @@ export class DiagramEventHandler { if (this.action === 'Drag' || this.action === 'Rotate') { this.diagram.diagramActions = this.diagram.diagramActions | DiagramAction.Interactions; } + //Bug 863516: Overview is not synced with diagram content while zoom-out the diagram. + //Checking page bounds before and after dragging node, and updating the overview rect if the page bounds modified after the drag. + const preDragBounds = this.diagram.scroller.getPageBounds(); this.mouseMoveExtend(e, obj); + const postDragBounds = this.diagram.scroller.getPageBounds(); + if(obj && (preDragBounds.width !== postDragBounds.width || + preDragBounds.height !== postDragBounds.height || + preDragBounds.x !== postDragBounds.x || + preDragBounds.y !== postDragBounds.y)){ + if(this.diagram.views && (this.diagram.views as any).overview){ + let overview = (this.diagram.views as any).overview; + overview.updateView(overview); + } + } } this.prevPosition = this.currentPosition; if (!this.isForeignObject(e.target as HTMLElement, true)) { diff --git a/controls/diagrams/src/diagram/interaction/line-distribution.ts b/controls/diagrams/src/diagram/interaction/line-distribution.ts index 3c274f85f6..f17348f576 100644 --- a/controls/diagrams/src/diagram/interaction/line-distribution.ts +++ b/controls/diagrams/src/diagram/interaction/line-distribution.ts @@ -192,7 +192,7 @@ export class LineDistribution { connectorObstacles.push(connectorObstacle); } - + this.sortConnectors(graph,diagram) const modifiedgrap: ModifiedgrapObject[] = []; for (let m: number = 0; m < graph.length; m++) { const row: GraphObject = graph[parseInt(m.toString(), 10)]; @@ -362,6 +362,31 @@ export class LineDistribution { } } } + //Bug 862601: Connectors are not rendered properly with lineRouting and lineDistribution enables during doLayout process. + //To sort the connectors order in graph based on its target point and orientation to avoid connector segments path in same line. + private sortConnectors(graph:GraphObject[],diagram:Diagram){ + for (let i = 0; i < graph.length; i++) { + for (let j = 0; j < graph[parseInt(i.toString(), 10)].value.length; j++) { + if (graph[parseInt(i.toString(), 10)].value.length > 1) { + if (diagram.layout.orientation === 'LeftToRight' || diagram.layout.orientation === 'RightToLeft') { + graph[parseInt(i.toString(), 10)].value.sort((a, b) => { + const connectorA = diagram.nameTable[`${a.id}`]; + const connectorB = diagram.nameTable[`${b.id}`]; + + return connectorA.targetPoint.y - connectorB.targetPoint.y; + }); + }else if(diagram.layout.orientation === 'TopToBottom' || diagram.layout.orientation === 'BottomToTop'){ + graph[parseInt(i.toString(), 10)].value.sort((a, b) => { + const connectorA = diagram.nameTable[`${a.id}`]; + const connectorB = diagram.nameTable[`${b.id}`]; + + return connectorA.targetPoint.x - connectorB.targetPoint.x; + }); + } + } + } + } + }; private inflate(rect: Rect, x: number, y: number): Rect { rect.x -= x; @@ -1074,7 +1099,7 @@ export class LineDistribution { public resetConnectorSegments(connector: Connector): void { const segements: OrthogonalSegment[] = connector.segments as OrthogonalSegment[]; - for (let i: number = segements.length; i > 1; i--) { + for (let i: number = segements.length; i > 0; i--) { segements.splice(i - 1, 1); } diff --git a/controls/diagrams/src/diagram/interaction/scroller.ts b/controls/diagrams/src/diagram/interaction/scroller.ts index 0f866f8096..0b4e966564 100644 --- a/controls/diagrams/src/diagram/interaction/scroller.ts +++ b/controls/diagrams/src/diagram/interaction/scroller.ts @@ -686,6 +686,14 @@ export class DiagramScroller { } this.diagram.setOffset(-this.horizontalOffset - pageBounds.x, -this.verticalOffset - pageBounds.y); updateRuler(this.diagram); + //Bug 863516: Overview is not synced with diagram content while zoom-out the diagram. + //Updating overview after the page scrolled or zoomed. + if(this.diagram.views && (this.diagram.views as any).overview){ + let overview = (this.diagram.views as any).overview; + var bounds = overview.scrollOverviewRect(overview.parent.scroller.horizontalOffset, overview.parent.scroller.verticalOffset, overview.parent.scroller.currentZoom, true); + overview.updateOverviewrect(-bounds.x, -bounds.y, bounds.width, bounds.height); + overview.updateView(overview); + } } } } diff --git a/controls/diagrams/src/overview/overview.ts b/controls/diagrams/src/overview/overview.ts index a6bb2955f3..955d451a16 100644 --- a/controls/diagrams/src/overview/overview.ts +++ b/controls/diagrams/src/overview/overview.ts @@ -664,8 +664,20 @@ export class Overview extends Component implements INotifyPropertyC const x: number = bounds.x = (hoffset / currentZoom) / scale; const y: number = bounds.y = (voffset / currentZoom) / scale; //const viewPort: Rect; - const width: number = bounds.width = (this.parent.scroller.viewPortWidth / currentZoom) / scale; - const height: number = bounds.height = (this.parent.scroller.viewPortHeight / currentZoom) / scale; + //Bug 863516: Overview is not synced with diagram content while zoom-out the diagram. + //Added below code to consider ruler size and scrollbar size while calculating the width and height for the overview rectangle. + let rulerSize = 0; + if(this.parent.rulerSettings.showRulers){ + rulerSize = 25 + } + const container = document.getElementById(this.parent.element.id + 'content'); + let wScrollBar = 0; let hScrollBar = 0; + if(container){ + wScrollBar = container.offsetWidth - container.clientWidth; + hScrollBar = container.offsetHeight - container.clientHeight; + } + const width = bounds.width = ((this.parent.scroller.viewPortWidth-rulerSize-wScrollBar )/ currentZoom) / scale; + const height = bounds.height = ((this.parent.scroller.viewPortHeight-rulerSize-hScrollBar )/ currentZoom) / scale; //const ratio: number = this.parent.scroller.viewPortWidth / this.parent.scroller.viewPortHeight; if (scaled) { const rect: Rect = new Rect(); diff --git a/controls/documenteditor/CHANGELOG.md b/controls/documenteditor/CHANGELOG.md index 26ffb828d0..8f82c91946 100644 --- a/controls/documenteditor/CHANGELOG.md +++ b/controls/documenteditor/CHANGELOG.md @@ -2,6 +2,17 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### DocumentEditor + +#### Bug Fixes + +- `#I532824` - Resolved list numbering issue in the attached document. +- `#I531058` - Resolved the reply comment sorted issue. +- `#I532310` - Resolved Issue while opening the document Editor exported document. +- `#F185679` - Resolve script error and deleting cell from table removes other texts outside table. + ## 24.1.45 (2024-01-09) ### DocumentEditor diff --git a/controls/documenteditor/package.json b/controls/documenteditor/package.json index 25194e11cb..08436d2f32 100644 --- a/controls/documenteditor/package.json +++ b/controls/documenteditor/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-documenteditor", - "version": "24.1.44", + "version": "24.1.45", "description": "Feature-rich document editor control with built-in support for context menu, options pane and dialogs.", "keywords": [ "ej2", diff --git a/controls/documenteditor/src/document-editor/document-editor.ts b/controls/documenteditor/src/document-editor/document-editor.ts index e46124a0ca..a82f44ed98 100644 --- a/controls/documenteditor/src/document-editor/document-editor.ts +++ b/controls/documenteditor/src/document-editor/document-editor.ts @@ -1309,9 +1309,11 @@ export class DocumentEditor extends Component implements INotifyPro } //pre render section this.findResultsList = []; - if (isNullOrUndefined(this.documentEditorSettings.popupTarget)) { - this.documentEditorSettings.popupTarget = document.body; - } + setTimeout(() => { + if (isNullOrUndefined(this.documentEditorSettings.popupTarget)) { + this.documentEditorSettings.popupTarget = document.body; + } + }, 0); if (!isNullOrUndefined(this.element) && this.element.id === '') { //Set unique id, if id is empty this.element.id = HelperMethods.getUniqueElementId(); diff --git a/controls/documenteditor/src/document-editor/implementation/editor/editor-helper.ts b/controls/documenteditor/src/document-editor/implementation/editor/editor-helper.ts index ddaddad92d..a28871f5b1 100644 --- a/controls/documenteditor/src/document-editor/implementation/editor/editor-helper.ts +++ b/controls/documenteditor/src/document-editor/implementation/editor/editor-helper.ts @@ -858,6 +858,15 @@ export class HelperMethods { } return temp; } + /* eslint-disable */ + public static removeInvalidXmlChars(text: string): string { + // From xml spec valid chars: + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + // and used unicodes in DocumentEditor \f | \v | \r | String.fromCharCode(14) | %02 | %03 | %04 + const invalidXMLChars = /[^\x09\x0A\x0C\x0D\v\f\r\u000E\u0002\u0003\u0004\x20-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/ug; + return text.replace(invalidXMLChars, ''); + } public static reverseString(text: string): string { if (!isNullOrUndefined(text) && text !== '') { diff --git a/controls/documenteditor/src/document-editor/implementation/editor/editor.ts b/controls/documenteditor/src/document-editor/implementation/editor/editor.ts index 463e41fd01..82666e9d54 100644 --- a/controls/documenteditor/src/document-editor/implementation/editor/editor.ts +++ b/controls/documenteditor/src/document-editor/implementation/editor/editor.ts @@ -938,11 +938,12 @@ export class Editor { this.initComplexHistory('InsertComment'); const currentCmtStart: CommentCharacterElementBox = commentWidget.commentStart; const currentCmtEnd: CommentCharacterElementBox = commentWidget.commentEnd; - const offset: number = currentCmtStart.line.getOffset(currentCmtStart, 1); + let replyCmtLength: number = commentWidget.replyComments.length; + const offset: number = currentCmtStart.line.getOffset(currentCmtStart, replyCmtLength + 1); let startPosition: TextPosition = new TextPosition(this.documentHelper.owner); startPosition.setPositionParagraph(currentCmtStart.line, offset); - const endOffset: number = currentCmtEnd.line.getOffset(currentCmtEnd, 1); + const endOffset: number = currentCmtEnd.line.getOffset(currentCmtEnd, replyCmtLength + 1); let endPosition: TextPosition = new TextPosition(this.documentHelper.owner); endPosition.setPositionParagraph(currentCmtEnd.line, endOffset); @@ -12661,7 +12662,7 @@ export class Editor { // If previous widget is splitted paragraph, combine paragraph widget. let block: BlockWidget = (!isNullOrUndefined(paragraph.previousRenderedWidget) && start.paragraph !== paragraph) ? paragraph.previousRenderedWidget.combineWidget(this.documentHelper.viewer) as BlockWidget : undefined; - if (!isNullOrUndefined(block) && block instanceof TableWidget && paragraph.isEmpty() && isNullOrUndefined(paragraph.nextRenderedWidget)) { + if (this.owner.enableTrackChanges && !isNullOrUndefined(block) && block instanceof TableWidget && paragraph.isEmpty() && isNullOrUndefined(paragraph.nextRenderedWidget)) { this.delBlockContinue = true; this.delBlock = block; return; @@ -15848,7 +15849,12 @@ export class Editor { if (offset === selection.getStartOffset(paragraph) && selection.start.currentWidget.isFirstLine()) { if (paragraph.paragraphFormat.listFormat && paragraph.paragraphFormat.listFormat.listId !== -1) { - this.onApplyListInternal(this.documentHelper.getListById(paragraph.paragraphFormat.listFormat.listId), paragraph.paragraphFormat.listFormat.listLevelNumber - 1); + // BUG_859140 - handled backspace for list as per word desktop behaviour + if (!isNullOrUndefined(this.editorHistory) && !isNullOrUndefined(this.editorHistory.undoStack) && this.editorHistory.undoStack[this.editorHistory.undoStack.length - 1].action === 'ListFormat') { + this.onApplyListInternal(this.documentHelper.getListById(paragraph.paragraphFormat.listFormat.listId), paragraph.paragraphFormat.listFormat.listLevelNumber - 1); + } else { + this.onApplyList(undefined); + } return; } if (paragraph.paragraphFormat.firstLineIndent !== 0) { diff --git a/controls/documenteditor/src/document-editor/implementation/viewer/layout.ts b/controls/documenteditor/src/document-editor/implementation/viewer/layout.ts index c66704fe86..cbce73eefa 100644 --- a/controls/documenteditor/src/document-editor/implementation/viewer/layout.ts +++ b/controls/documenteditor/src/document-editor/implementation/viewer/layout.ts @@ -4359,8 +4359,14 @@ export class Layout { index = startIndex; keepLinesTogether = false; keepWithNext = true; - if (this.viewer.owner.isDocumentLoaded && this.viewer.owner.editorModule && !isList) { - this.viewer.owner.editorModule.updateWholeListItems(paragraphWidget); + if (paragraphWidget instanceof ParagraphWidget) { + if (this.viewer.owner.isDocumentLoaded && this.viewer.owner.editorModule && !paragraphWidget.paragraphFormat.keepWithNext && !isList) { + this.viewer.owner.editorModule.updateWholeListItems(paragraphWidget); + } + } else { + if (this.viewer.owner.isDocumentLoaded && this.viewer.owner.editorModule && !isList) { + this.viewer.owner.editorModule.updateWholeListItems(paragraphWidget); + } } } } diff --git a/controls/documenteditor/src/document-editor/implementation/writer/sfdt-export.ts b/controls/documenteditor/src/document-editor/implementation/writer/sfdt-export.ts index 66205009b5..95cb59c245 100644 --- a/controls/documenteditor/src/document-editor/implementation/writer/sfdt-export.ts +++ b/controls/documenteditor/src/document-editor/implementation/writer/sfdt-export.ts @@ -994,11 +994,15 @@ export class SfdtExport { } else if (element instanceof BreakElementBox) { inline[breakClearTypeProperty[this.keywordIndex]] = this.keywordIndex == 1? HelperMethods.getBreakClearType(element.breakClearType): element.breakClearType; } else if (element instanceof TextElementBox) { + let elementText: string = element.text; + if (isNullOrUndefined(this.owner.editorModule) || !this.owner.editorModule.isPaste) { + elementText = HelperMethods.removeInvalidXmlChars(elementText); + } // replacing the no break hyphen character by '-' - if (element.text.indexOf(String.fromCharCode(30)) !== -1) { - inline[textProperty[this.keywordIndex]] = element.text.replace(/\u001e/g, '-'); - } else if (element.text.indexOf(String.fromCharCode(31)) !== -1) { - inline[textProperty[this.keywordIndex]] = element.text.replace(/\u001f/g, ''); + if (elementText.indexOf(String.fromCharCode(30)) !== -1) { + inline[textProperty[this.keywordIndex]] = elementText.replace(/\u001e/g, '-'); + } else if (elementText.indexOf(String.fromCharCode(31)) !== -1) { + inline[textProperty[this.keywordIndex]] = elementText.replace(/\u001f/g, ''); } else if (element.revisions.length !== 0) { if (!this.isExport && this.owner.enableTrackChanges && !this.isPartialExport) { this.copyWithTrackChange = true; @@ -1008,14 +1012,14 @@ export class SfdtExport { this.selectedRevisionId.push(revision.revisionID); } if (element.revisions[x].revisionType !== 'Deletion') { - inline[textProperty[this.keywordIndex]] = element.text; + inline[textProperty[this.keywordIndex]] = elementText; } } } else { - inline[textProperty[this.keywordIndex]] = element.text; + inline[textProperty[this.keywordIndex]] = elementText; } } else { - inline[textProperty[this.keywordIndex]] = element.text; + inline[textProperty[this.keywordIndex]] = elementText; } } else if (element instanceof EditRangeStartElementBox) { if(element.user !== ''){ diff --git a/controls/documenteditor/styles/document-editor/icons/_bootstrap-dark.scss b/controls/documenteditor/styles/document-editor/icons/_bootstrap-dark.scss index 7b861fa9c9..aa0463190e 100644 --- a/controls/documenteditor/styles/document-editor/icons/_bootstrap-dark.scss +++ b/controls/documenteditor/styles/document-editor/icons/_bootstrap-dark.scss @@ -650,6 +650,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_bootstrap.scss b/controls/documenteditor/styles/document-editor/icons/_bootstrap.scss index 58bed0550e..ca8358266d 100644 --- a/controls/documenteditor/styles/document-editor/icons/_bootstrap.scss +++ b/controls/documenteditor/styles/document-editor/icons/_bootstrap.scss @@ -650,6 +650,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_bootstrap4.scss b/controls/documenteditor/styles/document-editor/icons/_bootstrap4.scss index dcae1facc0..01af6d4a39 100644 --- a/controls/documenteditor/styles/document-editor/icons/_bootstrap4.scss +++ b/controls/documenteditor/styles/document-editor/icons/_bootstrap4.scss @@ -635,6 +635,11 @@ content: '\e91f'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_bootstrap5.scss b/controls/documenteditor/styles/document-editor/icons/_bootstrap5.scss index 893dc59cf7..2d6cd51de5 100644 --- a/controls/documenteditor/styles/document-editor/icons/_bootstrap5.scss +++ b/controls/documenteditor/styles/document-editor/icons/_bootstrap5.scss @@ -689,6 +689,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_fabric-dark.scss b/controls/documenteditor/styles/document-editor/icons/_fabric-dark.scss index 11b7991a42..827cf46514 100644 --- a/controls/documenteditor/styles/document-editor/icons/_fabric-dark.scss +++ b/controls/documenteditor/styles/document-editor/icons/_fabric-dark.scss @@ -650,6 +650,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_fabric.scss b/controls/documenteditor/styles/document-editor/icons/_fabric.scss index 8e40941382..c4326f536a 100644 --- a/controls/documenteditor/styles/document-editor/icons/_fabric.scss +++ b/controls/documenteditor/styles/document-editor/icons/_fabric.scss @@ -650,6 +650,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_fluent.scss b/controls/documenteditor/styles/document-editor/icons/_fluent.scss index 9912699f2b..cb20bbe774 100644 --- a/controls/documenteditor/styles/document-editor/icons/_fluent.scss +++ b/controls/documenteditor/styles/document-editor/icons/_fluent.scss @@ -692,6 +692,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_fusionnew.scss b/controls/documenteditor/styles/document-editor/icons/_fusionnew.scss index daa1c3d7a6..00d8e598a7 100644 --- a/controls/documenteditor/styles/document-editor/icons/_fusionnew.scss +++ b/controls/documenteditor/styles/document-editor/icons/_fusionnew.scss @@ -686,6 +686,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_highcontrast-light.scss b/controls/documenteditor/styles/document-editor/icons/_highcontrast-light.scss index e29913e237..749b1b1974 100644 --- a/controls/documenteditor/styles/document-editor/icons/_highcontrast-light.scss +++ b/controls/documenteditor/styles/document-editor/icons/_highcontrast-light.scss @@ -655,6 +655,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_highcontrast.scss b/controls/documenteditor/styles/document-editor/icons/_highcontrast.scss index 0b2cd41cbd..f998904539 100644 --- a/controls/documenteditor/styles/document-editor/icons/_highcontrast.scss +++ b/controls/documenteditor/styles/document-editor/icons/_highcontrast.scss @@ -650,6 +650,11 @@ content: '\e68a'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_material-dark.scss b/controls/documenteditor/styles/document-editor/icons/_material-dark.scss index 49b93ffc2d..e263680375 100644 --- a/controls/documenteditor/styles/document-editor/icons/_material-dark.scss +++ b/controls/documenteditor/styles/document-editor/icons/_material-dark.scss @@ -643,6 +643,11 @@ content: '\e91f'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_material.scss b/controls/documenteditor/styles/document-editor/icons/_material.scss index d4f03898f0..57924936fc 100644 --- a/controls/documenteditor/styles/document-editor/icons/_material.scss +++ b/controls/documenteditor/styles/document-editor/icons/_material.scss @@ -643,6 +643,11 @@ content: '\e91f'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_material3.scss b/controls/documenteditor/styles/document-editor/icons/_material3.scss index 213cdb2c67..64aa99028a 100644 --- a/controls/documenteditor/styles/document-editor/icons/_material3.scss +++ b/controls/documenteditor/styles/document-editor/icons/_material3.scss @@ -686,6 +686,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_tailwind-dark.scss b/controls/documenteditor/styles/document-editor/icons/_tailwind-dark.scss index fe8d0a2217..9a1c145a02 100644 --- a/controls/documenteditor/styles/document-editor/icons/_tailwind-dark.scss +++ b/controls/documenteditor/styles/document-editor/icons/_tailwind-dark.scss @@ -686,6 +686,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/documenteditor/styles/document-editor/icons/_tailwind.scss b/controls/documenteditor/styles/document-editor/icons/_tailwind.scss index d2a02fb97b..0ccf43f3b3 100644 --- a/controls/documenteditor/styles/document-editor/icons/_tailwind.scss +++ b/controls/documenteditor/styles/document-editor/icons/_tailwind.scss @@ -689,6 +689,11 @@ content: '\e882'; } + .e-de-share::before { + content: '\e8f2'; + font-family: 'e-icons'; + } + .e-de-preset-container { width: 95px; } diff --git a/controls/drawings/CHANGELOG.md b/controls/drawings/CHANGELOG.md index ff03f152bd..85aa09c076 100644 --- a/controls/drawings/CHANGELOG.md +++ b/controls/drawings/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### Drawings diff --git a/controls/dropdowns/CHANGELOG.md b/controls/dropdowns/CHANGELOG.md index 82f7154747..a9f3e10232 100644 --- a/controls/dropdowns/CHANGELOG.md +++ b/controls/dropdowns/CHANGELOG.md @@ -2,8 +2,26 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### MultiSelect + +#### Bug Fixes + +- `#I535771` - Fixed issue where Z-index was not being added to the popup element when initially opening the popup. + ## 24.1.45 (2024-01-09) +### Mention + +#### Bug Fixes + +- `#I534515` - Fixed an issue where unable to add image caption in `RTE` using the Mention integration. + +- `#I530749` - Now, the Change event triggers properly with the mention list in Rich Text Editor. + +## 24.1.44 (2024-01-03) + ### DropDownTree #### Bug Fixes diff --git a/controls/dropdowns/package.json b/controls/dropdowns/package.json index ac36a40b20..94eba74577 100644 --- a/controls/dropdowns/package.json +++ b/controls/dropdowns/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-dropdowns", - "version": "24.1.43", + "version": "24.1.45", "description": "Essential JS 2 DropDown Components", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/dropdowns/spec/drop-down-list/drop-down-list.spec.ts b/controls/dropdowns/spec/drop-down-list/drop-down-list.spec.ts index f14b8d3941..c783bc5791 100644 --- a/controls/dropdowns/spec/drop-down-list/drop-down-list.spec.ts +++ b/controls/dropdowns/spec/drop-down-list/drop-down-list.spec.ts @@ -3029,7 +3029,7 @@ describe('DDList', () => { listObj.showPopup(); setTimeout(() => { expect(listObj.index).toBe(0); - listObj.scrollHandler(); + listObj.hidePopup(); setTimeout(function () { expect(listObj.isPopupOpen).toBe(false); done(); @@ -3062,6 +3062,40 @@ describe('DDList', () => { expect(listObj.index).toBe(9); }); }); + + describe('Mobile popup appears when scrolling', () => { + let ele: HTMLElement = document.createElement('input'); + ele.id = 'newlist'; + let listObj: any; + let data: { [key: string]: Object }[] = [{ id: 'list1', text: 'JAVA', icon: 'icon' }, { id: 'list2', text: 'C#' }, + { id: 'list3', text: 'C++' }, { id: 'list4', text: '.NET', icon: 'icon' }, { id: 'list5', text: 'Oracle' }, + { id: 'lit2', text: 'PHP' }, { id: 'list22', text: 'Phython' }, { id: 'list32', text: 'Perl' }, + { id: 'list42', text: 'Core' }, { id: 'lis2', text: 'C' }, { id: 'list12', text: 'C##' }]; + beforeAll(() => { + let androidPhoneUa: string = 'Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JWR66Y) ' + + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.92 Safari/537.36'; + Browser.userAgent = androidPhoneUa; + document.body.appendChild(ele); + listObj = new DropDownList({ + dataSource: data, fields: { text: 'text', value: 'id' }, allowFiltering: true, + popupHeight: '100px', + popupWidth: '1000px' + }); + listObj.appendTo('#newlist'); + }); + afterAll(() => { + if (ele) { + ele.remove(); + } + }) + it('target element in view port popup not close when scrolling', () => { + listObj.showPopup(); + listObj.scrollHandler(); + setTimeout(function () { + expect(listObj.isPopupOpen).toBe(true); + }, 350); + }) + }); describe('Allowfiltering support in mobile', () => { let ele: HTMLElement = document.createElement('input'); ele.id = 'newlist'; diff --git a/controls/dropdowns/spec/multi-select/checkbox-selectall.spec.ts b/controls/dropdowns/spec/multi-select/checkbox-selectall.spec.ts index 12217e02d8..e8bd59fd88 100644 --- a/controls/dropdowns/spec/multi-select/checkbox-selectall.spec.ts +++ b/controls/dropdowns/spec/multi-select/checkbox-selectall.spec.ts @@ -415,11 +415,11 @@ describe('MultiSelect', () => { listObj = new MultiSelect({ dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', @@ -433,7 +433,7 @@ describe('MultiSelect', () => { listObj.appendTo(element); listObj.showPopup(); setTimeout(() => { - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); @@ -464,11 +464,11 @@ describe('MultiSelect', () => { listObj = new MultiSelect({ dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', mode: 'CheckBox', @@ -481,7 +481,7 @@ describe('MultiSelect', () => { listObj.appendTo(element); listObj.showPopup(); setTimeout(function () { - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); expect((listObj).isPopupOpen()).toBe(true); diff --git a/controls/dropdowns/spec/multi-select/checkbox-selection.spec.ts b/controls/dropdowns/spec/multi-select/checkbox-selection.spec.ts index 6e019bd1aa..9de695d0bc 100644 --- a/controls/dropdowns/spec/multi-select/checkbox-selection.spec.ts +++ b/controls/dropdowns/spec/multi-select/checkbox-selection.spec.ts @@ -696,11 +696,11 @@ describe('MultiSelect', () => { listObj = new MultiSelect({ dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', @@ -713,7 +713,7 @@ describe('MultiSelect', () => { listObj.appendTo(element); listObj.showPopup(); setTimeout(() => { - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); @@ -744,11 +744,11 @@ describe('MultiSelect', () => { listObj = new MultiSelect({ dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', mode: 'CheckBox', @@ -760,7 +760,7 @@ describe('MultiSelect', () => { listObj.appendTo(element); listObj.showPopup(); setTimeout(function () { - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); expect((listObj).checkBoxSelectionModule.checkAllParent.classList.contains('e-selectall-parent')).toBe(true); expect((listObj).isPopupOpen()).toBe(true); diff --git a/controls/dropdowns/spec/multi-select/multi-select.spec.ts b/controls/dropdowns/spec/multi-select/multi-select.spec.ts index cd29269490..f73d53c187 100644 --- a/controls/dropdowns/spec/multi-select/multi-select.spec.ts +++ b/controls/dropdowns/spec/multi-select/multi-select.spec.ts @@ -2685,11 +2685,11 @@ describe('MultiSelect', () => { hideSelectedItem: false, dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', placeholder: 'Select an employee', @@ -2698,14 +2698,14 @@ describe('MultiSelect', () => { }); listObj.appendTo(element); (listObj).renderPopup(); - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); - expect('
employee
Mona Sak
USA
').toBe((listObj).ulElement.querySelector("li.e-list-item").innerHTML); + expect('
employee
Mona Sak
USA
').toBe((listObj).ulElement.querySelector("li.e-list-item").innerHTML); mouseEventArgs.target = (listObj).ulElement.querySelector("li.e-list-item"); mouseEventArgs.type = 'click'; (listObj).onMouseClick(mouseEventArgs); let elem: HTMLElement = (listObj).chipCollectionWrapper.querySelector("span.e-chipcontent"); - expect('employee Mona Sak ').toBe(elem.innerHTML); + expect('employee Mona Sak ').toBe(elem.innerHTML); mouseEventArgs.target = (listObj).popupWrapper.querySelector(".head"); (listObj).onMouseClick(mouseEventArgs); listObj.destroy(); @@ -2715,9 +2715,9 @@ describe('MultiSelect', () => { hideSelectedItem: false, dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - itemTemplate: '
employee' + + itemTemplate: '
employee' + '
${text}
${country}
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', footerTemplate: '
Total Items Count: 5
', width: '250px', @@ -2727,9 +2727,9 @@ describe('MultiSelect', () => { }); listObj.appendTo(element); (listObj).renderPopup(); - listObj.headerTemplate = '
Photo Contact Info
'; + listObj.headerTemplate = '
PhotoContact Info
'; listObj.dataBind(); - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); listObj.footerTemplate = '
Total Items Count: 5
'; listObj.dataBind(); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); @@ -2740,9 +2740,9 @@ describe('MultiSelect', () => { hideSelectedItem: false, dataSource: empList, fields: { text: 'text', groupBy: 'country' }, - itemTemplate: '
employee' + + itemTemplate: '
employee' + '
${text}
${country}
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', placeholder: 'Select an employee', @@ -2751,9 +2751,9 @@ describe('MultiSelect', () => { }); listObj.appendTo(element); (listObj).renderPopup(); - listObj.headerTemplate = '
Photo Contact Info
'; + listObj.headerTemplate = '
PhotoContact Info
'; listObj.dataBind(); - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); listObj.footerTemplate = '
Total Items Count: 5
'; listObj.dataBind(); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); @@ -3391,11 +3391,11 @@ describe('MultiSelect', () => { hideSelectedItem: false, dataSource: empList, fields: { text: 'text' }, - headerTemplate: '
Photo Contact Info
', - itemTemplate: '
employee' + + headerTemplate: '
PhotoContact Info
', + itemTemplate: '
employee' + '
${text}
${country}
', footerTemplate: '
Total Items Count: 5
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', width: '250px', placeholder: 'Select an employee', @@ -3405,9 +3405,9 @@ describe('MultiSelect', () => { }); listObj.appendTo(element); listObj.showPopup(); - expect('
Photo Contact Info
').toBe((listObj).header.innerHTML); + expect('
PhotoContact Info
').toBe((listObj).header.innerHTML); expect('
Total Items Count: 5
').toBe((listObj).footer.innerHTML); - expect('
employee
Mona Sak
USA
').toBe((listObj).ulElement.querySelector("li.e-list-item").innerHTML); + expect('
employee
Mona Sak
USA
').toBe((listObj).ulElement.querySelector("li.e-list-item").innerHTML); (listObj).inputElement.value = "RUBY"; keyboardEventArgs.keyCode = 113; (listObj).keyDownStatus = true; @@ -6556,9 +6556,9 @@ describe('MultiSelect', () => { dataSource: empList, fields: { text: 'text', groupBy: 'country' }, value: ['Erik Linden'], - itemTemplate: '
employee' + + itemTemplate: '
employee' + '
${text}
${country}
', - valueTemplate: 'employee' + + valueTemplate: 'employee' + ' ${text} ', }); listObj.appendTo(element); diff --git a/controls/dropdowns/src/drop-down-list/drop-down-list.ts b/controls/dropdowns/src/drop-down-list/drop-down-list.ts index 6f13caf1fb..e041a3614f 100644 --- a/controls/dropdowns/src/drop-down-list/drop-down-list.ts +++ b/controls/dropdowns/src/drop-down-list/drop-down-list.ts @@ -2975,10 +2975,17 @@ export class DropDownList extends DropDownBase implements IInput { private scrollHandler(): void { if (Browser.isDevice && ((this.getModuleName() === 'dropdownlist' && !this.isFilterLayout()) || (this.getModuleName() === 'combobox' && !this.allowFiltering && this.isDropDownClick))) { - this.hidePopup(); + if (this.element && !(this.isElementInViewport(this.element))) { + this.hidePopup(); + } } } + private isElementInViewport(element : HTMLElement): boolean { + var elementRect = element.getBoundingClientRect(); + return (elementRect.top >= 0 && elementRect.left >= 0 && elementRect.bottom <= window.innerHeight && elementRect.right <= window.innerWidth); + }; + private setSearchBoxPosition(): void { const searchBoxHeight: number = this.filterInput.parentElement.getBoundingClientRect().height; this.popupObj.element.style.maxHeight = '100%'; @@ -4104,7 +4111,7 @@ export class DropDownList extends DropDownBase implements IInput { if (!this.enabled) { return; } - if (!this.enableVirtualization && this.getModuleName() === 'combobox') { + if (!this.enableVirtualization && (this.getModuleName() === 'combobox' || this.getModuleName() === 'autocomplete')) { this.isTyped = true; } this.hidePopup(e); diff --git a/controls/dropdowns/src/mention/mention.ts b/controls/dropdowns/src/mention/mention.ts index 38158d4e06..cad15f1244 100644 --- a/controls/dropdowns/src/mention/mention.ts +++ b/controls/dropdowns/src/mention/mention.ts @@ -669,7 +669,8 @@ export class Mention extends DropDownBase { return; } this.isTyped = e.code !== 'Enter' && e.code !== 'Space' && e.code !== 'ArrowDown' && e.code !== 'ArrowUp' ? true : false; - if (document.activeElement != this.inputElement) { + const isRteImage: boolean = document.activeElement.parentElement && document.activeElement.parentElement.querySelector('.e-rte-image') ? true : false; + if (document.activeElement != this.inputElement && !isRteImage) { this.inputElement.focus(); } if (this.isContentEditable(this.inputElement)) { this.range = this.getCurrentRange(); @@ -1544,6 +1545,11 @@ export class Mention extends DropDownBase { myField.selectionStart = startPos + value.length; myField.selectionEnd = startPos + value.length; if (this.isPopupOpen) { this.hidePopup(); } + //New event to update the RichTextEditor value, when a mention item is selected using mouse click action. + if (!isNullOrUndefined((e as PointerEvent).pointerType) && (e as PointerEvent).pointerType === 'mouse') { + const event: Event = new CustomEvent('content-changed', { detail: { click: true } }); + this.inputElement.dispatchEvent(event); + }; this.onChangeEvent(e); } else { endPos = this.getTriggerCharPosition() + this.mentionChar.length; diff --git a/controls/dropdowns/src/multi-select/multi-select.ts b/controls/dropdowns/src/multi-select/multi-select.ts index e3f06b105a..9c4e9f3bf3 100644 --- a/controls/dropdowns/src/multi-select/multi-select.ts +++ b/controls/dropdowns/src/multi-select/multi-select.ts @@ -4413,10 +4413,10 @@ export class MultiSelect extends DropDownBase implements IInput { if (!args.cancel) { if (!this.ulElement) { this.beforePopupOpen = true; - super.render(e); if (this.mode === 'CheckBox' && Browser.isDevice && this.allowFiltering) { this.notify('popupFullScreen', { module: 'CheckBoxSelection', enable: this.mode === 'CheckBox' }); } + super.render(e); return; } if (this.mode === 'CheckBox' && Browser.isDevice && this.allowFiltering) { diff --git a/controls/dropdowns/styles/drop-down-list/_layout.scss b/controls/dropdowns/styles/drop-down-list/_layout.scss index 0a9e8d851c..1f48318e0c 100644 --- a/controls/dropdowns/styles/drop-down-list/_layout.scss +++ b/controls/dropdowns/styles/drop-down-list/_layout.scss @@ -53,6 +53,11 @@ } } + .e-popup.e-ddl-device.e-popup-close { + display: block; + visibility: hidden; + } + .e-popup-full-page { bottom: 0; left: 0; diff --git a/controls/ej2/package.json b/controls/ej2/package.json index 32e29a656e..b5f248f06f 100644 --- a/controls/ej2/package.json +++ b/controls/ej2/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2", - "version": "24.3.0", + "version": "24.4.0", "description": "A modern JavaScript UI toolkit that has been built from the ground up to be lightweight, responsive, modular and touch friendly. It is written in TypeScript and has no external dependencies.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/excelexport/CHANGELOG.md b/controls/excelexport/CHANGELOG.md index 69446cb2c8..6010d7bfb6 100644 --- a/controls/excelexport/CHANGELOG.md +++ b/controls/excelexport/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### Excel Export diff --git a/controls/filemanager/CHANGELOG.md b/controls/filemanager/CHANGELOG.md index 2bb19e56d9..408b7e45ae 100644 --- a/controls/filemanager/CHANGELOG.md +++ b/controls/filemanager/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### FileManager diff --git a/controls/gantt/CHANGELOG.md b/controls/gantt/CHANGELOG.md index c93175d5c9..24f4543574 100644 --- a/controls/gantt/CHANGELOG.md +++ b/controls/gantt/CHANGELOG.md @@ -2,20 +2,39 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### GanttChart + +#### Bug Fixes + +- `#I533229` - Server call is triggered twice issue has been fixed. +- `#I531670` - When adding a record by method before saving, if the task ID is changed after taskbar hover exception thrown issue has been fixed. +- `#I538917` - Text is not rendered properly in header while using page size issue has been fixed. +- `#I185970` - Dynamic template updating in columns does not render issue has been fixed. + ## 24.1.45 (2024-01-09) ### GanttChart #### Bug Fixes -`#I531670` - When adding record by method before saving, if the task ID is changed after taskbar hover exception thrown issue has been fixed. +- `#I530808` - Progress width not rendered properly in split tasks issue has been fixed. +- `#F185683` - Resources are not updating properly in `actionBegin`event issue has been fixed. +- `#I532918` - Baseline width not rendered properly in PDF export issue has been fixed. +- `#F532918` - Issue with remote data while performing CRUD operation in various Gantt chart versions has been fixed. +- `#I521365` - Dates in tooltip not rendered correctly issue has been fixed. -## 24.1.43 (2023-12-27) +## 24.1.44 (2024-01-03) ### GanttChart #### Bug Fixes +`#I531670` - When adding record by method before saving, if the task ID is changed after taskbar hover exception thrown issue has been fixed. + +## 24.1.43 (2023-12-27) + - `#I527509` - Action begin event arguments not working properly issue has been fixed. - `#I517104` - Gantt component hangs whole page if timezone changed to UK(London) issue has been fixed. diff --git a/controls/gantt/package.json b/controls/gantt/package.json index 948fcf1701..c7f26178df 100644 --- a/controls/gantt/package.json +++ b/controls/gantt/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-gantt", - "version": "24.1.43", + "version": "24.1.45", "description": "Essential JS 2 Gantt Component", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/gantt/spec/action/taskbaredit.spec.ts b/controls/gantt/spec/action/taskbaredit.spec.ts index 4046bb5663..79e117dd92 100644 --- a/controls/gantt/spec/action/taskbaredit.spec.ts +++ b/controls/gantt/spec/action/taskbaredit.spec.ts @@ -672,7 +672,7 @@ describe('Gantt taskbar editing', () => { }); it('Milestone drag action', () => { ganttObj.taskbarEdited = (args: ITaskbarEditedEventArgs) => { - expect(ganttObj.getFormatedDate(args.data.ganttProperties.startDate, 'MM/dd/yyyy HH:mm')).toBe('04/02/2019 17:00'); + expect(ganttObj.getFormatedDate(args.data.ganttProperties.startDate, 'MM/dd/yyyy HH:mm')).toBe('04/03/2019 17:00'); }; let dragElement: HTMLElement = ganttObj.element.querySelector('#' + ganttObj.element.id + 'GanttTaskTableBody > tr:nth-child(2) > td >div.e-taskbar-main-container') as HTMLElement; triggerMouseEvent(dragElement, 'mousedown', dragElement.offsetLeft, dragElement.offsetTop); diff --git a/controls/gantt/spec/base/gantt.spec.ts b/controls/gantt/spec/base/gantt.spec.ts index 0e6329ada6..47bb86bffa 100644 --- a/controls/gantt/spec/base/gantt.spec.ts +++ b/controls/gantt/spec/base/gantt.spec.ts @@ -1350,4 +1350,121 @@ describe('Milestone Baseline render', () => { expect(ganttObj.columns.length).toBe(1); }); }); + describe('Split tasks progress value', () => { + let ganttObj: Gantt; + var splitTasksData = [ + { + TaskID: 1, + TaskName: 'Defining the product and its usage', + StartDate: new Date('2019-02-04T21:28:41'), + EndDate: new Date('2019-02-05T21:28:41'), + Duration: 180, + Progress: 47, + Segments: [ + { + StartDate: new Date('2019-02-04'), + Duration: 90, + }, + { + StartDate: new Date('2019-02-05'), + Duration: 90, + }, + ], + }, + ]; + beforeAll((done: Function) => { + ganttObj = createGantt({ + dataSource: splitTasksData, + taskFields: { + id: 'TaskID', + name: 'TaskName', + startDate: 'StartDate', + endDate: 'EndDate', + duration: 'Duration', + progress: 'Progress', + dependency: 'Predecessor', + child: 'subtasks', + segments: 'Segments', + durationUnit: 'durationUnit', + }, + editSettings: { + allowAdding: true, + allowEditing: true, + allowDeleting: true, + allowTaskbarEditing: true, + showDeleteConfirmDialog: true, + }, + columns: [ + { field: 'TaskID', width: 80 }, + { + field: 'TaskName', + headerText: 'Job Name', + width: '250', + clipMode: 'EllipsisWithTooltip', + }, + { field: 'StartDate' }, + { field: 'EndDate' }, + { field: 'Duration' }, + { field: 'Progress' }, + { field: 'Predecessor' }, + ], + durationUnit: 'Minute', + dayWorkingTime: [ + { + from: 0, + to: 24, + }, + ], + toolbar: [ + 'Add', + 'Edit', + 'Update', + 'Delete', + 'Cancel', + 'ExpandAll', + 'CollapseAll', + ], + enableContextMenu: true, + allowSelection: true, + height: '450px', + treeColumnIndex: 1, + highlightWeekends: true, + splitterSettings: { + position: '35%', + }, + projectEndDate: new Date('2019-02-14'), + projectStartDate: new Date('2019-02-04'), + labelSettings: { + leftLabel: 'TaskName', + taskLabel: '${Progress}%', + }, + timezone: 'Europe/Rome', + timelineSettings: { + timelineUnitSize: 40, + showTooltip: true, + timelineViewMode: 'Day', + topTier: { + unit: 'Day', + format: 'E, d MMMM', + count: 1, + }, + bottomTier: { + unit: 'Hour', + count: 1, + }, + weekStartDay: 1, + weekendBackground: 'rgba(0,0,0,0.1)', + updateTimescaleView: false, + }, + }, done); + + }); + afterAll(() => { + destroyGantt(ganttObj); + }); + it('check progress value', () => { + expect(ganttObj.currentViewData[0].ganttProperties.segments[0].progressWidth).toBe(56.4); + expect(ganttObj.currentViewData[0].ganttProperties.segments[1].progressWidth).toBe(-1); + }); + }); }); diff --git a/controls/gantt/spec/base/taskbar.spec.ts b/controls/gantt/spec/base/taskbar.spec.ts index 2ee696a02b..28b8f622a2 100644 --- a/controls/gantt/spec/base/taskbar.spec.ts +++ b/controls/gantt/spec/base/taskbar.spec.ts @@ -23,7 +23,7 @@ describe('Gantt taskbar rendering', () => { document.body.appendChild(lefttasklabel); let righttasklabel: Element = createElement('div', { id: 'righttasklabelTS', styles: 'visibility:hidden' }); - righttasklabel.innerHTML = '
Task Name - ${TaskName}
'; + righttasklabel.innerHTML = '
Task Name - ${TaskName}
'; document.body.appendChild(righttasklabel); let parentTaskTemplate: Element = createElement('div', { id: 'demoParentTaskTemplate', className: cls.traceParentTaskBar + ' ' + cls.parentTaskBarInnerDiv, styles: 'visibility:hidden' }); diff --git a/controls/gantt/src/gantt/actions/dialog-edit.ts b/controls/gantt/src/gantt/actions/dialog-edit.ts index 7363146808..bb2519fb6a 100644 --- a/controls/gantt/src/gantt/actions/dialog-edit.ts +++ b/controls/gantt/src/gantt/actions/dialog-edit.ts @@ -2208,6 +2208,9 @@ export class DialogEdit { const ganttObj: Gantt = this.parent; const resourceSettings: ResourceFieldsModel = ganttObj.resourceFields; const ganttData: IGanttData = this.beforeOpenArgs.rowData; + if (((this.beforeOpenArgs.requestType === 'beforeOpenEditDialog' && !isNullOrUndefined(this.editedRecord[this.parent.taskFields.resourceInfo])) || (this.beforeOpenArgs.requestType === 'beforeOpenAddDialog' && !isNullOrUndefined(this.editedRecord[this.parent.taskFields.resourceInfo]))) && (typeof (this.editedRecord[this.parent.taskFields.resourceInfo]) === "object")) { + this.parent.setRecordValue('resourceInfo', this.parent.dataOperation.setResourceInfo(this.editedRecord), ganttData.ganttProperties, true); + } const rowResource: Object[] = ganttData.ganttProperties.resourceInfo; const inputModel: TreeGridModel = this.beforeOpenArgs[itemName as string]; const resourceTreeGridId: string = ganttObj.element.id + '' + itemName + 'TabContainer'; @@ -2246,6 +2249,11 @@ export class DialogEdit { this.ganttResources.push(resourceInfo[i as number]); } } + else if (!this.isEdit && !isNullOrUndefined(resourceInfo)) { + for (let i: number = 0; i < resourceInfo.length; i++) { + this.ganttResources.push(resourceInfo[i as number]); + } + } inputModel.rowSelected = (args: RowSelectEventArgs): void => { this.updateResourceCollection(args, resourceTreeGridId); }; @@ -2646,11 +2654,17 @@ export class DialogEdit { tasksData[fieldName as string] = false; } } else { - tasksData[fieldName as string] = controlObj.value; - if (this.parent.enableHtmlSanitizer && typeof (controlObj.value) === 'string') { + if (fieldName === "Duration") { + let numericValue: number = parseFloat(String(controlObj.value));; + tasksData[fieldName as string] = numericValue + } + else { + tasksData[fieldName as string] = controlObj.value; + } + if (this.parent.enableHtmlSanitizer && typeof (controlObj.value) === 'string') { controlObj.value = SanitizeHtmlHelper.sanitize(controlObj.value); tasksData[fieldName as string] = controlObj.value; - } + } } } } diff --git a/controls/gantt/src/gantt/actions/edit.ts b/controls/gantt/src/gantt/actions/edit.ts index a7f7dd41fc..31ba7c217c 100644 --- a/controls/gantt/src/gantt/actions/edit.ts +++ b/controls/gantt/src/gantt/actions/edit.ts @@ -1559,9 +1559,11 @@ export class Edit { const taskID: string = updateRecord.ganttProperties.taskId; const resourceID: string = prevResource[index as number][this.parent.resourceFields.id]; const record: IGanttData = flatRecords[this.parent.getTaskIds().indexOf('R' + resourceID)]; - for (let j: number = 0; j < record.childRecords.length; j++) { - if (record.childRecords[j as number].ganttProperties.taskId === taskID) { - this.removeChildRecord(record.childRecords[j as number]); + if (!isNullOrUndefined(record)) { + for (let j: number = 0; j < record.childRecords.length; j++) { + if (record.childRecords[j as number].ganttProperties.taskId === taskID) { + this.removeChildRecord(record.childRecords[j as number]); + } } } } @@ -3077,10 +3079,15 @@ export class Edit { } if(!isNullOrUndefined(args.data[`${tempTaskID}`])) { if(args.data[tempTaskID as string] != args.data['ganttProperties']['taskId']) { + for (const key of Object.keys(this.parent.ids)) { + if (this.parent.ids[key as string] === args.data['ganttProperties']['taskId'].toString()) { + this.parent.ids[key as string] = args.data[tempTaskID as string].toString(); + break; + } + } args.data['ganttProperties']['taskId'] = args.data[tempTaskID as string]; args.newTaskData[tempTaskID as string] = args.data[tempTaskID as string]; args.data['ganttProperties']['rowUniqueID'] = args.data[tempTaskID as string].toString(); - this.parent.ids.push(args.data[tempTaskID as string].toString()); } } if (!args.cancel) { @@ -3090,7 +3097,6 @@ export class Edit { addedRecords: [args.newTaskData], // to check changedRecords: args.modifiedTaskData }; - const prevID: string = (args.data as IGanttData).ganttProperties.taskId.toString(); /* tslint:disable-next-line */ const query: Query = this.parent.query instanceof Query ? this.parent.query : new Query(); const adaptor: any = data.adaptor; @@ -3099,6 +3105,13 @@ export class Edit { const crud: Promise = data.saveChanges(updatedData, this.parent.taskFields.id, null, query) as Promise; crud.then((e: { addedRecords: Object[], changedRecords: Object[] }) => { + if (e.addedRecords[0][this.parent.taskFields.id].toString() != args.data['ganttProperties']['taskId']) { + args.data['ganttProperties']['taskId'] = e.addedRecords[0][this.parent.taskFields.id].toString(); + args.newTaskData[tempTaskID as string] = e.addedRecords[0][this.parent.taskFields.id].toString(); + args.data['ganttProperties']['rowUniqueID'] = e.addedRecords[0][this.parent.taskFields.id].toString(); + this.parent.ids.push(e.addedRecords[0][this.parent.taskFields.id].toString()); + } + const prevID: string = (args.data as IGanttData).ganttProperties.taskId.toString(); if (this.parent.taskFields.id && !isNullOrUndefined(e.addedRecords[0][this.parent.taskFields.id]) && e.addedRecords[0][this.parent.taskFields.id].toString() == prevID) { this.parent.setRecordValue( diff --git a/controls/gantt/src/gantt/actions/pdf-export.ts b/controls/gantt/src/gantt/actions/pdf-export.ts index 331c4abb26..0b95445bec 100644 --- a/controls/gantt/src/gantt/actions/pdf-export.ts +++ b/controls/gantt/src/gantt/actions/pdf-export.ts @@ -24,6 +24,7 @@ export class PdfExport { private isBlob: boolean; private blobPromise: Promise<{ blobData: Blob }>; public pdfPageDimensions: SizeF; + public pdfPage: PdfPage; /** * @param {Gantt} parent . * @hidden @@ -134,8 +135,8 @@ export class PdfExport { private processExport(data: IGanttData[], pdfExportProperties: PdfExportProperties, isMultipleExport: boolean): Promise { const section: PdfSection = this.pdfDocument.sections.add() as PdfSection; this.processSectionExportProperties(section, pdfExportProperties); - const pdfPage: PdfPage = section.pages.add(); - this.pdfPageDimensions = pdfPage.getClientSize(); + this.pdfPage = section.pages.add(); + this.pdfPageDimensions = this.pdfPage.getClientSize(); /* eslint-disable-next-line */ return new Promise((resolve: Function, reject: Function) => { this.helper.processGridExport(data, this.gantt, pdfExportProperties); @@ -144,7 +145,7 @@ export class PdfExport { }).then(() => { const format: PdfTreeGridLayoutFormat = new PdfTreeGridLayoutFormat(); format.break = PdfLayoutBreakType.FitElement; - const layouter: PdfTreeGridLayoutResult = this.gantt.drawGrid(pdfPage, 0, 0, format); + const layouter: PdfTreeGridLayoutResult = this.gantt.drawGrid(this.pdfPage, 0, 0, format); this.gantt.drawChart(layouter); if (this.helper.exportProps && this.helper.exportProps.fitToWidthSettings && this.helper.exportProps.fitToWidthSettings.isFitToWidth) { this.parent.zoomingProjectStartDate = this.helper.beforeSinglePageExport['zoomingProjectStartDate']; diff --git a/controls/gantt/src/gantt/actions/taskbar-edit.ts b/controls/gantt/src/gantt/actions/taskbar-edit.ts index f650c5f74f..128e31582a 100644 --- a/controls/gantt/src/gantt/actions/taskbar-edit.ts +++ b/controls/gantt/src/gantt/actions/taskbar-edit.ts @@ -1879,7 +1879,7 @@ export class TaskbarEdit extends DateProcessor { pStartDate.setTime(pStartDate.getTime() + (left * milliSecondsPerPixel)); /* To render the milestone in proper date while editing */ if (isMilestone && !isNullOrUndefined(property.predecessorsName) && property.predecessorsName !== '') { - pStartDate.setDate(pStartDate.getDate()-1); + // pStartDate.setDate(pStartDate.getDate()-1); this.parent.dateValidationModule.setTime(this.parent.defaultEndTime,pStartDate); pStartDate = this.parent.dateValidationModule.checkStartDate(pStartDate,property,true) } @@ -1888,9 +1888,10 @@ export class TaskbarEdit extends DateProcessor { if (tierMode !== 'Hour' && tierMode !== 'Minutes') { if (this.parent.isInDst(new Date(this.parent.timelineModule.timelineStartDate.toString())) && !this.parent.isInDst(pStartDate)) { pStartDate.setTime(pStartDate.getTime() + (60 * 60 * 1000)); - } else if (!this.parent.isInDst(new Date(this.parent.timelineModule.timelineStartDate.toString())) && this.parent.isInDst(pStartDate)) { - pStartDate.setTime(pStartDate.getTime() - (60 * 60 * 1000)); - } + } + // else if (!this.parent.isInDst(new Date(this.parent.timelineModule.timelineStartDate.toString())) && this.parent.isInDst(pStartDate)) { + // pStartDate.setTime(pStartDate.getTime() - (60 * 60 * 1000)); + // } } return pStartDate; } diff --git a/controls/gantt/src/gantt/base/task-processor.ts b/controls/gantt/src/gantt/base/task-processor.ts index 55908d0488..3b3e71f490 100644 --- a/controls/gantt/src/gantt/base/task-processor.ts +++ b/controls/gantt/src/gantt/base/task-processor.ts @@ -77,19 +77,24 @@ export class TaskProcessor extends DateProcessor { const queryManager: Query = this.parent.query instanceof Query ? this.parent.query : new Query(); queryManager.requiresCount(); const dataManager: DataManager = this.parent.dataSource as DataManager; - dataManager.executeQuery(queryManager).then((e: ReturnOption) => { - this.dataArray = e.result; + if (!this.parent.loadChildOnDemand && this.parent.taskFields.hasChildMapping) { this.processTimeline(); - if (this.parent.loadChildOnDemand || (!this.parent.loadChildOnDemand && !(this.parent.taskFields.hasChildMapping))) { - this.cloneDataSource(); - } - this.parent.renderGantt(isChange); - }).catch((e: ReturnType) => { - // Trigger action failure event - this.parent.processTimeline(); - this.parent.renderGantt(isChange); - this.parent.trigger('actionFailure', { error: e }); - }); + this.parent.renderGantt(isChange) + } else { + dataManager.executeQuery(queryManager).then((e: ReturnOption) => { + this.dataArray = e.result; + this.processTimeline(); + if (this.parent.loadChildOnDemand || (!this.parent.loadChildOnDemand && !(this.parent.taskFields.hasChildMapping))) { + this.cloneDataSource(); + } + this.parent.renderGantt(isChange); + }).catch((e: ReturnType) => { + // Trigger action failure event + this.parent.processTimeline(); + this.parent.renderGantt(isChange); + this.parent.trigger('actionFailure', { error: e }); + }); + } } private constructDataSource(dataSource: Object[]): void { const mappingData: Object[] = new DataManager(dataSource).executeLocal(new Query() @@ -2223,13 +2228,11 @@ export class TaskProcessor extends DateProcessor { */ public updateWidthLeft(data: IGanttData): void { const ganttRecord: ITaskData = data.ganttProperties; + let totalSegmentsProgressWidth: number = 0; // task endDate may be changed in segment calculation so this must be calculated first. // task width calculating was based on endDate if (!isNullOrUndefined(ganttRecord.segments) && ganttRecord.segments.length > 0) { const segments: ITaskSegment[] = ganttRecord.segments; - let fixedWidth: boolean = true; - const totalTaskWidth: number = this.splitTasksDuration(segments) * ((this.parent.timelineModule.bottomTier === "Hour" || this.parent.timelineModule.bottomTier === "Minutes") ? this.parent.timelineSettings.timelineUnitSize : this.parent.perDayWidth);; - let totalProgressWidth: number = this.parent.dataOperation.getProgressWidth(totalTaskWidth, ganttRecord.progress); for (let i: number = 0; i < segments.length; i++) { const segment: ITaskSegment = segments[i as number]; if (i === 0 && !isNullOrUndefined(ganttRecord.startDate) && @@ -2242,21 +2245,27 @@ export class TaskProcessor extends DateProcessor { this.parent.chartRowsModule.incrementSegments(segments, 0, data); } segment.width = this.getSplitTaskWidth(segment.startDate, segment.duration, data); + totalSegmentsProgressWidth = totalSegmentsProgressWidth + segment.width; segment.showProgress = false; segment.progressWidth = -1; if (i !== 0) { const pStartDate: Date = new Date(ganttRecord.startDate.getTime()); segment.left = this.getSplitTaskLeft(segment.startDate, pStartDate); } - if (totalProgressWidth > 0 && totalProgressWidth > segment.width) { - totalProgressWidth = totalProgressWidth - segment.width; - segment.progressWidth = segment.width; - segment.showProgress = false; - } else if (fixedWidth) { - segment.progressWidth = totalProgressWidth; - segment.showProgress = true; - totalProgressWidth = totalProgressWidth - segment.width; - fixedWidth = false; + } + let setProgress: number = this.parent.dataOperation.getProgressWidth(totalSegmentsProgressWidth, ganttRecord.progress); + let isValid: boolean = true; + for (let i: number = 0; i < segments.length; i++) { + if (isValid) { + if (setProgress <= segments[i as number].width) { + segments[i as number].progressWidth = setProgress; + segments[i as number].showProgress = true; + isValid = false; + } + else { + segments[i as number].progressWidth = segments[i as number].width; + setProgress = setProgress - segments[i as number].progressWidth; + } } } this.parent.setRecordValue('segments', ganttRecord.segments, ganttRecord, true); @@ -2264,14 +2273,12 @@ export class TaskProcessor extends DateProcessor { } this.parent.setRecordValue('width', this.parent.dataOperation.calculateWidth(data), ganttRecord, true); this.parent.setRecordValue('left', this.parent.dataOperation.calculateLeft(ganttRecord), ganttRecord, true); - this.parent.setRecordValue( - 'progressWidth', - this.parent.dataOperation.getProgressWidth( - (ganttRecord.isAutoSchedule || !data.hasChildRecords ? ganttRecord.width : ganttRecord.autoWidth), - ganttRecord.progress), - ganttRecord, - true - ); + if (!isNullOrUndefined(ganttRecord.segments) && ganttRecord.segments.length > 0) { + this.parent.setRecordValue('progressWidth', this.parent.dataOperation.getProgressWidth(totalSegmentsProgressWidth, ganttRecord.progress), ganttRecord, true); + } + else { + this.parent.setRecordValue('progressWidth', this.parent.dataOperation.getProgressWidth((ganttRecord.isAutoSchedule || !data.hasChildRecords ? ganttRecord.width : ganttRecord.autoWidth), ganttRecord.progress), ganttRecord, true); + } } /** * method to update left, width, progress width in record diff --git a/controls/gantt/src/gantt/base/tree-grid.ts b/controls/gantt/src/gantt/base/tree-grid.ts index 6ea2b1c609..cf0d811996 100644 --- a/controls/gantt/src/gantt/base/tree-grid.ts +++ b/controls/gantt/src/gantt/base/tree-grid.ts @@ -26,6 +26,7 @@ export class GanttTreeGrid { * @private */ public currentEditRow: {}; + private registeredTemplate: Object; public addedRecord:boolean; private previousScroll: { top: number, left: number } = { top: 0, left: 0 }; /** @hidden */ @@ -79,6 +80,9 @@ export class GanttTreeGrid { this.bindEvents(); const root: string = 'root'; this.parent.treeGrid[root as string] = this.parent[root as string] ? this.parent[root as string] : this.parent; + setValue('registeredTemplate', this.registeredTemplate, this.parent.treeGrid.grid); + const ref: string = 'viewContainerRef'; + setValue('viewContainerRef', this[`${ref}`], this.parent.treeGrid.grid); this.parent.treeGrid.appendTo(this.treeGridElement); if (this.parent.treeGrid.grid && this.parent.toolbarModule && (this.parent as any).isReact) { (this.parent.treeGrid.grid as any).portals = (this.parent as any).portals; diff --git a/controls/gantt/src/gantt/export/export-helper.ts b/controls/gantt/src/gantt/export/export-helper.ts index 3222e1b7c7..69329454d2 100644 --- a/controls/gantt/src/gantt/export/export-helper.ts +++ b/controls/gantt/src/gantt/export/export-helper.ts @@ -705,9 +705,11 @@ export class ExportHelper { compositeField.draw(footer.graphics, new PointF(0, 0)); pdfDoc.template.bottom = footer; } + const PdfPage = this.parent.pdfExportModule.pdfPage; + const pageSize = PdfPage.size; + const clientSize: SizeF = !isNullOrUndefined(pageSize)? pageSize : this.pdfDoc.pageSettings.size; // code for draw header content if (!isNullOrUndefined(this.exportProps.header)) { - const clientSize: SizeF = this.pdfDoc.pageSettings.size; const headerProp: PdfHeader = this.exportProps.header; const position: PointF = new PointF(0, headerProp.fromTop); const size: SizeF = new SizeF((clientSize.width * 1.1), ((headerProp && headerProp.height) ? headerProp.height * 0.75 : 50)); @@ -717,7 +719,6 @@ export class ExportHelper { } // code for customization of footer if (!this.exportProps.enableFooter && !isNullOrUndefined(this.exportProps.footer)) { - const clientSize: any = this.pdfDoc.pageSettings.size; const footer: any = this.exportProps.footer; const position: PointF = new PointF(0, ((clientSize.width - 80) - ((footer && footer.fromBottom) ? footer.fromBottom * 0.75 : 0))); diff --git a/controls/gantt/src/gantt/export/pdf-taskbar.ts b/controls/gantt/src/gantt/export/pdf-taskbar.ts index 50d16b060b..da6730b370 100644 --- a/controls/gantt/src/gantt/export/pdf-taskbar.ts +++ b/controls/gantt/src/gantt/export/pdf-taskbar.ts @@ -191,13 +191,32 @@ export class PdfGanttTaskbarCollection { progressFormat.alignment = PdfTextAlignment.Left; } let pageIndex: number = -1; + let renderBaselineWidth: number = 0; + if (this.baselineWidth > detail.totalWidth) { + if (this.parent.timelineModule.isZoomedToFit || this.isAutoFit()) { + renderBaselineWidth = detail.totalWidth - this.baselineLeft; + } + else { + renderBaselineWidth = detail.totalWidth; + } + this.baselineWidth = this.baselineWidth - detail.totalWidth; + } + else { + if ((this.parent.timelineModule.isZoomedToFit || this.isAutoFit()) && this.baselineWidth + this.baselineLeft > detail.totalWidth) { + renderBaselineWidth = detail.totalWidth - this.baselineLeft; + } + else { + renderBaselineWidth = this.baselineWidth; + } + } + const baselinePen: PdfPen = new PdfPen(taskbar.baselineBorderColor); + const baselineBrush: PdfBrush = new PdfSolidBrush(taskbar.baselineColor); + let renderedBaseline: boolean = false; if (!taskbar.isMilestone) { const taskbarPen: PdfPen = new PdfPen(taskbar.taskBorderColor); const taskBrush: PdfBrush = new PdfSolidBrush(taskbar.taskColor); - const baselinePen: PdfPen = new PdfPen(taskbar.baselineBorderColor); const manualParentBorderPen = new PdfPen(taskbar.manualParentBorder); const manualChildBorderPen = new PdfPen(taskbar.manualChildBorder) - const baselineBrush: PdfBrush = new PdfSolidBrush(taskbar.baselineColor); const manualTaskbarPen: PdfPen = new PdfPen(taskbar.manuallineColor); const manualParentPen: PdfPen = new PdfPen(taskbar.manualParentProgress); const manualline:PdfPen =new PdfPen(taskbar.manuallineColor) @@ -289,11 +308,12 @@ export class PdfGanttTaskbarCollection { } else { if (this.parent.renderBaseline && taskbar.baselineStartDate && taskbar.baselineEndDate) { if(this.isAutoFit()) { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (renderBaselineWidth), pixelToPoint(this.baselineHeight)); } else { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(renderBaselineWidth), pixelToPoint(this.baselineHeight)); } + renderedBaseline = true; } if (taskbar.isSpliterTask) { splitline.dashStyle = PdfDashStyle.Dot; @@ -526,11 +546,12 @@ export class PdfGanttTaskbarCollection { else { if (this.parent.renderBaseline && taskbar.baselineStartDate && taskbar.baselineEndDate) { if (this.isAutoFit()) { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (renderBaselineWidth), pixelToPoint(this.baselineHeight)); } else { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(renderBaselineWidth), pixelToPoint(this.baselineHeight)); } + renderedBaseline = true; } if (taskbar.isSpliterTask) { let pervwidth = 0; @@ -689,11 +710,12 @@ export class PdfGanttTaskbarCollection { } if (this.parent.renderBaseline && taskbar.baselineStartDate && taskbar.baselineEndDate) { if (this.isAutoFit()) { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (renderBaselineWidth), pixelToPoint(this.baselineHeight)); } else { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(renderBaselineWidth), pixelToPoint(this.baselineHeight)); } + renderedBaseline = true; } if (!this.isScheduledTask && this.unscheduledTaskBy === 'duration') { let brush1: PdfLinearGradientBrush; @@ -866,11 +888,12 @@ export class PdfGanttTaskbarCollection { } if (this.parent.renderBaseline && taskbar.baselineStartDate && taskbar.baselineEndDate) { if (this.isAutoFit()) { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (renderBaselineWidth), pixelToPoint(this.baselineHeight)); } else { - taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(this.baselineWidth), pixelToPoint(this.baselineHeight)); + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(renderBaselineWidth), pixelToPoint(this.baselineHeight)); } + renderedBaseline = true; } if (!this.isScheduledTask && this.unscheduledTaskBy === 'duration') { let brush1: PdfLinearGradientBrush; @@ -1116,6 +1139,15 @@ export class PdfGanttTaskbarCollection { this.drawMilestone(page, startPoint, detail, cumulativeWidth); } } + if (this.baselineEndDate >= detail.startDate && !renderedBaseline && detail.startIndex != 1 && this.parent.renderBaseline && taskbar.baselineStartDate && taskbar.baselineEndDate) { + const adjustHeight: number = pixelToPoint((this.parent.rowHeight - this.height) / 4.5); + if (this.isAutoFit()) { + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + (taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), (renderBaselineWidth), pixelToPoint(this.baselineHeight)); + } + else { + taskGraphics.drawRectangle(baselinePen, baselineBrush, startPoint.x + pixelToPoint(taskbar.baselineLeft - cumulativeWidth) + 0.5, startPoint.y + adjustHeight + pixelToPoint(taskbar.height + 3), pixelToPoint(renderBaselineWidth), pixelToPoint(this.baselineHeight)); + } + } this.drawRightLabel(page, startPoint, detail, cumulativeWidth); return isNextPage; } diff --git a/controls/gantt/styles/gantt/_layout.scss b/controls/gantt/styles/gantt/_layout.scss index 83a2295c1e..1d0e4e802a 100644 --- a/controls/gantt/styles/gantt/_layout.scss +++ b/controls/gantt/styles/gantt/_layout.scss @@ -813,7 +813,7 @@ } .e-zero-spacing { - border-spacing: .25px; + border-spacing: 0; } .e-chart-row:first-child .e-chart-row-border { diff --git a/controls/grids/CHANGELOG.md b/controls/grids/CHANGELOG.md index de36f19f5a..01420dbc14 100644 --- a/controls/grids/CHANGELOG.md +++ b/controls/grids/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Grid + +#### Bug fixes + +- `#FB49473` - Resolved the issue where preventing checkbox selection by setting `isSelectable` property to false was not effective when using a column template in the grid. + ## 24.1.45 (2024-01-09) ### Grid @@ -12,6 +20,7 @@ - `#I520335` - Resolved an issue regarding incorrect `filterBeforeOpen requestType` naming and updated the `options` property scope as public within the `actionBegin` event argument when opening the filter menu. - `#I533690` - Script error thrown when clicking the cancel button in batch edit has been fixed. - `#FB49544` - The problem with `persistSelection` in custom data binding has been successfully resolved. +- `#FB49437` - Auto suggestion not working when using the right click paste issue has been fixed. ## 24.1.44 (2024-01-03) diff --git a/controls/grids/package.json b/controls/grids/package.json index 5f3d1206ab..b0d60d866d 100644 --- a/controls/grids/package.json +++ b/controls/grids/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-grids", - "version": "24.1.44", + "version": "24.1.45", "description": "Feature-rich JavaScript datagrid (datatable) control with built-in support for editing, filtering, grouping, paging, sorting, and exporting to Excel.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/grids/spec/grid/actions/infinite-scroll.spec.ts b/controls/grids/spec/grid/actions/infinite-scroll.spec.ts index cc0a4c5968..9c20304591 100644 --- a/controls/grids/spec/grid/actions/infinite-scroll.spec.ts +++ b/controls/grids/spec/grid/actions/infinite-scroll.spec.ts @@ -654,6 +654,8 @@ describe('Infinite scroll cache mode with frozen columns => ', () => { expect(gridObj.getRows().length).toBe(visibleRowsCount); expect((gridObj as any).contentModule.rowElements.length).toBe(visibleRowsCount); expect(gridObj.getRows()[0].getAttribute('data-rowindex')).toBe(gridObj.pageSettings.pageSize.toString()); + // EJ2-850288 - Performance Improvement - Dynamically changing grid height with infinite scrolling + expect((gridObj as any).infiniteScrollModule.infiniteCache[2].length).toBeTruthy(); }); afterAll(() => { diff --git a/controls/grids/src/grid/actions/infinite-scroll.ts b/controls/grids/src/grid/actions/infinite-scroll.ts index 0ef423b24e..543acb04a7 100644 --- a/controls/grids/src/grid/actions/infinite-scroll.ts +++ b/controls/grids/src/grid/actions/infinite-scroll.ts @@ -28,7 +28,6 @@ export class InfiniteScroll implements IAction { private dataBoundFunction: Function; private infiniteCache: { [x: number]: Row[] } = {}; private infiniteCurrentViewData: { [x: number]: Object[] } = {}; - private infiniteFrozenCache: { [x: number]: Row[][] } = {}; private isDownScroll: boolean = false; private isUpScroll: boolean = false; private isScroll: boolean = true; @@ -45,7 +44,6 @@ export class InfiniteScroll implements IAction { protected cellIndex: number; private rowTop: number = 0; private empty: number | string; - private isInitialMovableRender: boolean = true; private editRowIndex: number; private virtualInfiniteData: Object = {}; private isAdd: boolean; @@ -831,7 +829,6 @@ export class InfiniteScroll implements IAction { const isCache: boolean = this.parent.infiniteScrollSettings.enableCache; if (isCache) { this.infiniteCache = {}; - this.infiniteFrozenCache = {}; this.infiniteCurrentViewData = {}; query.skip(this.firstIndex); query.take(initialBlocks * this.parent.pageSettings.pageSize); @@ -1181,9 +1178,6 @@ export class InfiniteScroll implements IAction { if (this.infiniteCache[args.prevPage].length < pageSize) { cnt = this.parent.pageSettings.pageSize - this.infiniteCache[args.prevPage].length; } - if (this.parent.isFrozenGrid() && this.infiniteFrozenCache[args.prevPage][1].length < pageSize) { - cnt = this.parent.pageSettings.pageSize - this.infiniteFrozenCache[args.prevPage][1].length; - } for (let i: number = maxIndx; cnt < pageSize; i--) { cnt++; remove(rows[parseInt(i.toString(), 10)]); @@ -1227,12 +1221,11 @@ export class InfiniteScroll implements IAction { this.initialRender = true; scrollEle.scrollTop = 0; this.parent.pageSettings.currentPage = 1; - this.infiniteCache = this.infiniteFrozenCache = {}; + this.infiniteCache = {}; this.infiniteCurrentViewData = {}; this.resetContentModuleCache({}); this.isRemove = false; this.top = 0; - this.isInitialMovableRender = true; this.isInitialCollapse = false; (<{ isRemove?: boolean }>(this.parent as Grid).contentModule).isRemove = this.isRemove; (<{ isAddRows?: boolean }>(this.parent as Grid).contentModule).isAddRows = this.isRemove; @@ -1247,9 +1240,7 @@ export class InfiniteScroll implements IAction { const isEdit: boolean = e.args.requestType !== 'infiniteScroll' && (this.requestType === 'delete' || this.requestType === 'add'); const currentPage: number = this.parent.pageSettings.currentPage; - if ((this.parent.isFrozenGrid() && this.isInitialMovableRender) || - (!this.parent.isFrozenGrid() && !Object.keys(this.infiniteCache).length) || isEdit) { - this.isInitialMovableRender = !e.args.isFrozen; + if (!Object.keys(this.infiniteCache).length || isEdit) { this.setInitialCache(e.modelData, e.args, isEdit); } if (isNullOrUndefined(this.infiniteCache[this.parent.pageSettings.currentPage])) { diff --git a/controls/grids/src/grid/base/grid.ts b/controls/grids/src/grid/base/grid.ts index 9659829bfc..480ed37dc8 100644 --- a/controls/grids/src/grid/base/grid.ts +++ b/controls/grids/src/grid/base/grid.ts @@ -3628,9 +3628,12 @@ export class Grid extends Component implements INotifyPropertyChang this.removeMediaListener(); this.notify(events.destroy, {}); this.destroyDependentModules(); - if ((<{ isReact?: boolean }>this).isReact || (<{ isVue?: boolean }>this).isVue) { + if ((<{ isReact?: boolean }>this).isReact) { this.destroyTemplate(['template']); } + if ((<{ isVue?: boolean }>this).isVue) { + this.destroyTemplate(); + } if (hasGridChild) { super.destroy(); } this.toolTipObj.destroy(); if ((<{ isReact?: boolean }>this).isReact && !Browser.isIE) { diff --git a/controls/grids/src/grid/common/checkbox-filter-base.ts b/controls/grids/src/grid/common/checkbox-filter-base.ts index 76ac7d0f15..8c61aca4ed 100644 --- a/controls/grids/src/grid/common/checkbox-filter-base.ts +++ b/controls/grids/src/grid/common/checkbox-filter-base.ts @@ -135,6 +135,7 @@ export class CheckBoxFilterBase { const elem: Element = this.dialogObj.element.querySelector('.e-searchinput'); if (elem) { EventHandler.add(elem, 'keyup', this.searchHandler, this); + EventHandler.add(elem, 'input', this.searchHandler, this); } } @@ -144,6 +145,7 @@ export class CheckBoxFilterBase { const elem: Element = this.dialogObj.element.querySelector('.e-searchinput'); if (elem) { EventHandler.remove(elem, 'keyup', this.searchHandler); + EventHandler.remove(elem, 'input', this.searchHandler); } } diff --git a/controls/grids/src/grid/renderer/row-renderer.ts b/controls/grids/src/grid/renderer/row-renderer.ts index 719097aa00..113106040d 100644 --- a/controls/grids/src/grid/renderer/row-renderer.ts +++ b/controls/grids/src/grid/renderer/row-renderer.ts @@ -243,7 +243,7 @@ export class RowRenderer implements IRowRenderer { thisRef.parent.trigger(rowDataBound, eventArg); if (!eventArg.isSelectable) { row.isSelectable = eventArg.isSelectable; - this.disableRowSelection(thisRef, row, args, eventArg); + thisRef.disableRowSelection(thisRef, row, args, eventArg); } }); } diff --git a/controls/grids/styles/grid/_layout.scss b/controls/grids/styles/grid/_layout.scss index 992570e90e..58fdcb4180 100644 --- a/controls/grids/styles/grid/_layout.scss +++ b/controls/grids/styles/grid/_layout.scss @@ -1064,7 +1064,8 @@ & tr.e-row:first-child .e-rowcell.e-dragborder, & .e-rowcell.e-dragborder, & .e-rowdragdrop.e-dragborder, - & .e-detailrowcollapse.e-dragborder { + & .e-detailrowcollapse.e-dragborder, + & .e-detailrowexpand.e-dragborder { z-index: $grid-dragborder-z-index; } } @@ -1083,7 +1084,10 @@ & .e-rowdragdrop.e-dragborder.e-fixedfreeze, & .e-detailrowcollapse.e-dragborder.e-leftfreeze, & .e-detailrowcollapse.e-dragborder.e-rightfreeze, - & .e-detailrowcollapse.e-dragborder.e-fixedfreeze { + & .e-detailrowcollapse.e-dragborder.e-fixedfreeze, + & .e-detailrowexpand.e-dragborder.e-leftfreeze, + & .e-detailrowexpand.e-dragborder.e-rightfreeze, + & .e-detailrowexpand.e-dragborder.e-fixedfreeze { z-index: 6; } } @@ -1094,7 +1098,8 @@ & tr.e-row:first-child .e-rowcell.e-dragborder, & .e-rowcell.e-dragborder, & .e-rowdragdrop.e-dragborder, - & .e-detailrowcollapse.e-dragborder { + & .e-detailrowcollapse.e-dragborder, + & .e-detailrowexpand.e-dragborder { box-shadow: $grid-dragborder-box-shadow; } } @@ -1130,7 +1135,8 @@ & tr.e-row:first-child .e-rowcell.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze), & .e-rowcell.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze), & .e-rowdragdrop.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze), - & .e-detailrowcollapse.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze) { + & .e-detailrowcollapse.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze) + & .e-detailrowexpand.e-dragborder:not(.e-leftfreeze, .e-rightfreeze, .e-fixedfreeze) { position: $grid-dragborder-position; } } @@ -4522,6 +4528,11 @@ margin: $grid-group-animator-cell-margin; overflow-y: auto; } + .e-gridcontent .e-content, + .e-detailrowcollapse.e-dragborder, + .e-detailrowexpand.e-dragborder{ + position: $grid-dragborder-position; + } .e-gridcontent .e-content.e-yscroll { overflow-y: scroll; } diff --git a/controls/heatmap/CHANGELOG.md b/controls/heatmap/CHANGELOG.md index aee5ea845e..4484409074 100644 --- a/controls/heatmap/CHANGELOG.md +++ b/controls/heatmap/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### HeatMap diff --git a/controls/imageeditor/CHANGELOG.md b/controls/imageeditor/CHANGELOG.md index ffc2f4173c..df28ca48e7 100644 --- a/controls/imageeditor/CHANGELOG.md +++ b/controls/imageeditor/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Image Editor + +#### Bug Fixes + +- The issue with "Zoom events not triggered while using Zoom method" has been resolved. + +- The issue with "Ratio Selection dimension not proper" has been resolved. + +- The issue with "Get Shape Settings method returns duplicate shape id" has been resolved. + ## 24.1.45 (2024-01-09) ### Image Editor diff --git a/controls/imageeditor/package.json b/controls/imageeditor/package.json index ff5ec427e5..38cdc339c3 100644 --- a/controls/imageeditor/package.json +++ b/controls/imageeditor/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-image-editor", - "version": "24.1.44", + "version": "24.1.45", "description": "Essential JS 2 ImageEditor", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", @@ -16,7 +16,6 @@ "@syncfusion/ej2-splitbuttons": "*" }, "devDependencies": { - "json-sql": "^0.4.11", "@types/chai": "^3.4.28", "@types/es6-promise": "0.0.28", "@types/jasmine": "2.8.9", diff --git a/controls/imageeditor/spec/image-editor.spec.ts b/controls/imageeditor/spec/image-editor.spec.ts index 70b78f1607..49c82a6c7d 100644 --- a/controls/imageeditor/spec/image-editor.spec.ts +++ b/controls/imageeditor/spec/image-editor.spec.ts @@ -8102,37 +8102,37 @@ describe('ImageEditor', () => { imageEditor.select('2:3', {x: 100, y: 100}, 500, 500); expect(imageEditor.activeObj.shape).toEqual('crop-2:3'); imageEditor.crop(); - expect(imageEditor.img.destLeft).toEqual(260.103305785124); + expect(imageEditor.img.destLeft).toEqual(257.5); expect(imageEditor.img.destTop).toEqual(15.5); - expect(imageEditor.img.destWidth).toEqual(246.79338842975204); + expect(imageEditor.img.destWidth).toEqual(252); expect(imageEditor.img.destHeight).toEqual(378); imageEditor.select('3:4', {x: 100, y: 100}, 500, 500); expect(imageEditor.activeObj.shape).toEqual('crop-3:4'); imageEditor.crop(); - expect(imageEditor.img.destLeft).toEqual(243.70247933884298); + expect(imageEditor.img.destLeft).toEqual(241.75); expect(imageEditor.img.destTop).toEqual(15.5); - expect(imageEditor.img.destWidth).toEqual(279.59504132231405); + expect(imageEditor.img.destWidth).toEqual(283.5); expect(imageEditor.img.destHeight).toEqual(378); imageEditor.select('4:5', {x: 100, y: 100}, 500, 500); expect(imageEditor.activeObj.shape).toEqual('crop-4:5'); imageEditor.crop(); - expect(imageEditor.img.destLeft).toEqual(233.86198347107444); + expect(imageEditor.img.destLeft).toEqual(232.30000000000004); expect(imageEditor.img.destTop).toEqual(15.5); - expect(imageEditor.img.destWidth).toEqual(299.2760330578511); + expect(imageEditor.img.destWidth).toEqual(302.3999999999999); expect(imageEditor.img.destHeight).toEqual(378); imageEditor.select('5:7', {x: 100, y: 100}, 500, 500); expect(imageEditor.activeObj.shape).toEqual('crop-5:7'); imageEditor.crop(); - expect(imageEditor.img.destLeft).toEqual(250.7314049586777); + expect(imageEditor.img.destLeft).toEqual(248.5); expect(imageEditor.img.destTop).toEqual(15.5); - expect(imageEditor.img.destWidth).toEqual(265.5371900826446); + expect(imageEditor.img.destWidth).toEqual(270); expect(imageEditor.img.destHeight).toEqual(378); imageEditor.select('9:16'); expect(imageEditor.activeObj.shape).toEqual('crop-9:16'); imageEditor.crop(); - expect(imageEditor.img.destLeft).toEqual(280.6043388429752); + expect(imageEditor.img.destLeft).toEqual(277.1875); expect(imageEditor.img.destTop).toEqual(15.5); - expect(imageEditor.img.destWidth).toEqual(205.7913223140496); + expect(imageEditor.img.destWidth).toEqual(212.625); expect(imageEditor.img.destHeight).toEqual(378); done(); }, 100); diff --git a/controls/imageeditor/src/image-editor/action/selection.ts b/controls/imageeditor/src/image-editor/action/selection.ts index 89a2eec4fe..e66143e756 100644 --- a/controls/imageeditor/src/image-editor/action/selection.ts +++ b/controls/imageeditor/src/image-editor/action/selection.ts @@ -3782,8 +3782,8 @@ export class Selection { actPoint.height = height; actPoint.startX = (this.dragPoint.startX = (originalWidth - width) / 2) + arcRadius; actPoint.startY = (this.dragPoint.startY = (originalHeight - height) / 2) + arcRadius; - actPoint.endX = ((originalWidth - width) / 2 + width) - arcRadius; - actPoint.endY = ((originalHeight - height) / 2 + height) - arcRadius; + actPoint.endX = actPoint.startX + actPoint.width; + actPoint.endY = actPoint.startY + actPoint.height; if (actPoint.startX < destLeft && destLeft + destWidth > parent.lowerCanvas.clientWidth) { actPoint.startX = destLeft; actPoint.endX = actPoint.startX + width - arcRadius; @@ -4229,7 +4229,9 @@ export class Selection { } if (!isInside) { if (isNullOrUndefined(parent.activeObj.currIndex)) { - parent.activeObj.currIndex = 'shape_' + (parent.objColl.length + 1); + const shapeIDObj: Object = {id: 'shape_' + (parent.objColl.length + 1) }; + parent.notify('shape', { prop: 'getNewShapeId', onPropertyChange: false, value: {obj: shapeIDObj }}); + parent.activeObj.currIndex = shapeIDObj['id']; } parent.notify('shape', { prop: 'updImgRatioForActObj', onPropertyChange: false}); if (parent.activeObj.horTopLine !== undefined && parent.activeObj.horTopLine.startX !== 0 && parent.activeObj.horTopLine.endX diff --git a/controls/imageeditor/src/image-editor/action/shape.ts b/controls/imageeditor/src/image-editor/action/shape.ts index df23a297bf..74da7a7e08 100644 --- a/controls/imageeditor/src/image-editor/action/shape.ts +++ b/controls/imageeditor/src/image-editor/action/shape.ts @@ -267,6 +267,9 @@ export class Shape { case 'setFlipState': this.setFlipState(args.value['x'], args.value['y'], args.value['obj'], args.value['object']); break; + case 'getNewShapeId': + args.value['obj']['id'] = this.getNewShapeId(); + break; } } @@ -651,6 +654,7 @@ export class Shape { parent.notify('selection', { prop: 'isShapeInserted', onPropertyChange: false, value: {bool: true} }); if (isBlazor()) { parent.updateToolbar(parent.element, 'text'); + parent.getFontSizes(); } else { parent.notify('toolbar', { prop: 'refresh-toolbar', onPropertyChange: false, value: {type: 'text', isApplyBtn: null, isCropping: null, isZooming: null, cType: null}}); @@ -2474,7 +2478,7 @@ export class Shape { } if (!isActObj) { if (isNullOrUndefined(parent.activeObj.currIndex)) { - parent.activeObj.currIndex = 'shape_' + (parent.objColl.length + 1); + parent.activeObj.currIndex = this.getNewShapeId(); } this.updImgRatioForActObj(); const splitWords: string[] = parent.activeObj.currIndex.split('_'); @@ -2505,6 +2509,17 @@ export class Shape { } } + private getNewShapeId(): string { + const parent: ImageEditor = this.parent; + let value: number = parent.objColl.length + 1; + for (let i: number = 0; i < parent.objColl.length; i++) { + if (parent.objColl[i as number].currIndex === 'shape_' + value) { + value++; i = -1; + } + } + return 'shape_' + value; + } + private alignTextAreaIntoCanvas(): void { const parent: ImageEditor = this.parent; const letters: string = parent.textArea.value; parent.textArea.value = ''; @@ -2601,6 +2616,7 @@ export class Shape { case 'text': shapeDetails.text = obj.keyHistory; shapeDetails.fontSize = obj.textSettings.fontSize; + shapeDetails.fontFamily = obj.textSettings.fontFamily; shapeDetails.color = obj.strokeSettings.strokeColor; shapeDetails.fontStyle = []; if (obj.textSettings.bold) {shapeDetails.fontStyle.push('bold'); } diff --git a/controls/imageeditor/src/image-editor/base/image-editor.ts b/controls/imageeditor/src/image-editor/base/image-editor.ts index 2d993b96aa..6325ae1dba 100644 --- a/controls/imageeditor/src/image-editor/base/image-editor.ts +++ b/controls/imageeditor/src/image-editor/base/image-editor.ts @@ -1840,6 +1840,7 @@ export class ImageEditor extends Component implements INotifyPro * */ public zoom(zoomFactor: number, zoomPoint?: Point): void { + this.isZoomBtnClick = true; this.notify('transform', { prop: 'zoom', onPropertyChange: false, value: {zoomFactor: zoomFactor, zoomPoint: zoomPoint}}); this.notify('draw', { prop: 'redrawDownScale' }); diff --git a/controls/inputs/CHANGELOG.md b/controls/inputs/CHANGELOG.md index 3b441825fa..c3c317a4c2 100644 --- a/controls/inputs/CHANGELOG.md +++ b/controls/inputs/CHANGELOG.md @@ -8,6 +8,14 @@ #### Bug Fixes +- `#I510300` - Issue with "drawn points will be in a significantly different location than the cursor while zooming" has been resolved. + +## 24.1.41 (2023-12-18) + +### Signature + +#### Bug Fixes + - `#I510300` - Issue with "`isEmpty` method return wrong value in signature control" has been resolved. ### ColorPicker diff --git a/controls/inputs/package.json b/controls/inputs/package.json index ed78c20b07..ed5ba8b282 100644 --- a/controls/inputs/package.json +++ b/controls/inputs/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-inputs", - "version": "18.78.12", + "version": "24.1.45", "description": "A package of Essential JS 2 input components such as Textbox, Color-picker, Masked-textbox, Numeric-textbox, Slider, Upload, and Form-validator that is used to get input from the users.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/inputs/spec/rating.spec.ts b/controls/inputs/spec/rating.spec.ts index 31a4691979..540ea904d9 100644 --- a/controls/inputs/spec/rating.spec.ts +++ b/controls/inputs/spec/rating.spec.ts @@ -1027,6 +1027,28 @@ describe('Rating', () => { expect(document.body.querySelector('.e-rating-tooltip-content').innerHTML).toEqual('testTemplate'); }); + it('Tooltip with dynamic template', (done) => { + let template = '${if(value==1)}Angry ${else if(value==2)}Sad ${else if(value==3)}Neutral ${else if(value==4)}Good ${else}Happy${/if}'; + let renderer = createElement("script", { id: "tooltipTemplate", innerHTML: template }); + renderer.setAttribute("type", "text/x-jsrender"); + document.body.appendChild(renderer); + rating = new Rating({ + showTooltip: true, + tooltipTemplate: '#tooltipTemplate' + }); + rating.appendTo('#rating'); + (rating as any).isAngular = true; + let liElementArray: any = ratingElement.parentElement.querySelectorAll('.e-rating-item-container'); + expect(ratingElement.parentElement.querySelector('.e-rating-item-list').classList.contains('e-tooltip')).toBe(true); + mouseEventArs.target = liElementArray[1]; + EventHandler.trigger(liElementArray[1], "mousemove"); + (rating as any).tooltipObj.open(liElementArray[1]); + expect(document.body.querySelector('.e-rating-tooltip-content') != null).toEqual(true); + expect(document.body.querySelector('.e-rating-tooltip-content').innerHTML).toEqual('Sad '); + /* To Signal that the asynchronous code has completed */ + setTimeout(() => { done(); }); + }); + it('Custom tooltip using cssClass', () => { rating = new Rating({ cssClass: 'testClass', diff --git a/controls/inputs/src/common/signature-base.ts b/controls/inputs/src/common/signature-base.ts index 758e544af0..a60f77a40f 100644 --- a/controls/inputs/src/common/signature-base.ts +++ b/controls/inputs/src/common/signature-base.ts @@ -161,7 +161,6 @@ export abstract class SignatureBase extends Component { this.element.height = this.element.offsetHeight; this.element.width = this.element.offsetWidth; } - this.canvasContext.scale(1, 1); this.canvasContext.fillStyle = this.strokeColor; this.tempCanvas = this.createElement('canvas', { className: 'e-' + this.getModuleName() + '-temp' }); this.tempContext = this.tempCanvas.getContext('2d'); diff --git a/controls/inputs/src/rating/rating.ts b/controls/inputs/src/rating/rating.ts index 8f891877bf..04b282442e 100644 --- a/controls/inputs/src/rating/rating.ts +++ b/controls/inputs/src/rating/rating.ts @@ -409,6 +409,7 @@ export class Rating extends Component implements INotifyPropertyCha private tooltipOpen: boolean; private isReact?: boolean; private isTouchSelected: boolean; + private isAngular: boolean; /** * Constructor for creating the widget @@ -679,6 +680,12 @@ export class Rating extends Component implements INotifyPropertyCha const templateFunction: Function = this.getTemplateString(this.tooltipTemplate); append(templateFunction({ value: this.currentValue }, this, 'ratingTooltipTemplate', (this.element.id + 'tooltipTemplate'), this.isStringTemplate), (content as HTMLElement)); this.tooltipObj.setProperties({ content: content }, isChange); + if (this.isAngular) { + setTimeout(() => { + let ratingSpan: NodeListOf = this.ratingItemList.querySelectorAll('.' +ITEMCONTAINER+ '.' +SELECTED); + this.tooltipObj.refresh(ratingSpan[ratingSpan.length -1]); + }); + } } else { content = this.currentValue.toString(); diff --git a/controls/inputs/styles/data-form/_material3-definition.scss b/controls/inputs/styles/data-form/_material3-definition.scss index 52b8813c3c..6951efd618 100644 --- a/controls/inputs/styles/data-form/_material3-definition.scss +++ b/controls/inputs/styles/data-form/_material3-definition.scss @@ -26,7 +26,7 @@ $form-group-title-border-height: 1px !default; $form-group-title-border-bottom-style: 1px solid $group-title-border-color !default; //tooltip styles -$form-tooltip-background-color: $danger-light !default; +$form-tooltip-background-color: var(--danger-light, #f9dedc) !default; $form-tooltip-color: var(--danger, #b3261e) !default; $form-validation-message-font-size: 12px; diff --git a/controls/kanban/CHANGELOG.md b/controls/kanban/CHANGELOG.md index 1cd9f8dce3..a8b0e5745f 100644 --- a/controls/kanban/CHANGELOG.md +++ b/controls/kanban/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Kanban + +#### Bug Fixes + +`#I535989` - Now, drop clone works properly when slowly dragging and dropping the cards in the last position in the Kanban column. + ## 24.1.41 (2023-12-18) ### Kanban diff --git a/controls/kanban/package.json b/controls/kanban/package.json index debf6acf22..057352e966 100644 --- a/controls/kanban/package.json +++ b/controls/kanban/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-kanban", - "version": "24.1.43", + "version": "24.1.45", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", "main": "./dist/ej2-kanban.umd.min.js", diff --git a/controls/kanban/src/kanban/actions/drag.ts b/controls/kanban/src/kanban/actions/drag.ts index 611199fe17..b06808780c 100644 --- a/controls/kanban/src/kanban/actions/drag.ts +++ b/controls/kanban/src/kanban/actions/drag.ts @@ -152,17 +152,12 @@ export class DragAndDrop { cardElement = (e.target as HTMLElement).previousElementSibling.querySelector('.e-target-dropped-clone').nextElementSibling as HTMLElement; } let targetEle: HTMLElement = e.target as HTMLElement; - const firstCloneCard: HTMLElement = this.parent.element.querySelector('.e-target-dropped-clone'); - let previousSiblingCard: HTMLElement = null; - if (!isNoU(firstCloneCard)) { - previousSiblingCard = firstCloneCard.previousSibling as HTMLElement; - } if (!isNoU((e.target as HTMLElement).parentElement)) { if ((e.target as HTMLElement).nodeName === 'SPAN' && (e.target as HTMLElement).classList.contains('e-empty-card')) { targetEle = (e.target as HTMLElement).parentElement; } else if ((e.target as HTMLElement).nodeName === 'DIV' && (e.target as HTMLElement).classList.contains('e-kanban-border')) { - if (!isNoU(previousSiblingCard) || !isNoU((e.target as HTMLElement).parentElement.querySelector('.e-empty-card'))) { + if (!(this.parent.element.querySelector('.e-target-dropped-clone') === (e.target as HTMLElement).nextElementSibling.firstChild)) { targetEle = (e.target as HTMLElement).parentElement; } } diff --git a/controls/lineargauge/CHANGELOG.md b/controls/lineargauge/CHANGELOG.md index 7c03fe2df2..e1ca79c487 100644 --- a/controls/lineargauge/CHANGELOG.md +++ b/controls/lineargauge/CHANGELOG.md @@ -8,7 +8,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### LinearGauge diff --git a/controls/maps/CHANGELOG.md b/controls/maps/CHANGELOG.md index efb16d2167..4e2cc77818 100644 --- a/controls/maps/CHANGELOG.md +++ b/controls/maps/CHANGELOG.md @@ -8,7 +8,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### Maps diff --git a/controls/navigations/CHANGELOG.md b/controls/navigations/CHANGELOG.md index 60c840fa0f..4eb6360b65 100644 --- a/controls/navigations/CHANGELOG.md +++ b/controls/navigations/CHANGELOG.md @@ -2,6 +2,26 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Menu + +#### Bug Fixes + +- `#F519984` - The issue with "Keyboard action is not working properly in the Menu Component" has been resolved. + +### TreeView + +#### Bug Fixes + +- `#I531520` - TreeView component's title not decoded properly same as in tree node text has been resolved. + +### Toolbar + +#### Bug fixes + +- `#I508465` - The issue with the differentiate toolbar active and focus states has been resolved. + ## 24.1.41 (2023-12-18) ### TreeView diff --git a/controls/navigations/spec/stepper.spec.ts b/controls/navigations/spec/stepper.spec.ts index 9e08b27bab..1a07d6300f 100644 --- a/controls/navigations/spec/stepper.spec.ts +++ b/controls/navigations/spec/stepper.spec.ts @@ -778,7 +778,7 @@ describe('Stepper', () => { expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(4); expect(stepperElement.querySelectorAll('.e-step-completed').length).toBe(0); - + }); it('stepper icon with start label position', () => { @@ -1924,22 +1924,21 @@ describe('Stepper', () => { keyboardEventArgs.action = 'uparrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); + expect((liElementArray[0] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); keyboardEventArgs.action = 'downarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); - expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'rightarrow'; - (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); - keyboardEventArgs.action = 'rightarrow'; + keyboardEventArgs.action = 'tab'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); + expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); keyboardEventArgs.action = 'rightarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[3] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); @@ -1955,7 +1954,7 @@ describe('Stepper', () => { expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'leftarrow'; + keyboardEventArgs.action = 'shiftTab'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); @@ -1978,7 +1977,7 @@ describe('Stepper', () => { expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); keyboardEventArgs.action = 'enter'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(0); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(2); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-inprogress')).toEqual(true); keyboardEventArgs.action = 'rightarrow'; @@ -1986,7 +1985,7 @@ describe('Stepper', () => { expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); keyboardEventArgs.action = 'space'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(0); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(1); expect((liElementArray[2] as HTMLElement).classList.contains('e-step-inprogress')).toEqual(true); keyboardEventArgs.action = 'home'; @@ -2011,23 +2010,23 @@ describe('Stepper', () => { (stepper as any).keyActionHandler(keyboardEventArgs); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(0); expect((liElementArray[3] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); - keyboardEventArgs.action = 'rightarrow'; + keyboardEventArgs.action = 'leftarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); + expect((liElementArray[0] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); keyboardEventArgs.action = 'enter'; (stepper as any).keyActionHandler(keyboardEventArgs); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); - expect((liElementArray[2] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); + expect((liElementArray[3] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); keyboardEventArgs.action = 'rightarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[3] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); + expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); keyboardEventArgs.action = 'enter'; (stepper as any).keyActionHandler(keyboardEventArgs); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect((liElementArray[3] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); keyboardEventArgs.action = 'tab'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(0); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); // for coverage keyboardEventArgs.target = stepperElement.querySelector('.e-step-container'); keyboardEventArgs.action = 'rightarrow'; @@ -2052,20 +2051,18 @@ describe('Stepper', () => { expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); keyboardEventArgs.action = 'rightarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); - expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'downarrow'; - (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); keyboardEventArgs.action = 'downarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'downarrow'; + expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); + keyboardEventArgs.action = 'tab'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[3] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); @@ -2080,12 +2077,12 @@ describe('Stepper', () => { expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'uparrow'; + keyboardEventArgs.action = 'leftarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'uparrow'; + keyboardEventArgs.action = 'shiftTab'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[0] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); @@ -2103,7 +2100,7 @@ describe('Stepper', () => { expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); keyboardEventArgs.action = 'enter'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(0); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(2); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-inprogress')).toEqual(true); keyboardEventArgs.action = 'downarrow'; @@ -2111,7 +2108,7 @@ describe('Stepper', () => { expect((liElementArray[2] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); keyboardEventArgs.action = 'space'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(0); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(1); expect((liElementArray[2] as HTMLElement).classList.contains('e-step-inprogress')).toEqual(true); (stepper as any).updateStepFocus(); @@ -2133,11 +2130,16 @@ describe('Stepper', () => { keyboardEventArgs.target = stepperElement.querySelector('.e-step-container'); keyboardEventArgs.action = 'uparrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); + expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); + expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); keyboardEventArgs.action = 'downarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); + expect((liElementArray[0] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); + expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); + expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); keyboardEventArgs.action = 'leftarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); @@ -2261,14 +2263,9 @@ describe('Stepper', () => { expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); keyboardEventArgs.action = 'downarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); - expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); - expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - keyboardEventArgs.action = 'rightarrow'; - (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[1] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); - expect(stepperElement.querySelectorAll('.e-step-focus').length).toBe(1); + expect((liElementArray[0] as HTMLElement).classList.contains('e-step-selected')).toEqual(true); expect(stepperElement.querySelectorAll('.e-step-notstarted').length).toBe(3); - expect(stepperElement.querySelectorAll('.e-step-selected').length).toBe(1); keyboardEventArgs.action = 'rightarrow'; (stepper as any).keyActionHandler(keyboardEventArgs); expect((liElementArray[3] as HTMLElement).classList.contains('e-step-focus')).toEqual(true); diff --git a/controls/navigations/spec/treeview/treeview.spec.ts b/controls/navigations/spec/treeview/treeview.spec.ts index 0fcb036a21..f9476ea123 100644 --- a/controls/navigations/spec/treeview/treeview.spec.ts +++ b/controls/navigations/spec/treeview/treeview.spec.ts @@ -2147,6 +2147,11 @@ describe('TreeView control', () => { treeObj.dataBind(); expect(treeObj.element.querySelectorAll('li').length).toBe(0); }); + it('dragArea property testing', () => { + treeObj.allowDragAndDrop = true; + treeObj.dragArea = '#tree1'; + treeObj.dataBind(); + }) }); describe('mouse events testing', () => { let mouseEventArgs: any; @@ -2983,7 +2988,17 @@ describe('TreeView control', () => { treeObj.touchClickObj.tap(tapEvent); expect(i).toEqual(1); }); - + it('nodeExpanding allowTextWrap check', () => { + treeObj = new TreeView({ + fields: { dataSource: hierarchicalData1, id: "nodeId", text: "nodeText", child:"nodeChild" }, + nodeExpanding: clickFn, + allowTextWrap: true + },'#tree1'); + let li: Element[] = >treeObj.element.querySelectorAll('li'); + mouseEventArgs.target = li[0].querySelector('.e-icons'); + treeObj.touchClickObj.tap(tapEvent); + expect(i).toEqual(1); + }); it('Ripple Effect testing', () => { enableRipple(true); treeObj = new TreeView({ @@ -9575,6 +9590,23 @@ describe('TreeView control', () => { expect(document.getElementById('tree1').classList.contains('customTree')).toEqual(true); expect(document.getElementById('tree1').classList.contains('productTree')).toEqual(true); }); + it('checking the type of the child node in remote data', () => { + let data: DataManager = new DataManager({ + url: 'https://services.odata.org/V4/Northwind/Northwind.svc', + adaptor: new ODataV4Adaptor, + crossDomain: true, + }); + let query: Query = new Query().from('Employees').select('EmployeeID,FirstName,Title').take(5); + let query1: Query = new Query().from('Orders').select('OrderID,EmployeeID,ShipName').take(5); + + let treeObj1: TreeView = new TreeView({ + fields: { dataSource: data, query: query, id: 'EmployeeID', text: 'FirstName', hasChildren: 'EmployeeID', + child: { dataSource: data, query: query1, id: 'OrderID', parentID: 'EmployeeID', text: 'ShipName' } + } + }); + expect((treeObj1 as any).isChildObject()).toBe(true); + expect((treeObj as any).isChildObject()).toBe(false); + }); }); describe('Add nodes method', () => { let mouseEventArgs: any = { @@ -9590,6 +9622,7 @@ describe('TreeView control', () => { tapCount: 1 }; let treeObj: any; + let i = 0; let ele: HTMLElement = createElement('div', { id: 'tree1' }); let dataManager1: DataManager let originalTimeout: any; @@ -9633,6 +9666,84 @@ describe('TreeView control', () => { done(); }, 450); }); + it('update node testing for remote data', (done: Function) => { + let li: Element[] = >treeObj.element.querySelectorAll('li'); + treeObj.allowEditing = true; + treeObj.updateNode(li[0], 'John'); + expect(treeObj.getTreeData('01')[0].nodeText).toBe('John'); + done(); + }); + it('remove node testing for remote data', (done: Function) => { + expect(treeObj.getTreeData().length).toBe(26); + treeObj.removeNodes(['01']); + expect(treeObj.getTreeData().length).toBe(24); + done(); + }); + it('field property change', function (done) { + treeObj.isReact = true; + treeObj.fields.tooltip = 'John'; + treeObj.dataBind(); + expect(i).toBe(0); + done(); + }); + it('refresh node testing', (done: Function) => { + var data = [{ nodeText: 'RefreshedNode', nodeIcon: 'folder'}]; + treeObj.refreshNode('01', data); + treeObj.dataBind(); + done(); + }); + it('Add nodes testing', (done: Function) => { + let data = [{nodeId: 1, nodeText: 'John'}]; + treeObj.addNodes(data, '03', 2, true); + treeObj.dataBind(); + done(); + }); + it('Checking null in refresh node method', (done: Function) => { + treeObj.refreshNode(null, null); + treeObj.dataBind(); + done(); + }); + it('Checking fullRowSelect field property', (done: Function) => { + treeObj.fullRowSelect = true; + treeObj.allowTextWrap = true; + treeObj.dataBind(); + done(); + }); + it('checking child nodes', (done: Function) => { + let customDataSource = [{ EmployeeID: 1, FirstName: 'John', Title: 'Manager', + Shipments: [ + { OrderID: 1, ShipName: 'Ship1' }, + { OrderID: 2, ShipName: 'Ship2' }, + ] + }, + { EmployeeID: 2, FirstName: 'Jane', Title: 'Supervisor', + Shipments: [ + { OrderID: 3, ShipName: 'Ship3' }, + { OrderID: 4, ShipName: 'Ship4' }, + ] + }, + ]; + + let newTreeObj: TreeView = new TreeView({ + fields: { + dataSource: customDataSource, + id: 'EmployeeID', + text: 'FirstName', + hasChildren: 'Shipments', // Use 'Shipments' as the property indicating child nodes + child: { + dataSource: customDataSource.reduce((acc, employee) => acc.concat(employee.Shipments), []), + // query: query1, + id: 'OrderID', + text: 'ShipName' + } + } + }); + treeObj.fields.child = newTreeObj.fields.child; + treeObj.dataType = 2 + treeObj.getChildNodes(customDataSource, '03', null, 2) + treeObj.dataBind(); + done(); + }); }); xdescribe('with id as number', () => { let mouseEventArgs: any = { diff --git a/controls/navigations/src/common/menu-base.ts b/controls/navigations/src/common/menu-base.ts index febeb173bf..c1a9310b19 100644 --- a/controls/navigations/src/common/menu-base.ts +++ b/controls/navigations/src/common/menu-base.ts @@ -804,7 +804,24 @@ export abstract class MenuBase extends Component implements IN fli.classList.add(SELECTED); eventArgs = { element: fli as HTMLElement, item: item, event: e }; this.trigger('select', eventArgs); + const aEle: HTMLElement = fli.querySelector('.e-menu-url'); + if (item.url && aEle) { + switch(aEle.getAttribute('target')) { + case '_blank': + window.open(item.url, '_blank'); + break; + case '_parent': + window.parent.location.href = item.url; + break; + default: + window.location.href = item.url; + } + } this.closeMenu(null, e); + const sli: Element = this.getLIByClass(this.getUlByNavIdx(), SELECTED); + if (sli) { + sli.classList.add(FOCUSED); (sli as HTMLElement).focus(); + } } } } @@ -889,6 +906,7 @@ export abstract class MenuBase extends Component implements IN } closeArgs = { element: ul, parentItem: item, items: items }; this.trigger('onClose', closeArgs); this.navIdx.pop(); + if (this.navIdx.length === 0) { this.showSubMenu = false; } if (!this.isMenu) { EventHandler.remove(ul, 'keydown', this.keyHandler); if (this.keyType === 'right') { diff --git a/controls/navigations/src/stepper/stepper.ts b/controls/navigations/src/stepper/stepper.ts index ff457be112..5c37bcae64 100644 --- a/controls/navigations/src/stepper/stepper.ts +++ b/controls/navigations/src/stepper/stepper.ts @@ -375,6 +375,7 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { home: 'home', end: 'end', tab: 'tab', + shiftTab: 'shift+tab', escape: 'escape' }; this.tooltipOpen = false; @@ -936,7 +937,7 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { const itemElement: HTMLElement = this.stepperItemElements[parseInt(i.toString(), 10)]; const item: StepModel = this.steps[parseInt(i.toString(), 10)]; itemElement.classList.remove(SELECTED, INPROGRESS, COMPLETED, NOTSTARTED); - if (i === this.activeStep || this.activeStep === this.steps.length - 1) { itemElement.classList.add(SELECTED); } + if (i === this.activeStep) { itemElement.classList.add(SELECTED); } if (this.activeStep >= 0 && this.progressbar) { if (this.element.classList.contains(HORIZSTEP)) { if ((this.element.classList.contains(LABELBEFORE) || this.element.classList.contains(LABELAFTER)) && !this.element.classList.contains(STEPINDICATOR) && @@ -975,7 +976,7 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { else { attributes(itemElement, { 'tabindex': '-1', 'aria-current': 'false' }); } const prevOnChange: boolean = this.isProtectedOnChange; this.isProtectedOnChange = true; - if (isUpdated != false) { + if (isUpdated !== false) { if (i < this.activeStep || (this.steps.length - 1 === this.activeStep && item.status.toLowerCase() === "completed")) { item.status = StepStatus.Completed; } else if (i === this.activeStep) { item.status = StepStatus.InProgress; } else if (i > this.activeStep) { item.status = StepStatus.NotStarted; } @@ -1070,13 +1071,12 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { switch (e.action) { case 'uparrow': case 'downarrow': - if (this.element.classList.contains(VERTICALSTEP)) { this.handleNavigation(e.action === 'uparrow' ? false : true, e); } - break; case 'leftarrow': case 'rightarrow': - if (this.element.classList.contains(HORIZSTEP)) { this.handleNavigation(this.enableRtl ? e.action === 'leftarrow' : e.action === 'rightarrow', e); } - break; case 'tab': + case 'shiftTab': + this.handleNavigation(this.enableRtl && this.element.classList.contains(HORIZSTEP) ? (e.action === 'leftarrow' || e.action === 'shiftTab' || e.action === 'uparrow') : (e.action === 'rightarrow' || e.action === 'tab' || e.action === 'downarrow'), e); + break; case 'space': case 'enter': case 'escape': @@ -1096,8 +1096,15 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { if (!focusedEle) { focusedEle = this.element.querySelector('.' + SELECTED); } const stepItems: HTMLElement[] = Array.prototype.slice.call(this.stepperItemList.children); let index: number = stepItems.indexOf(focusedEle); - if (e.action === 'tab' || e.action === 'escape') { stepItems[parseInt(index.toString(), 10)].classList.remove(FOCUS); this.element.classList.remove('e-steps-focus'); } - if (!(e.action === 'space' || e.action === 'enter' || e.action === 'tab')) { + if (e.action === 'tab' || e.action === 'shiftTab' || e.action === 'downarrow' || e.action === 'uparrow' || e.action === 'space' || e.action === 'home' || e.action === 'end') { + if ((e.action === 'tab' && index === stepItems.length - 1) || (e.action === 'shiftTab' && index === 0)) { + if (focusedEle.classList.contains(FOCUS)) { this.updateStepFocus(); return; } + } else { + e.preventDefault(); + } + } + if (e.action === 'escape') { stepItems[parseInt(index.toString(), 10)].classList.remove(FOCUS); this.element.classList.remove('e-steps-focus'); } + if (!(e.action === 'space' || e.action === 'enter')) { const prevIndex: number = index; index = isNextStep ? index + 1 : index - 1; while ((index >= 0 && index < stepItems.length) && stepItems[parseInt(index.toString(), 10)].classList.contains(DISABLED)) { @@ -1113,12 +1120,16 @@ export class Stepper extends StepperBase implements INotifyPropertyChanged { } if (index >= 0 && index < stepItems.length) { stepItems[parseInt(index.toString(), 10)].classList.add(FOCUS); } } else if ((e.action === 'space' || e.action === 'enter')) { + let isupdateFocus: boolean = false; if (this.linear) { const linearModeValue: number = this.activeStep - index; - if (Math.abs(linearModeValue) === 1) { this.navigateToStep(index, null, null, true); } + if (Math.abs(linearModeValue) === 1) { this.navigateToStep(index, null, null, true); isupdateFocus = true; } + } + else { this.navigateToStep(index, null, null, true); isupdateFocus = true; } + if (isupdateFocus) { + this.updateStepFocus(); + (this.stepperItemElements[index as number] as HTMLElement).focus(); } - else { this.navigateToStep(index, null, null, true); } - } } diff --git a/controls/navigations/src/treeview/treeview.ts b/controls/navigations/src/treeview/treeview.ts index 15563bd53a..131d9b86c7 100644 --- a/controls/navigations/src/treeview/treeview.ts +++ b/controls/navigations/src/treeview/treeview.ts @@ -1496,6 +1496,14 @@ export class TreeView extends Component implements INotifyPropertyC } } + private isChildObject(): boolean { + if (typeof this.fields.child === 'object') { + return true; + } else { + return false; + } + } + private renderItems(isSorted: boolean): void { /* eslint-disable */ this.listBaseOption.ariaAttributes.level = 1; @@ -1813,6 +1821,9 @@ export class TreeView extends Component implements INotifyPropertyC if ((typeof mapper.child === 'string') && !isNOU(getValue(mapper.child, ds[i]))) { return 2; } + if (this.isChildObject()) { + return 2; + } if (!isNOU(getValue(mapper.parentID, ds[i])) || !isNOU(getValue(mapper.hasChildren, ds[i]))) { return 1; } @@ -2730,7 +2741,7 @@ export class TreeView extends Component implements INotifyPropertyC }); } } else { - childItems = this.getChildNodes(this.treeData, parentLi.getAttribute('data-uid')); + childItems = this.getChildNodes(this.treeData, parentLi.getAttribute('data-uid'), false, parseFloat(parentLi.getAttribute('aria-level')) + 1); this.currentLoadData = this.getSortedData(childItems); if (isNOU(childItems) || childItems.length === 0) { detach(eicon); @@ -2838,7 +2849,7 @@ export class TreeView extends Component implements INotifyPropertyC return (typeof mapper.child === 'string' || isNOU(mapper.child)) ? mapper : mapper.child; } - private getChildNodes(obj: { [key: string]: Object }[], parentId: string, isRoot: boolean = false): { [key: string]: Object }[] { + private getChildNodes(obj: { [key: string]: Object }[], parentId: string, isRoot: boolean = false, level?:number): { [key: string]: Object }[] { let childNodes: { [key: string]: Object }[]; if (isNOU(obj)) { return childNodes; @@ -2863,6 +2874,37 @@ export class TreeView extends Component implements INotifyPropertyC } } } + } else if (this.isChildObject()) { + let tempField: any = !isNOU(level) ? this.fields : this.fields.child; + let i =1; + while(i < level) { + if (!isNOU(tempField.child)) { + tempField = tempField.child; + } else { + break; + } + i++; + } + this.updateListProp(tempField); + let index: number = obj.findIndex((data) => getValue(this.fields.id, data) && getValue(this.fields.id, data).toString() === parentId); + if (index !== -1) { + return <{ [key: string]: Object }[]>getValue(('child' as string), obj[index]); + } + if(index === -1){ + for (let i: number = 0, objlen: number = obj.length; i < objlen; i++) { + let tempArray: { [key: string]: Object }[] = getValue('child', obj[i]); + let childIndex: number= !isNOU(tempArray)?tempArray.findIndex((data) => getValue((this.fields.child as any).id, data) && getValue((this.fields.child as any).id, data).toString() === parentId) : -1; + if(childIndex!==-1){ + return <{ [key: string]: Object }[]>getValue('child', tempArray[childIndex]); + } + else if (!isNOU(tempArray)) { + childNodes = this.getChildNodes(tempArray, parentId, false, level); + if (childNodes !== undefined) { + break; + } + } + } + } } } return childNodes; @@ -3916,6 +3958,13 @@ export class TreeView extends Component implements INotifyPropertyC if (newList !== undefined) { break; } + } else if (this.isChildObject()) { + let children = 'child'; + let childData: Object = getValue(children, obj[i]); + newList = this.getChildNodeObject(<{ [key: string]: Object }[]>childData, this.getChildMapper(mapper), id); + if (newList !== undefined) { + break; + } } } return newList; diff --git a/controls/navigations/styles/stepper/_bootstrap-dark-definition.scss b/controls/navigations/styles/stepper/_bootstrap-dark-definition.scss index 2073c9bce9..4f5fc3e935 100644 --- a/controls/navigations/styles/stepper/_bootstrap-dark-definition.scss +++ b/controls/navigations/styles/stepper/_bootstrap-dark-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $gray-base, 0 0 0 4px $brand-primary, 0 0 0 8px $gray-base !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $gray-base, 0 0 0 4px $grey-light-font, 0 0 0 8px $gray-base !default; $step-valid-li-shadow: 0 0 0 2px $gray-base, 0 0 0 4px $brand-success, 0 0 0 8px $gray-base !default; $step-error-li-shadow: 0 0 0 2px $gray-base, 0 0 0 4px $brand-danger, 0 0 0 8px $gray-base !default; $step-li-shadow: 0 0 0 8px $gray-base !default; diff --git a/controls/navigations/styles/stepper/_bootstrap-definition.scss b/controls/navigations/styles/stepper/_bootstrap-definition.scss index 19268af3bd..98f390a342 100644 --- a/controls/navigations/styles/stepper/_bootstrap-definition.scss +++ b/controls/navigations/styles/stepper/_bootstrap-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $brand-primary, 0 0 0 8px $grey-white !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $base-font, 0 0 0 8px $grey-white !default; $step-valid-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $brand-success, 0 0 0 8px $grey-white !default; $step-error-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $brand-danger, 0 0 0 8px $grey-white !default; $step-li-shadow: 0 0 0 8px $grey-white !default; diff --git a/controls/navigations/styles/stepper/_bootstrap4-definition.scss b/controls/navigations/styles/stepper/_bootstrap4-definition.scss index e024476073..799125d734 100644 --- a/controls/navigations/styles/stepper/_bootstrap4-definition.scss +++ b/controls/navigations/styles/stepper/_bootstrap4-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $white, 0 0 0 4px $primary, 0 0 0 8px $white !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $white, 0 0 0 4px $gray-700, 0 0 0 8px $white !default; $step-valid-li-shadow: 0 0 0 2px $white, 0 0 0 4px $success, 0 0 0 8px $white !default; $step-error-li-shadow: 0 0 0 2px $white, 0 0 0 4px $danger, 0 0 0 8px $white !default; $step-li-shadow: 0 0 0 8px $white !default; diff --git a/controls/navigations/styles/stepper/_bootstrap5-definition.scss b/controls/navigations/styles/stepper/_bootstrap5-definition.scss index ee592ec990..7faa3f3a9f 100644 --- a/controls/navigations/styles/stepper/_bootstrap5-definition.scss +++ b/controls/navigations/styles/stepper/_bootstrap5-definition.scss @@ -44,6 +44,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $primary, 0 0 0 8px $content-bg-color !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $content-text-color, 0 0 0 8px $content-bg-color !default; $step-valid-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $success, 0 0 0 8px $content-bg-color !default; $step-error-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $danger, 0 0 0 8px $content-bg-color !default; $step-li-shadow: 0 0 0 8px $content-bg-color !default; diff --git a/controls/navigations/styles/stepper/_fabric-dark-definition.scss b/controls/navigations/styles/stepper/_fabric-dark-definition.scss index 7057e7d804..dac3f8eba8 100644 --- a/controls/navigations/styles/stepper/_fabric-dark-definition.scss +++ b/controls/navigations/styles/stepper/_fabric-dark-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $theme-primary, 0 0 0 8px $neutral-white !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $neutral-light-fontalt, 0 0 0 8px $neutral-white !default; $step-valid-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $success-bg, 0 0 0 8px $neutral-white !default; $step-error-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $error-bg, 0 0 0 8px $neutral-white !default; $step-li-shadow: 0 0 0 8px $neutral-white !default; diff --git a/controls/navigations/styles/stepper/_fabric-definition.scss b/controls/navigations/styles/stepper/_fabric-definition.scss index 51edcd395e..9388ac30bf 100644 --- a/controls/navigations/styles/stepper/_fabric-definition.scss +++ b/controls/navigations/styles/stepper/_fabric-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $theme-primary, 0 0 0 8px $neutral-white !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $base-font, 0 0 0 8px $neutral-white !default; $step-valid-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $success-bg, 0 0 0 8px $neutral-white !default; $step-error-li-shadow: 0 0 0 2px $neutral-white, 0 0 0 4px $error-bg, 0 0 0 8px $neutral-white !default; $step-li-shadow: 0 0 0 8px $neutral-white !default; diff --git a/controls/navigations/styles/stepper/_fluent-definition.scss b/controls/navigations/styles/stepper/_fluent-definition.scss index 9e3d2f4d57..0816ccc259 100644 --- a/controls/navigations/styles/stepper/_fluent-definition.scss +++ b/controls/navigations/styles/stepper/_fluent-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $primary-border-color, 0 0 0 8px $content-bg-color !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $content-text-color, 0 0 0 8px $content-bg-color !default; $step-valid-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $success, 0 0 0 8px $content-bg-color !default; $step-error-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $danger, 0 0 0 8px $content-bg-color !default; $step-li-shadow: 0 0 0 8px $content-bg-color !default; diff --git a/controls/navigations/styles/stepper/_fusionnew-definition.scss b/controls/navigations/styles/stepper/_fusionnew-definition.scss index d18bcd07c5..6a97d8afbc 100644 --- a/controls/navigations/styles/stepper/_fusionnew-definition.scss +++ b/controls/navigations/styles/stepper/_fusionnew-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $primary-border-color, 0 0 0 8px $content-bg-color !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $content-text-color, 0 0 0 8px $content-bg-color !default; $step-valid-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $success, 0 0 0 8px $content-bg-color !default; $step-error-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $danger, 0 0 0 8px $content-bg-color !default; $step-li-shadow: 0 0 0 8px $content-bg-color !default; diff --git a/controls/navigations/styles/stepper/_highcontrast-definition.scss b/controls/navigations/styles/stepper/_highcontrast-definition.scss index 5c3fcd5eaa..f13dee8344 100644 --- a/controls/navigations/styles/stepper/_highcontrast-definition.scss +++ b/controls/navigations/styles/stepper/_highcontrast-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $selection-bg, 0 0 0 8px $bg-base-0 !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $content-font, 0 0 0 8px $bg-base-0 !default; $step-valid-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $success-alt, 0 0 0 8px $bg-base-0 !default; $step-error-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $error-alt, 0 0 0 8px $bg-base-0 !default; $step-li-shadow: 0 0 0 8px $bg-base-0 !default; diff --git a/controls/navigations/styles/stepper/_highcontrast-light-definition.scss b/controls/navigations/styles/stepper/_highcontrast-light-definition.scss index 3413f161f0..5a91d14260 100644 --- a/controls/navigations/styles/stepper/_highcontrast-light-definition.scss +++ b/controls/navigations/styles/stepper/_highcontrast-light-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $selection-bg, 0 0 0 8px $bg-base-0 !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $content-font, 0 0 0 8px $bg-base-0 !default; $step-valid-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $success-alt, 0 0 0 8px $bg-base-0 !default; $step-error-li-shadow: 0 0 0 2px $bg-base-0, 0 0 0 4px $error-alt, 0 0 0 8px $bg-base-0 !default; $step-li-shadow: 0 0 0 8px $bg-base-0 !default; diff --git a/controls/navigations/styles/stepper/_material-dark-definition.scss b/controls/navigations/styles/stepper/_material-dark-definition.scss index 801aa313ea..d6b5270363 100644 --- a/controls/navigations/styles/stepper/_material-dark-definition.scss +++ b/controls/navigations/styles/stepper/_material-dark-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $overlay-bg-color, 0 0 0 4px $primary, 0 0 0 8px $overlay-bg-color !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $overlay-bg-color, 0 0 0 4px $grey-dark-font, 0 0 0 8px $overlay-bg-color !default; $step-valid-li-shadow: 0 0 0 2px $overlay-bg-color, 0 0 0 4px $success-bg, 0 0 0 8px $overlay-bg-color !default; $step-error-li-shadow: 0 0 0 2px $overlay-bg-color, 0 0 0 4px $error-font, 0 0 0 8px $overlay-bg-color !default; $step-li-shadow: 0 0 0 8px $overlay-bg-color !default; diff --git a/controls/navigations/styles/stepper/_material-definition.scss b/controls/navigations/styles/stepper/_material-definition.scss index 1cbbe9084b..93f74c9c37 100644 --- a/controls/navigations/styles/stepper/_material-definition.scss +++ b/controls/navigations/styles/stepper/_material-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $primary, 0 0 0 8px $grey-white !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $base-font, 0 0 0 8px $grey-white !default; $step-valid-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px $msg-success-color-alt1, 0 0 0 8px $grey-white !default; $step-error-li-shadow: 0 0 0 2px $grey-white, 0 0 0 4px darken($error-font, 11%), 0 0 0 8px $grey-white !default; $step-li-shadow: 0 0 0 8px $grey-white !default; diff --git a/controls/navigations/styles/stepper/_material3-definition.scss b/controls/navigations/styles/stepper/_material3-definition.scss index 49b5cd534f..58d96c5dac 100644 --- a/controls/navigations/styles/stepper/_material3-definition.scss +++ b/controls/navigations/styles/stepper/_material3-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px rgba($content-bg-color), 0 0 0 4px rgba($primary), 0 0 0 8px rgba($content-bg-color) !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px rgba($content-bg-color), 0 0 0 4px rgba($content-text-color), 0 0 0 8px rgba($content-bg-color) !default; $step-valid-li-shadow: 0 0 0 2px rgba($content-bg-color), 0 0 0 4px rgba($success), 0 0 0 8px rgba($content-bg-color) !default; $step-error-li-shadow: 0 0 0 2px rgba($content-bg-color), 0 0 0 4px rgba($danger), 0 0 0 8px rgba($content-bg-color) !default; $step-li-shadow: 0 0 0 8px rgba($content-bg-color) !default; diff --git a/controls/navigations/styles/stepper/_tailwind-definition.scss b/controls/navigations/styles/stepper/_tailwind-definition.scss index d63546fbf0..7d927b032d 100644 --- a/controls/navigations/styles/stepper/_tailwind-definition.scss +++ b/controls/navigations/styles/stepper/_tailwind-definition.scss @@ -43,6 +43,7 @@ $stepper-bigger-small-optional-size: 12px !default; $stepper-bigger-small-text-size: 14px !default; $step-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $primary, 0 0 0 8px $content-bg-color !default; +$step-keyboard-selected-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $content-text-color, 0 0 0 8px $content-bg-color !default; $step-valid-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $success-border-color, 0 0 0 8px $content-bg-color !default; $step-error-li-shadow: 0 0 0 2px $content-bg-color, 0 0 0 4px $border-error, 0 0 0 8px $content-bg-color !default; $step-li-shadow: 0 0 0 8px $content-bg-color !default; diff --git a/controls/navigations/styles/stepper/_theme.scss b/controls/navigations/styles/stepper/_theme.scss index 50d2c37abe..74bf3a4bdd 100644 --- a/controls/navigations/styles/stepper/_theme.scss +++ b/controls/navigations/styles/stepper/_theme.scss @@ -132,12 +132,18 @@ } } - &:not(.e-steps-focus) .e-stepper-steps .e-step-container.e-step-selected:not(.e-step-text-only):not(.e-step-label-only) .e-step, + &:not(.e-steps-focus) .e-stepper-steps .e-step-container.e-step-selected:not(.e-step-text-only):not(.e-step-label-only) .e-step { + box-shadow: $step-selected-li-shadow; + } + .e-stepper-steps .e-step-container.e-step-focus .e-step, .e-stepper-steps .e-step-container.e-step-label-only.e-step-focus .e-label, .e-stepper-steps .e-step-container.e-step-text-only.e-step-focus .e-text, - .e-stepper-steps .e-step-container.e-step-template.e-step-focus { - box-shadow: $step-selected-li-shadow; + .e-stepper-steps .e-step-container.e-step-template.e-step-focus, + &:not(.e-steps-focus) .e-stepper-steps .e-step-container:focus-visible .e-step, + &:not(.e-steps-focus) .e-stepper-steps .e-step-container.e-step-text-only:focus-visible .e-text, + &:not(.e-steps-focus) .e-stepper-steps .e-step-container.e-step-label-only:focus-visible .e-label { + box-shadow: $step-keyboard-selected-li-shadow !important; /* stylelint-disable-line declaration-no-important */ } &:not(.e-steps-focus) .e-stepper-steps .e-step-container.e-step-valid.e-step-selected:not(.e-step-text-only):not(.e-step-label-only) .e-step, diff --git a/controls/navigations/styles/toolbar/_bootstrap5-definition.scss b/controls/navigations/styles/toolbar/_bootstrap5-definition.scss index f41cc7c295..c272c99bf6 100644 --- a/controls/navigations/styles/toolbar/_bootstrap5-definition.scss +++ b/controls/navigations/styles/toolbar/_bootstrap5-definition.scss @@ -136,7 +136,7 @@ $tbar-pressed-bg: $secondary-bg-color-focus !default; $tbar-pressed-font: $content-text-color-alt2 !default; $tbar-select-font: $content-text-color-alt2 !default; $tbar-default-icon-overlay: $content-text-color-alt2 !default; -$tbar-focus-bg: $tbar-hover-bg !default; +$tbar-focus-bg: none !default; $tbar-press-font: $content-text-color-alt2 !default; $tbar-default-font-overlay: $content-text-color-alt2 !default; $tbar-active-font-color: $primary-text-color !default; @@ -150,7 +150,7 @@ $tbar-nrml-btn-border-radius: 4px !default; $tbar-nrml-btn-focus-padding: 0 4px !default; $tbar-nrml-btn-focus-outline: 0 !default; -$tbar-btn-icons-focus-color: $primary-text-color !default; +$tbar-btn-icons-focus-color: $icon-color !default; $tbar-btn-text-focus-color: $primary-text-color !default; $tbar-btn-focus-border-color: $tbar-focus-border-color !default; $tbar-btn-hover-border-size: $tbar-border-size !default; @@ -169,7 +169,7 @@ $tbar-flat-btn-active-box-shadow: none !default; $tbar-ext-btn-focus-padding: 0 4px !default; $tbar-ext-btn-icon-padding: 5px 6px !default; $tbar-ext-btn-icon-font-size: $text-base !default; -$tbar-ext-btn-focus-box-shadow: none !default; +$tbar-ext-btn-focus-box-shadow: $secondary-shadow-focus !default; $tbar-ext-btn-hover-border-color: $tbar-hover-border-color !default; $tbar-ext-btn-border: none !default; diff --git a/controls/navigations/styles/toolbar/_fluent-definition.scss b/controls/navigations/styles/toolbar/_fluent-definition.scss index e57cc98f9c..1f1c6f1f2a 100644 --- a/controls/navigations/styles/toolbar/_fluent-definition.scss +++ b/controls/navigations/styles/toolbar/_fluent-definition.scss @@ -38,7 +38,7 @@ $tbar-separator-border: $border-dark !default; $tbar-default-border: $border-light !default; $tbar-hover-border-color: $icon-color !default; $tbar-focus-border-color: $tbar-hover-border-color !default; -$tbar-focus-bg: $content-bg-color-alt4 !default; +$tbar-focus-bg: none !default; $tbar-press-font: $content-text-color !default; $tbar-default-font-overlay: $content-text-color !default; @@ -159,7 +159,7 @@ $tbar-flat-btn-active-box-shadow: none !default; $tbar-ext-btn-focus-padding: 0 4px !default; $tbar-ext-btn-icon-padding: 0 !default; $tbar-ext-btn-icon-font-size: $text-sm !default; -$tbar-ext-btn-focus-box-shadow: none !default; +$tbar-ext-btn-focus-box-shadow: inset 0 0 0 1px !default; $tbar-ext-btn-hover-border-color: $tbar-hover-border-color !default; $tbar-ext-btn-border: none !default; diff --git a/controls/navigations/styles/toolbar/_tailwind-definition.scss b/controls/navigations/styles/toolbar/_tailwind-definition.scss index ef068216e5..c4cda855de 100644 --- a/controls/navigations/styles/toolbar/_tailwind-definition.scss +++ b/controls/navigations/styles/toolbar/_tailwind-definition.scss @@ -38,7 +38,7 @@ $tbar-separator-border: $border-light !default; $tbar-default-border: $border-light !default; $tbar-hover-border-color: $icon-color !default; $tbar-focus-border-color: $tbar-hover-border-color !default; -$tbar-focus-bg: $tbar-hover-bg !default; +$tbar-focus-bg: none !default; $tbar-press-font: $content-text-color-alt2 !default; $tbar-default-font-overlay: $content-text-color-alt2 !default; @@ -159,7 +159,7 @@ $tbar-flat-btn-active-box-shadow: $shadow-sm !default; $tbar-ext-btn-focus-padding: 0 4px !default; $tbar-ext-btn-icon-padding: 5px 6px !default; $tbar-ext-btn-icon-font-size: $text-base !default; -$tbar-ext-btn-focus-box-shadow: none !default; +$tbar-ext-btn-focus-box-shadow: $shadow-focus-ring1 !default; $tbar-ext-btn-hover-border-color: $tbar-hover-border-color !default; $tbar-ext-btn-border: none !default; diff --git a/controls/pdf/CHANGELOG.md b/controls/pdf/CHANGELOG.md index 78885fe094..91d06e0f29 100644 --- a/controls/pdf/CHANGELOG.md +++ b/controls/pdf/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### PDF Parser diff --git a/controls/pdfexport/CHANGELOG.md b/controls/pdfexport/CHANGELOG.md index bb716ea1fc..1f207bde71 100644 --- a/controls/pdfexport/CHANGELOG.md +++ b/controls/pdfexport/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.46 (2024-01-17) ### Pdf Export diff --git a/controls/pdfviewer/CHANGELOG.md b/controls/pdfviewer/CHANGELOG.md index ee9be7d84b..70092a7e97 100644 --- a/controls/pdfviewer/CHANGELOG.md +++ b/controls/pdfviewer/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### PDF Viewer + +#### Bug Fixes + +- `#I537980` - Now, the form fields have rendered properly without injecting the annotation, text selection, and text search modules. +- `#I533753` - Now, only the formFieldSelect event is called when select the `formField`. +- `#I537955` - Now, programmatically opening the handwritten signature dialog is works properly after opening the Initial dialog. +- `#I536567` - Now, duplicate comment and replay content are not added to the annotation when exporting/importing the text markup annotation. +- `#I536470` - Now, a script error is not occurs when rapidly adding a TextBox across multiple pages. + ## 24.1.45 (2024-01-09) ### PDF Viewer @@ -16,6 +28,7 @@ - `#I524631` - Now, the custom stamp options are not being removed in the custom stamp menu when updating after control initialization. - `#I531042` - Now, the custom data of the annotation is properly preserved after undoing the deletion of the imported annotation. - `#I533144` - Now, the documents load properly in the Stand-alone PDF Viewer when using the document path and open option alternatively. +- `#I528636` - Now, the saved text markup annotation is rendered properly when adding the annotation programmatically. ## 24.1.44 (2024-01-03) diff --git a/controls/pdfviewer/package.json b/controls/pdfviewer/package.json index 4b1162f378..c91ec721ba 100644 --- a/controls/pdfviewer/package.json +++ b/controls/pdfviewer/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-pdfviewer", - "version": "24.1.44", + "version": "24.1.45", "description": "Essential JS 2 PDF viewer Component", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/pdfviewer/src/pdfviewer/annotation/annotation.ts b/controls/pdfviewer/src/pdfviewer/annotation/annotation.ts index 5fcb1f09ff..45c3ce6050 100644 --- a/controls/pdfviewer/src/pdfviewer/annotation/annotation.ts +++ b/controls/pdfviewer/src/pdfviewer/annotation/annotation.ts @@ -4057,7 +4057,20 @@ export class Annotation { this.storeAnnotationCollections(annotation, pageNumber); const pageAnnotation: IPageAnnotations = { pageIndex: pageNumber, annotations: [] }; pageAnnotation.annotations.push(annotation); - index = pageAnnotation.annotations.indexOf(annotation); + if (this.pdfViewer.annotationCollection) { + this.pdfViewer.annotationCollection.forEach((item: any) => { + if (annotation.annotName !== item.annotationId) { + if ('annotationId' in item) { + const newItem: any = { + ...item, + annotName: item.annotationId, + }; + pageNumber = item.pageNumber; + pageAnnotation.annotations.push(newItem); + } + } + }); + } const annotationCollection: IPageAnnotations[] = []; annotationCollection.push(pageAnnotation); const annotationStringified: string = JSON.stringify(annotationCollection); diff --git a/controls/pdfviewer/src/pdfviewer/annotation/sticky-notes-annotation.ts b/controls/pdfviewer/src/pdfviewer/annotation/sticky-notes-annotation.ts index 3e994cc5db..1528488ca7 100644 --- a/controls/pdfviewer/src/pdfviewer/annotation/sticky-notes-annotation.ts +++ b/controls/pdfviewer/src/pdfviewer/annotation/sticky-notes-annotation.ts @@ -2584,12 +2584,14 @@ export class StickyNotesAnnotation { currentAnnotation.comments[j].modifiedDate = this.getDateAndTime(); } } - // eslint-disable-next-line max-len - const newArray: ICommentsCollection = { annotName: annotName, parentId: parentElement, subject: currentAnnotation.subject, comments: [], author: author, note: text, shapeAnnotationType: '', state: '', stateModel: '', modifiedDate: this.getDateAndTime(), review: { state: '', stateModel: '', modifiedDate: this.getDateAndTime(), author: author }, isLock: false }; - if (!isComment) { - currentAnnotation.comments[currentAnnotation.comments.length] = newArray; + if (currentAnnotation.annotName === parentElement) { + // eslint-disable-next-line max-len + const newArray: ICommentsCollection = { annotName: annotName, parentId: parentElement, subject: currentAnnotation.subject, comments: [], author: author, note: text, shapeAnnotationType: '', state: '', stateModel: '', modifiedDate: this.getDateAndTime(), review: { state: '', stateModel: '', modifiedDate: this.getDateAndTime(), author: author }, isLock: false }; + if (!isComment) { + currentAnnotation.comments[currentAnnotation.comments.length] = newArray; + } } - } else { + } else if (currentAnnotation.annotName === parentElement) { // eslint-disable-next-line max-len const newArray: ICommentsCollection = { annotName: annotName, parentId: parentElement, subject: currentAnnotation.subject, comments: [], author: author, note: text, shapeAnnotationType: '', state: '', stateModel: '', modifiedDate: this.getDateAndTime(), review: { state: '', stateModel: '', modifiedDate: this.getDateAndTime(), author: author }, isLock: false }; currentAnnotation.comments[currentAnnotation.comments.length] = newArray; diff --git a/controls/pdfviewer/src/pdfviewer/annotation/text-markup-annotation.ts b/controls/pdfviewer/src/pdfviewer/annotation/text-markup-annotation.ts index bdd2675714..d571818594 100644 --- a/controls/pdfviewer/src/pdfviewer/annotation/text-markup-annotation.ts +++ b/controls/pdfviewer/src/pdfviewer/annotation/text-markup-annotation.ts @@ -2984,11 +2984,20 @@ export class TextMarkupAnnotation { if (annotation.IsLocked) { annotation.AnnotationSettings.isLock = annotation.IsLocked; } + if (annotation.TextMarkupAnnotationType === "Highlight") { + annotation.AnnotationSelectorSettings = annotation.annotSelectorSettings ? annotation.annotSelectorSettings : this.pdfViewer.highlightSettings.annotationSelectorSettings; + } + else if (annotation.TextMarkupAnnotationType === "Underline") { + annotation.AnnotationSelectorSettings = annotation.annotSelectorSettings ? annotation.annotSelectorSettings : this.pdfViewer.underlineSettings.annotationSelectorSettings; + } + else { + annotation.AnnotationSelectorSettings = annotation.annotSelectorSettings ? annotation.annotSelectorSettings : this.pdfViewer.strikethroughSettings.annotationSelectorSettings; + } annotationObject = { // eslint-disable-next-line max-len textMarkupAnnotationType: annotation.TextMarkupAnnotationType, allowedInteractions: annotation.allowedInteractions, color: annotation.Color, opacity: annotation.Opacity, bounds: annotation.Bounds, author: annotation.Author, subject: annotation.Subject, modifiedDate: annotation.ModifiedDate, note: annotation.Note, rect: annotation.Rect, annotationId: annotation.AnnotName, comments: this.pdfViewer.annotationModule.getAnnotationComments(annotation.Comments, annotation, annotation.Author), review: { state: annotation.State, stateModel: annotation.StateModel, modifiedDate: annotation.ModifiedDate, author: annotation.Author }, shapeAnnotationType: 'textMarkup', pageNumber: pageNumber, isMultiSelect: annotation.IsMultiSelect, annotNameCollection: annotation.AnnotNameCollection, annotpageNumbers: annotation.AnnotpageNumbers, customData: this.pdfViewer.annotation.getCustomData(annotation), - annotationSettings : annotation.AnnotationSettings, isLocked: annotation.IsLocked,isPrint: annotation.IsPrint, isCommentLock: annotation.IsCommentLock + annotationSettings : annotation.AnnotationSettings, isLocked: annotation.IsLocked,isPrint: annotation.IsPrint, isCommentLock: annotation.IsCommentLock, annotationSelectorSettings: annotation.AnnotationSelectorSettings }; return annotationObject; } diff --git a/controls/pdfviewer/src/pdfviewer/base/pdfviewer-base.ts b/controls/pdfviewer/src/pdfviewer/base/pdfviewer-base.ts index cf6a55d82c..7716edfa43 100644 --- a/controls/pdfviewer/src/pdfviewer/base/pdfviewer-base.ts +++ b/controls/pdfviewer/src/pdfviewer/base/pdfviewer-base.ts @@ -5918,7 +5918,7 @@ export class PdfViewerBase { // eslint-disable-next-line max-len this.pdfViewer.annotationModule.createAnnotationLayer(pageDiv, pageWidth, pageHeight, pageNumber, displayMode); } - if (this.pdfViewer.textSearchModule || this.pdfViewer.textSelectionModule || this.pdfViewer.annotationModule) { + if (this.pdfViewer.textSearchModule || this.pdfViewer.textSelectionModule || this.pdfViewer.formFieldsModule || this.pdfViewer.annotationModule) { this.textLayer.addTextLayer(pageNumber, pageWidth, pageHeight, pageDiv); } if (this.pdfViewer.formDesignerModule && !this.pdfViewer.annotationModule) { diff --git a/controls/pdfviewer/src/pdfviewer/base/signature.ts b/controls/pdfviewer/src/pdfviewer/base/signature.ts index 804124bc2a..cff2127b18 100644 --- a/controls/pdfviewer/src/pdfviewer/base/signature.ts +++ b/controls/pdfviewer/src/pdfviewer/base/signature.ts @@ -2652,6 +2652,7 @@ export class Signature { */ public setAnnotationMode(): void { this.pdfViewerBase.isToolbarSignClicked = true; + this.pdfViewerBase.isInitialField = false; this.showSignatureDialog(true); } diff --git a/controls/pdfviewer/src/pdfviewer/drawing/drawing.ts b/controls/pdfviewer/src/pdfviewer/drawing/drawing.ts index e5208b7140..0bd65c95ed 100644 --- a/controls/pdfviewer/src/pdfviewer/drawing/drawing.ts +++ b/controls/pdfviewer/src/pdfviewer/drawing/drawing.ts @@ -540,6 +540,7 @@ export class Drawing { freeTextEle.content = obj.dynamicText; freeTextEle.style.opacity = obj.opacity; freeTextEle.margin.left = 4; + freeTextEle.margin.right = 5; freeTextEle.margin.top = 5 * (obj.fontSize / 16); if (this.pdfViewer.freeTextSettings.enableAutoFit) { freeTextEle.style.textWrapping = 'Wrap'; diff --git a/controls/pdfviewer/src/pdfviewer/drawing/tools.ts b/controls/pdfviewer/src/pdfviewer/drawing/tools.ts index 08586c46f0..99a560ed1e 100644 --- a/controls/pdfviewer/src/pdfviewer/drawing/tools.ts +++ b/controls/pdfviewer/src/pdfviewer/drawing/tools.ts @@ -449,7 +449,7 @@ export class SelectTool extends ToolBase { this.commandHandler.clearSelection(this.pdfViewerBase.activeElements.activePageID); } if (object) { - if (!isNullOrUndefined(this.pdfViewerBase.isFreeTextSelected) && !this.pdfViewerBase.isFreeTextSelected) { + if ((isNullOrUndefined(formField) || (formField && formField.id !== (object as PdfAnnotationBaseModel).id)) && !isNullOrUndefined(this.pdfViewerBase.isFreeTextSelected) && !this.pdfViewerBase.isFreeTextSelected) { this.commandHandler.select([(object as PdfAnnotationBaseModel).id], currentSelctor); this.commandHandler.viewerBase.isAnnotationMouseDown = true; } @@ -653,7 +653,8 @@ export class MoveTool extends ToolBase { const previousPosition: IFormFieldBound = { X: this.offset.x, Y: this.offset.y, Width: args.source.wrapper.actualSize.width, Height: args.source.wrapper.actualSize.height }; this.commandHandler.fireFormFieldMoveEvent('formFieldMove', field, node.pageIndex, previousPosition, currentPosition); } - if(this.redoElement.bounds.height !== newShapeObject.bounds.height || this.redoElement.bounds.width !== newShapeObject.bounds.width || this.redoElement.bounds.x !== newShapeObject.bounds.x || this.redoElement.bounds.y !== newShapeObject.bounds.y){ + // eslint-disable-next-line max-len + if(!isNullOrUndefined(this.redoElement) && (this.redoElement.bounds.height !== newShapeObject.bounds.height || this.redoElement.bounds.width !== newShapeObject.bounds.width || this.redoElement.bounds.x !== newShapeObject.bounds.x || this.redoElement.bounds.y !== newShapeObject.bounds.y)){ isDragged = true; } if (this.commandHandler.annotation && isDragged) { diff --git a/controls/pdfviewer/src/pdfviewer/pdfviewer.ts b/controls/pdfviewer/src/pdfviewer/pdfviewer.ts index 3733031669..af3d64bd44 100644 --- a/controls/pdfviewer/src/pdfviewer/pdfviewer.ts +++ b/controls/pdfviewer/src/pdfviewer/pdfviewer.ts @@ -7573,6 +7573,7 @@ export class PdfViewer extends Component implements INotifyProperty break; case 'customStampSettings': if (newProp.customStampSettings.customStamps) { + this.viewerBase.customStampCollection = []; for (let i: number = 0; i < newProp.customStampSettings.customStamps.length; i++) { this.viewerBase.customStampCollection.push({ customStampName: this.customStampSettings.customStamps[i].customStampName, customStampImageSource: this.customStampSettings.customStamps[i].customStampImageSource }); } diff --git a/controls/pivotview/CHANGELOG.md b/controls/pivotview/CHANGELOG.md index 2cc6a5a6b8..9ae446cca8 100644 --- a/controls/pivotview/CHANGELOG.md +++ b/controls/pivotview/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### PivotTable + +#### Bug fixes + +- `#F185974` - The selected node state will now be properly maintained in the filter dialog for the pivot table's date type field. +- `#I539759` - On-demand grouping will now work properly with CSV data source. + ## 24.1.45 (2024-01-09) ### PivotTable diff --git a/controls/pivotview/package.json b/controls/pivotview/package.json index 177cd67652..bb4f938116 100644 --- a/controls/pivotview/package.json +++ b/controls/pivotview/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-pivotview", - "version": "24.1.44", + "version": "24.1.45", "description": "The pivot grid, or pivot table, is used to visualize large sets of relational data in a cross-tabular format, similar to an Excel pivot table.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/pivotview/spec/base/grouping.spec.ts b/controls/pivotview/spec/base/grouping.spec.ts index bad6a0c6a1..45eb4f155b 100644 --- a/controls/pivotview/spec/base/grouping.spec.ts +++ b/controls/pivotview/spec/base/grouping.spec.ts @@ -1,6 +1,6 @@ import { IDataSet } from '../../src/base/engine'; import { PivotView } from '../../src/pivotview/base/pivotview'; -import { createElement, remove, EmitType } from '@syncfusion/ej2-base'; +import { createElement, remove, EmitType, isNullOrUndefined } from '@syncfusion/ej2-base'; import { GroupingBar } from '../../src/common/grouping-bar/grouping-bar'; import { BeginDrillThroughEventArgs @@ -322,6 +322,108 @@ describe('Group By Date feature', () => { } }); }); + describe('CSV - GROUPING', () => { + let pivotGridObj: PivotView; + let elem: HTMLElement = createElement('div', { id: 'PivotGrid' }); + let csvdata: string = "Region,Country,Item Type,Sales Channel,Order Priority,Order Date,Order ID,Ship Date,Units Sold,Unit Price,Unit Cost,Total Revenue,Total Cost,Total Profit\r\n" + + "Middle East and North Africa,Libya,Cereal,Offline,M,10/18/2014,686800706,10/31/2014,8446,437.20,263.33,3692591.20,2224085.18,1468506.02\r\n" + + "North America,Canada,Cosmetics,Online,M,11/7/2011,185941302,12/8/2011,3018,154.06,90.93,464953.08,274426.74,190526.34\r\n" + + "Asia,Japan,Cereal,Offline,C,4/10/2010,161442649,5/12/2010,3322,205.70,117.11,683335.40,389039.42,294295.98\r\n" + + "Sub-Saharan Africa,Chad,Cosmetics,Offline,H,8/16/2011,645713555,8/31/2011,9845,9.33,6.92,91853.85,68127.40,23726.45\r\n" + + "Europe,Armenia,Cosmetics,Online,H,11/24/2014,683458888,12/28/2014,9528,205.70,117.11,1959909.60,1115824.08,844085.52\r\n" + + "Sub-Saharan Africa,Eritrea,Cereal,Online,H,3/4/2015,679414975,4/17/2015,2844,205.70,117.11,585010.80,333060.84,251949.96\r\n"; + if (document.getElementById(elem.id)) { + remove(document.getElementById(elem.id)); + } + document.body.appendChild(elem); + afterAll(() => { + if (pivotGridObj) { + pivotGridObj.destroy(); + } + remove(elem); + }); + beforeAll(() => { + const isDef = (o: any) => o !== undefined && o !== null; + if (!isDef(window.performance)) { + console.log("Unsupported environment, window.performance.memory is unavailable"); + this.skip(); //Skips test (in Chai) + return; + } + if (document.getElementById(elem.id)) { + remove(document.getElementById(elem.id)); + } + document.body.appendChild(elem); + pivotGridObj = new PivotView({ + dataSourceSettings: { + dataSource: getCSVData(), + type: 'CSV', + expandAll: true, + enableSorting: true, + allowLabelFilter: true, + allowValueFilter: true, + rows: [ + { name: 'Region' }, + { name: 'Country' } + ], columns: [ + { name: 'Item Type' }, + { name: 'Sales Channel' } + ], values: [ + { name: 'Total Cost' }, + { name: 'Total Revenue' }, + { name: 'Total', type: 'CalculatedField' } + ], + filters: [], + calculatedFieldSettings: [{ name: 'Total', formula: '"Sum(Total Cost)"' }] + }, + showFieldList: true, + allowGrouping: true, + width: 600, + height: 300 + }); + pivotGridObj.appendTo('#PivotGrid'); + function getCSVData(): string[][] { + let dataSource: string[][] = []; + let jsonObject: string[] = csvdata.split(/\r?\n|\r/); + for (let i: number = 0; i < jsonObject.length; i++) { + if (!isNullOrUndefined(jsonObject[i]) && jsonObject[i] !== '') { + dataSource.push(jsonObject[i].split(',')); + } + } + return dataSource; + } + }); + it('For sample render', (done: Function) => { + setTimeout(() => { + expect(1).toBe(1); + done(); + }, 200); + }); + it('Check groups initially', (done: Function) => { + setTimeout(() => { + expect(pivotGridObj.element.querySelectorAll('td[aria-colindex="1"]')[0].textContent).toBe('Asia'); + done(); + }, 100); + }); + it('Check group settings updated using an proptery', (done: Function) => { + pivotGridObj.dataSourceSettings.groupSettings = [{ + name: 'Region', type: 'Custom', customGroups: [{ + groupName: 'Asian Countries', items: [ + 'Asia', 'Europe' + ] + }] + }]; + setTimeout(() => { + expect(1).toBe(1); + done(); + }, 400); + }); + it('Check custom grouping', (done: Function) => { + setTimeout(() => { + expect(pivotGridObj.element.querySelectorAll('td[aria-colindex="1"]')[0].textContent).toBe('Asian Countries'); + done(); + }, 100); + }); + }); }); let pivotDatas: IDataSet[] = [ diff --git a/controls/pivotview/src/base/engine.ts b/controls/pivotview/src/base/engine.ts index 8c60986e6c..5b22b1411c 100644 --- a/controls/pivotview/src/base/engine.ts +++ b/controls/pivotview/src/base/engine.ts @@ -1297,8 +1297,14 @@ export class PivotEngine { field.formula = field.formula.replace(/abs\(/g, 'Math.abs('); } field.name = calcProperties ? calcProperties.name : field.name; - keys = keys.filter((key: string) => { return key !== field.name; }); - keys.push(field.name); // eslint-disable-next-line no-useless-escape + if (this.dataSourceSettings.type === 'CSV') { + if (keys.indexOf(field.name) === -1) { + keys.push(field.name); + } + } else { + keys = keys.filter((key: string) => { return key !== field.name; }); + keys.push(field.name); + } // eslint-disable-next-line no-useless-escape const formulaType: string[] = actualFormula.split('\"'); for (let len: number = 0, lmt: number = formulaType.length; len < lmt; len++) { const type: string = formulaType[len as number].trim(); // eslint-disable-next-line no-useless-escape diff --git a/controls/pivotview/src/base/util.ts b/controls/pivotview/src/base/util.ts index a71ab28368..b242d21974 100644 --- a/controls/pivotview/src/base/util.ts +++ b/controls/pivotview/src/base/util.ts @@ -56,6 +56,11 @@ export class PivotUtil { } return clonedData; } + /** @hidden */ + public static getClonedCSVData(data: string[][]): string[][] { + const clonedData: string[][] = data.map((row: string[]) => [...row]); + return clonedData; + } /* eslint-disable @typescript-eslint/no-explicit-any */ private static getDefinedObj(data: { [key: string]: any }): { [key: string]: any } { diff --git a/controls/pivotview/src/common/actions/event-base.ts b/controls/pivotview/src/common/actions/event-base.ts index 5c683956a3..d514d6a1d0 100644 --- a/controls/pivotview/src/common/actions/event-base.ts +++ b/controls/pivotview/src/common/actions/event-base.ts @@ -584,7 +584,7 @@ export class EventBase { name: memberName, isSelected: isInclude ? false : true }; - if (filterObj[actualText as string] !== undefined) { + if (filterObj[memberName as string] !== undefined) { obj.isSelected = isInclude ? true : false; } if (memberCount <= this.parent.control.maxNodeLimitInMemberEditor) { diff --git a/controls/pivotview/src/common/popups/grouping.ts b/controls/pivotview/src/common/popups/grouping.ts index 14aadfde8e..b10d2d761d 100644 --- a/controls/pivotview/src/common/popups/grouping.ts +++ b/controls/pivotview/src/common/popups/grouping.ts @@ -596,8 +596,8 @@ export class Grouping implements IAction { let groupFields: IGroupSettings[] = PivotUtil.cloneGroupSettings(this.parent.dataSourceSettings.groupSettings); if (groupFields.length === 0 && !this.parent.clonedDataSet && !this.parent.clonedReport) { - const dataSet: IDataSet[] = this.parent.engineModule.data as IDataSet[]; - this.parent.clonedDataSet = PivotUtil.getClonedData(dataSet) as IDataSet[]; + const dataSet: IDataSet[] | string[][] = this.parent.engineModule.data as IDataSet[] | string[][]; + this.parent.clonedDataSet = this.parent.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(dataSet as string[][]) as string[][] : PivotUtil.getClonedData(dataSet as IDataSet[]) as IDataSet[]; this.parent.setProperties({ dataSourceSettings: { dataSource: [] } }, true); this.parent.clonedReport = extend({}, this.parent.dataSourceSettings, null, true) as DataSourceSettings; this.parent.setProperties({ dataSourceSettings: { dataSource: dataSet } }, true); diff --git a/controls/pivotview/src/pivotchart/base/pivotchart.ts b/controls/pivotview/src/pivotchart/base/pivotchart.ts index c181240cd4..8ff75ed1b8 100644 --- a/controls/pivotview/src/pivotchart/base/pivotchart.ts +++ b/controls/pivotview/src/pivotchart/base/pivotchart.ts @@ -48,6 +48,7 @@ export class PivotChart { private measuresNames: { [key: string]: string } = {}; private accumulationType: ChartSeriesType[] = ['Pie', 'Pyramid', 'Doughnut', 'Funnel']; private accEmptyPoint: boolean; + private isChartInitial: boolean = true; /** @hidden */ public calculatedWidth: number; /** @hidden */ @@ -169,7 +170,8 @@ export class PivotChart { } } } - if (this.parent.enableVirtualization && this.parent.isInitial) { + if (this.parent.enableVirtualization && this.isChartInitial) { + this.isChartInitial = false; // eslint-disable-next-line @typescript-eslint/no-explicit-any (this.parent as any).onContentReady(); } diff --git a/controls/pivotview/src/pivotfieldlist/base/field-list.ts b/controls/pivotview/src/pivotfieldlist/base/field-list.ts index f70cd9620d..385ff2232f 100644 --- a/controls/pivotview/src/pivotfieldlist/base/field-list.ts +++ b/controls/pivotview/src/pivotfieldlist/base/field-list.ts @@ -73,7 +73,7 @@ export class PivotFieldList extends Component implements INotifyPro public isRequiredUpdate: boolean = true; /** @hidden */ - public clonedDataSet: IDataSet[]; + public clonedDataSet: IDataSet[] | string[][]; /** @hidden */ public clonedReport: IDataOptions; /** @hidden */ @@ -1218,8 +1218,9 @@ export class PivotFieldList extends Component implements INotifyPro PivotUtil.updateDataSourceSettings(this, observedArgs.dataSourceSettings); if (this.dataType === 'pivot') { if (this.dataSourceSettings.groupSettings && this.dataSourceSettings.groupSettings.length > 0) { - const pivotDataSet: IDataSet[] = this.dataSourceSettings.dataSource as IDataSet[]; - this.clonedDataSet = (this.clonedDataSet ? this.clonedDataSet : PivotUtil.getClonedData(pivotDataSet)) as IDataSet[]; + const pivotDataSet: IDataSet[] | string[][] = this.dataSourceSettings.dataSource as IDataSet[] | string[][]; + this.clonedDataSet = (this.clonedDataSet ? this.clonedDataSet : this.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(pivotDataSet as string[][]) as string[][] + : PivotUtil.getClonedData(pivotDataSet as IDataSet[])) as IDataSet[]; const dataSourceSettings: IDataOptions = JSON.parse(this.getPersistData()).dataSourceSettings as IDataOptions; dataSourceSettings.dataSource = []; this.clonedReport = this.clonedReport ? this.clonedReport : dataSourceSettings; diff --git a/controls/pivotview/src/pivotfieldlist/renderer/tree-renderer.ts b/controls/pivotview/src/pivotfieldlist/renderer/tree-renderer.ts index f9a9c296c0..27a7929bad 100644 --- a/controls/pivotview/src/pivotfieldlist/renderer/tree-renderer.ts +++ b/controls/pivotview/src/pivotfieldlist/renderer/tree-renderer.ts @@ -516,7 +516,8 @@ export class TreeViewRenderer implements IAction { const list: { [key: string]: Object } = this.parent.pivotFieldList; const selectedNode: { [key: string]: Object } = list[fieldName as string] as { [key: string]: Object }; this.parent.pivotCommon.dataSourceUpdate.control = this.parent.getModuleName() === 'pivotview' ? this.parent : - (this.parent.isPopupView && (this.parent as PivotFieldList).pivotGridModule ? (this.parent as PivotFieldList).pivotGridModule : this.parent); + (this.parent.isPopupView && (this.parent as PivotFieldList).pivotGridModule ? + (this.parent as PivotFieldList).pivotGridModule : this.parent); if (this.parent.pivotCommon.nodeStateModified.onStateModified(args, fieldName)) { if (this.parent.allowDeferLayoutUpdate) { selectedNode.isSelected = true; diff --git a/controls/pivotview/src/pivotview/base/pivotview.ts b/controls/pivotview/src/pivotview/base/pivotview.ts index f18262f871..6d8ebcf020 100644 --- a/controls/pivotview/src/pivotview/base/pivotview.ts +++ b/controls/pivotview/src/pivotview/base/pivotview.ts @@ -586,7 +586,7 @@ export class PivotView extends Component implements INotifyProperty /** @hidden */ public isSummaryCellHyperlink: boolean; /** @hidden */ - public clonedDataSet: IDataSet[]; + public clonedDataSet: IDataSet[] | string[][]; /** @hidden */ public clonedReport: DataSourceSettingsModel; /** @hidden */ @@ -3654,7 +3654,8 @@ export class PivotView extends Component implements INotifyProperty this.tooltip.destroy(); } if (this.dataSourceSettings.groupSettings && this.dataSourceSettings.groupSettings.length > 0 && this.clonedDataSet) { - const dataSet: IDataSet[] = PivotUtil.getClonedData(this.clonedDataSet) as IDataSet[]; + const dataSet: IDataSet[] | string[][] = this.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(this.clonedDataSet as string[][]) as string[][] + : PivotUtil.getClonedData(this.clonedDataSet as IDataSet[]) as IDataSet[]; this.setProperties({ dataSourceSettings: { dataSource: dataSet } }, true); } super.refresh(); @@ -5898,8 +5899,9 @@ export class PivotView extends Component implements INotifyProperty }; if (this.dataType === 'pivot') { if (this.dataSourceSettings.groupSettings && this.dataSourceSettings.groupSettings.length > 0) { - const dataSet: IDataSet[] = this.engineModule.data as IDataSet[]; - this.clonedDataSet = (this.clonedDataSet ? this.clonedDataSet : PivotUtil.getClonedData(dataSet)) as IDataSet[]; + const dataSet: IDataSet[] | string[][] = this.engineModule.data as IDataSet[] | string[][]; + this.clonedDataSet = (this.clonedDataSet ? this.clonedDataSet : this.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(dataSet as string[][]) as string[][] + : PivotUtil.getClonedData(dataSet as IDataSet[])) as IDataSet[]; const dataSourceSettings: IDataOptions = JSON.parse(this.getPersistData()).dataSourceSettings as IDataOptions; dataSourceSettings.dataSource = []; this.clonedReport = this.clonedReport ? this.clonedReport : dataSourceSettings; @@ -6266,14 +6268,14 @@ export class PivotView extends Component implements INotifyProperty public updateGroupingReport(newGroupSettings: IGroupSettings[], updateGroupType: GroupType): void { if (!this.clonedDataSet && !this.clonedReport) { - const dataSet: IDataSet[] = this.engineModule.data as IDataSet[]; - this.clonedDataSet = PivotUtil.getClonedData(dataSet) as IDataSet[]; + const dataSet: IDataSet[] | string[][] = this.engineModule.data as IDataSet[] | string[][]; + this.clonedDataSet = this.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(dataSet as string[][]) as string[][] :PivotUtil.getClonedData(dataSet as IDataSet[]) as IDataSet[]; const dataSourceSettings: IDataOptions = JSON.parse(this.getPersistData()).dataSourceSettings as IDataOptions; dataSourceSettings.dataSource = []; this.clonedReport = this.clonedReport ? this.clonedReport : dataSourceSettings; } const dateGroup: RegExp = /_date_group_years|_date_group_quarters|_date_group_quarterYear|_date_group_months|_date_group_days|_date_group_hours|_date_group_minutes|_date_group_seconds/g; - const data: IDataSet[] = PivotUtil.getClonedData(this.clonedDataSet) as IDataSet[]; + const data: IDataSet[] | string[][] = this.dataSourceSettings.type === 'CSV' ? PivotUtil.getClonedCSVData(this.clonedDataSet as string[][]) as string[][] : PivotUtil.getClonedData(this.clonedDataSet as IDataSet[]) as IDataSet[]; const dataSource: IDataOptions = this.dataSourceSettings; const clonedReport: IDataOptions = (<{ [key: string]: Object }>this.clonedReport).properties ? (<{ [key: string]: Object }>this.clonedReport).properties : this.clonedReport; diff --git a/controls/pivotview/src/pivotview/renderer/render.ts b/controls/pivotview/src/pivotview/renderer/render.ts index 4688e47f09..4d79bf024a 100644 --- a/controls/pivotview/src/pivotview/renderer/render.ts +++ b/controls/pivotview/src/pivotview/renderer/render.ts @@ -1266,7 +1266,10 @@ export class Render { if (this.parent.enableVirtualization) { if (cell.ordinal > -1 && this.parent.olapEngineModule.tupRowInfo.length > 0) { const tupInfo: ITupInfo = this.parent.olapEngineModule.tupRowInfo[cell.ordinal]; - const memberPosition: number = tupInfo.uNameCollection.indexOf(cell.actualText.toString()); + const cellActualText: string = cell.memberType === 3 ? + (this.engine.fieldList[cell.actualText.toString()] as IOlapFieldListOptions).tag.toString() : + cell.actualText.toString(); + const memberPosition: number = tupInfo.uNameCollection.indexOf(cellActualText); const cropUName: string = tupInfo.uNameCollection.substring(0, memberPosition) + (cell.memberType === 3 ? '' : cell.actualText.toString()); const fieldSep: string[] = cropUName.split('::[').map((item: string) => { diff --git a/controls/popups/CHANGELOG.md b/controls/popups/CHANGELOG.md index 4facf60edc..10a76483c6 100644 --- a/controls/popups/CHANGELOG.md +++ b/controls/popups/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Tooltip + +#### Bug Fixes + +- `#I533557` - The console error while assigning a function type value to the `Content` property in the Tooltip component has been resolved. + ## 24.1.44 (2024-01-03) ### Tooltip @@ -9,7 +17,6 @@ #### Bug Fixes - `#I531367` - The issue with the Tooltip's incorrect positioning when template content is rendered has been resolved. -- `#I533557` - The console error while assigning a function type value to the `Content` property in the Tooltip component has been resolved. ## 21.1.35 (2023-03-23) diff --git a/controls/querybuilder/CHANGELOG.md b/controls/querybuilder/CHANGELOG.md index 9ae65df215..7a95dff1a2 100644 --- a/controls/querybuilder/CHANGELOG.md +++ b/controls/querybuilder/CHANGELOG.md @@ -2,6 +2,22 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### QueryBuilder + +#### Bug Fixes + +- `#I534895` - Issue with `getValidRules` method of query builder returns improper rule for in operator has been fixed. + +## 24.1.45 (2024-01-09) + +### QueryBuilder + +#### Bug Fixes + +- `#I526596` - Issue with Dropdown tree item expand and selection related issue in query builder has been fixed. + ## 24.1.44 (2024-01-03) ### QueryBuilder diff --git a/controls/querybuilder/package.json b/controls/querybuilder/package.json index 38f70bbc59..49d0fb9cac 100644 --- a/controls/querybuilder/package.json +++ b/controls/querybuilder/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-querybuilder", - "version": "24.1.44", + "version": "24.1.45", "description": "Essential JS 2 QueryBuilder", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/querybuilder/spec/query-builder.spec.ts b/controls/querybuilder/spec/query-builder.spec.ts index 6f3a835951..e0f8337fbe 100644 --- a/controls/querybuilder/spec/query-builder.spec.ts +++ b/controls/querybuilder/spec/query-builder.spec.ts @@ -3418,6 +3418,28 @@ describe('QueryBuilder', () => { queryBuilder.setRulesFromSql("Name.FirstName LIKE ('Date.parse('yyyy-MM-dd','1980-05-24')%')"); expect(queryBuilder.rule.rules[0].value).toEqual("Date.parse('yyyy-MM-dd','1980-05-24')"); }); + + it('EJ2-863630 - GetValidRules method of query builder returns improper rule for in operator', () => { + let customFieldData: ColumnsModel[] = [ + { field: 'EmployeeID', label: 'Employee ID', type: 'number' }, + { field: 'FirstName', label: 'First Name', type: 'string' } + ]; + queryBuilder = new QueryBuilder({ + dataSource: employeeData, + columns: customFieldData + + }, '#querybuilder'); + let filterElem: DropDownList = queryBuilder.element.querySelector('.e-rule-filter .e-control').ej2_instances[0]; + filterElem.showPopup(); + let items: NodeListOf = document.getElementById('querybuilder_group0_rule0_filterkey_options').querySelectorAll('li'); + items[0].click(); + let operatorElem: DropDownList = queryBuilder.element.querySelector('.e-rule-operator .e-control').ej2_instances[0]; + operatorElem.showPopup(); + let itemsCln: NodeListOf = document.getElementById('querybuilder_group0_rule0_operatorkey_options').querySelectorAll('li'); + itemsCln[8].click(); + expect(operatorElem.value).toEqual('in'); + expect(queryBuilder.getValidRules()).toEqual({}); + }); }); diff --git a/controls/querybuilder/src/query-builder/query-builder-model.d.ts b/controls/querybuilder/src/query-builder/query-builder-model.d.ts index 8c4bcaa275..9b6fb21973 100644 --- a/controls/querybuilder/src/query-builder/query-builder-model.d.ts +++ b/controls/querybuilder/src/query-builder/query-builder-model.d.ts @@ -1,4 +1,4 @@ -import { Component, INotifyPropertyChanged, NotifyPropertyChanges, getComponent, MouseEventArgs, Browser, compile, append } from '@syncfusion/ej2-base';import { Property, ChildProperty, Complex, L10n, closest, extend, isNullOrUndefined, Collection, cldrData } from '@syncfusion/ej2-base';import { getInstance, addClass, removeClass, rippleEffect, detach, classList } from '@syncfusion/ej2-base';import { Internationalization, DateFormatOptions, KeyboardEventArgs, getUniqueID, select } from '@syncfusion/ej2-base';import { Button, CheckBox, RadioButton, ChangeEventArgs as ButtonChangeEventArgs, RadioButtonModel } from '@syncfusion/ej2-buttons';import { DropDownList, ChangeEventArgs as DropDownChangeEventArgs, FieldSettingsModel, CheckBoxSelection, DropDownTreeModel, DropDownTree, DdtSelectEventArgs } from '@syncfusion/ej2-dropdowns';import { MultiSelect, MultiSelectChangeEventArgs, PopupEventArgs, MultiSelectModel, DropDownListModel } from '@syncfusion/ej2-dropdowns';import { EmitType, Event, EventHandler, getValue, Animation, BaseEventArgs } from '@syncfusion/ej2-base';import { Query, Predicate, DataManager, Deferred } from '@syncfusion/ej2-data';import { TextBox, NumericTextBox, InputEventArgs, ChangeEventArgs as InputChangeEventArgs } from '@syncfusion/ej2-inputs';import { TextBoxModel, NumericTextBoxModel } from '@syncfusion/ej2-inputs';import { DatePicker, ChangeEventArgs as CalendarChangeEventArgs, DatePickerModel } from '@syncfusion/ej2-calendars';import { DropDownButton, ItemModel, MenuEventArgs } from '@syncfusion/ej2-splitbuttons';import { Tooltip, TooltipEventArgs, createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups';import { compile as templateCompiler } from '@syncfusion/ej2-base'; +import { Component, INotifyPropertyChanged, NotifyPropertyChanges, getComponent, MouseEventArgs, Browser, compile, append } from '@syncfusion/ej2-base';import { Property, ChildProperty, Complex, L10n, closest, extend, isNullOrUndefined, Collection, cldrData } from '@syncfusion/ej2-base';import { getInstance, addClass, removeClass, rippleEffect, detach, classList } from '@syncfusion/ej2-base';import { Internationalization, DateFormatOptions, KeyboardEventArgs, getUniqueID, select } from '@syncfusion/ej2-base';import { Button, CheckBox, RadioButton, ChangeEventArgs as ButtonChangeEventArgs, RadioButtonModel } from '@syncfusion/ej2-buttons';import { DropDownList, ChangeEventArgs as DropDownChangeEventArgs, FieldSettingsModel, CheckBoxSelection, DropDownTreeModel, DropDownTree } from '@syncfusion/ej2-dropdowns';import { MultiSelect, MultiSelectChangeEventArgs, PopupEventArgs, MultiSelectModel, DropDownListModel } from '@syncfusion/ej2-dropdowns';import { EmitType, Event, EventHandler, getValue, Animation, BaseEventArgs } from '@syncfusion/ej2-base';import { Query, Predicate, DataManager, Deferred } from '@syncfusion/ej2-data';import { TextBox, NumericTextBox, InputEventArgs, ChangeEventArgs as InputChangeEventArgs } from '@syncfusion/ej2-inputs';import { TextBoxModel, NumericTextBoxModel } from '@syncfusion/ej2-inputs';import { DatePicker, ChangeEventArgs as CalendarChangeEventArgs, DatePickerModel } from '@syncfusion/ej2-calendars';import { DropDownButton, ItemModel, MenuEventArgs } from '@syncfusion/ej2-splitbuttons';import { Tooltip, createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups';import { compile as templateCompiler } from '@syncfusion/ej2-base'; import {TemplateColumn,Validation,FormatObject,ActionEventArgs,ChangeEventArgs,RuleChangeEventArgs,FieldMode,DisplayMode,SortDirection} from "./query-builder"; import {ComponentModel} from '@syncfusion/ej2-base'; diff --git a/controls/querybuilder/src/query-builder/query-builder.d.ts b/controls/querybuilder/src/query-builder/query-builder.d.ts index f9bf839994..79d3d71df9 100644 --- a/controls/querybuilder/src/query-builder/query-builder.d.ts +++ b/controls/querybuilder/src/query-builder/query-builder.d.ts @@ -302,6 +302,7 @@ export declare class QueryBuilder extends Component implements I private isDestroy; private isGetNestedData; private isCustomOprCols; + private dummyDropdownTreeDs; /** * Triggers when the component is created. * @@ -511,6 +512,7 @@ export declare class QueryBuilder extends Component implements I private appendRuleElem; private addRuleElement; private addRuleSuccessCallBack; + private updateDropdowntreeDS; private updateAddedRule; private changeRuleTemplate; private renderToolTip; @@ -541,7 +543,6 @@ export declare class QueryBuilder extends Component implements I private filterValue; private changeValueSuccessCallBack; private fieldClose; - private onSelectField; private changeField; private changeRule; private changeFilter; diff --git a/controls/querybuilder/src/query-builder/query-builder.ts b/controls/querybuilder/src/query-builder/query-builder.ts index d16d2357f3..74c98ba100 100644 --- a/controls/querybuilder/src/query-builder/query-builder.ts +++ b/controls/querybuilder/src/query-builder/query-builder.ts @@ -7,7 +7,7 @@ import { getInstance, addClass, removeClass, rippleEffect, detach, classList } f import { Internationalization, DateFormatOptions, KeyboardEventArgs, getUniqueID, select } from '@syncfusion/ej2-base'; import { QueryBuilderModel, ShowButtonsModel, ColumnsModel, RuleModel, ValueModel } from './query-builder-model'; import { Button, CheckBox, RadioButton, ChangeEventArgs as ButtonChangeEventArgs, RadioButtonModel } from '@syncfusion/ej2-buttons'; -import { DropDownList, ChangeEventArgs as DropDownChangeEventArgs, FieldSettingsModel, CheckBoxSelection, DropDownTreeModel, DropDownTree, DdtSelectEventArgs } from '@syncfusion/ej2-dropdowns'; +import { DropDownList, ChangeEventArgs as DropDownChangeEventArgs, FieldSettingsModel, CheckBoxSelection, DropDownTreeModel, DropDownTree } from '@syncfusion/ej2-dropdowns'; import { MultiSelect, MultiSelectChangeEventArgs, PopupEventArgs, MultiSelectModel, DropDownListModel } from '@syncfusion/ej2-dropdowns'; import { EmitType, Event, EventHandler, getValue, Animation, BaseEventArgs } from '@syncfusion/ej2-base'; import { Query, Predicate, DataManager, Deferred } from '@syncfusion/ej2-data'; @@ -15,7 +15,7 @@ import { TextBox, NumericTextBox, InputEventArgs, ChangeEventArgs as InputChange import { TextBoxModel, NumericTextBoxModel } from '@syncfusion/ej2-inputs'; import { DatePicker, ChangeEventArgs as CalendarChangeEventArgs, DatePickerModel } from '@syncfusion/ej2-calendars'; import { DropDownButton, ItemModel, MenuEventArgs } from '@syncfusion/ej2-splitbuttons'; -import { Tooltip, TooltipEventArgs, createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups'; +import { Tooltip, createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups'; import { compile as templateCompiler } from '@syncfusion/ej2-base'; type ReturnType = { result: Object[], count: number, aggregates?: Object }; @@ -343,6 +343,7 @@ export class QueryBuilder extends Component implements INotifyPr private isDestroy: boolean = false; private isGetNestedData: boolean = false; private isCustomOprCols: string[] = []; + private dummyDropdownTreeDs: Object; /**     * Triggers when the component is created. * @@ -653,7 +654,8 @@ export class QueryBuilder extends Component implements INotifyPr if (categories.indexOf(columns[i as number].category) < 0) { categories.push(columns[i as number].category); } - if (!columns[i as number].operators || (this.isLocale && this.isCustomOprCols.indexOf(columns[i as number].field) !== 0)) { + if (!columns[i as number].operators || + (this.isLocale && this.isCustomOprCols.indexOf(columns[i as number].field) !== 0)) { columns[i as number].operators = this.customOperators[columns[i as number].type + 'Operator']; } } @@ -1038,13 +1040,16 @@ export class QueryBuilder extends Component implements INotifyPr } else { let ddlField: DropDownTreeModel; const ddlValue: string = this.isImportRules ? (rule.field as string) : rule.field; + this.dummyDropdownTreeDs = extend([], this.columns, [], true) as { [key: string]: Object }[]; + this.updateDropdowntreeDS(this.dummyDropdownTreeDs as { [key: string]: Object }[]); ddlField = { - fields: {dataSource: this.columns as { [key: string]: Object }[], - value: 'field', text: 'label', child: 'columns', expanded: 'expanded'}, + fields: {dataSource: this.dummyDropdownTreeDs as { [key: string]: Object }[], + value: 'field', text: 'label', child: 'columns', expanded: 'expanded', selectable: 'selectable'}, placeholder: this.l10n.getConstant('SelectField'), showClearButton: false, popupHeight: ((this.columns.length > 5) ? height : 'auto'), changeOnBlur: false, change: this.changeField.bind(this), value: !isNullOrUndefined(ddlValue) ? [ddlValue] : null, - open: this.popupOpen.bind(this, false), treeSettings: {expandOn: 'Click'}, select: this.onSelectField + open: this.popupOpen.bind(this, false), treeSettings: { expandOn: 'Click' }, + cssClass: 'e-qb-ddt' }; if (this.fieldModel) { ddlField = {...ddlField, ...this.fieldModel as DropDownTreeModel}; @@ -1072,6 +1077,15 @@ export class QueryBuilder extends Component implements INotifyPr } } + private updateDropdowntreeDS(columns: { [key: string]: Object }[]): void { + for (let i: number = 0; i < columns.length; i++) { + if (columns[parseInt(i.toString(), 10)].type === 'object') { + columns[parseInt(i.toString(), 10)].selectable = false; + this.updateDropdowntreeDS(columns[parseInt(i.toString(), 10)].columns as { [key: string]: Object }[]); + } + } + } + private updateAddedRule(target: Element, rule: RuleModel, newRule: RuleModel, isRuleTemplate?: boolean, pId?: string): void { let ruleElem: Element; let index: number = 0; let groupElem: Element; let rules: RuleModel; @@ -1128,7 +1142,7 @@ export class QueryBuilder extends Component implements INotifyPr private renderToolTip(element: HTMLElement): void { const tooltip: Tooltip = new Tooltip({ content: this.l10n.getConstant('ValidationMessage'), - position: 'BottomCenter', cssClass: 'e-querybuilder-error', afterClose: (args: TooltipEventArgs): void => { + position: 'BottomCenter', cssClass: 'e-querybuilder-error', afterClose: (): void => { tooltip.destroy(); }}); tooltip.appendTo(element); @@ -1426,7 +1440,8 @@ export class QueryBuilder extends Component implements INotifyPr const groupHdr: HTMLElement = groupElem.querySelector('.e-group-header'); if (this.headerTemplate) { args = { requestType: 'header-template-initialize', ruleID: groupElem.id, - notCondition: this.enableNotCondition ? not : undefined, condition: condition, rule: this.getRuleCollection(rule, true), groupID: groupID }; + notCondition: this.enableNotCondition ? not : undefined, + condition: condition, rule: this.getRuleCollection(rule, true), groupID: groupID }; this.trigger('actionBegin', args); this.headerFn = this.templateParser(this.headerTemplate); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1680,13 +1695,6 @@ export class QueryBuilder extends Component implements INotifyPr this.isFieldChange = false; } - private onSelectField(args: DdtSelectEventArgs): void { - if (args.itemData.hasChildren) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this as any).showPopup(); - } - } - private changeField(args: DropDownChangeEventArgs): void { if (args.isInteracted) { if (isNullOrUndefined(args.value)) { @@ -2759,7 +2767,8 @@ export class QueryBuilder extends Component implements INotifyPr const valElemColl: Element[] = this.columnTemplateFn(args, this, ruleID, templateID); valElem = (valElemColl[0].nodeType === 3) ? valElemColl[1] : valElemColl[0]; target.nextElementSibling.appendChild(valElem as Element); - } else if ((this as any).isVue3) { + } // eslint-disable-next-line @typescript-eslint/no-explicit-any + else if ((this as any).isVue3) { valElem = this.columnTemplateFn(args, this, 'Template', templateID); // eslint-disable-next-line @typescript-eslint/no-explicit-any append(valElem as any, target.nextElementSibling); @@ -3397,7 +3406,7 @@ export class QueryBuilder extends Component implements INotifyPr this.element.style.height = this.height; break; case 'rule': - if (this.rule.rules.length == 0 && !isNullOrUndefined(this.rule)) { + if (this.rule.rules.length === 0 && !isNullOrUndefined(this.rule)) { this.reset(); } this.setProperties({ rule: newProp.rule }, true); @@ -3888,6 +3897,9 @@ export class QueryBuilder extends Component implements INotifyPr // eslint-disable-next-line @typescript-eslint/no-explicit-any (rule as any).custom = customObj; } + if ((rule.operator === 'in' || rule.operator === 'notin') && rule.value && (rule.value as any).length === 0) { + rule = {}; + } } else { rule = {}; } diff --git a/controls/ribbon/CHANGELOG.md b/controls/ribbon/CHANGELOG.md index e786c54d85..36b10340a8 100644 --- a/controls/ribbon/CHANGELOG.md +++ b/controls/ribbon/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -## 24.1.45 (2024-01-09) +## 24.1.44 (2024-01-03) ### Ribbon diff --git a/controls/ribbon/README.md b/controls/ribbon/README.md index 20ea8ef93b..ea8383c1bd 100644 --- a/controls/ribbon/README.md +++ b/controls/ribbon/README.md @@ -5,6 +5,7 @@ The [JavaScript Ribbon](https://www.syncfusion.com/javascript-ui-controls/js-ribbon?utm_source=npm&utm_medium=listing&utm_campaign=javascript-ribbon-npm) control provides a structured and easy-to-use user interface for users to access different features and functions through series of tabs, improving the overall user experience and making your application more efficient.

+ Getting Started | Online demos | Learn more

@@ -37,12 +38,12 @@ Ribbon control is also offered in the following list of frameworks. ## Key features -* **Built-in items**: Several built-in support items, such as buttons, checkboxes, dropdown buttons, split buttons, combo boxes, and color pickers that can be customized and used to execute specific actions. -* **Modes**: Offers the `classic mode` that organizes items and groups in a traditional form, and `simplified mode` that organizes items and groups into a single row for improved usability and reduced clutter. -* **Tooltip**: Provide additional information when a user hovers over a ribbon item, improving user experience and increasing the usability of the application. -* **File menu**: A built-in menu that to add file related actions easily. -* **Templates**: Customize ribbon items and the help pane content using templates. - +* [Built-in items](https://ej2.syncfusion.com/documentation/ribbon/items): Several built-in support items, such as buttons, checkboxes, dropdown buttons, split buttons, combo boxes, group button, and color pickers that can be customized and used to execute specific actions. +* [Modes](https://ej2.syncfusion.com/documentation/ribbon/layouts): Offers the `classic mode` that organizes items and groups in a traditional form, and `simplified mode` that organizes items and groups into a single row for improved usability and reduced clutter. +* [Tooltip](https://ej2.syncfusion.com/documentation/ribbon/tooltip): Provide additional information when a user hovers over a ribbon item, improving user experience and increasing the usability of the application. +* [File menu](https://ej2.syncfusion.com/documentation/ribbon/file-menu): A built-in menu that to add file related actions easily. +* [Backstage](https://ej2.syncfusion.com/documentation/ribbon/backstage): It is an extension of traditional file menu for displaying information, based on the user interactions with backstage options. +* [Templates](https://ej2.syncfusion.com/documentation/ribbon/help-pane-template): Customize ribbon items and the help pane content using templates. ## Support diff --git a/controls/ribbon/package.json b/controls/ribbon/package.json index 7c7385fc6b..7bce72e002 100644 --- a/controls/ribbon/package.json +++ b/controls/ribbon/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-ribbon", - "version": "24.1.41", + "version": "24.1.44", "description": "Essential JS 2 Component", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/ribbon/styles/ribbon/_bootstrap-dark-definition.scss b/controls/ribbon/styles/ribbon/_bootstrap-dark-definition.scss index b78cdadce7..79a9902f52 100644 --- a/controls/ribbon/styles/ribbon/_bootstrap-dark-definition.scss +++ b/controls/ribbon/styles/ribbon/_bootstrap-dark-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 6px 6px !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 2px !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 40px !default; $ribbon-bigger-large-items-btn-padding: 0 6px !default; $ribbon-bigger-large-items-btn-size: 14px !default; $ribbon-bigger-large-items-btn-height: 22px !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_bootstrap-definition.scss b/controls/ribbon/styles/ribbon/_bootstrap-definition.scss index e92eca825b..f21be7fd4e 100644 --- a/controls/ribbon/styles/ribbon/_bootstrap-definition.scss +++ b/controls/ribbon/styles/ribbon/_bootstrap-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 6px 6px !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 4px !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 40px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 14px !default; $ribbon-bigger-large-items-btn-height: 22px !default; +$ribbon-bigger-large-items-btn-gap: 9px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_bootstrap4-definition.scss b/controls/ribbon/styles/ribbon/_bootstrap4-definition.scss index c8d31dcbd1..d6f5900dfa 100644 --- a/controls/ribbon/styles/ribbon/_bootstrap4-definition.scss +++ b/controls/ribbon/styles/ribbon/_bootstrap4-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 22px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 5px 6px !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 4px !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 40px !default; $ribbon-bigger-large-items-btn-padding: 0 3px !default; $ribbon-bigger-large-items-btn-size: 16px !default; $ribbon-bigger-large-items-btn-height: 24px !default; +$ribbon-bigger-large-items-btn-gap: 6px !default; $ribbon-bigger-items-padding: 1px !default; $ribbon-bigger-items-btn-size: 16px !default; $ribbon-bigger-items-height: 24px !default; diff --git a/controls/ribbon/styles/ribbon/_bootstrap5-definition.scss b/controls/ribbon/styles/ribbon/_bootstrap5-definition.scss index 0e2ebe122e..858772428d 100644 --- a/controls/ribbon/styles/ribbon/_bootstrap5-definition.scss +++ b/controls/ribbon/styles/ribbon/_bootstrap5-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: $text-2xl !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 6px 4px !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 4px !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 32px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: $text-sm !default; $ribbon-bigger-large-items-btn-height: 18px !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: $text-sm !default; $ribbon-bigger-items-height: 18px !default; diff --git a/controls/ribbon/styles/ribbon/_fabric-dark-definition.scss b/controls/ribbon/styles/ribbon/_fabric-dark-definition.scss index ce26c53521..c3a5e15067 100644 --- a/controls/ribbon/styles/ribbon/_fabric-dark-definition.scss +++ b/controls/ribbon/styles/ribbon/_fabric-dark-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 12px 7px 4px !default; +$ribbon-large-items-btn-gap: 6px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 14px !default; $ribbon-bigger-large-items-btn-height: 22px !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_fabric-definition.scss b/controls/ribbon/styles/ribbon/_fabric-definition.scss index 5c8fba3598..a56c505d9b 100644 --- a/controls/ribbon/styles/ribbon/_fabric-definition.scss +++ b/controls/ribbon/styles/ribbon/_fabric-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 22px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 12px 5px 4px !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 6px !default; $ribbon-bigger-large-items-btn-size: 14px !default; $ribbon-bigger-large-items-btn-height: 22px !default; +$ribbon-bigger-large-items-btn-gap: 9px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_fluent-definition.scss b/controls/ribbon/styles/ribbon/_fluent-definition.scss index d380cae27f..4de48bfe86 100644 --- a/controls/ribbon/styles/ribbon/_fluent-definition.scss +++ b/controls/ribbon/styles/ribbon/_fluent-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-padding: 0 2px !default; $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: $text-2xl !default; $ribbon-large-items-max-width: 10ch !default; +$ribbon-large-items-btn-gap: 4px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 2px !default; $ribbon-items-font-weight: $font-weight-normal !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: $text-4xl !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: $text-sm !default; $ribbon-bigger-large-items-btn-height: 125% !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: $text-sm !default; $ribbon-bigger-items-height: 125% !default; diff --git a/controls/ribbon/styles/ribbon/_fusionnew-definition.scss b/controls/ribbon/styles/ribbon/_fusionnew-definition.scss index 294f1a7954..6112d4d1ea 100644 --- a/controls/ribbon/styles/ribbon/_fusionnew-definition.scss +++ b/controls/ribbon/styles/ribbon/_fusionnew-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-padding: 0 2px !default; $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: $text-2xl !default; $ribbon-large-items-max-width: 10ch !default; +$ribbon-large-items-btn-gap: 5px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 2px !default; $ribbon-items-font-weight: $font-weight-normal !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: $text-4xl !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: $text-sm !default; $ribbon-bigger-large-items-btn-height: 125% !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: $text-sm !default; $ribbon-bigger-items-height: 125% !default; diff --git a/controls/ribbon/styles/ribbon/_highcontrast-definition.scss b/controls/ribbon/styles/ribbon/_highcontrast-definition.scss index 89dcf0ab3e..fc4233c886 100644 --- a/controls/ribbon/styles/ribbon/_highcontrast-definition.scss +++ b/controls/ribbon/styles/ribbon/_highcontrast-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 22px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 12px 10px 4px !default; +$ribbon-large-items-btn-gap: 8px !default; $ribbon-items-border: 2px solid !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 16px !default; $ribbon-bigger-large-items-btn-height: 24px !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_highcontrast-light-definition.scss b/controls/ribbon/styles/ribbon/_highcontrast-light-definition.scss index f17762d237..960571f0f8 100644 --- a/controls/ribbon/styles/ribbon/_highcontrast-light-definition.scss +++ b/controls/ribbon/styles/ribbon/_highcontrast-light-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 22px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 12px 10px 4px !default; +$ribbon-large-items-btn-gap: 8px !default; $ribbon-items-border: 2px solid !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 16px !default; $ribbon-bigger-large-items-btn-height: 24px !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_layout.scss b/controls/ribbon/styles/ribbon/_layout.scss index 9f7905973d..4028e466c4 100644 --- a/controls/ribbon/styles/ribbon/_layout.scss +++ b/controls/ribbon/styles/ribbon/_layout.scss @@ -648,6 +648,10 @@ .e-icon-top { height: 50%; } + + &.e-top-icon-btn { + gap: $ribbon-large-items-btn-gap; + } } & > .e-split-btn-wrapper.e-vertical .e-btn { @@ -1273,6 +1277,10 @@ font-size: $ribbon-bigger-large-items-icon-size; padding: $ribbon-bigger-large-items-icon-padding; } + + &.e-top-icon-btn { + gap: $ribbon-bigger-large-items-btn-gap; + } } .e-dropdown-btn { diff --git a/controls/ribbon/styles/ribbon/_material-dark-definition.scss b/controls/ribbon/styles/ribbon/_material-dark-definition.scss index 3ddfdea4a1..66361eaab2 100644 --- a/controls/ribbon/styles/ribbon/_material-dark-definition.scss +++ b/controls/ribbon/styles/ribbon/_material-dark-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 6px 6px !default; +$ribbon-large-items-btn-gap: 7px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 16px !default; $ribbon-bigger-large-items-btn-height: 24px !default; +$ribbon-bigger-large-items-btn-gap: 6px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_material-definition.scss b/controls/ribbon/styles/ribbon/_material-definition.scss index 6cc1817186..4a069ccfdb 100644 --- a/controls/ribbon/styles/ribbon/_material-definition.scss +++ b/controls/ribbon/styles/ribbon/_material-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: 32px !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 8px 6px 6px !default; +$ribbon-large-items-btn-gap: 7px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: unset !default; $ribbon-items-font-weight: 400 !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: 16px !default; $ribbon-bigger-large-items-btn-height: 24px !default; +$ribbon-bigger-large-items-btn-gap: 6px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: 14px !default; $ribbon-bigger-items-height: 22px !default; diff --git a/controls/ribbon/styles/ribbon/_material3-definition.scss b/controls/ribbon/styles/ribbon/_material3-definition.scss index 250f0e649d..a822eaca9e 100644 --- a/controls/ribbon/styles/ribbon/_material3-definition.scss +++ b/controls/ribbon/styles/ribbon/_material3-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-height: 15px !default; $ribbon-large-items-icon-size: $text-4xl !default; $ribbon-large-items-max-width: 10ch !default; $ribbon-large-items-icon-padding: 4px 3px !default; +$ribbon-large-items-btn-gap: 4px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 4px !default; $ribbon-items-font-weight: $font-weight-normal !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 36px !default; $ribbon-bigger-large-items-btn-padding: 0 3px !default; $ribbon-bigger-large-items-btn-size: $text-sm !default; $ribbon-bigger-large-items-btn-height: 18px !default; +$ribbon-bigger-large-items-btn-gap: 9px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: $text-sm !default; $ribbon-bigger-items-height: 18px !default; diff --git a/controls/ribbon/styles/ribbon/_tailwind-definition.scss b/controls/ribbon/styles/ribbon/_tailwind-definition.scss index e9ebd47aac..959e04c09f 100644 --- a/controls/ribbon/styles/ribbon/_tailwind-definition.scss +++ b/controls/ribbon/styles/ribbon/_tailwind-definition.scss @@ -57,6 +57,7 @@ $ribbon-large-items-btn-padding: 0 2px !default; $ribbon-large-items-btn-height: 18px !default; $ribbon-large-items-icon-size: $text-2xl !default; $ribbon-large-items-max-width: 10ch !default; +$ribbon-large-items-btn-gap: 4px !default; $ribbon-items-border: none !default; $ribbon-items-border-radius: 4px !default; $ribbon-items-font-weight: $font-weight-normal !default; @@ -153,6 +154,7 @@ $ribbon-bigger-large-items-icon-size: 32px !default; $ribbon-bigger-large-items-btn-padding: 0 5px !default; $ribbon-bigger-large-items-btn-size: $text-sm !default; $ribbon-bigger-large-items-btn-height: 125% !default; +$ribbon-bigger-large-items-btn-gap: 8px !default; $ribbon-bigger-items-padding: 2px !default; $ribbon-bigger-items-btn-size: $text-sm !default; $ribbon-bigger-items-height: 125% !default; diff --git a/controls/richtexteditor/CHANGELOG.md b/controls/richtexteditor/CHANGELOG.md index c24c5a9be2..a28e452b5a 100644 --- a/controls/richtexteditor/CHANGELOG.md +++ b/controls/richtexteditor/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### RichTextEditor + +#### Bug Fixes + +- `#I537067` - Now, the content at the top is displayed properly when maximizing the Rich Text Editor with `enableFloating` set to `false`. + +- `#I534484` - Now, applying the bold repeatedly to the content in the Rich Text Editor works properly, and the content doesn't get deleted. + ## 24.1.45 (2024-01-09) ### RichTextEditor diff --git a/controls/richtexteditor/package.json b/controls/richtexteditor/package.json index 5426f470b2..0c7039cf7e 100644 --- a/controls/richtexteditor/package.json +++ b/controls/richtexteditor/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-richtexteditor", - "version": "24.1.43", + "version": "24.1.45", "description": "Essential JS 2 RichTextEditor component", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/richtexteditor/spec/cr-issues/rich-text-editor.spec.ts b/controls/richtexteditor/spec/cr-issues/rich-text-editor.spec.ts index f0836429e5..2eda6f5acb 100644 --- a/controls/richtexteditor/spec/cr-issues/rich-text-editor.spec.ts +++ b/controls/richtexteditor/spec/cr-issues/rich-text-editor.spec.ts @@ -6,6 +6,7 @@ import { FormValidator } from "@syncfusion/ej2-inputs"; import { dispatchEvent } from '../../src/rich-text-editor/base/util'; import { RichTextEditor } from '../../src/rich-text-editor/base/rich-text-editor'; import { renderRTE, destroy, setCursorPoint, dispatchEvent as dispatchEve } from './../rich-text-editor/render.spec'; +import { SelectionCommands } from '../../src/editor-manager/plugin/selection-commands'; import { NodeSelection } from '../../src/selection/selection'; import { IRenderer, QuickToolbar } from '../../src/index'; @@ -3266,4 +3267,31 @@ describe('RTE CR issues', () => { expect(rteObj.inputElement.innerHTML == '

​​Rich Text Editor

').toBe(true); }); }); + describe("863440: Too many times applying bold to a text, sometimes the text got deleted in RichTextEditor.", () => { + let rteEle: HTMLElement; + let rteObj: RichTextEditor; + let domSelection: NodeSelection = new NodeSelection(); + let parentDiv: HTMLDivElement; + beforeEach(() => { + rteObj = renderRTE({ + enterKey: 'BR', + value:`

second rtec

` + }); + rteEle = rteObj.element; + parentDiv = document.getElementById('div1') as HTMLDivElement; + }); + afterEach(() => { + destroy(rteObj); + }); + it('Apply Bold tag for cursor position', () => { + let node1: Node = document.getElementById('paragraph1'); + let text1: Text = node1.childNodes[0] as Text; + domSelection.setSelectionText(document, text1, text1, 1, 1); + SelectionCommands.applyFormat(document, 'bold', parentDiv, 'P'); + expect(node1.childNodes[0].nodeName.toLowerCase()).toEqual('strong'); + domSelection.setSelectionText(document, text1, text1, 5, 5); + SelectionCommands.applyFormat(document, 'bold', parentDiv, 'P'); + expect(rteObj.inputElement.innerHTML).toEqual('

second rtec

'); + }); + }); }); diff --git a/controls/richtexteditor/spec/editor-manager/plugin/msword-cleanup.spec.ts b/controls/richtexteditor/spec/editor-manager/plugin/msword-cleanup.spec.ts index ae4e4b28e6..b6910e39f1 100644 --- a/controls/richtexteditor/spec/editor-manager/plugin/msword-cleanup.spec.ts +++ b/controls/richtexteditor/spec/editor-manager/plugin/msword-cleanup.spec.ts @@ -9318,7 +9318,7 @@ it('V Shape image paste from MSWord', () => { pasteOK[0].click(); } let pastedElem: any = (rteObj as any).inputElement.innerHTML; - let expectedElem: string = '
  • The Rich Text Editor is WYSIWYG ("what you see is what you\nget") editor useful to create and edit content and return the valid HTML markup or markdown of the content

  • Toolbar

50

'; + let expectedElem: string = '
  • The Rich Text Editor is WYSIWYG ("what you see is what you\nget") editor useful to create and edit content and return the valid HTML markup or markdown of the content

  • Toolbar

50

'; expect(expectedElem === pastedElem).toBe(true); done(); }, 400); diff --git a/controls/richtexteditor/spec/editor-manager/plugin/selection-commands.spec.ts b/controls/richtexteditor/spec/editor-manager/plugin/selection-commands.spec.ts index 842eb6356f..3a19aa3b93 100644 --- a/controls/richtexteditor/spec/editor-manager/plugin/selection-commands.spec.ts +++ b/controls/richtexteditor/spec/editor-manager/plugin/selection-commands.spec.ts @@ -1359,7 +1359,7 @@ describe('EJ2-70136 - Font Size value not updating while on selected text', () = let domSelection: NodeSelection = new NodeSelection(); it('EJ2-70136 - Font Size value not updating while on selected text', () => { rteObj = renderRTE({ - value: `

The Rich Text Editor is a WYSIWYG ("what you see is what you get") editor useful to create and edit content and return the valid HTML markup or markdown of the content

`, + value: `

The Rich Text Editor is a WYSIWYG ("what you see is what you get") editor useful to create and edit content and return the valid HTML markup or markdown of the content

`, toolbarSettings: { items: ['FontSize'] } @@ -1374,7 +1374,7 @@ describe('EJ2-70136 - Font Size value not updating while on selected text', () = fontSizePicker.click(); var fontSizeChooser : HTMLElement = document.querySelectorAll(".e-item")[5]; fontSizeChooser.click(); - expect(rteEle.childNodes[2].childNodes[0].innerHTML).toBe('

The Rich Text Editor is a WYSIWYG ("what you see is what you get") editor useful to create and edit content and return the valid HTML markup or markdown of the content

'); + expect(rteEle.childNodes[2].childNodes[0].innerHTML).toBe('

The Rich Text Editor is a WYSIWYG ("what you see is what you get") editor useful to create and edit content and return the valid HTML markup or markdown of the content

'); expect(fontSizePicker.childNodes[0].textContent).toEqual('24 pt'); }); afterEach(() => { diff --git a/controls/richtexteditor/spec/rich-text-editor/actions/paste-clean-up.spec.ts b/controls/richtexteditor/spec/rich-text-editor/actions/paste-clean-up.spec.ts index 2033d02dee..b16aa4c64e 100644 --- a/controls/richtexteditor/spec/rich-text-editor/actions/paste-clean-up.spec.ts +++ b/controls/richtexteditor/spec/rich-text-editor/actions/paste-clean-up.spec.ts @@ -637,7 +637,7 @@ describe("paste cleanup testing", () => { expect(pastedElm.children[0].children[0].childNodes[1].tagName.toLowerCase() === 'a').toBe(true); expect(pastedElm.children[0].children[0].childNodes[1].getAttribute('href') === 'https://ej2.syncfusion.com').toBe(true); let expected: boolean = false; - let expectedElem: string = `

Hi syncfusion website https://ej2.syncfusion.com is here14

`; + let expectedElem: string = `

Hi syncfusion website https://ej2.syncfusion.com is here14

`; if (pastedElm.innerHTML === expectedElem) { expected = true; } @@ -669,7 +669,7 @@ describe("paste cleanup testing", () => { expect(pastedElm.children[0].children[0].childNodes[1].tagName.toLowerCase() === 'a').toBe(true); expect(pastedElm.children[0].children[0].childNodes[1].getAttribute('href') === 'https://ej2.syncfusion.com').toBe(true); let expected: boolean = false; - let expectedElem: string = `

Hi syncfusion website https://ej2.syncfusion.com is here with another URL https://ej2.syncfusion.com text after second URL15

`; + let expectedElem: string = `

Hi syncfusion website https://ej2.syncfusion.com is here with another URL https://ej2.syncfusion.com text after second URL15

`; if (pastedElm.innerHTML === expectedElem) { expected = true; } @@ -703,7 +703,7 @@ third line`; setTimeout(() => { let pastedElm: any = (rteObj as any).inputElement.innerHTML; let expected: boolean = false; - let expectedElem: string = `

first line

        Second line with space https://ej2.syncfusion.com



third line

16

`; + let expectedElem: string = `

first line

        Second line with space https://ej2.syncfusion.com



third line

16

`; if (pastedElm === expectedElem) { expected = true; } @@ -736,7 +736,7 @@ third line`; setTimeout(() => { let pastedElm: any = (rteObj as any).inputElement.innerHTML; let expected: boolean = false; - let expectedElem: string = `

http://www.google.com?first=a¶meters=foo 160

`; + let expectedElem: string = `

http://www.google.com?first=a¶meters=foo 160

`; if (pastedElm === expectedElem) { expected = true; } @@ -2337,7 +2337,7 @@ describe("Pasting the link element added in the editor without prompt", () => { setCursorPoint((rteObj as any).inputElement.firstElementChild.firstChild.firstChild, 7); rteObj.onPaste(keyBoardEvent); setTimeout(() => { - expect((rteObj as any).inputElement.innerHTML === `

TestingTesting

`).toBe(true) + expect((rteObj as any).inputElement.innerHTML === `

TestingTesting

`).toBe(true) done(); }, 100); }); @@ -2392,7 +2392,7 @@ describe("Pasting the link element added in the editor with prompt", () => { let pasteOK: any = document.getElementById(rteObj.getID() + '_pasteCleanupDialog').getElementsByClassName(CLS_RTE_PASTE_OK); pasteOK[0].click(); } - expect((rteObj as any).inputElement.innerHTML === `

TestingTesting

`).toBe(true) + expect((rteObj as any).inputElement.innerHTML === `

TestingTesting

`).toBe(true) done(); }, 100); }); diff --git a/controls/richtexteditor/spec/rich-text-editor/actions/toolbar.spec.ts b/controls/richtexteditor/spec/rich-text-editor/actions/toolbar.spec.ts index 3f4d4289ea..6960620e09 100644 --- a/controls/richtexteditor/spec/rich-text-editor/actions/toolbar.spec.ts +++ b/controls/richtexteditor/spec/rich-text-editor/actions/toolbar.spec.ts @@ -2595,4 +2595,33 @@ describe('849075 - Checking the tab index on navigating the toolbar items using done(); }, 800); }); +}); +describe('864182-Texts got hidden under the toolbar, when we maximize the RichTextEditor when enableFloating is false.', () => { + let rteObj: RichTextEditor; + let rteEle: HTMLElement; + beforeAll(() => { + rteObj = renderRTE({ + toolbarSettings: { + enableFloating: false, + items: ['fullscreen','Bold','SourceCode'] + }, + }); + rteEle = rteObj.element; + }); + + it('Test - toolbar height testing when maximize/minimize and check toolbar wrapper is not null.', () => { + const toolbarElement = rteEle.querySelector('.e-toolbar-wrapper') as HTMLElement | null; + expect(toolbarElement != null).toBe(true); + const toolbarHeight: string | null = toolbarElement.style.height; + const trgEle: HTMLElement = rteEle.querySelectorAll(".e-toolbar-item")[0]; + trgEle.click(); + trgEle.click(); + const newElement = rteEle.querySelector('.e-toolbar-wrapper') as HTMLElement | null; + const newHeight: string | null = newElement.style.height; + expect(newHeight).toBe(toolbarHeight); + + }); + afterAll(() => { + destroy(rteObj); + }); }); \ No newline at end of file diff --git a/controls/richtexteditor/spec/rich-text-editor/base/rich-text-editor.spec.ts b/controls/richtexteditor/spec/rich-text-editor/base/rich-text-editor.spec.ts index 94aaa7a532..2dd9e20bd5 100644 --- a/controls/richtexteditor/spec/rich-text-editor/base/rich-text-editor.spec.ts +++ b/controls/richtexteditor/spec/rich-text-editor/base/rich-text-editor.spec.ts @@ -2173,7 +2173,7 @@ describe('RTE base module', () => { expect(allElem.children[0].childNodes[0].childNodes[0].childNodes[1].tagName.toLowerCase() === 'a').toBe(true); expect(allElem.children[0].childNodes[0].childNodes[0].childNodes[1].getAttribute('href') === 'https://ej2.syncfusion.com').toBe(true); let expected: boolean = false; - let expectedElem: string = `
  1. Hi syncfusion website https://ej2.syncfusion.com is here with another URL https://ej2.syncfusion.com text after second URLFirst p node-0

First p node-1

`; + let expectedElem: string = `
  1. Hi syncfusion website https://ej2.syncfusion.com is here with another URL https://ej2.syncfusion.com text after second URLFirst p node-0

First p node-1

`; if (allElem.innerHTML === expectedElem) { expected = true; } diff --git a/controls/richtexteditor/spec/rich-text-editor/renderer/link-module.spec.ts b/controls/richtexteditor/spec/rich-text-editor/renderer/link-module.spec.ts index 88a8135bf8..4ab74c7cc4 100644 --- a/controls/richtexteditor/spec/rich-text-editor/renderer/link-module.spec.ts +++ b/controls/richtexteditor/spec/rich-text-editor/renderer/link-module.spec.ts @@ -1554,9 +1554,9 @@ describe('Link Module', () => { rteObj.linkModule.dialogObj.contentEle.querySelector('.e-rte-linkurl').value = 'https://www.syncfusion.com'; let target : HTMLElement= rteObj.linkModule.dialogObj.primaryButtonEle; rteObj.linkModule.dialogObj.primaryButtonEle.click({ target: target, preventDefault: function() {} }); - let firstChild : string = 'syncfusion'; + let firstChild : string = 'syncfusion'; expect((pEle.childNodes[0] as HTMLElement).innerHTML === firstChild).toBe(true); - let secondChild : string = 'Chennai'; + let secondChild : string = 'Chennai'; expect((pEle.childNodes[1] as HTMLElement).innerHTML === secondChild).toBe(true); }); it('For single element with bold and image', function() { @@ -1570,7 +1570,7 @@ describe('Link Module', () => { let target : string = rteObj.linkModule.dialogObj.primaryButtonEle; rteObj.linkModule.dialogObj.primaryButtonEle.click({ target: target, preventDefault: function() {} }); (rteEle.querySelectorAll(".e-toolbar-item")[0] as HTMLElement).click(); - let firstChild : string= 'Syncfusion' + + let firstChild : string= 'Syncfusion' + 'undefined RichTextEditor'; expect(rteObj.inputElement.childNodes[0].innerHTML === firstChild).toBe(true); }); @@ -1602,9 +1602,9 @@ describe('Link Module', () => { rteObj.linkModule.dialogObj.contentEle.querySelector('.e-rte-linkurl').value = 'https://www.syncfusion.com'; let target : HTMLElement= rteObj.linkModule.dialogObj.primaryButtonEle; rteObj.linkModule.dialogObj.primaryButtonEle.click({ target: target, preventDefault: function() {} }); - let firstChild : string = 'syncfusion'; + let firstChild : string = 'syncfusion'; expect((pEle.childNodes[0] as HTMLElement).innerHTML === firstChild).toBe(true); - let secondChild : string = 'Chennai'; + let secondChild : string = 'Chennai'; expect((pEle.childNodes[1] as HTMLElement).innerHTML === secondChild).toBe(true); }); it('For single element with bold and image-Iframe', function() { @@ -1618,7 +1618,7 @@ describe('Link Module', () => { let target : string = rteObj.linkModule.dialogObj.primaryButtonEle; rteObj.linkModule.dialogObj.primaryButtonEle.click({ target: target, preventDefault: function() {} }); (rteEle.querySelectorAll(".e-toolbar-item")[0] as HTMLElement).click(); - let firstChild : string= 'Syncfusion' + + let firstChild : string= 'Syncfusion' + 'undefined RichTextEditor'; expect(rteObj.inputElement.childNodes[0].innerHTML === firstChild).toBe(true); }); diff --git a/controls/richtexteditor/spec/rich-text-editor/renderer/toolbar-renderer.spec.ts b/controls/richtexteditor/spec/rich-text-editor/renderer/toolbar-renderer.spec.ts index a07060898e..477e51c0a0 100644 --- a/controls/richtexteditor/spec/rich-text-editor/renderer/toolbar-renderer.spec.ts +++ b/controls/richtexteditor/spec/rich-text-editor/renderer/toolbar-renderer.spec.ts @@ -1,7 +1,7 @@ /** * Toolbar renderer spec */ -import { Browser } from "@syncfusion/ej2-base"; +import { Browser, isNullOrUndefined } from "@syncfusion/ej2-base"; import { renderRTE,dispatchEvent, destroy } from './../render.spec'; describe('Toolbar - Renderer', () => { @@ -147,4 +147,33 @@ describe('Toolbar - Renderer', () => { Browser.userAgent = defaultUA; }); }); + describe('863259: dropdown active state not working when drop down is opened', function () { + let rteObj : any; + let rteEle : any; + beforeAll(function () { + rteObj = renderRTE({ + toolbarSettings: { + items: ['FontName', 'FontSize', 'FontColor', 'BackgroundColor', '|',] + } + }); + rteEle = rteObj.element; + }); + it('Check the fontColor dropdown active element', function () { + let trgEle : HTMLElement = rteEle.querySelectorAll(".e-toolbar-item")[0]; + (trgEle.firstElementChild as HTMLElement).click(); + dispatchEvent(trgEle.firstElementChild, 'mousedown'); + let activeEle = (document.querySelector('.e-dropdown-popup .e-segoe-ui.e-active') as HTMLElement); + expect(!isNullOrUndefined(activeEle)).toBe(true); + }); + it('Check the fontSize dropdown active element', function () { + let trgEle : HTMLElement = rteEle.querySelectorAll(".e-toolbar-item")[1]; + (trgEle.firstElementChild as HTMLElement).click(); + dispatchEvent(trgEle.firstElementChild, 'mousedown'); + let activeEle = (document.querySelector('.e-font-size-tbar-btn .e-item.e-active') as HTMLElement); + expect(!isNullOrUndefined(activeEle)).toBe(true); + }); + afterAll(function () { + destroy(rteObj); + }); + }); }); \ No newline at end of file diff --git a/controls/richtexteditor/src/editor-manager/plugin/link.ts b/controls/richtexteditor/src/editor-manager/plugin/link.ts index 322b574fb2..422d36e218 100644 --- a/controls/richtexteditor/src/editor-manager/plugin/link.ts +++ b/controls/richtexteditor/src/editor-manager/plugin/link.ts @@ -236,6 +236,9 @@ export class LinkCommand { if (!isNOU(e.item.target)) { anchorEle.setAttribute('target', e.item.target); } + if (!isNOU(e.item.ariaLabel)) { + anchorEle.setAttribute('aria-label', e.item.ariaLabel); + } return anchorEle; } diff --git a/controls/richtexteditor/src/editor-manager/plugin/nodecutter.ts b/controls/richtexteditor/src/editor-manager/plugin/nodecutter.ts index ab4a157f4c..136ebb96b3 100644 --- a/controls/richtexteditor/src/editor-manager/plugin/nodecutter.ts +++ b/controls/richtexteditor/src/editor-manager/plugin/nodecutter.ts @@ -168,7 +168,7 @@ export class NodeCutter { if ( (indexes.indexOf(range.startOffset) >= 0) || ( (indexes.indexOf(range.startOffset - 1) >= 0) && ( range.startOffset !== 1 || ( range.startOffset === 1 && new RegExp('\\s').test(str[0])) ) - || (((indexes[indexes.length - 1] - 1 ) === range.startOffset) && !new RegExp('\\s').test(str[0])))) { + || (((indexes[indexes.length - 1] - 1 ) === range.startOffset) && range.endOffset !== (str.length - 1) && !new RegExp('\\s').test(str[0])))) { cursorRange = range; this.position = 1; } else { diff --git a/controls/richtexteditor/src/rich-text-editor/actions/html-editor.ts b/controls/richtexteditor/src/rich-text-editor/actions/html-editor.ts index 80e3a79b99..eeb6b70d8e 100644 --- a/controls/richtexteditor/src/rich-text-editor/actions/html-editor.ts +++ b/controls/richtexteditor/src/rich-text-editor/actions/html-editor.ts @@ -2,7 +2,7 @@ import * as events from '../base/constant'; import { IRichTextEditor, IToolbarItemModel, IColorPickerRenderArgs, IRenderer } from '../base/interface'; import { NotifyArgs, IToolbarOptions, ActionBeginEventArgs } from '../base/interface'; import { ServiceLocator } from '../services/service-locator'; -import { isNullOrUndefined, closest, KeyboardEventArgs, attributes, removeClass, addClass, Browser, detach, MouseEventArgs, EventHandler } from '@syncfusion/ej2-base'; +import { isNullOrUndefined, closest, KeyboardEventArgs, attributes, removeClass, addClass, Browser, detach, MouseEventArgs, EventHandler, L10n } from '@syncfusion/ej2-base'; import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base'; import { HTMLFormatter } from '../formatter/html-formatter'; import { RendererFactory } from '../services/renderer-factory'; @@ -573,7 +573,7 @@ export class HtmlEditor { for (let j: number = 0 ; j < splitTextContent.length; j++) { if (splitTextContent[j as number].match(httpRegex) || splitTextContent[j as number].match(wwwRegex)) { resultSplitContent += '' + splitTextContent[j as number] + ' '; + '" title="' + splitTextContent[j as number] + '" target="_blank"' + ' aria-label="' + this.parent.serviceLocator.getService('rteLocale').getConstant("linkAriaLabel") + '">' + splitTextContent[j as number] + ' '; } else { resultSplitContent += splitTextContent[j as number] + ' '; } diff --git a/controls/richtexteditor/src/rich-text-editor/actions/paste-clean-up.ts b/controls/richtexteditor/src/rich-text-editor/actions/paste-clean-up.ts index af90c466c1..06bc17e11b 100644 --- a/controls/richtexteditor/src/rich-text-editor/actions/paste-clean-up.ts +++ b/controls/richtexteditor/src/rich-text-editor/actions/paste-clean-up.ts @@ -709,7 +709,7 @@ export class PasteCleanup { events.afterPasteCleanup, { value : clipBoardElem.innerHTML, filesData: filesData }, (updatedArgs: PasteCleanupArgs) => { value = updatedArgs.value; }); - clipBoardElem.innerHTML = value; + clipBoardElem.innerHTML = this.parent.addAnchorAriaLabel(value); clipBoardElem = this.addTableClass(clipBoardElem); this.parent.formatter.editorManager.execCommand( 'inserthtml', diff --git a/controls/richtexteditor/src/rich-text-editor/actions/toolbar.ts b/controls/richtexteditor/src/rich-text-editor/actions/toolbar.ts index 85c1f2f4f1..fcebc95050 100644 --- a/controls/richtexteditor/src/rich-text-editor/actions/toolbar.ts +++ b/controls/richtexteditor/src/rich-text-editor/actions/toolbar.ts @@ -88,7 +88,7 @@ export class Toolbar { if (!Browser.isDevice && this.parent.inlineMode.enable && isIDevice()) { return; } else { - if (this.parent.toolbarSettings.enableFloating && !this.parent.inlineMode.enable) { + if (!this.parent.inlineMode.enable) { this.tbWrapper = this.parent.createElement('div', { id: this.parent.getID() + '_toolbar_wrapper', innerHTML: this.tbElement.outerHTML, diff --git a/controls/richtexteditor/src/rich-text-editor/base/interface.ts b/controls/richtexteditor/src/rich-text-editor/base/interface.ts index ebbfcf3f06..880208201d 100644 --- a/controls/richtexteditor/src/rich-text-editor/base/interface.ts +++ b/controls/richtexteditor/src/rich-text-editor/base/interface.ts @@ -215,6 +215,7 @@ export interface IRichTextEditor extends Component { currentTarget: HTMLElement focusIn(): void showEmojiPicker?(x?: number, y?: number): void + addAnchorAriaLabel?(value: string): string } /** * @deprecated diff --git a/controls/richtexteditor/src/rich-text-editor/base/rich-text-editor.ts b/controls/richtexteditor/src/rich-text-editor/base/rich-text-editor.ts index 3374bd2505..1221996d5b 100644 --- a/controls/richtexteditor/src/rich-text-editor/base/rich-text-editor.ts +++ b/controls/richtexteditor/src/rich-text-editor/base/rich-text-editor.ts @@ -1694,6 +1694,7 @@ export class RichTextEditor extends Component implements INotifyPro * @deprecated */ protected render(): void { + this.value = (!(this.editorMode === 'Markdown') && !isNOU(this.value)) ? this.addAnchorAriaLabel(this.value) : this.value; if (this.value && !this.valueTemplate) { this.setProperties({ value: this.serializeValue(this.value) }, true); } @@ -3367,6 +3368,18 @@ export class RichTextEditor extends Component implements INotifyPro args.callBack(value); } + public addAnchorAriaLabel(value: string): string { + let valueElementWrapper: HTMLElement = document.createElement("div"); + valueElementWrapper.innerHTML = value; + let item: NodeListOf = valueElementWrapper.querySelectorAll("a"); + if (item.length > 0) { + for (let i: number = 0; i < item.length; i++) { + (item[i as number].hasAttribute("target") && item[i as number].getAttribute("target") === '_blank') ? item[i as number].setAttribute("aria-label", (this.serviceLocator.getService('rteLocale') as any).getConstant("linkAriaLabel")) : item[i as number]; + } + } + return valueElementWrapper.innerHTML; + } + private removeResizeElement(value: string): string { let valueElementWrapper: HTMLElement = document.createElement("div"); valueElementWrapper.innerHTML = value; diff --git a/controls/richtexteditor/src/rich-text-editor/renderer/toolbar-renderer.ts b/controls/richtexteditor/src/rich-text-editor/renderer/toolbar-renderer.ts index e9a0ba92b7..345a87ce97 100644 --- a/controls/richtexteditor/src/rich-text-editor/renderer/toolbar-renderer.ts +++ b/controls/richtexteditor/src/rich-text-editor/renderer/toolbar-renderer.ts @@ -271,7 +271,7 @@ export class ToolbarRenderer implements IRenderer { } } //Formats preselect - if ((args.items[0 as number] as any).command === 'Formats') { + if ((args.items[0 as number] as any).command === 'Formats' || (args.items[0 as number] as any).command === 'Font') { for (let index: number = 0; index < args.element.childNodes.length; index++) { const divNode: HTMLDivElement = this.parent.createElement('div') as HTMLDivElement; divNode.innerHTML = dropDown.content.trim(); diff --git a/controls/splitbuttons/package.json b/controls/splitbuttons/package.json index 1da4e64ae7..262bae1470 100644 --- a/controls/splitbuttons/package.json +++ b/controls/splitbuttons/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-splitbuttons", - "version": "24.1.44", + "version": "24.1.45", "description": "A package of feature-rich Essential JS 2 components such as DropDownButton, SplitButton, ProgressButton and ButtonGroup.", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/splitbuttons/styles/button-group/_layout.scss b/controls/splitbuttons/styles/button-group/_layout.scss index 6043b63de3..af9797b040 100644 --- a/controls/splitbuttons/styles/button-group/_layout.scss +++ b/controls/splitbuttons/styles/button-group/_layout.scss @@ -16,7 +16,7 @@ input:focus+label.e-btn, .e-btn:focus, .e-btn:hover { - @if $skin-name == 'tailwind' or $skin-name == 'bootstrap5' { + @if $skin-name == 'tailwind' or $skin-name == 'bootstrap5' or $skin-name == 'bootstrap4' { z-index: 2; } } diff --git a/controls/splitbuttons/styles/button-group/_theme.scss b/controls/splitbuttons/styles/button-group/_theme.scss index c22c1d9c5a..0abcf0f54c 100644 --- a/controls/splitbuttons/styles/button-group/_theme.scss +++ b/controls/splitbuttons/styles/button-group/_theme.scss @@ -10,7 +10,7 @@ } } - @if $skin-name == 'material' or $skin-name == 'bootstrap4' or $skin-name == 'Material3' { + @if $skin-name == 'material' or $skin-name == 'Material3' { .e-btn { box-shadow: none; } @@ -54,7 +54,7 @@ .e-btn:focus, input:focus+label.e-btn { @include button-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -71,7 +71,7 @@ &.e-primary { @include primary-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -85,7 +85,7 @@ &.e-success { @include success-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -99,7 +99,7 @@ &.e-info { @include info-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -113,7 +113,7 @@ &.e-warning { @include warning-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -127,7 +127,7 @@ &.e-danger { @include danger-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -149,7 +149,7 @@ &.e-outline { @include outline-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' $skin-name != 'bootstrap4' and and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -158,7 +158,7 @@ &.e-primary { @include outline-primary-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -168,7 +168,7 @@ &.e-success { @include outline-success-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -178,7 +178,7 @@ &.e-info { @include outline-info-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -188,7 +188,7 @@ &.e-warning { @include outline-warning-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { @@ -198,7 +198,7 @@ &.e-danger { @include outline-danger-focus; - @if $skin-name != 'bootstrap5' and $skin-name != 'tailwind' { + @if $skin-name != 'bootstrap5' and $skin-name != 'bootstrap4' and $skin-name != 'tailwind' { box-shadow: none; } @if $skin-name == 'bootstrap5' { diff --git a/controls/spreadsheet/README.md b/controls/spreadsheet/README.md index a4909e88e9..16998f1ba3 100644 --- a/controls/spreadsheet/README.md +++ b/controls/spreadsheet/README.md @@ -1,5 +1,3 @@ -[![coverage](http://ej2.syncfusion.com/badges/ej2-spreadsheet/coverage.svg)](http://ej2.syncfusion.com/badges/ej2-spreadsheet) - # JavaScript Spreadsheet Control The [JavaScript Spreadsheet](https://www.syncfusion.com/javascript-ui-controls/js-spreadsheet?utm_source=npm&utm_medium=listing&utm_campaign=javascript-spreadsheet-npm) is an user interactive control to organize and analyze data in tabular format with configuration options for customization. It will load data by importing an Excel/CSV file or from local and remote data sources such as JSON, RESTful services, OData services, and more. The populated data can be exported as Excel with xlsx, xls, CSV and PDF formats. diff --git a/controls/spreadsheet/package.json b/controls/spreadsheet/package.json index 13bee4e45b..3a7eacdff1 100644 --- a/controls/spreadsheet/package.json +++ b/controls/spreadsheet/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-spreadsheet", - "version": "24.1.44", + "version": "24.1.45", "description": "Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/spreadsheet/spec/spreadsheet/integrations/formula.spec.ts b/controls/spreadsheet/spec/spreadsheet/integrations/formula.spec.ts index c831cc4c22..a36deda47a 100644 --- a/controls/spreadsheet/spec/spreadsheet/integrations/formula.spec.ts +++ b/controls/spreadsheet/spec/spreadsheet/integrations/formula.spec.ts @@ -14822,6 +14822,15 @@ describe('Spreadsheet formula module ->', () => { expect(tdEle.textContent).toBe('-22'); done(); }); + it('I864921 -> Throws error while performing arithmetic operation with percentage formatted value', (done: Function) => { + helper.edit('J7', '=5.68899%+1.457288%'); + expect(helper.invoke('getCell', [6, 9]).textContent).toBe('0.07146278'); + helper.edit('J8', '=(5.68899%)+1.457288%'); + expect(helper.invoke('getCell', [7, 9]).textContent).toBe('0.07146278'); + helper.edit('J9', '=(5.68899%)+(1.457288%)'); + expect(helper.invoke('getCell', [8, 9]).textContent).toBe('0.07146278'); + done(); + }); }); describe('Provide the support to handle the wrong formula in spreadsheet and display alert dialog -> ', () => { beforeEach((done: Function) => { diff --git a/controls/spreadsheet/src/calculate/base/parser.ts b/controls/spreadsheet/src/calculate/base/parser.ts index f3d91a148b..c0af3b6f3f 100644 --- a/controls/spreadsheet/src/calculate/base/parser.ts +++ b/controls/spreadsheet/src/calculate/base/parser.ts @@ -571,7 +571,7 @@ export class Parser { let period: boolean = false; while (j > -1 && (this.parent.isDigit(text[j as number]) || (!period && (text[j as number] === this.parent.getParseDecimalSeparator() || text[j as number] === '%')))) { - if (!this.parent.isDigit(text[j as number])) { + if (!this.parent.isDigit(text[j as number]) && text[j as number] !== '%') { period = true; } j = j - 1; diff --git a/controls/spreadsheet/src/spreadsheet/actions/data-validation.ts b/controls/spreadsheet/src/spreadsheet/actions/data-validation.ts index a3493a5182..28f5f2edc9 100644 --- a/controls/spreadsheet/src/spreadsheet/actions/data-validation.ts +++ b/controls/spreadsheet/src/spreadsheet/actions/data-validation.ts @@ -52,7 +52,11 @@ export class DataValidation { } private addEventListener(): void { - EventHandler.add(this.parent.element, 'dblclick', this.listOpen, this); + if (Browser.isDevice && Browser.info.name === 'safari' && (Browser.isIos || Browser.isIos7)) { + EventHandler.add(this.parent.element, 'touchend', this.listOpen, this); + } else { + EventHandler.add(this.parent.element, 'dblclick', this.listOpen, this); + } this.parent.on(initiateDataValidation, this.initiateDataValidationHandler, this); this.parent.on(invalidData, this.invalidDataHandler, this); this.parent.on(isValidation, this.checkDataValidation, this); @@ -63,7 +67,11 @@ export class DataValidation { } private removeEventListener(): void { - EventHandler.remove(this.parent.element, 'dblclick', this.listOpen); + if (Browser.isDevice && Browser.info.name === 'safari' && (Browser.isIos || Browser.isIos7)) { + EventHandler.remove(this.parent.element, 'touchend', this.listOpen); + } else { + EventHandler.remove(this.parent.element, 'dblclick', this.listOpen); + } if (!this.parent.isDestroyed) { this.parent.off(initiateDataValidation, this.initiateDataValidationHandler); this.parent.off(invalidData, this.invalidDataHandler); @@ -147,7 +155,7 @@ export class DataValidation { private listOpen(e: MouseEvent): void { const target: HTMLElement = e.target as HTMLElement; - if (this.listObj && target.classList.contains('e-cell') && target.querySelector('.e-validation-list')) { + if (this.listObj && target.classList.contains('e-cell') && target.querySelector('.e-validation-list') && this.parent.isEdit) { this.listObj.showPopup(); } } @@ -181,6 +189,7 @@ export class DataValidation { const indexes: number[] = getCellIndexes(sheet.activeCell); const cell: CellModel = getCell(indexes[0], indexes[1], sheet); const tdEle: HTMLElement = this.parent.getCell(indexes[0], indexes[1]); + let isDevice: boolean; if (!tdEle) { return; } if (document.getElementsByClassName('e-validation-list')[0]) { if (this.listObj) { @@ -215,20 +224,20 @@ export class DataValidation { width: '0px', popupHeight: '200px', change: () => this.listValueChange(this.listObj.text), + beforeOpen: (args: PopupEventArgs) => { + isDevice = (window as any).browserDetails.isDevice; + if (isDevice) { (window as any).browserDetails.isDevice = false; } + }, open: (args: PopupEventArgs) => { args.popup.offsetX = - (tdEle.offsetWidth - 20) + 4; args.popup.offsetY = -13; args.popup.element.style.width = tdEle.offsetWidth - 1 + 'px'; - // Positioning popup in mobile device based on transform css applied on virtual element as suggested by dropdown team - if (Browser.isDevice && this.parent.scrollModule) { - const offset: { left: IOffset, top: IOffset } = this.parent.scrollModule.offset; - const viewport: IViewport = this.parent.viewport; - args.popup.offsetY += viewport.topIndex ? offset.top.size - - getRowsHeight(sheet, viewport.topIndex + 1, offset.top.idx, true) : 0; - args.popup.offsetX += viewport.leftIndex ? offset.left.size - - getColumnsWidth(sheet, viewport.leftIndex + 1, offset.left.idx, true) : 0; - args.popup.refresh(); - args.popup.element.style.width = tdEle.offsetWidth - 1 + 'px'; + if (isDevice) { + const listButton: HTMLElement = tdEle.querySelector('.e-validation-list') as HTMLElement; + const listElem: HTMLElement = tdEle.querySelector('.e-control-wrapper.e-ddl') as HTMLElement; + args.popup.offsetX = - (tdEle.offsetWidth - (listButton ? listButton.offsetWidth : 20)) + 4; + args.popup.offsetY = - ((listElem ? listElem.offsetHeight : 37) - (listButton ? listButton.offsetHeight : 18)); + (window as any).browserDetails.isDevice = true; } }, close: (args: PopupEventArgs): void => { diff --git a/controls/spreadsheet/src/spreadsheet/actions/selection.ts b/controls/spreadsheet/src/spreadsheet/actions/selection.ts index c85ed3d4b6..a11e318663 100644 --- a/controls/spreadsheet/src/spreadsheet/actions/selection.ts +++ b/controls/spreadsheet/src/spreadsheet/actions/selection.ts @@ -339,7 +339,7 @@ export class Selection { (isTouchStart(e) && activeIdx[0] === rowIdx && activeIdx[1] === colIdx)) || isColSelected || isRowSelected)) { document.addEventListener(getMoveEvent().split(' ')[0], this.mouseMoveEvt); if (!Browser.isPointer) { - if (Browser.isIos && isTouchStart(e)) { + if (Browser.isIos && isTouchStart(e) && e.target && (e.target as HTMLElement).classList.contains('e-cell')) { e.preventDefault(); } document.addEventListener(getMoveEvent().split(' ')[1], this.mouseMoveEvt, { passive: false }); diff --git a/controls/spreadsheet/src/spreadsheet/integrations/formula-bar.ts b/controls/spreadsheet/src/spreadsheet/integrations/formula-bar.ts index a5cdb4c2e3..0080bdc517 100644 --- a/controls/spreadsheet/src/spreadsheet/integrations/formula-bar.ts +++ b/controls/spreadsheet/src/spreadsheet/integrations/formula-bar.ts @@ -4,7 +4,7 @@ import { mouseUpAfterSelection, click } from '../common/index'; import { getRangeIndexes, getRangeFromAddress, getCellAddress, getCellIndexes } from './../../workbook/common/address'; import { CellModel, getSheetName, getSheet, SheetModel, checkIsFormula, Workbook, getCell, isCustomDateTime } from '../../workbook/index'; import { updateSelectedRange, getSheetNameFromAddress, getSheetIndex, DefineNameModel, isLocked, getColumn } from '../../workbook/index'; -import { ComboBox, DropDownList, SelectEventArgs as DdlSelectArgs } from '@syncfusion/ej2-dropdowns'; +import { ComboBox, DropDownList, SelectEventArgs as DdlSelectArgs, PopupEventArgs } from '@syncfusion/ej2-dropdowns'; import { BeforeOpenEventArgs } from '@syncfusion/ej2-popups'; import { rippleEffect, L10n, EventHandler, detach, Internationalization, isNullOrUndefined, select } from '@syncfusion/ej2-base'; import { isUndefined, getNumericObject, initializeCSPTemplate } from '@syncfusion/ej2-base'; @@ -28,6 +28,7 @@ export class FormulaBar { private formulaList: ListView; private dialog: Dialog; private isGoto: boolean = false; + private isDevice: boolean; constructor(parent: Spreadsheet) { this.parent = parent; this.addEventListener(); @@ -56,6 +57,9 @@ export class FormulaBar { beforeOpen: this.nameBoxBeforeOpen.bind(this), blur: this.nameBoxBlur.bind(this), select: this.nameBoxSelect.bind(this), + open: (args: PopupEventArgs) => { + if (this.isDevice) { (window as any).browserDetails.isDevice = true; } + }, change: () => { /** */ } @@ -137,6 +141,8 @@ export class FormulaBar { args.cancel = true; } else { (this.comboBoxInstance.element).select(); + this.isDevice = (window as any).browserDetails.isDevice; + if (this.isDevice) { (window as any).browserDetails.isDevice = false; } } } diff --git a/controls/spreadsheet/src/workbook/base/workbook-model.d.ts b/controls/spreadsheet/src/workbook/base/workbook-model.d.ts index 559660295d..eb7ff0193c 100644 --- a/controls/spreadsheet/src/workbook/base/workbook-model.d.ts +++ b/controls/spreadsheet/src/workbook/base/workbook-model.d.ts @@ -1,4 +1,4 @@ -import { Component, Property, NotifyPropertyChanges, INotifyPropertyChanged, Collection, Complex, EmitType } from '@syncfusion/ej2-base';import { initSheet, getSheet, getSheetIndexFromId, getSheetIndexByName, getSheetIndex, Sheet, moveSheet, duplicateSheet } from './sheet';import { Event, ModuleDeclaration, merge, L10n, isNullOrUndefined } from '@syncfusion/ej2-base';import { getWorkbookRequiredModules } from '../common/module';import { SheetModel, CellModel, ColumnModel, RowModel, getData, RangeModel } from './index';import { OpenOptions, BeforeOpenEventArgs, OpenFailureArgs } from '../../spreadsheet/common/interface';import { DefineName, CellStyle, updateRowColCount, getIndexesFromAddress, localeData, workbookLocale, BorderType, getSheetIndexFromAddress, inRange } from '../common/index';import * as events from '../common/event';import { CellStyleModel, DefineNameModel, HyperlinkModel, insertModel, InsertDeleteModelArgs, getAddressInfo } from '../common/index';import { setCellFormat, sheetCreated, deleteModel, ModelType, ProtectSettingsModel, ValidationModel, setLockCells } from '../common/index';import { BeforeSaveEventArgs, SaveCompleteEventArgs, BeforeCellFormatArgs, UnprotectArgs, ExtendedRange } from '../common/interface';import { SaveOptions, SetCellFormatArgs, ClearOptions, AutoFillSettings, AutoFillDirection, AutoFillType, dateToInt } from '../common/index';import { SortOptions, BeforeSortEventArgs, SortEventArgs, FindOptions, CellInfoEventArgs, ConditionalFormatModel } from '../common/index';import { FilterEventArgs, FilterOptions, BeforeFilterEventArgs, ChartModel, getCellIndexes, getCellAddress, unMerge } from '../common/index';import { setMerge, MergeType, MergeArgs, ImageModel, FilterCollectionModel, SortCollectionModel, dataChanged } from '../common/index';import { getCell, skipDefaultValue, setCell, wrap as wrapText } from './cell';import { DataBind, setRow, setColumn, InsertDeleteEventArgs, NumberFormatArgs } from '../index';import { WorkbookSave, WorkbookFormula, WorkbookOpen, WorkbookSort, WorkbookFilter, WorkbookImage } from '../integrations/index';import { WorkbookChart } from '../integrations/index';import { WorkbookNumberFormat, getFormatFromType } from '../integrations/number-format';import { WorkbookEdit, WorkbookCellFormat, WorkbookHyperlink, WorkbookInsert, WorkbookProtectSheet, WorkbookAutoFill } from '../actions/index';import { WorkbookDataValidation, WorkbookMerge } from '../actions/index';import { ServiceLocator } from '../services/index';import { setLinkModel, setImage, setChart, activeCellChanged, setAutoFill, BeforeCellUpdateArgs, updateCell, isNumber } from '../common/index';import { deleteChart, formulaBarOperation } from '../../spreadsheet/common/event';import { beginAction, WorkbookFindAndReplace, getRangeIndexes, workbookEditOperation, clearCFRule, CFArgs, setCFRule } from '../index';import { WorkbookConditionalFormat } from '../actions/conditional-formatting';import { AutoFillSettingsModel } from '../..';import { checkCellValid, VisibleMergeIndexArgs, setVisibleMergeIndex } from '../common/index';import { IFormulaColl } from '../../calculate/common/interface'; +import { Component, Property, NotifyPropertyChanges, INotifyPropertyChanged, Collection, Complex, EmitType } from '@syncfusion/ej2-base';import { initSheet, getSheet, getSheetIndexFromId, getSheetIndexByName, getSheetIndex, Sheet, moveSheet, duplicateSheet } from './sheet';import { Event, ModuleDeclaration, merge, L10n, isNullOrUndefined } from '@syncfusion/ej2-base';import { getWorkbookRequiredModules } from '../common/module';import { SheetModel, CellModel, ColumnModel, RowModel, getData, RangeModel } from './index';import { OpenOptions, BeforeOpenEventArgs, OpenFailureArgs } from '../../spreadsheet/common/interface';import { DefineName, CellStyle, updateRowColCount, getIndexesFromAddress, localeData, workbookLocale, BorderType, getSheetIndexFromAddress, inRange } from '../common/index';import * as events from '../common/event';import { CellStyleModel, DefineNameModel, HyperlinkModel, insertModel, InsertDeleteModelArgs, getAddressInfo } from '../common/index';import { setCellFormat, sheetCreated, deleteModel, ModelType, ProtectSettingsModel, ValidationModel, setLockCells } from '../common/index';import { BeforeSaveEventArgs, SaveCompleteEventArgs, BeforeCellFormatArgs, UnprotectArgs, ExtendedRange } from '../common/interface';import { SaveOptions, SetCellFormatArgs, ClearOptions, AutoFillSettings, AutoFillDirection, AutoFillType, dateToInt } from '../common/index';import { SortOptions, BeforeSortEventArgs, SortEventArgs, FindOptions, CellInfoEventArgs, ConditionalFormatModel } from '../common/index';import { FilterEventArgs, FilterOptions, BeforeFilterEventArgs, ChartModel, getCellIndexes, getCellAddress, unMerge } from '../common/index';import { setMerge, MergeType, MergeArgs, ImageModel, FilterCollectionModel, SortCollectionModel, dataChanged } from '../common/index';import { getCell, skipDefaultValue, setCell, wrap as wrapText, Cell } from './cell';import { DataBind, setRow, setColumn, InsertDeleteEventArgs, NumberFormatArgs } from '../index';import { WorkbookSave, WorkbookFormula, WorkbookOpen, WorkbookSort, WorkbookFilter, WorkbookImage } from '../integrations/index';import { WorkbookChart } from '../integrations/index';import { WorkbookNumberFormat, getFormatFromType } from '../integrations/number-format';import { WorkbookEdit, WorkbookCellFormat, WorkbookHyperlink, WorkbookInsert, WorkbookProtectSheet, WorkbookAutoFill } from '../actions/index';import { WorkbookDataValidation, WorkbookMerge } from '../actions/index';import { ServiceLocator } from '../services/index';import { setLinkModel, setImage, setChart, activeCellChanged, setAutoFill, BeforeCellUpdateArgs, updateCell, isNumber } from '../common/index';import { deleteChart, formulaBarOperation } from '../../spreadsheet/common/event';import { beginAction, WorkbookFindAndReplace, getRangeIndexes, workbookEditOperation, clearCFRule, CFArgs, setCFRule } from '../index';import { WorkbookConditionalFormat } from '../actions/conditional-formatting';import { AutoFillSettingsModel } from '../..';import { checkCellValid, VisibleMergeIndexArgs, setVisibleMergeIndex } from '../common/index';import { IFormulaColl } from '../../calculate/common/interface'; import {ComponentModel} from '@syncfusion/ej2-base'; /** diff --git a/controls/spreadsheet/src/workbook/base/workbook.ts b/controls/spreadsheet/src/workbook/base/workbook.ts index 0431d01654..e98f68a2f5 100644 --- a/controls/spreadsheet/src/workbook/base/workbook.ts +++ b/controls/spreadsheet/src/workbook/base/workbook.ts @@ -14,7 +14,7 @@ import { SaveOptions, SetCellFormatArgs, ClearOptions, AutoFillSettings, AutoFil import { SortOptions, BeforeSortEventArgs, SortEventArgs, FindOptions, CellInfoEventArgs, ConditionalFormatModel } from '../common/index'; import { FilterEventArgs, FilterOptions, BeforeFilterEventArgs, ChartModel, getCellIndexes, getCellAddress, unMerge } from '../common/index'; import { setMerge, MergeType, MergeArgs, ImageModel, FilterCollectionModel, SortCollectionModel, dataChanged } from '../common/index'; -import { getCell, skipDefaultValue, setCell, wrap as wrapText } from './cell'; +import { getCell, skipDefaultValue, setCell, wrap as wrapText, Cell } from './cell'; import { DataBind, setRow, setColumn, InsertDeleteEventArgs, NumberFormatArgs } from '../index'; import { WorkbookSave, WorkbookFormula, WorkbookOpen, WorkbookSort, WorkbookFilter, WorkbookImage } from '../integrations/index'; import { WorkbookChart } from '../integrations/index'; @@ -1148,17 +1148,15 @@ export class Workbook extends Component implements INotifyPropertyC isFullPost: true, needBlobData: false, cancel: false, + autoDetectFormat: false, pdfLayoutSettings: { fitSheetOnOnePage: false, orientation: 'Portrait' } }; this.trigger('beforeSave', eventArgs); this.notify(beginAction, { eventArgs: eventArgs, action: 'beforeSave' }); if (!eventArgs.cancel) { this.notify( - events.beginSave, { - saveSettings: eventArgs, isFullPost: eventArgs.isFullPost, - needBlobData: eventArgs.needBlobData, customParams: eventArgs.customParams, pdfLayoutSettings: - eventArgs.pdfLayoutSettings - }); + events.beginSave, { saveSettings: eventArgs, isFullPost: eventArgs.isFullPost, needBlobData: eventArgs.needBlobData, + customParams: eventArgs.customParams, pdfLayoutSettings: eventArgs.pdfLayoutSettings }); } } } diff --git a/controls/spreadsheet/src/workbook/common/event.ts b/controls/spreadsheet/src/workbook/common/event.ts index dedd6bffbd..fe97500b5e 100644 --- a/controls/spreadsheet/src/workbook/common/event.ts +++ b/controls/spreadsheet/src/workbook/common/event.ts @@ -52,6 +52,8 @@ export const workbookEditOperation: string = 'workbookEditOperation'; /** @hidden */ export const checkDateFormat: string = 'checkDateFormat'; /** @hidden */ +export const checkNumberFormat: string = 'checkNumberFormat'; +/** @hidden */ export const getFormattedBarText: string = 'getFormattedBarText'; /** @hidden */ export const activeCellChanged: string = 'activeCellChanged'; diff --git a/controls/spreadsheet/src/workbook/common/interface.ts b/controls/spreadsheet/src/workbook/common/interface.ts index 1acfe5ad32..7be0023492 100644 --- a/controls/spreadsheet/src/workbook/common/interface.ts +++ b/controls/spreadsheet/src/workbook/common/interface.ts @@ -28,6 +28,7 @@ export interface BeforeSaveEventArgs extends SaveOptions { isFullPost: boolean; needBlobData: boolean; cancel: boolean; + autoDetectFormat?: boolean; } export interface SaveCompleteEventArgs extends SaveOptions { @@ -164,16 +165,11 @@ interface RangeInfo { /** @hidden */ export interface AutoDetectInfo { value: string; - args: { sheet: Sheet, indexes: number[] }; + sheet: SheetModel; cell: CellModel; rowIndex: number; colIndex: number; - i: number; - j: number; - k: number; - sRanges: number[]; - range: ExtendedRange; - sheetIndex?: number; + sheetIndex: number; } /** @@ -518,7 +514,7 @@ export interface NumberFormatArgs { } /** @hidden */ export interface DateFormatCheckArgs { - value: string; + value: string | number; cell?: CellModel; rowIndex?: number; colIndex?: number; @@ -530,6 +526,20 @@ export interface DateFormatCheckArgs { isEdit?: boolean; intl?: Internationalization; skipCellFormat?: boolean; + updateValue?: boolean; +} +/** @hidden */ +export interface AutoDetectGeneralFormatArgs { + args?: NumberFormatArgs & DateFormatCheckArgs; + fResult?: string; + intl?: Internationalization; + currencySymbol?: string; + isRightAlign?: boolean; + curCode?: string; + cell?: CellModel; + rowIdx?: number; + colIdx?: number; + sheet?: SheetModel; } /** @hidden */ export interface checkCellValid { diff --git a/controls/spreadsheet/src/workbook/common/util.ts b/controls/spreadsheet/src/workbook/common/util.ts index a4945ccbb5..4092d2e760 100644 --- a/controls/spreadsheet/src/workbook/common/util.ts +++ b/controls/spreadsheet/src/workbook/common/util.ts @@ -1,9 +1,10 @@ import { CellModel, ColumnModel, getCell, SheetModel, setCell, Workbook, getSheetIndex, CellStyleModel, getCellIndexes } from './../index'; -import { getCellAddress, getRangeIndexes, BeforeCellUpdateArgs, beforeCellUpdate, workbookEditOperation, CellUpdateArgs, isNumber } from './index'; +import { getCellAddress, getRangeIndexes, BeforeCellUpdateArgs, beforeCellUpdate, workbookEditOperation, CellUpdateArgs } from './index'; import { InsertDeleteModelArgs, getColumnHeaderText, ConditionalFormat, ConditionalFormatModel, clearFormulaDependentCells } from './index'; -import { isHiddenCol, isHiddenRow, VisibleMergeIndexArgs } from './../index'; -import { isUndefined, defaultCurrencyCode, getNumberDependable, getNumericObject } from '@syncfusion/ej2-base'; +import { isHiddenCol, isHiddenRow, VisibleMergeIndexArgs, checkDateFormat, checkNumberFormat, DateFormatCheckArgs } from './../index'; +import { isUndefined, defaultCurrencyCode, getNumberDependable, getNumericObject, Internationalization } from '@syncfusion/ej2-base'; import { parseThousandSeparator } from './internalization'; +import { AutoDetectGeneralFormatArgs, isNumber } from './../index'; /** * Check whether the text is formula or not. @@ -804,3 +805,30 @@ export function setVisibleMergeIndex(args: VisibleMergeIndexArgs): void { } } } + +/** + * Return a function that will auto-detect the number format of the formatted cell value. + * + * @param {Workbook} context - Specifies the Workbook instance. + * @returns {void} - Defines the common variables and returns the auto-detect number format function. + * @hidden + */ +export function getAutoDetectFormatParser(context: Workbook): (cell: CellModel) => void { + const intl: Internationalization = new Internationalization(); + const option: { currency?: string } = {}; + intl.getNumberFormat(option); + const eventArgs: DateFormatCheckArgs = { intl: intl, updateValue: true, value: '' }; + const options: AutoDetectGeneralFormatArgs = { args: eventArgs, intl: intl, + currencySymbol: getNumberDependable(context.locale, option.currency) }; + return (cell: CellModel): void => { + if (!cell.format && cell.value && !isNumber(cell.value)) { + eventArgs.cell = cell; + eventArgs.value = cell.value; + context.notify(checkDateFormat, eventArgs); + if (!cell.format && (cell.value.includes(options.currencySymbol) || cell.value.includes('%'))) { + options.fResult = cell.value; + context.notify(checkNumberFormat, options); + } + } + }; +} diff --git a/controls/spreadsheet/src/workbook/integrations/data-bind.ts b/controls/spreadsheet/src/workbook/integrations/data-bind.ts index 18f08c59ec..4db232bb8e 100644 --- a/controls/spreadsheet/src/workbook/integrations/data-bind.ts +++ b/controls/spreadsheet/src/workbook/integrations/data-bind.ts @@ -1,9 +1,8 @@ import { DataManager, Query, Deferred, ReturnOption, QueryOptions } from '@syncfusion/ej2-data'; -import { Workbook, getCell, CellModel, RowModel, SheetModel, setCell, getSheetIndexFromId, isFilterHidden } from '../base/index'; -import { getRangeIndexes, checkIsFormula, updateSheetFromDataSource, checkDateFormat, dataSourceChanged } from '../common/index'; -import { ExtendedSheet, ExtendedRange, AutoDetectInfo, getCellIndexes, dataChanged, getCellAddress, isInRange } from '../common/index'; -import { triggerDataChange, UsedRangeModel } from '../index'; -import { getFormatFromType } from './number-format'; +import { Workbook, getCell, CellModel, RowModel, SheetModel, setCell, isFilterHidden } from '../base/index'; +import { getRangeIndexes, checkIsFormula, updateSheetFromDataSource, checkDateFormat, dataSourceChanged, isNumber } from '../common/index'; +import { ExtendedSheet, ExtendedRange, DateFormatCheckArgs, getCellIndexes, dataChanged, getCellAddress, isInRange } from '../common/index'; +import { triggerDataChange, UsedRangeModel, getFormattedCellObject, NumberFormatArgs, getAutoDetectFormatParser } from '../index'; import { extend, isNullOrUndefined } from '@syncfusion/ej2-base'; /** @@ -52,18 +51,25 @@ export class DataBind { private updateSheetFromDataSourceHandler( args: { sheet: ExtendedSheet, indexes: number[], promise: Promise, rangeSettingCount?: number[], formulaCellRef?: string, - sheetIndex?: number, dataSourceChange?: boolean, isFinite?: boolean, resolveAfterFullDataLoaded?: boolean }): void { + sheetIndex?: number, dataSourceChange?: boolean, isFinite?: boolean, resolveAfterFullDataLoaded?: boolean, + loadComplete?: Function, isSaveAction?: boolean, autoDetectFormat?: boolean }): void { let cell: CellModel; let flds: string[]; let sCellIdx: number[]; let result: Object[]; let remoteUrl: string; let isLocal: boolean; let dataManager: DataManager; - const requestedRange: boolean[] = []; const sRanges: number[] = []; let rowIdx: number; + const requestedRange: boolean[] = []; const sRanges: number[] = []; let rowIdx: number; let colIdx: number; const deferred: Deferred = new Deferred(); let sRowIdx: number; let sColIdx: number; let loadedInfo: { isNotLoaded: boolean, unloadedRange: number[] }; - args.promise = deferred.promise; + args.promise = deferred.promise; let startCellIndexes: number[]; + const autoDetectFormat: boolean = args.autoDetectFormat; + const autoDetectFormatFn: (cell: CellModel) => void = autoDetectFormat && getAutoDetectFormatParser(this.parent); if (args.sheet && args.sheet.ranges.length) { for (let k: number = args.sheet.ranges.length - 1; k >= 0; k--) { - let sRange: number = args.indexes[0]; let eRange: number = args.indexes[2]; const range: ExtendedRange = args.sheet.ranges[k as number]; - sRowIdx = getRangeIndexes(range.startCell)[0]; + startCellIndexes = getRangeIndexes(range.startCell); + if (args.isSaveAction) { + args.indexes = startCellIndexes; + } + let sRange: number = args.indexes[0]; let eRange: number = args.indexes[2]; + sRowIdx = startCellIndexes[0]; dataManager = range.dataSource instanceof DataManager ? range.dataSource as DataManager : range.dataSource ? new DataManager(range.dataSource) : new DataManager(); remoteUrl = remoteUrl || dataManager.dataSource.url; args.sheet.isLocalData = isLocal || !dataManager.dataSource.url; @@ -93,7 +99,9 @@ export class DataBind { } else if (eRange > count) { eRange = count; } - this.requestedInfo.push({ deferred: deferred, indexes: args.indexes, isNotLoaded: loadedInfo.isNotLoaded }); + if (!args.loadComplete) { + this.requestedInfo.push({ deferred: deferred, indexes: args.indexes, isNotLoaded: loadedInfo.isNotLoaded }); + } if (sRange >= 0 && loadedInfo.isNotLoaded && !isEndReached) { sRanges[k as number] = sRange; requestedRange[k as number] = false; const query: Query = (range.query ? range.query : new Query()).clone(); @@ -133,24 +141,23 @@ export class DataBind { } }); } - const curSheetIdx: number = args.formulaCellRef ? getSheetIndexFromId(this.parent, args.sheet.id) : undefined; result.forEach((item: { [key: string]: string }, i: number) => { rowIdx = sRowIdx + sRanges[k as number] + i + (range.showFieldAsHeader ? 1 : 0) + insertRowCount; for (let j: number = 0; j < flds.length; j++) { - cell = getCell(rowIdx, sColIdx + j, args.sheet, true); + colIdx = sColIdx + j; + cell = getCell(rowIdx, colIdx, args.sheet, true); if (cell) { if (!flds[j as number].includes('emptyCell')) { setCell( - rowIdx, sColIdx + j, args.sheet, this.getCellDataFromProp(item[flds[j as number]]), true); + rowIdx, colIdx, args.sheet, this.getCellDataFromProp(item[flds[j as number]]), true); } } else { - args.sheet.rows[rowIdx as number].cells[sColIdx + j] = + cell = args.sheet.rows[rowIdx as number].cells[colIdx as number] = flds[j as number].includes('emptyCell') ? {} : this.getCellDataFromProp(item[flds[j as number]]); } - // this.checkDataForFormat({ - // args: args, cell: cell, colIndex: sColIdx + j, rowIndex: rowIdx, i: i, j: j, k: k, range: range, - // sRanges: sRanges, value: item[flds[j]], sheetIndex: curSheetIdx - // }); + if (autoDetectFormat) { + autoDetectFormatFn(cell); + } } }); } else { @@ -196,12 +203,13 @@ export class DataBind { dataLoading = true; //if (remoteUrl) { const unloadedArgs: { sheet: ExtendedSheet, indexes: number[], promise: Promise, - rangeSettingCount?: number[], isFinite?: boolean, resolveAfterFullDataLoaded?: boolean } = { + rangeSettingCount?: number[], isFinite?: boolean, resolveAfterFullDataLoaded?: boolean, + loadComplete?: Function, autoDetectFormat?: boolean } = { sheet: args.sheet, indexes: [0, 0, totalRows, totalCols], // eslint-disable-next-line @typescript-eslint/no-unused-vars promise: new Promise((resolve: Function, reject: Function) => { resolve((() => { /** */ })()); }), - rangeSettingCount: args.rangeSettingCount, isFinite: args.isFinite, - resolveAfterFullDataLoaded: args.resolveAfterFullDataLoaded + rangeSettingCount: args.rangeSettingCount, isFinite: args.isFinite, loadComplete: args.loadComplete, + autoDetectFormat: args.autoDetectFormat, resolveAfterFullDataLoaded: args.resolveAfterFullDataLoaded }; this.updateSheetFromDataSourceHandler(unloadedArgs); unloadedArgs.promise.then((): void => { @@ -210,11 +218,15 @@ export class DataBind { if (!args.rangeSettingCount.length) { this.parent.notify('created', null); } if (args.formulaCellRef) { this.notfyFormulaCellRefresh(args.formulaCellRef, args.sheetIndex); + } else if (args.loadComplete) { + args.loadComplete(); } }); //} } else if (args.formulaCellRef) { this.notfyFormulaCellRefresh(args.formulaCellRef, args.sheetIndex); + } else if (args.loadComplete) { + args.loadComplete(); } if (!(dataLoading && args.resolveAfterFullDataLoaded)) { this.checkResolve(args.indexes); @@ -303,42 +315,6 @@ export class DataBind { return data; } - private checkDataForFormat(args: AutoDetectInfo): void { - if (args.value !== '') { - const dateEventArgs: { [key: string]: string | number | boolean } = { - value: args.value, - rowIndex: args.rowIndex, - colIndex: args.colIndex, - isDate: false, - updatedVal: args.value, - isTime: false, - sheetIndex: args.sheetIndex - }; - this.parent.notify(checkDateFormat, dateEventArgs); - if (dateEventArgs.isDate) { - if (args.cell) { - if (!args.cell.format) { args.cell.format = getFormatFromType('ShortDate'); } - args.cell.value = dateEventArgs.updatedVal; - } else { - args.args.sheet.rows[args.rowIndex] - .cells[args.colIndex].format = getFormatFromType('ShortDate'); - args.args.sheet.rows[args.rowIndex] - .cells[args.colIndex].value = dateEventArgs.updatedVal; - } - } else if (dateEventArgs.isTime) { - if (args.cell) { - if (!args.cell.format) { args.cell.format = getFormatFromType('Time'); } - args.cell.value = dateEventArgs.updatedVal; - } else { - args.args.sheet.rows[args.rowIndex] - .cells[args.colIndex].format = getFormatFromType('Time'); - args.args.sheet.rows[args.rowIndex] - .cells[args.colIndex].value = dateEventArgs.updatedVal; - } - } - } - } - private getLoadedInfo(sRange: number, eRange: number, range: ExtendedRange): { isNotLoaded: boolean, unloadedRange: number[] } { let isNotLoaded: boolean = true; range.info.loadedRange.forEach((range: number[]) => { diff --git a/controls/spreadsheet/src/workbook/integrations/number-format.ts b/controls/spreadsheet/src/workbook/integrations/number-format.ts index 1f839c2607..03e2cdf895 100644 --- a/controls/spreadsheet/src/workbook/integrations/number-format.ts +++ b/controls/spreadsheet/src/workbook/integrations/number-format.ts @@ -6,6 +6,7 @@ import { isNumber, toFraction, intToDate, toDate, dateToInt, ToDateArgs, DateFor import { applyNumberFormatting, getFormattedCellObject, refreshCellElement, checkDateFormat, getFormattedBarText } from '../common/index'; import { getTextSpace, NumberFormatArgs, isCustomDateTime, VisibleMergeIndexArgs, setVisibleMergeIndex } from './../index'; import { checkIsNumberAndGetNumber, parseThousandSeparator } from '../common/internalization'; +import { AutoDetectGeneralFormatArgs, checkNumberFormat } from './../common/index'; /** * Specifies number format. */ @@ -801,41 +802,53 @@ export class WorkbookNumberFormat { options.isRightAlign = true; } if (options.fResult) { - let res: string = options.fResult.toString(); - if (this.isPercentageValue(res, options.args, options.cell)) { - options.cell.format = options.args.format = res.includes(this.decimalSep) ? getFormatFromType('Percentage') : '0%'; + this.updateAutoDetectNumberFormat(options); + } + if (addressFormula) { + options.isRightAlign = false; + options.fResult = val; + } + } + private updateAutoDetectNumberFormat(options: AutoDetectGeneralFormatArgs): void { + let res: string = options.fResult.toString(); + const cell: CellModel = options.args.cell || options.cell; + if (this.isPercentageValue(res, options.args, cell)) { + cell.format = res.includes(this.decimalSep) ? getFormatFromType('Percentage') : '0%'; + if (!options.args.updateValue) { + options.args.format = cell.format; options.fResult = this.percentageFormat(options.args, options.intl); options.isRightAlign = true; - } else { - let format: string = ''; - if (res.includes(options.currencySymbol)) { // Auto detect 1000 separator format with currency symbol - format = res.includes(this.decimalSep) ? '$#,##0.00' : '$#,##0'; - res = res.replace(options.currencySymbol, ''); - } - const isEdit: boolean = this.decimalSep === '.' || options.args.isEdit; - if (isEdit && res.includes(this.groupSep) && parseThousandSeparator(res, this.parent.locale, this.groupSep, this.decimalSep)) { - res = res.split(this.groupSep).join(''); - if (!format) { // Auto detect 1000 separator format - format = (res.includes(this.decimalSep) ? '#,##0.00' : '#,##0'); - } + } + } else { + let format: string = ''; + if (res.includes(options.currencySymbol)) { // Auto detect 1000 separator format with currency symbol + format = res.includes(this.decimalSep) ? '$#,##0.00' : '$#,##0'; + res = res.replace(options.currencySymbol, ''); + } + const isEdit: boolean = this.decimalSep === '.' || options.args.isEdit; + if (isEdit && res.includes(this.groupSep) && parseThousandSeparator(res, this.parent.locale, this.groupSep, this.decimalSep)) { + res = res.split(this.groupSep).join(''); + if (!format) { // Auto detect 1000 separator format + format = (res.includes(this.decimalSep) ? '#,##0.00' : '#,##0'); } - if (format) { - res = res.replace(this.decimalSep, '.'); - if (isNumber(res)) { - options.args.value = Number(res).toString(); + } + if (format) { + res = res.replace(this.decimalSep, '.'); + if (isNumber(res)) { + options.args.value = Number(res).toString(); + if (options.args.updateValue) { + options.args.cell.value = options.args.value; + options.args.cell.format = format; + } else { setCell(options.rowIdx, options.colIdx, options.sheet, { value: options.args.value, format: format }, true); options.fResult = this.getFormattedNumber(format, Number(options.args.value)); options.isRightAlign = true; } - } else if (this.decimalSep !== '.' && options.args.format === 'General' && isNumber(res) && res.includes('.')) { - options.fResult = Number(res).toString().replace('.', this.decimalSep); } + } else if (this.decimalSep !== '.' && options.args.format === 'General' && isNumber(res) && res.includes('.')) { + options.fResult = Number(res).toString().replace('.', this.decimalSep); } } - if (addressFormula) { - options.isRightAlign = false; - options.fResult = val; - } } private isPercentageValue(value: string, args: NumberFormatArgs, cell: CellModel): boolean { @@ -1235,6 +1248,10 @@ export class WorkbookNumberFormat { } else { cell.format = getFormatFromType('ShortDate'); } + if (args.updateValue) { + cell.value = props.val; + return; + } } args.isDate = dateObj.type === 'date' || dateObj.type === 'datetime'; args.isTime = dateObj.type === 'time'; @@ -1513,6 +1530,7 @@ export class WorkbookNumberFormat { this.parent.on(getFormattedCellObject, this.getFormattedCell, this); this.parent.on(checkDateFormat, this.checkDateFormat, this); this.parent.on(getFormattedBarText, this.formattedBarText, this); + this.parent.on(checkNumberFormat, this.updateAutoDetectNumberFormat, this); } /** @@ -1526,6 +1544,7 @@ export class WorkbookNumberFormat { this.parent.off(getFormattedCellObject, this.getFormattedCell); this.parent.off(checkDateFormat, this.checkDateFormat); this.parent.off(getFormattedBarText, this.formattedBarText); + this.parent.off(checkNumberFormat, this.updateAutoDetectNumberFormat); } } @@ -1671,16 +1690,3 @@ export function getTypeFromFormat(format: string, isRibbonUpdate?: boolean): str } return code; } - -interface AutoDetectGeneralFormatArgs { - args: NumberFormatArgs; - fResult: string; - intl: Internationalization; - currencySymbol: string; - isRightAlign: boolean; - curCode: string; - cell: CellModel; - rowIdx: number; - colIdx: number; - sheet: SheetModel; -} diff --git a/controls/spreadsheet/src/workbook/integrations/save.ts b/controls/spreadsheet/src/workbook/integrations/save.ts index 590cb64b1a..66aa2bfde7 100644 --- a/controls/spreadsheet/src/workbook/integrations/save.ts +++ b/controls/spreadsheet/src/workbook/integrations/save.ts @@ -1,9 +1,9 @@ import { Workbook, CellModel, getCell, setCell, SheetModel, getSheet } from '../base/index'; -import { executeTaskAsync } from '../common/worker'; -import { pdfLayoutSettings, SaveOptions, checkIsFormula, workbookFormulaOperation, removeUniquecol } from '../common/index'; +import { executeTaskAsync, getAutoDetectFormatParser } from '../common/index'; +import { pdfLayoutSettings, SaveOptions, checkIsFormula, workbookFormulaOperation, removeUniquecol, ExtendedRange } from '../common/index'; import * as events from '../common/event'; import { SaveWorker } from '../workers/save-worker'; -import { SaveCompleteEventArgs, ChartModel } from '../common/index'; +import { SaveCompleteEventArgs, ChartModel, updateSheetFromDataSource, BeforeSaveEventArgs } from '../common/index'; import { detach } from '@syncfusion/ej2-base'; /** @@ -17,7 +17,7 @@ export class WorkbookSave extends SaveWorker { private isFullPost: boolean = false; private needBlobData: boolean = false; private customParams: Object = null; - private pdfLayoutSettings: pdfLayoutSettings = {fitSheetOnOnePage: false}; + private pdfLayoutSettings: pdfLayoutSettings = { fitSheetOnOnePage: false }; /** * Constructor for WorkbookSave module in Workbook library. @@ -77,7 +77,7 @@ export class WorkbookSave extends SaveWorker { * @returns {void} - Initiate save process. */ private initiateSave(args: { [key: string]: Object }): void { - const saveSettings: SaveOptions = args.saveSettings; + const saveSettings: BeforeSaveEventArgs = args.saveSettings; this.parent.notify(events.getFilteredCollection, null); this.saveSettings = { saveType: saveSettings.saveType, @@ -91,7 +91,7 @@ export class WorkbookSave extends SaveWorker { this.customParams = args.customParams; this.pdfLayoutSettings = args.pdfLayoutSettings; this.updateBasicSettings(); - this.processSheets(); + this.processSheets(saveSettings.autoDetectFormat); } /** @@ -119,18 +119,45 @@ export class WorkbookSave extends SaveWorker { /** * Process sheets properties. * + * @param {boolean} autoDetectFormat - Auto detect the format based on the cell value. * @hidden * @returns {void} - Process sheets properties. */ - private processSheets(): void { + private processSheets(autoDetectFormat?: boolean): void { const skipProps: string[] = ['dataSource', 'startCell', 'query', 'showFieldAsHeader', 'result']; - // eslint-disable-next-line - if ((this.parent as any).isAngular) { + if ((this.parent as { isAngular?: boolean }).isAngular) { skipProps.push('template'); } - for (let i: number = 0, sheetCount: number = this.parent.sheets.length; i < sheetCount; i++) { - executeTaskAsync( - this, this.processSheet, this.updateSheet, [this.getStringifyObject(this.parent.sheets[i as number], skipProps, i), i]); + let isNotLoaded: boolean; let isDataBinding: boolean; let sheet: SheetModel; let range: ExtendedRange; + for (let sheetIdx: number = 0, sheetCount: number = this.parent.sheets.length; sheetIdx < sheetCount; sheetIdx++) { + sheet = this.parent.sheets[sheetIdx as number]; + isNotLoaded = false; isDataBinding = false; + for (let rangeIdx: number = 0, rangeCount: number = sheet.ranges.length; rangeIdx < rangeCount; rangeIdx++) { + range = sheet.ranges[rangeIdx as number]; + if (range.dataSource) { + isDataBinding = true; + if (!range.info || !range.info.loadedRange || !range.info.loadedRange.length) { + isNotLoaded = true; + break; + } + } + } + if (isNotLoaded) { + const loadCompleteHandler: Function = (idx: number): void => { + executeTaskAsync( + this, this.processSheet, this.updateSheet, + [this.getStringifyObject(this.parent.sheets[idx as number], skipProps, idx), idx]); + }; + const context: { scrollSettings?: { isFinite: boolean } } = <{ scrollSettings?: { isFinite: boolean } }>this.parent; + this.parent.notify( + updateSheetFromDataSource, { sheet: sheet, sheetIndex: sheetIdx, loadComplete: loadCompleteHandler.bind(this, sheetIdx), + isFinite: context.scrollSettings && context.scrollSettings.isFinite, isSaveAction: true, + autoDetectFormat: autoDetectFormat }); + } else { + executeTaskAsync( + this, this.processSheet, this.updateSheet, + [this.getStringifyObject(sheet, skipProps, sheetIdx, autoDetectFormat && isDataBinding), sheetIdx]); + } } } @@ -271,14 +298,16 @@ export class WorkbookSave extends SaveWorker { * @hidden * @param {object} model - Specifies the workbook or sheet model. * @param {string[]} skipProp - specifies the skipprop. - * @param {string[]} sheetIdx - Specifies the sheet index. + * @param {number} sheetIdx - Specifies the sheet index. + * @param {boolean} autoDetectFormat - Auto detect the format based on the cell value. * @returns {string} - Get stringified workbook object. */ - private getStringifyObject(model: object, skipProp: string[] = [], sheetIdx?: number): string { + private getStringifyObject(model: object, skipProp: string[] = [], sheetIdx?: number, autoDetectFormat?: boolean): string { if (sheetIdx === 0) { this.parent.notify(removeUniquecol, { clearAll: true }); } const chartColl: { index: number[], chart: ChartModel[] }[] = []; let chartModel: ChartModel[]; + const autoDetectFormatFn: (cell: CellModel) => void = autoDetectFormat && getAutoDetectFormatParser(this.parent); const json: string = JSON.stringify(model, (key: string, value: { [key: string]: object }) => { if (skipProp.indexOf(key) > -1) { return undefined; @@ -288,12 +317,14 @@ export class WorkbookSave extends SaveWorker { const cell: CellModel = value.cells[i as number]; const cellIdx: number[] = [Number(key), i]; if (cell) { - if (!cell.value && cell.formula && cell.formula.indexOf('=UNIQUE(') < 0) { + if (cell.value) { + if (autoDetectFormat && !cell.formula) { + autoDetectFormatFn(cell); + } + } else if (cell.formula && cell.formula.indexOf('=UNIQUE(') < 0) { this.parent.notify( - workbookFormulaOperation, { - action: 'refreshCalculate', value: cell.formula, rowIndex: cellIdx[0], - colIndex: i, isFormula: checkIsFormula(cell.formula), sheetIndex: sheetIdx, isRefreshing: true - }); + workbookFormulaOperation, { action: 'refreshCalculate', value: cell.formula, rowIndex: cellIdx[0], + colIndex: i, isFormula: checkIsFormula(cell.formula), sheetIndex: sheetIdx, isRefreshing: true }); cell.value = getCell(cellIdx[0], i, model).value; } if (cell.chart) { diff --git a/controls/svgbase/CHANGELOG.md b/controls/svgbase/CHANGELOG.md index 81df547c2d..7c22f8b33f 100644 --- a/controls/svgbase/CHANGELOG.md +++ b/controls/svgbase/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] +## 24.1.46 (2024-01-17) + +### Svg Base + +- `#I538387` - Now, the Accumulation chart tooltip renders properly even when it does not fit in the container. +- `#I528508` - Now, the shared tooltip template is working properly when used with multiple series. + ## 24.1.45 (2024-01-09) ### Svg Base diff --git a/controls/svgbase/package.json b/controls/svgbase/package.json index e5bd661e9e..cdf2a734af 100644 --- a/controls/svgbase/package.json +++ b/controls/svgbase/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-svg-base", - "version": "24.1.41", + "version": "24.1.45", "description": "Essential JS 2 SVG Base Components", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/svgbase/src/tooltip/tooltip.ts b/controls/svgbase/src/tooltip/tooltip.ts index 67eb677989..e4ba773f33 100644 --- a/controls/svgbase/src/tooltip/tooltip.ts +++ b/controls/svgbase/src/tooltip/tooltip.ts @@ -1070,7 +1070,7 @@ export class Tooltip extends Component implements INotifyPropertyCh if (i === 0) { templateElement = sharedTemplateElement; } else { - templateElement[templateElement.length - 1].outerHTML += '
' + sharedTemplateElement[0].outerHTML; + templateElement[templateElement.length - 1].outerHTML += sharedTemplateElement[0].outerHTML; } } } @@ -1257,7 +1257,7 @@ export class Tooltip extends Component implements INotifyPropertyCh tipLocation.x = arrowLocation.x = 0; tipLocation.y = arrowLocation.y = height / 2; if ((location.x + this.arrowPadding + width > boundsX + bounds.width) || (this.isNegative)) { - location.x = (symbolLocation.x > bounds.width ? bounds.width : symbolLocation.x) + location.x = (symbolLocation.x > boundsX + bounds.width ? bounds.width : symbolLocation.x) + clipX - markerHeight - (this.arrowPadding + width); } if (location.x < boundsX) { @@ -1381,7 +1381,7 @@ export class Tooltip extends Component implements INotifyPropertyCh } else { tooltipDiv.style.left = (x + currenDiff * (rect.x - x)) + 'px'; tooltipDiv.style.top = (y + currenDiff * (rect.y - y)) + 'px'; - tooltipDiv.style.transform = ''; + tooltipDiv.style.transform = this.controlName === 'RangeNavigator' ? tooltipDiv.style.transform : ''; } }, end: (model: AnimationOptions): void => { @@ -1400,7 +1400,7 @@ export class Tooltip extends Component implements INotifyPropertyCh } else { tooltipDiv.style.left = x + 'px'; tooltipDiv.style.top = y + 'px'; - tooltipDiv.style.transform = ''; + tooltipDiv.style.transform = this.controlName === 'RangeNavigator' ? tooltipDiv.style.transform : ''; } } diff --git a/controls/treegrid/CHANGELOG.md b/controls/treegrid/CHANGELOG.md index fde054d615..81d59371ee 100644 --- a/controls/treegrid/CHANGELOG.md +++ b/controls/treegrid/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## 24.1.45 (2024-01-09) + +### Tree Grid + +#### Bug Fixes + +- `#I534170` - Fixed an issue where indent/outdent option is not displayed in the context menu when in cell edit mode. + ## 24.1.44 (2024-01-03) ### Tree Grid diff --git a/controls/treegrid/package.json b/controls/treegrid/package.json index 6346959b5a..2ab3f8dd66 100644 --- a/controls/treegrid/package.json +++ b/controls/treegrid/package.json @@ -1,6 +1,6 @@ { "name": "@syncfusion/ej2-treegrid", - "version": "24.1.44", + "version": "24.1.45", "description": "Essential JS 2 TreeGrid Component", "author": "Syncfusion Inc.", "license": "SEE LICENSE IN license", diff --git a/controls/treegrid/src/treegrid/actions/context-menu.ts b/controls/treegrid/src/treegrid/actions/context-menu.ts index b089429814..2d6d417788 100644 --- a/controls/treegrid/src/treegrid/actions/context-menu.ts +++ b/controls/treegrid/src/treegrid/actions/context-menu.ts @@ -53,7 +53,14 @@ export class ContextMenu { const selectedrow: HTMLTableRowElement = tObj.getSelectedRows()[0] as HTMLTableRowElement; if ((indent || outdent) && !isNullOrUndefined(selectedrow)) { const targetElement: Element = (args.event.target as Element).closest('td'); - if (isNullOrUndefined(targetElement) || (!isNullOrUndefined(targetElement) && (!targetElement.classList.contains('e-rowcell') || targetElement.querySelectorAll('.e-gridform').length !== 0))) { + if (isNullOrUndefined(targetElement) || (!isNullOrUndefined(targetElement) && (!targetElement.classList.contains('e-rowcell') || + targetElement.querySelectorAll('.e-gridform').length !== 0))) { + for (const items of args.items) { + if (items.text === 'Outdent' || items.text === 'Indent') { + tObj.grid.contextMenuModule['hiddenItems'].push(items.text); + } + } + tObj.grid.contextMenuModule.contextMenu.hideItems(tObj.grid.contextMenuModule['hiddenItems']); indent.style.display = outdent.style.display = 'none'; } else { @@ -82,9 +89,13 @@ export class ContextMenu { } } else { - if (tObj.grid.isEdit && isNullOrUndefined(selectedrow)) { - indent.style.display = 'none'; - outdent.style.display = 'none'; + if (((indent || outdent) || tObj.grid.isEdit) && isNullOrUndefined(selectedrow)) { + for (const items of args.items) { + if (items.text === 'Outdent' || items.text === 'Indent') { + tObj.grid.contextMenuModule['hiddenItems'].push(items.text); + } + } + tObj.grid.contextMenuModule.contextMenu.hideItems(tObj.grid.contextMenuModule['hiddenItems']); } } }