diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..e980c994d30 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto + +*.js eol=lf +*.ts eol=lf +*.json eol=lf \ No newline at end of file diff --git a/README.md b/README.md index eb34de5e27b..cbdc75cd0a0 100644 --- a/README.md +++ b/README.md @@ -1,254 +1,254 @@ -# Ignite UI for Angular - from Infragistics - -![Node.js CI](https://github.com/IgniteUI/igniteui-angular/workflows/Node.js%20CI/badge.svg) -[![Build Status](https://dev.azure.com/IgniteUI/igniteui-angular/_apis/build/status/IgniteUI.igniteui-angular)](https://dev.azure.com/IgniteUI/igniteui-angular/_build/latest?definitionId=3) -[![Coverage Status](https://coveralls.io/repos/github/IgniteUI/igniteui-angular/badge.svg?branch=master)](https://coveralls.io/github/IgniteUI/igniteui-angular?branch=master) -[![npm version](https://badge.fury.io/js/igniteui-angular.svg)](https://badge.fury.io/js/igniteui-angular) -[![Discord](https://img.shields.io/discord/836634487483269200?logo=discord&logoColor=ffffff)](https://discord.gg/39MjrTRqds) - -[Ignite UI for Angular](https://www.infragistics.com/products/ignite-ui-angular) is a complete set of Material-based UI Widgets, Components & Sketch UI kits, supporting directives for [Angular](https://angular.io/) by Infragistics. Ignite UI for Angular is designed to enable developers to build enterprise-ready, high-performance HTML5 & JavaScript apps for modern desktop browsers. With the use of all features, the world’s fastest Angular grid, 60+ real-time Angular charts, and more, you are empowered to engineer excellent mobile experiences and deliver progressive web apps (PWA’s) targeting Google's Angular framework. - -You can find source files under the [`src`](https://github.com/IgniteUI/igniteui-angular/tree/master/src) folder, including samples and tests. - - -### Angular Data Grid Overview - -The Ignite UI for [Angular Data Grid](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) equips you with all the necessary features for manipulating and visualizing tabular data in a series of rows and columns with ease. You can find powerful grid elements for no-lag scrolling while rendering and going through millions of data points. - -Built for optimization and speed, our [Angular grid](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) component lets you quickly bind data with very little code and allows you to implement a variety of events in order to tailor different behaviors. - -#### [View running Grid samples here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) - -### Angular Charts & Graphs Overview - -Ignite UI for Angular arrives with an extensive library of data visualizations that enable stunning, interactive charts and dashboards for your modern web and mobile apps. All of them are designed to work flawlessly on every modern browser and provide complete touch as well as interactivity. Our comprehensive [Angular Charts](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/chart-overview) component supports more than 65 chart types that let you display all sorts of data representations and statistics. And with the rich and easy-to-use API, you can plot various types of charts. - -Some of the Angular chart types included are: [Polar chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/polar-chart), [Pie chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/pie-chart), [Donut chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/donut-chart), [Bubble chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/bubble-chart), [Area chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/area-chart), [Treemap chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/treemap-chart), and many others. And if you look for [Angular financial charts](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/stock-chart), with Ignite UI you can get the same features as the ones you come across with Google Finance and Yahoo Finance Charts. - -### Build Apps with Ignite UI for Angular faster using our [App Builder](https://www.infragistics.com/products/indigo-design/app-builder) -![5661 drag drop](https://user-images.githubusercontent.com/1472513/132676597-09eec222-42f7-40ff-bd0d-fe8b91fd0c1c.gif) -### Generate your Angular code projects using the [App Builder](https://www.infragistics.com/products/indigo-design/app-builder) -![0871 change-preview-code](https://user-images.githubusercontent.com/1472513/132676607-3851f308-416b-45d6-99bc-c34266b55c44.gif) - -### Current List of Components Include: - -|Components|Status|||Directives|Status||| -|:--|:--:|:--|:--|:--|:--:|:--|:--| -|accordion|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/accordion/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/accordion)| -|avatar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/avatar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/avatar)|autocomplete|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/autocomplete/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/autocomplete)| -|badge|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/badge/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/badge)|button|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/button/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button)| -|banner|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/banner/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/banner)|date time editor|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/date-time-editor/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-time-editor)| -|bottom navigation|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tabs/bottom-nav/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tabbar)|divider|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/button/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button)| -|button group|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/buttonGroup/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button-group)|dragdrop|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/divider/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/divider)| -|calendar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/calendar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/calendar)|filter|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/filter/README-FILTER.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/list)| -|card|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/card/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/card)|focus-trap|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/focus-trap/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/dialog)| -|carousel|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/carousel/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/carousel)|forOf|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/for-of/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/for-of)| -|checkbox|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/checkbox/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/checkbox)|hint|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| -|chips|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/chips/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/chip)|input|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/input/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| -|circular progress|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/progressbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/circular-progress)|label|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/label/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/label-input)| -|combo|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/combo/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/combo)|layout|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/layout/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/layout)| -|date picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/date-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-picker)|mask|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/mask/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/mask)| -|date range picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/date-range-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-range-picker)|prefix|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| -|dialog|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/dialog/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/dialog)|radio-group|:white_check_mark:||[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radio-button)| -|drop down|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/drop-down/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/drop-down)|ripple|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/ripple/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/ripple)| -|expansion panel|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/expansion-panel/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/expansion-panel)|suffix|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| -|grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid)|text-highlight|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/text-highlight/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/texthighlight)| -|hierarchical grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/hierarchical-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/hierarchicalgrid/hierarchical-grid)|toggle|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/toggle/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/toggle)| -|icon|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/icon/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/icon)|tooltip|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/tooltip/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip)| -|input group|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)|Others|Status|Docs|| -|linear progress|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/progressbar)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/linear-progress)|Animations|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/animations/README.md)|| -|list|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/list/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/list)|dataUtil|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/data-operations/README-DATAUTIL.md)|| -|month picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/calendar/month-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/month-picker)|dataContainer|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/data-operations/README-DATACONTAINER.md)|| -|navbar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/navbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/navbar)|IgxGridState|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/state-persistence)|| -|navigation drawer|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/navigation-drawer/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/navdrawer)||||| -|pivot grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/pivot-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/pivotgrid/pivot-grid) -|radio|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/radio/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radio-button)||||| -|select|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/select/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/select)||||| -|simple-combo|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/simple-combo/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/simple-combo)||||| -|slider|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/slider/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/slider/slider)||||| -|snackbar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/snackbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/snackbar)||||| -|stepper|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/stepper/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/stepper)| -|switch|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/switch/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/switch)||||| -|tabs|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tabs/tabs/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tabs)||||| -|time picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/time-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/time-picker)||||| -|toast|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/toast/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/toast)||||| -|tree|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tree/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tree)||||| -|tree grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/tree-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/treegrid/tree-grid)||||| - -### Components available in [igniteui-angular-charts](https://www.npmjs.com/package/igniteui-angular-charts) -|Components|| -|:---|:---| -|Bar Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/bar-chart)| -|Line Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/line-chart)| -|Financial Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/stock-chart)| -|Doughnut Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/donut-chart)| -|Pie Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/pie-chart)| - -### Components available in [igniteui-angular-gauges](https://www.npmjs.com/package/igniteui-angular-gauges) -|Components|| -|:---|:---| -|Bullet Graph|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/bullet-graph)| -|Linear Gauge|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/linear-gauge)| -|Radial Gauge|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radial-gauge)| - -### Components available in [igniteui-angular-excel](https://www.npmjs.com/package/igniteui-angular-excel) -|Components|| -|:---|:---| -|Excel Library|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/excel-library)| - - -### Components available in [igniteui-angular-spreadsheet](https://www.npmjs.com/package/igniteui-angular-spreadsheet) -|Components|| -|:---|:---| -|Spreadsheet|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/spreadsheet-overview)| - -## Setup -From the root folder run: - -``` -npm install -``` - -## Create new Project with Ignite UI CLI -To get started with the Ignite UI CLI and Ignite UI for Angular: - -``` -npm i -g igniteui-cli -ig new --framework=angular -cd -ig add grid -ig start -``` - -## Adding IgniteUI for Angular to Existing Project - -Including the `igniteui-angular` and `igniteui-cli` packages to your project: - -``` -ng add igniteui-angular -``` - -After this operation you can use the Ignite UI CLI commands in your project, such as `ig` and `ig add`. -[Learn more](https://github.com/IgniteUI/igniteui-cli#usage) - -## Updating Existing Project - -Analyze your project for possible migrations: - -``` -ng update -``` - -If there are new versions available, update your packages: - -``` -ng update igniteui-angular -... -ng update igniteui-cli -``` - -## Building the Library -``` -// build the code -ng build igniteui-angular - -// build the css -npm run build:style - -// build them both -npm run build:lib -``` - -You can find the build ouput under `dist/igniteui-angular`. - -## Running the tests - -Running the tests in watch mode: - -``` -ng test igniteui-angular // or npm run test:lib:watch -``` - -Running the tests once with code coverage enabled: -``` -npm run test:lib -``` - -## Building the API Docs -The API docs are produced using TypeDoc and SassDoc. In order to build the docs, all you need to do is run: - -``` -npm run build:docs -``` - -The output of the API docs build is located under `dist/igniteui-angular/docs`. - -## Run Demos Application - -The repository includes a sample application featuring the showcasing the different components/directives. -In order to run the demo samples, build the library first and start the application. -``` -npm start -``` - -**NOTE**: Experimental demos are not always stable. - -## NPM Package - -You can include Ignite UI for Angular in your project as a dependency using the NPM package. - -`npm install igniteui-angular` - -## Contributing -[General Naming and Coding Guidelines for Ignite UI for Angular](https://github.com/IgniteUI/igniteui-angular/wiki/General-Naming-and-Coding--Guidelines-for-Ignite-UI-for-Angular) - -## Demo Apps & Documentation - -### List of Angular Demo Apps -- [Warehouse Picklist App](https://github.com/IgniteUI/warehouse-js-blocks) - Demonstrates using several Ignite UI for Angular widgets together to build a modern, mobile app. - -- [FinTech Grid App]( https://github.com/Infragistics/angular-samples/tree/master/Grid/FinJS) - The Ignite UI for Angular Grid component is able to handle thousands of updates per second, while keeping the grid responsive for any interaction that the user may undertake. This sample demonstrates the Angular Grid handling thousands of updates per second. - -- [FinTech Tree Grid App](https://github.com/Infragistics/angular-samples/tree/master/TreeGrid/FinJS) - The Ignite UI for Angular Tree Grid component is able to handle thousands of updates per second, while keeping the grid responsive for any interaction that the user may undertake. This sample demonstrates the Tree Grid handling thousands of updates per second. - -- [Crypto Portfolio App](https://github.com/IgniteUI/crypto-portfolio-app) - This is a web and mobile application, developed with Ignite UI for Angular components and styled with our one of a kind theming engine. - -- [Task Planner Application](https://github.com/IgniteUI/TaskPlanner) – Task Planner is an Angular web application. It provides an effective means for managing projects and related tasks. Thus, it loads data from the Web API endpoint, enabling the user to start managing - filtering and sorting tasks, editing tasks, adding new tasks. It shows nice UX UI perks like ability to Drag and Drop items from and to the List and Data Grid. - -- [Dock Manager with Data Analysis Tool](https://github.com/IgniteUI/DockManager-DataAnalysis) - The Data Analysis sample application provides users with the flexibility to customize the data visualization using one of several chart types. Built with Angular UI components, it showcases the Angular Data Grid integrated with an Angular Data Chart, Angular Pie Chart, and an Angular Category Chart, to provide an interactive and engaging visualization. The Dock Manager web component provides a windowing experience, allowing users to customize the layout and view, and make the data more accessible. - -- [COVID-19 Dashboard](https://github.com/IgniteUI/COVID-19-Dashboard) - This dynamic dashboard was built using Indigo.Design and Ignite UI for Angular leveraging timely reports data from CSSEGISandData/COVID-19 to create an useful and impactful visualization. Built in a matter of hours, it showcases the Ignite UI Category and Data Charts, Map and List components for Angular and the how easy it is to get those quickly configured and populated with data. - -- [Inventory Management App](https://github.com/IgniteUI/InventoryManagementApp) - The Inventory Management App consists of 2 pages: The Products Page and the Dashboard Page. The Products Page contains a grid with product information and includes a number of useful features - -### Angular apps with ASP.NET Core Web Application -If you consider Angular client side application with ASP.NET Core application you can check out our [ASP.NET-Core-Samples](https://github.com/IgniteUI/ASP.NET-Core-Samples) - -### Documentation -To get started with the Data Grid, use the steps in the [grid walk-through](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid). - -All help, related API documents and walk-throughs can be found for each control [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/general/getting-started). - - -## Roadmap -[Roadmap document](https://github.com/IgniteUI/igniteui-angular/blob/master/ROADMAP.md) - -## Support -Developer support is provided as part of the commercial, paid-for license via [Infragistics Forums](https://www.infragistics.com/community/forums/), or via Chat & Phone with a Priority Support license. To acquire a license for paid support or Priority Support, please visit this [page](https://www.infragistics.com/how-to-buy/product-pricing#developers). - -Community support for open source usage of this product is available at [StackOverflow](https://stackoverflow.com/questions/tagged/ignite-ui-angular). - -## License -This is a commercial product, requiring a valid paid-for license for commercial use. -This product is free to use for non-commercial educational use for students in K through 12 grades or University programs, and for educators to use in a classroom setting as examples / tools in their curriculum. -In order for us to verify your eligibility for free usage, please [register for trial](https://Infragistics.com/Angular) and open a support ticket with a request for free license. - -To acquire a license for commercial usage, please [register for trial](https://Infragistics.com/Angular) and refer to the purchasing options in the pricing section on the product page. - -© Copyright 2020 INFRAGISTICS. All Rights Reserved. -The Infragistics Ultimate license & copyright applies to this distribution. -For information on that license, please go to our website [https://www.infragistics.com/legal/license](https://www.infragistics.com/legal/license). - - - - +# Ignite UI for Angular - from Infragistics + +![Node.js CI](https://github.com/IgniteUI/igniteui-angular/workflows/Node.js%20CI/badge.svg) +[![Build Status](https://dev.azure.com/IgniteUI/igniteui-angular/_apis/build/status/IgniteUI.igniteui-angular)](https://dev.azure.com/IgniteUI/igniteui-angular/_build/latest?definitionId=3) +[![Coverage Status](https://coveralls.io/repos/github/IgniteUI/igniteui-angular/badge.svg?branch=master)](https://coveralls.io/github/IgniteUI/igniteui-angular?branch=master) +[![npm version](https://badge.fury.io/js/igniteui-angular.svg)](https://badge.fury.io/js/igniteui-angular) +[![Discord](https://img.shields.io/discord/836634487483269200?logo=discord&logoColor=ffffff)](https://discord.gg/39MjrTRqds) + +[Ignite UI for Angular](https://www.infragistics.com/products/ignite-ui-angular) is a complete set of Material-based UI Widgets, Components & Sketch UI kits, supporting directives for [Angular](https://angular.io/) by Infragistics. Ignite UI for Angular is designed to enable developers to build enterprise-ready, high-performance HTML5 & JavaScript apps for modern desktop browsers. With the use of all features, the world’s fastest Angular grid, 60+ real-time Angular charts, and more, you are empowered to engineer excellent mobile experiences and deliver progressive web apps (PWA’s) targeting Google's Angular framework. + +You can find source files under the [`src`](https://github.com/IgniteUI/igniteui-angular/tree/master/src) folder, including samples and tests. + + +### Angular Data Grid Overview + +The Ignite UI for [Angular Data Grid](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) equips you with all the necessary features for manipulating and visualizing tabular data in a series of rows and columns with ease. You can find powerful grid elements for no-lag scrolling while rendering and going through millions of data points. + +Built for optimization and speed, our [Angular grid](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) component lets you quickly bind data with very little code and allows you to implement a variety of events in order to tailor different behaviors. + +#### [View running Grid samples here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid) + +### Angular Charts & Graphs Overview + +Ignite UI for Angular arrives with an extensive library of data visualizations that enable stunning, interactive charts and dashboards for your modern web and mobile apps. All of them are designed to work flawlessly on every modern browser and provide complete touch as well as interactivity. Our comprehensive [Angular Charts](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/chart-overview) component supports more than 65 chart types that let you display all sorts of data representations and statistics. And with the rich and easy-to-use API, you can plot various types of charts. + +Some of the Angular chart types included are: [Polar chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/polar-chart), [Pie chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/pie-chart), [Donut chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/donut-chart), [Bubble chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/bubble-chart), [Area chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/area-chart), [Treemap chart](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/treemap-chart), and many others. And if you look for [Angular financial charts](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/stock-chart), with Ignite UI you can get the same features as the ones you come across with Google Finance and Yahoo Finance Charts. + +### Build Apps with Ignite UI for Angular faster using our [App Builder](https://www.infragistics.com/products/indigo-design/app-builder) +![5661 drag drop](https://user-images.githubusercontent.com/1472513/132676597-09eec222-42f7-40ff-bd0d-fe8b91fd0c1c.gif) +### Generate your Angular code projects using the [App Builder](https://www.infragistics.com/products/indigo-design/app-builder) +![0871 change-preview-code](https://user-images.githubusercontent.com/1472513/132676607-3851f308-416b-45d6-99bc-c34266b55c44.gif) + +### Current List of Components Include: + +|Components|Status|||Directives|Status||| +|:--|:--:|:--|:--|:--|:--:|:--|:--| +|accordion|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/accordion/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/accordion)| +|avatar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/avatar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/avatar)|autocomplete|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/autocomplete/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/autocomplete)| +|badge|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/badge/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/badge)|button|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/button/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button)| +|banner|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/banner/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/banner)|date time editor|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/date-time-editor/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-time-editor)| +|bottom navigation|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tabs/bottom-nav/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tabbar)|divider|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/button/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button)| +|button group|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/buttonGroup/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/button-group)|dragdrop|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/divider/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/divider)| +|calendar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/calendar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/calendar)|filter|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/filter/README-FILTER.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/list)| +|card|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/card/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/card)|focus-trap|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/focus-trap/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/dialog)| +|carousel|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/carousel/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/carousel)|forOf|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/for-of/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/for-of)| +|checkbox|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/checkbox/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/checkbox)|hint|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| +|chips|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/chips/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/chip)|input|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/input/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| +|circular progress|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/progressbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/circular-progress)|label|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/label/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/label-input)| +|combo|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/combo/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/combo)|layout|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/layout/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/layout)| +|date picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/date-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-picker)|mask|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/mask/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/mask)| +|date range picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/date-range-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-range-picker)|prefix|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| +|dialog|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/dialog/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/dialog)|radio-group|:white_check_mark:||[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radio-button)| +|drop down|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/drop-down/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/drop-down)|ripple|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/ripple/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/ripple)| +|expansion panel|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/expansion-panel/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/expansion-panel)|suffix|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)| +|grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid)|text-highlight|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/directives/text-highlight/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/texthighlight)| +|hierarchical grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/hierarchical-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/hierarchicalgrid/hierarchical-grid)|toggle|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/toggle/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/toggle)| +|icon|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/icon/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/icon)|tooltip|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/directives/tooltip/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip)| +|input group|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/input-group/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/input-group)|Others|Status|Docs|| +|linear progress|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/src/lib/progressbar)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/linear-progress)|Animations|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/animations/README.md)|| +|list|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/list/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/list)|dataUtil|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/data-operations/README-DATAUTIL.md)|| +|month picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/calendar/month-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/month-picker)|dataContainer|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/data-operations/README-DATACONTAINER.md)|| +|navbar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/navbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/navbar)|IgxGridState|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/state-persistence)|| +|navigation drawer|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/navigation-drawer/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/navdrawer)||||| +|pivot grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/pivot-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/pivotgrid/pivot-grid) +|radio|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/radio/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radio-button)||||| +|select|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/select/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/select)||||| +|simple-combo|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/simple-combo/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/simple-combo)||||| +|slider|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/slider/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/slider/slider)||||| +|snackbar|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/snackbar/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/snackbar)||||| +|stepper|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/stepper/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/stepper)| +|switch|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/switch/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/switch)||||| +|tabs|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tabs/tabs/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tabs)||||| +|time picker|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/time-picker/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/time-picker)||||| +|toast|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/toast/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/toast)||||| +|tree|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/tree/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tree)||||| +|tree grid|:white_check_mark:|[Readme](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/tree-grid/README.md)|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/treegrid/tree-grid)||||| + +### Components available in [igniteui-angular-charts](https://www.npmjs.com/package/igniteui-angular-charts) +|Components|| +|:---|:---| +|Bar Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/bar-chart)| +|Line Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/line-chart)| +|Financial Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/stock-chart)| +|Doughnut Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/donut-chart)| +|Pie Chart|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/charts/types/pie-chart)| + +### Components available in [igniteui-angular-gauges](https://www.npmjs.com/package/igniteui-angular-gauges) +|Components|| +|:---|:---| +|Bullet Graph|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/bullet-graph)| +|Linear Gauge|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/linear-gauge)| +|Radial Gauge|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/radial-gauge)| + +### Components available in [igniteui-angular-excel](https://www.npmjs.com/package/igniteui-angular-excel) +|Components|| +|:---|:---| +|Excel Library|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/excel-library)| + + +### Components available in [igniteui-angular-spreadsheet](https://www.npmjs.com/package/igniteui-angular-spreadsheet) +|Components|| +|:---|:---| +|Spreadsheet|[Docs](https://www.infragistics.com/products/ignite-ui-angular/angular/components/spreadsheet-overview)| + +## Setup +From the root folder run: + +``` +npm install +``` + +## Create new Project with Ignite UI CLI +To get started with the Ignite UI CLI and Ignite UI for Angular: + +``` +npm i -g igniteui-cli +ig new --framework=angular +cd +ig add grid +ig start +``` + +## Adding IgniteUI for Angular to Existing Project + +Including the `igniteui-angular` and `igniteui-cli` packages to your project: + +``` +ng add igniteui-angular +``` + +After this operation you can use the Ignite UI CLI commands in your project, such as `ig` and `ig add`. +[Learn more](https://github.com/IgniteUI/igniteui-cli#usage) + +## Updating Existing Project + +Analyze your project for possible migrations: + +``` +ng update +``` + +If there are new versions available, update your packages: + +``` +ng update igniteui-angular +... +ng update igniteui-cli +``` + +## Building the Library +``` +// build the code +ng build igniteui-angular + +// build the css +npm run build:style + +// build them both +npm run build:lib +``` + +You can find the build ouput under `dist/igniteui-angular`. + +## Running the tests + +Running the tests in watch mode: + +``` +ng test igniteui-angular // or npm run test:lib:watch +``` + +Running the tests once with code coverage enabled: +``` +npm run test:lib +``` + +## Building the API Docs +The API docs are produced using TypeDoc and SassDoc. In order to build the docs, all you need to do is run: + +``` +npm run build:docs +``` + +The output of the API docs build is located under `dist/igniteui-angular/docs`. + +## Run Demos Application + +The repository includes a sample application featuring the showcasing the different components/directives. +In order to run the demo samples, build the library first and start the application. +``` +npm start +``` + +**NOTE**: Experimental demos are not always stable. + +## NPM Package + +You can include Ignite UI for Angular in your project as a dependency using the NPM package. + +`npm install igniteui-angular` + +## Contributing +[General Naming and Coding Guidelines for Ignite UI for Angular](https://github.com/IgniteUI/igniteui-angular/wiki/General-Naming-and-Coding--Guidelines-for-Ignite-UI-for-Angular) + +## Demo Apps & Documentation + +### List of Angular Demo Apps +- [Warehouse Picklist App](https://github.com/IgniteUI/warehouse-js-blocks) - Demonstrates using several Ignite UI for Angular widgets together to build a modern, mobile app. + +- [FinTech Grid App]( https://github.com/Infragistics/angular-samples/tree/master/Grid/FinJS) - The Ignite UI for Angular Grid component is able to handle thousands of updates per second, while keeping the grid responsive for any interaction that the user may undertake. This sample demonstrates the Angular Grid handling thousands of updates per second. + +- [FinTech Tree Grid App](https://github.com/Infragistics/angular-samples/tree/master/TreeGrid/FinJS) - The Ignite UI for Angular Tree Grid component is able to handle thousands of updates per second, while keeping the grid responsive for any interaction that the user may undertake. This sample demonstrates the Tree Grid handling thousands of updates per second. + +- [Crypto Portfolio App](https://github.com/IgniteUI/crypto-portfolio-app) - This is a web and mobile application, developed with Ignite UI for Angular components and styled with our one of a kind theming engine. + +- [Task Planner Application](https://github.com/IgniteUI/TaskPlanner) – Task Planner is an Angular web application. It provides an effective means for managing projects and related tasks. Thus, it loads data from the Web API endpoint, enabling the user to start managing - filtering and sorting tasks, editing tasks, adding new tasks. It shows nice UX UI perks like ability to Drag and Drop items from and to the List and Data Grid. + +- [Dock Manager with Data Analysis Tool](https://github.com/IgniteUI/DockManager-DataAnalysis) - The Data Analysis sample application provides users with the flexibility to customize the data visualization using one of several chart types. Built with Angular UI components, it showcases the Angular Data Grid integrated with an Angular Data Chart, Angular Pie Chart, and an Angular Category Chart, to provide an interactive and engaging visualization. The Dock Manager web component provides a windowing experience, allowing users to customize the layout and view, and make the data more accessible. + +- [COVID-19 Dashboard](https://github.com/IgniteUI/COVID-19-Dashboard) - This dynamic dashboard was built using Indigo.Design and Ignite UI for Angular leveraging timely reports data from CSSEGISandData/COVID-19 to create an useful and impactful visualization. Built in a matter of hours, it showcases the Ignite UI Category and Data Charts, Map and List components for Angular and the how easy it is to get those quickly configured and populated with data. + +- [Inventory Management App](https://github.com/IgniteUI/InventoryManagementApp) - The Inventory Management App consists of 2 pages: The Products Page and the Dashboard Page. The Products Page contains a grid with product information and includes a number of useful features + +### Angular apps with ASP.NET Core Web Application +If you consider Angular client side application with ASP.NET Core application you can check out our [ASP.NET-Core-Samples](https://github.com/IgniteUI/ASP.NET-Core-Samples) + +### Documentation +To get started with the Data Grid, use the steps in the [grid walk-through](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid). + +All help, related API documents and walk-throughs can be found for each control [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/general/getting-started). + + +## Roadmap +[Roadmap document](https://github.com/IgniteUI/igniteui-angular/blob/master/ROADMAP.md) + +## Support +Developer support is provided as part of the commercial, paid-for license via [Infragistics Forums](https://www.infragistics.com/community/forums/), or via Chat & Phone with a Priority Support license. To acquire a license for paid support or Priority Support, please visit this [page](https://www.infragistics.com/how-to-buy/product-pricing#developers). + +Community support for open source usage of this product is available at [StackOverflow](https://stackoverflow.com/questions/tagged/ignite-ui-angular). + +## License +This is a commercial product, requiring a valid paid-for license for commercial use. +This product is free to use for non-commercial educational use for students in K through 12 grades or University programs, and for educators to use in a classroom setting as examples / tools in their curriculum. +In order for us to verify your eligibility for free usage, please [register for trial](https://Infragistics.com/Angular) and open a support ticket with a request for free license. + +To acquire a license for commercial usage, please [register for trial](https://Infragistics.com/Angular) and refer to the purchasing options in the pricing section on the product page. + +© Copyright 2020 INFRAGISTICS. All Rights Reserved. +The Infragistics Ultimate license & copyright applies to this distribution. +For information on that license, please go to our website [https://www.infragistics.com/legal/license](https://www.infragistics.com/legal/license). + + + + diff --git a/projects/igniteui-angular/migrations/update-12_1_0/changes/classes.json b/projects/igniteui-angular/migrations/update-12_1_0/changes/classes.json index 8364bb4a435..510bfff0e00 100644 --- a/projects/igniteui-angular/migrations/update-12_1_0/changes/classes.json +++ b/projects/igniteui-angular/migrations/update-12_1_0/changes/classes.json @@ -1,28 +1,28 @@ -{ - "$schema": "../../common/schema/class.schema.json", - "changes": [{ - "name": "IgxHierarchicalGridCellComponent", - "replaceWith": "CellType" - }, - { - "name": "IgxGridCellComponent", - "replaceWith": "CellType" - }, - { - "name": "IgxTreeGridCellComponent", - "replaceWith": "CellType" - }, - { - "name": "IgxGridExpandableCellComponent", - "replaceWith": "CellType" - }, - { - "name": "IComboSelectionChangeEventArgs", - "replaceWith": "IComboSelectionChangingEventArgs" - }, - { - "name": "AutocompleteItemSelectionEventArgs", - "replaceWith": "AutocompleteSelectionChangingEventArgs" - } - ] -} +{ + "$schema": "../../common/schema/class.schema.json", + "changes": [{ + "name": "IgxHierarchicalGridCellComponent", + "replaceWith": "CellType" + }, + { + "name": "IgxGridCellComponent", + "replaceWith": "CellType" + }, + { + "name": "IgxTreeGridCellComponent", + "replaceWith": "CellType" + }, + { + "name": "IgxGridExpandableCellComponent", + "replaceWith": "CellType" + }, + { + "name": "IComboSelectionChangeEventArgs", + "replaceWith": "IComboSelectionChangingEventArgs" + }, + { + "name": "AutocompleteItemSelectionEventArgs", + "replaceWith": "AutocompleteSelectionChangingEventArgs" + } + ] +} diff --git a/projects/igniteui-angular/src/lib/card/card.component.ts b/projects/igniteui-angular/src/lib/card/card.component.ts index f3736eaac88..61b5c5a843a 100644 --- a/projects/igniteui-angular/src/lib/card/card.component.ts +++ b/projects/igniteui-angular/src/lib/card/card.component.ts @@ -316,7 +316,7 @@ export class IgxCardActionsComponent implements OnInit, OnChanges { */ @HostBinding('class.igx-card-actions--vertical') @Input() - public vertical: boolean; + public vertical = false; /** * A getter that returns `true` when the layout has been @@ -365,7 +365,9 @@ export class IgxCardActionsComponent implements OnInit, OnChanges { * @internal */ public ngOnInit() { - this.vertical = !this.isVerticalSet && this.card.horizontal; + if (!this.isVerticalSet && this.card.horizontal) { + this.vertical = true; + }; } } diff --git a/projects/igniteui-angular/src/lib/carousel/carousel-base.ts b/projects/igniteui-angular/src/lib/carousel/carousel-base.ts index 2c1cea690cc..947dacaefc3 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel-base.ts +++ b/projects/igniteui-angular/src/lib/carousel/carousel-base.ts @@ -1,189 +1,189 @@ -import { AnimationBuilder, AnimationPlayer, AnimationReferenceMetadata, useAnimation } from '@angular/animations'; -import { ChangeDetectorRef, EventEmitter } from '@angular/core'; -import { fadeIn } from '../animations/fade'; -import { slideInLeft } from '../animations/slide'; -import { mkenum } from '../core/utils'; - -export enum Direction { NONE, NEXT, PREV } - -export const HorizontalAnimationType = mkenum({ - none: 'none', - slide: 'slide', - fade: 'fade' -}); -export type HorizontalAnimationType = (typeof HorizontalAnimationType)[keyof typeof HorizontalAnimationType]; - -export interface CarouselAnimationSettings { - enterAnimation: AnimationReferenceMetadata; - leaveAnimation: AnimationReferenceMetadata; -} - -/** @hidden */ -export interface IgxSlideComponentBase { - direction: Direction; - previous: boolean; -} - -/** @hidden */ -export abstract class IgxCarouselComponentBase { - /** @hidden */ - public animationType: HorizontalAnimationType = HorizontalAnimationType.slide; - - /** @hidden @internal */ - public enterAnimationDone = new EventEmitter(); - /** @hidden @internal */ - public leaveAnimationDone = new EventEmitter(); - - /** @hidden */ - protected currentItem: IgxSlideComponentBase; - /** @hidden */ - protected previousItem: IgxSlideComponentBase; - /** @hidden */ - protected enterAnimationPlayer?: AnimationPlayer; - /** @hidden */ - protected leaveAnimationPlayer?: AnimationPlayer; - /** @hidden */ - protected defaultAnimationDuration = 320; - /** @hidden */ - protected animationPosition = 0; - /** @hidden */ - protected newDuration = 0; - - constructor(private builder: AnimationBuilder, private cdr: ChangeDetectorRef) { - } - - /** @hidden */ - protected triggerAnimations() { - if (this.animationType !== HorizontalAnimationType.none) { - if (this.animationStarted(this.leaveAnimationPlayer) || this.animationStarted(this.enterAnimationPlayer)) { - requestAnimationFrame(() => { - this.resetAnimations(); - this.playAnimations(); - }); - } else { - this.playAnimations(); - } - } - } - - /** @hidden */ - protected animationStarted(animation: AnimationPlayer): boolean { - return animation && animation.hasStarted(); - } - - /** @hidden */ - protected playAnimations() { - this.playLeaveAnimation(); - this.playEnterAnimation(); - } - - private resetAnimations() { - if (this.animationStarted(this.leaveAnimationPlayer)) { - this.leaveAnimationPlayer.reset(); - this.leaveAnimationDone.emit(); - } - - if (this.animationStarted(this.enterAnimationPlayer)) { - this.enterAnimationPlayer.reset(); - this.enterAnimationDone.emit(); - this.cdr.markForCheck(); - } - } - - private getAnimation(): CarouselAnimationSettings { - let duration; - if (this.newDuration) { - duration = this.animationPosition ? this.animationPosition * this.newDuration : this.newDuration; - } else { - duration = this.animationPosition ? this.animationPosition * this.defaultAnimationDuration : this.defaultAnimationDuration; - } - - switch (this.animationType) { - case HorizontalAnimationType.slide: - const trans = this.animationPosition ? this.animationPosition * 100 : 100; - return { - enterAnimation: useAnimation(slideInLeft, - { - params: { - delay: '0s', - duration: `${duration}ms`, - endOpacity: 1, - startOpacity: 1, - fromPosition: `translateX(${this.currentItem.direction === 1 ? trans : -trans}%)`, - toPosition: 'translateX(0%)' - } - }), - leaveAnimation: useAnimation(slideInLeft, - { - params: { - delay: '0s', - duration: `${duration}ms`, - endOpacity: 1, - startOpacity: 1, - fromPosition: `translateX(0%)`, - toPosition: `translateX(${this.currentItem.direction === 1 ? -trans : trans}%)`, - } - }) - }; - case HorizontalAnimationType.fade: - return { - enterAnimation: useAnimation(fadeIn, - { params: { duration: `${duration}ms`, startOpacity: `${this.animationPosition}` } }), - leaveAnimation: null - }; - } - return { - enterAnimation: null, - leaveAnimation: null - }; - } - - private playEnterAnimation() { - const animation = this.getAnimation().enterAnimation; - if (!animation) { - return; - } - const animationBuilder = this.builder.build(animation); - - this.enterAnimationPlayer = animationBuilder.create(this.getCurrentElement()); - - this.enterAnimationPlayer.onDone(() => { - if (this.enterAnimationPlayer) { - this.enterAnimationPlayer.reset(); - this.enterAnimationPlayer = null; - } - this.animationPosition = 0; - this.newDuration = 0; - this.previousItem.previous = false; - this.enterAnimationDone.emit(); - this.cdr.markForCheck(); - }); - this.previousItem.previous = true; - this.enterAnimationPlayer.play(); - } - - private playLeaveAnimation() { - const animation = this.getAnimation().leaveAnimation; - if (!animation) { - return; - } - - const animationBuilder = this.builder.build(animation); - this.leaveAnimationPlayer = animationBuilder.create(this.getPreviousElement()); - - this.leaveAnimationPlayer.onDone(() => { - if (this.leaveAnimationPlayer) { - this.leaveAnimationPlayer.reset(); - this.leaveAnimationPlayer = null; - } - this.animationPosition = 0; - this.newDuration = 0; - this.leaveAnimationDone.emit(); - }); - this.leaveAnimationPlayer.play(); - } - - protected abstract getPreviousElement(): HTMLElement; - - protected abstract getCurrentElement(): HTMLElement; -} +import { AnimationBuilder, AnimationPlayer, AnimationReferenceMetadata, useAnimation } from '@angular/animations'; +import { ChangeDetectorRef, EventEmitter } from '@angular/core'; +import { fadeIn } from '../animations/fade'; +import { slideInLeft } from '../animations/slide'; +import { mkenum } from '../core/utils'; + +export enum Direction { NONE, NEXT, PREV } + +export const HorizontalAnimationType = mkenum({ + none: 'none', + slide: 'slide', + fade: 'fade' +}); +export type HorizontalAnimationType = (typeof HorizontalAnimationType)[keyof typeof HorizontalAnimationType]; + +export interface CarouselAnimationSettings { + enterAnimation: AnimationReferenceMetadata; + leaveAnimation: AnimationReferenceMetadata; +} + +/** @hidden */ +export interface IgxSlideComponentBase { + direction: Direction; + previous: boolean; +} + +/** @hidden */ +export abstract class IgxCarouselComponentBase { + /** @hidden */ + public animationType: HorizontalAnimationType = HorizontalAnimationType.slide; + + /** @hidden @internal */ + public enterAnimationDone = new EventEmitter(); + /** @hidden @internal */ + public leaveAnimationDone = new EventEmitter(); + + /** @hidden */ + protected currentItem: IgxSlideComponentBase; + /** @hidden */ + protected previousItem: IgxSlideComponentBase; + /** @hidden */ + protected enterAnimationPlayer?: AnimationPlayer; + /** @hidden */ + protected leaveAnimationPlayer?: AnimationPlayer; + /** @hidden */ + protected defaultAnimationDuration = 320; + /** @hidden */ + protected animationPosition = 0; + /** @hidden */ + protected newDuration = 0; + + constructor(private builder: AnimationBuilder, private cdr: ChangeDetectorRef) { + } + + /** @hidden */ + protected triggerAnimations() { + if (this.animationType !== HorizontalAnimationType.none) { + if (this.animationStarted(this.leaveAnimationPlayer) || this.animationStarted(this.enterAnimationPlayer)) { + requestAnimationFrame(() => { + this.resetAnimations(); + this.playAnimations(); + }); + } else { + this.playAnimations(); + } + } + } + + /** @hidden */ + protected animationStarted(animation: AnimationPlayer): boolean { + return animation && animation.hasStarted(); + } + + /** @hidden */ + protected playAnimations() { + this.playLeaveAnimation(); + this.playEnterAnimation(); + } + + private resetAnimations() { + if (this.animationStarted(this.leaveAnimationPlayer)) { + this.leaveAnimationPlayer.reset(); + this.leaveAnimationDone.emit(); + } + + if (this.animationStarted(this.enterAnimationPlayer)) { + this.enterAnimationPlayer.reset(); + this.enterAnimationDone.emit(); + this.cdr.markForCheck(); + } + } + + private getAnimation(): CarouselAnimationSettings { + let duration; + if (this.newDuration) { + duration = this.animationPosition ? this.animationPosition * this.newDuration : this.newDuration; + } else { + duration = this.animationPosition ? this.animationPosition * this.defaultAnimationDuration : this.defaultAnimationDuration; + } + + switch (this.animationType) { + case HorizontalAnimationType.slide: + const trans = this.animationPosition ? this.animationPosition * 100 : 100; + return { + enterAnimation: useAnimation(slideInLeft, + { + params: { + delay: '0s', + duration: `${duration}ms`, + endOpacity: 1, + startOpacity: 1, + fromPosition: `translateX(${this.currentItem.direction === 1 ? trans : -trans}%)`, + toPosition: 'translateX(0%)' + } + }), + leaveAnimation: useAnimation(slideInLeft, + { + params: { + delay: '0s', + duration: `${duration}ms`, + endOpacity: 1, + startOpacity: 1, + fromPosition: `translateX(0%)`, + toPosition: `translateX(${this.currentItem.direction === 1 ? -trans : trans}%)`, + } + }) + }; + case HorizontalAnimationType.fade: + return { + enterAnimation: useAnimation(fadeIn, + { params: { duration: `${duration}ms`, startOpacity: `${this.animationPosition}` } }), + leaveAnimation: null + }; + } + return { + enterAnimation: null, + leaveAnimation: null + }; + } + + private playEnterAnimation() { + const animation = this.getAnimation().enterAnimation; + if (!animation) { + return; + } + const animationBuilder = this.builder.build(animation); + + this.enterAnimationPlayer = animationBuilder.create(this.getCurrentElement()); + + this.enterAnimationPlayer.onDone(() => { + if (this.enterAnimationPlayer) { + this.enterAnimationPlayer.reset(); + this.enterAnimationPlayer = null; + } + this.animationPosition = 0; + this.newDuration = 0; + this.previousItem.previous = false; + this.enterAnimationDone.emit(); + this.cdr.markForCheck(); + }); + this.previousItem.previous = true; + this.enterAnimationPlayer.play(); + } + + private playLeaveAnimation() { + const animation = this.getAnimation().leaveAnimation; + if (!animation) { + return; + } + + const animationBuilder = this.builder.build(animation); + this.leaveAnimationPlayer = animationBuilder.create(this.getPreviousElement()); + + this.leaveAnimationPlayer.onDone(() => { + if (this.leaveAnimationPlayer) { + this.leaveAnimationPlayer.reset(); + this.leaveAnimationPlayer = null; + } + this.animationPosition = 0; + this.newDuration = 0; + this.leaveAnimationDone.emit(); + }); + this.leaveAnimationPlayer.play(); + } + + protected abstract getPreviousElement(): HTMLElement; + + protected abstract getCurrentElement(): HTMLElement; +} diff --git a/projects/igniteui-angular/src/lib/carousel/public_api.ts b/projects/igniteui-angular/src/lib/carousel/public_api.ts index 4e82234d664..619ce8843f6 100644 --- a/projects/igniteui-angular/src/lib/carousel/public_api.ts +++ b/projects/igniteui-angular/src/lib/carousel/public_api.ts @@ -1,4 +1,4 @@ -export * from './carousel-base'; -export * from './carousel.component'; -export * from './slide.component'; -export * from './carousel.directives'; +export * from './carousel-base'; +export * from './carousel.component'; +export * from './slide.component'; +export * from './carousel.directives'; diff --git a/projects/igniteui-angular/src/lib/combo/combo.api.ts b/projects/igniteui-angular/src/lib/combo/combo.api.ts index 45b016daf4f..5f946ac3bbf 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.api.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.api.ts @@ -41,7 +41,7 @@ export class IgxComboAPIService { public set_selected_item(itemID: any, event?: Event): void { const selected = this.combo.isItemSelected(itemID); - if (itemID === null || itemID === undefined) { + if (!itemID && itemID !== 0) { return; } if (!selected) { diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.ts b/projects/igniteui-angular/src/lib/combo/combo.component.ts index ed3653bc480..dd31509b332 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.ts @@ -28,7 +28,7 @@ import { IgxDropDownModule } from '../drop-down/public_api'; import { IgxInputGroupModule } from '../input-group/input-group.component'; import { IgxComboItemComponent } from './combo-item.component'; import { IgxComboDropDownComponent } from './combo-dropdown.component'; -import { IgxComboFilteringPipe, IgxComboGroupingPipe } from './combo.pipes'; +import { IgxComboCleanPipe, IgxComboFilteringPipe, IgxComboGroupingPipe } from './combo.pipes'; import { DisplayDensityToken, IDisplayDensityOptions } from '../core/density'; import { IGX_COMBO_COMPONENT, IgxComboBaseDirective } from './combo.common'; import { IgxComboAddItemComponent } from './combo-add-item.component'; @@ -448,6 +448,7 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie IgxComboDropDownComponent, IgxComboEmptyDirective, IgxComboFilteringPipe, + IgxComboCleanPipe, IgxComboFooterDirective, IgxComboGroupingPipe, IgxComboHeaderDirective, @@ -464,6 +465,7 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie IgxComboDropDownComponent, IgxComboEmptyDirective, IgxComboFilteringPipe, + IgxComboCleanPipe, IgxComboFooterDirective, IgxComboGroupingPipe, IgxComboHeaderDirective, diff --git a/projects/igniteui-angular/src/lib/combo/combo.pipes.ts b/projects/igniteui-angular/src/lib/combo/combo.pipes.ts index eb24ebbf884..eda97385476 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.pipes.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.pipes.ts @@ -5,10 +5,17 @@ import { IGX_COMBO_COMPONENT, IgxComboBase } from './combo.common'; import { DefaultSortingStrategy, SortingDirection } from '../data-operations/sorting-strategy'; import { IComboFilteringOptions } from './combo.component'; +/** @hidden */ +@Pipe({ + name: 'comboClean' +}) +export class IgxComboCleanPipe implements PipeTransform { + public transform(collection: any[]) { + return collection.filter(e => !!e); + } +} -/** - * @hidden - */ +/** @hidden */ @Pipe({ name: 'comboFiltering' }) @@ -23,8 +30,8 @@ export class IgxComboFilteringPipe implements PipeTransform { } else { const searchTerm = filteringOptions.caseSensitive ? searchValue.trim() : searchValue.toLowerCase().trim(); if (displayKey != null) { - return collection.filter(e => filteringOptions.caseSensitive ? e[displayKey].includes(searchTerm) : - e[displayKey].toString().toLowerCase().includes(searchTerm)); + return collection.filter(e => filteringOptions.caseSensitive ? e[displayKey]?.includes(searchTerm) : + e[displayKey]?.toString().toLowerCase().includes(searchTerm)); } else { return collection.filter(e => filteringOptions.caseSensitive ? e.includes(searchTerm) : e.toString().toLowerCase().includes(searchTerm)); @@ -33,9 +40,7 @@ export class IgxComboFilteringPipe implements PipeTransform { } } -/** - * @hidden - */ +/** @hidden */ @Pipe({ name: 'comboGrouping' }) export class IgxComboGroupingPipe implements PipeTransform { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss index c5e05ee8238..22cc4e43b18 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss @@ -219,6 +219,7 @@ &[igxButton] { border-radius: 0; + border-color: var-get($theme, 'item-border-color'); } &:not(:nth-child(0)) { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/button/_button-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/button/_button-theme.scss index f9e68043059..d1630fb604b 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/button/_button-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/button/_button-theme.scss @@ -22,6 +22,8 @@ /// @param {Color} $hover-foreground [null] - The hover text color of the button. /// @param {Color} $focus-background [null] - The focus background color of the button. /// @param {Color} $focus-foreground [null] - The focus text color of the button. +/// @param {Color} $active-background [null] - The active background of the button. +/// @param {Color} $active-foreground [null] - The active text color of the button. /// @param {Number} $border-radius [null] - The border radius of the button. /// @param {Color} $border-color [null] - The border color of the button. /// @param {Color} $shadow-color [null] - The shadow color of the button. @@ -61,6 +63,9 @@ $focus-background: null, $focus-foreground: null, + $active-background: null, + $active-foreground: null, + $border-radius: null, $border-color: null, @@ -159,6 +164,9 @@ focus-background: $focus-background, focus-foreground: $focus-foreground, + active-background: $active-background, + active-foreground: $active-foreground, + border-radius: $_border-radius, border-color: $border-color, @@ -224,7 +232,13 @@ $button-floating-width: rem(56px); $button-floating-height: $button-floating-width; - $button-icon-width: rem(36px, 16px); + $button-icon-width: map.get(( + material: rem(36px, 16px), + fluent: rem(32px, 16px), + bootstrap: rem(36px, 16px), + indigo-design: rem(36px, 16px), + ), $variant); + $button-icon-height: $button-icon-width; $button-icon-font-size: rem(24px, 24px); $button-icon-padding: 0; @@ -322,7 +336,13 @@ compact: rem(4px) ); - $icon-in-button-size: rem(18px); + $icon-sizes: map.get(( + material: rem(18px), + fluent: rem(16px), + bootstrap: rem(18px), + indigo-design: rem(18px), + ), $variant); + $icon-in-button-size: $icon-sizes; $raised-shadow: map.get(( material: var-get($raised-theme, 'resting-shadow'), @@ -429,6 +449,7 @@ color: var-get($flat-theme, 'foreground'); border-radius: var-get($flat-theme, 'border-radius'); + &:hover { background: var-get($flat-theme, 'hover-background'); color: var-get($flat-theme, 'hover-foreground'); @@ -436,10 +457,24 @@ &:focus, &:active { - background: var-get($flat-theme, 'focus-background'); - color: var-get($flat-theme, 'focus-foreground'); + @if($variant != 'fluent') { + background: var-get($flat-theme, 'focus-background'); + color: var-get($flat-theme, 'focus-foreground'); + } box-shadow: 0 0 0 rem(3px) var-get($flat-theme, 'shadow-color'); } + + @if($variant == 'fluent') { + border: rem(1px) solid transparent; + + &:active { + color: var-get($flat-theme, 'active-foreground'); + } + + &:focus-visible { + border-color: var-get($flat-theme, 'border-color'); + } + } } %igx-button--outlined { @@ -466,10 +501,33 @@ &:focus, &:active { - background: var-get($outlined-theme, 'focus-background'); + @if($variant != 'fluent') { + background: var-get($outlined-theme, 'focus-background'); + } color: var-get($outlined-theme, 'focus-foreground'); box-shadow: 0 0 0 rem(3px) var-get($outlined-theme, 'shadow-color'); } + + @if($variant == 'fluent') { + &:active { + background: var-get($outlined-theme, 'active-background'); + color: var-get($outlined-theme, 'active-foreground'); + } + + &:focus-visible { + &::after { + $outline-btn-indent: rem(2px); + content: ''; + position: absolute; + top: $outline-btn-indent; + left: $outline-btn-indent; + pointer-events: none; + width: calc(100% - (#{$outline-btn-indent} * 2)); + height: calc(100% - (#{$outline-btn-indent} * 2)); + box-shadow: 0 0 0 rem(1px) var-get($outlined-theme, 'border-color'); + } + } + } } %igx-button--outlined-cosy { @@ -504,15 +562,38 @@ &:focus, &:active { - color: var-get($raised-theme, 'focus-foreground'); - background: var-get($raised-theme, 'focus-background'); - @if $variant == 'material' or $variant == 'fluent' { box-shadow: 0 0 0 rem(3px) var-get($raised-theme, 'shadow-color'), $raised-shadow--focus; } @else { box-shadow: $raised-shadow--focus; } } + + &:focus { + color: var-get($raised-theme, 'focus-foreground'); + background: var-get($raised-theme, 'focus-background'); + } + + &:active { + color: var-get($raised-theme, 'active-foreground'); + background: var-get($raised-theme, 'active-background'); + } + + @if($variant == 'fluent') { + &:focus-visible { + &::after { + $outline-btn-indent: rem(3px); + content: ''; + position: absolute; + top: $outline-btn-indent; + left: $outline-btn-indent; + pointer-events: none; + width: calc(100% - (#{$outline-btn-indent} * 2)); + height: calc(100% - (#{$outline-btn-indent} * 2)); + box-shadow: 0 0 0 rem(1px) var-get($outlined-theme, 'border-color'); + } + } + } } %igx-button--round { @@ -551,9 +632,6 @@ &:focus, &:active { - color: var-get($fab-theme, 'focus-foreground'); - background: var-get($fab-theme, 'focus-background'); - @if $variant == 'material' or $variant == 'fluent' { box-shadow: 0 0 0 rem(3px) var-get($fab-theme, 'shadow-color'), $fab-shadow--focus; } @else { @@ -561,6 +639,33 @@ } } + &:focus { + color: var-get($raised-theme, 'focus-foreground'); + background: var-get($raised-theme, 'focus-background'); + } + + &:active { + color: var-get($raised-theme, 'active-foreground'); + background: var-get($raised-theme, 'active-background'); + } + + @if($variant == 'fluent') { + &:focus-visible { + &::after { + $outline-btn-indent: rem(3px); + content: ''; + position: absolute; + top: $outline-btn-indent; + left: $outline-btn-indent; + pointer-events: none; + border-radius: calc(#{var-get($fab-theme, 'border-radius')} - #{$outline-btn-indent}); + width: calc(100% - (#{$outline-btn-indent} * 2)); + height: calc(100% - (#{$outline-btn-indent} * 2)); + box-shadow: 0 0 0 rem(1px) var-get($outlined-theme, 'border-color'); + } + } + } + @include icon-w-margin( map.get($icon-in-button-margin, 'comfortable'), $left @@ -600,17 +705,57 @@ background: var-get($icon-theme, 'background'); border-radius: var-get($icon-theme, 'border-radius'); + @if $variant == 'fluent' { + border: 1px solid transparent; + } + &:hover { - transition: $button-transition; + @if $variant != 'fluent' { + transition: $button-transition; + } + color: var-get($icon-theme, 'hover-foreground'); background: var-get($icon-theme, 'hover-background'); } - &:focus, + &:focus { + color: var-get($icon-theme, 'focus-foreground'); + + @if $variant != 'fluent' { + background: var-get($icon-theme, 'focus-background'); + } + + &:hover { + color: var-get($icon-theme, 'hover-foreground'); + background: var-get($icon-theme, 'hover-background'); + } + + &:active { + color: var-get($icon-theme, 'active-foreground'); + background: var-get($icon-theme, 'active-background'); + } + } + &:active { + color: var-get($icon-theme, 'active-foreground'); + background: var-get($icon-theme, 'active-background'); + } + + &:focus-visible { color: var-get($icon-theme, 'focus-foreground'); - background: var-get($icon-theme, 'focus-background'); + @if $variant == 'fluent' { + border: rem(1px) solid var-get($icon-theme, 'border-color') ; + } + + &:hover { + color: var-get($icon-theme, 'hover-foreground'); + background: var-get($icon-theme, 'hover-background'); + } + } + + &:focus, + &:active { @if $variant == 'material' or $variant == 'fluent' { box-shadow: 0 0 0 rem(3px) var-get($icon-theme, 'shadow-color'), $icon-shadow; } @else { @@ -625,6 +770,16 @@ color: var-get($flat-theme, 'disabled-foreground'); background: transparent; + @if $variant == 'fluent' { + &%igx-button--outlined { + background: var-get($flat-theme, 'disabled-background'); + } + + &%igx-button--icon { + background: var-get($flat-theme, 'disabled-background'); + } + } + &%igx-button--raised, &%igx-button--fab { background: var-get($flat-theme, 'disabled-background'); diff --git a/projects/igniteui-angular/src/lib/core/styles/components/checkbox/_checkbox-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/checkbox/_checkbox-theme.scss index 4e289720737..62416c56a78 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/checkbox/_checkbox-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/checkbox/_checkbox-theme.scss @@ -21,10 +21,11 @@ /// @param {Color} $fill-color [null] - The checked border and fill colors. /// @param {Color} $tick-color [null] - The checked mark color. /// @param {Color} $disabled-color [null] - The disabled border and fill colors. +/// @param {Color} $disabled-indeterminate-color [null] - The disabled border and fill colors in indeterminate state. /// @param {Color} $disabled-color-label [null] - The disabled label color. /// @param {border-radius} $border-radius [null] - The border radius used for checkbox component. /// @param {border-radius} $border-radius-ripple [null] - The border radius used for checkbox ripple. -/// @param {Color} $focus-outline-color [null] - TThe focus outlined color. +/// @param {Color} $focus-outline-color [null] - The focus outlined color. /// Set to light when the surrounding area is dark. /// /// @requires $default-palette @@ -46,6 +47,7 @@ $fill-color: null, $tick-color: null, $disabled-color: null, + $disabled-indeterminate-color: null, $disabled-color-label: null, $border-radius-ripple: null, $focus-outline-color: null @@ -77,6 +79,7 @@ fill-color: $fill-color, tick-color: $tick-color, disabled-color: $disabled-color, + disabled-indeterminate-color: $disabled-indeterminate-color, disabled-color-label: $disabled-color-label, border-radius: $border-radius, border-radius-ripple: $border-radius-ripple, @@ -103,7 +106,7 @@ // If updating the WIDTH of the checkbox here, please update it in the grid theme as well. // It is under the name of $cbx-size $size: em(20px); - $size-bs: em(14px); + $size-bs: em(16px); $checkbox-radius: math.div($size, 2); $variant: map.get($theme, variant); @@ -135,7 +138,7 @@ $mark-stroke: map.get(( material: 3, fluent: 1, - bootstrap: 2, + bootstrap: 3, indigo-design: 3, ), $variant); @@ -229,31 +232,19 @@ background: transparent; @if $bootstrap-theme { - %cbx-composite-mark { - stroke: var-get($theme, 'empty-color'); - } + border-color: var-get($theme, 'disabled-color-label'); } } %cbx-composite--x--disabled { - @if $bootstrap-theme { - background: transparent; - } @else { - background: var-get($theme, 'disabled-color'); - } + background: var-get($theme, 'disabled-color'); &::after { - @if $bootstrap-theme { - background: transparent; - } @else { - background: var-get($theme, 'disabled-color'); - } + background: var-get($theme, 'disabled-color'); } @if $bootstrap-theme { - %cbx-composite-mark--x { - stroke: var-get($theme, 'empty-color'); - } + border-color: var-get($theme, 'disabled-color'); } } @@ -279,6 +270,55 @@ top: $mark-offset; left: $mark-offset; } + + @if $variant == 'material' { + %cbx-composite--x--disabled { + border: var-get($theme, 'disabled-indeterminate-color'); + + &::after { + background: var-get($theme, 'disabled-indeterminate-color'); + } + } + } + + @if $variant == 'fluent' { + %cbx-composite-mark { + stroke: transparent; + } + + %cbx-composite { + background: transparent; + + &::after { + background: transparent; + } + + &::before { + content: ''; + position: absolute; + top: calc($size / 2 - rem(6px)); + left: calc($size / 2 - rem(6px)); + width: rem(10px); + height: rem(10px); + border-radius: rem(2px); + border: rem(5px) solid var-get($theme, 'fill-color'); + z-index: 1; + } + } + + %cbx-composite--x--disabled { + background: transparent; + + &::after { + background: transparent; + } + + &::before { + border-color: var-get($theme, 'disabled-color'); + background: var-get($theme, 'disabled-color'); + } + } + } } %cbx-composite-mark--x { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss index 270c316fe50..b3d7e534c83 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss @@ -443,9 +443,14 @@ } %form-group-bundle--box { - padding: 0 rem(16px, map.get($base-scale-size, 'comfortable')); + padding: 0 0 0 rem(16px, map.get($base-scale-size, 'comfortable')); background: var-get($theme, 'box-background'); box-shadow: inset 0 -2px 0 0 var-get($theme, 'idle-bottom-line-color'); + + igx-suffix, + [igxSuffix] { + padding-right: rem(16px, map.get($base-scale-size, 'comfortable')); + } } %form-group-bundle--indigo { @@ -477,11 +482,21 @@ } %form-group-bundle--box-cosy { - padding: 0 rem(16px, map.get($base-scale-size, 'cosy')); + padding: 0 0 0 rem(16px, map.get($base-scale-size, 'cosy')); + + igx-suffix, + [igxSuffix] { + padding-right: rem(16px, map.get($base-scale-size, 'cosy')); + } } %form-group-bundle--box-compact { - padding: 0 rem(16px, map.get($base-scale-size, 'compact')); + padding: 0 0 0 rem(16px, map.get($base-scale-size, 'compact')); + + igx-suffix, + [igxSuffix] { + padding-right: rem(16px, map.get($base-scale-size, 'compact')); + } } %form-group-bundle--hover { @@ -545,11 +560,21 @@ } %form-group-bundle--border-cosy { - padding: 0 rem(16px, map.get($base-scale-size, 'cosy')); + padding: 0 0 0 rem(16px, map.get($base-scale-size, 'cosy')); + + igx-suffix, + [igxSuffix] { + padding-right: rem(16px, map.get($base-scale-size, 'cosy')); + } } %form-group-bundle--border-compact { - padding: 0 rem(16px, map.get($base-scale-size, 'compact')); + padding: 0 0 0 rem(16px, map.get($base-scale-size, 'compact')); + + igx-suffix, + [igxSuffix] { + padding-right: rem(16px, map.get($base-scale-size, 'compact')); + } } %form-group-bundle-border--hover { @@ -723,11 +748,11 @@ padding: 0; min-height: 32px; border: 1px solid var-get($theme, 'border-color'); - box-shadow: none; border-radius: var-get($theme, 'border-border-radius'); background: var-get($theme, 'border-background'); position: relative; align-items: stretch; + box-shadow: none !important; } %form-group-bundle--fluent--hover { @@ -778,6 +803,7 @@ %form-group-bundle-main--fluent { padding-#{$left}: rem(8px); align-self: center; + cursor: default; } igx-prefix + %form-group-bundle-main--fluent, @@ -1248,10 +1274,15 @@ padding: rem(8px, map.get($base-scale-size, 'compact')) 0; } + igx-prefix.igx-prefix--upload, + [igxPrefix].igx-prefix--upload { + padding: 0 !important; + } + %form-group-prefix-fluent { color: var-get($theme, 'input-prefix-color'); background: var-get($theme, 'input-prefix-background'); - padding: rem(8px, map.get($base-scale-size, 'comfortable')); + padding: 0 rem(8px, map.get($base-scale-size, 'comfortable')); width: auto; height: auto; line-height: normal; @@ -1270,7 +1301,7 @@ } %form-group-prefix-fluent-search { - padding: rem(8px, map.get($base-scale-size, 'comfortable')); + padding: 0 rem(8px, map.get($base-scale-size, 'comfortable')); igx-icon { width: rem(18px); @@ -1285,7 +1316,7 @@ %form-group-suffix-fluent { color: var-get($theme, 'input-suffix-color'); - padding: rem(8px, map.get($base-scale-size, 'comfortable')); + padding: 0 rem(8px, map.get($base-scale-size, 'comfortable')); background: var-get($theme, 'input-suffix-background'); width: auto; height: auto; @@ -1306,7 +1337,7 @@ %form-group-suffix-fluent-search { width: auto; height: auto; - padding: rem(8px, map.get($base-scale-size, 'comfortable')); + padding: 0 rem(8px, map.get($base-scale-size, 'comfortable')); line-height: normal; igx-icon { @@ -1320,6 +1351,16 @@ } } + %form-group-prefix-fluent, + %form-group-suffix-fluent { + .igx-typography [igx-button], + .igx-typography igx-button, + .igx-typography button, + button { + border-radius: rem(1px) 0 0 rem(1px); + } + } + %form-group-prefix-fluent-search--cosy, %form-group-suffix-fluent-search--cosy, %form-group-prefix-fluent--cosy, @@ -1339,22 +1380,22 @@ %form-group-prefix-fluent-search--cosy, %form-group-prefix-fluent--cosy { - padding: rem(8px, map.get($base-scale-size, 'cosy')); + padding: 0 rem(8px, map.get($base-scale-size, 'cosy')); } %form-group-prefix-fluent-search--compact, %form-group-prefix-fluent--compact { - padding: rem(8px, map.get($base-scale-size, 'compact')); + padding: 0 rem(8px, map.get($base-scale-size, 'compact')); } %form-group-suffix-fluent-search--cosy, %form-group-suffix-fluent--cosy { - padding: rem(8px, map.get($base-scale-size, 'cosy')); + padding: 0 rem(8px, map.get($base-scale-size, 'cosy')); } %form-group-suffix-fluent-search--compact, %form-group-suffix-fluent--compact { - padding: rem(8px, map.get($base-scale-size, 'compact')); + padding: 0 rem(8px, map.get($base-scale-size, 'compact')); } // FLUENT END diff --git a/projects/igniteui-angular/src/lib/core/styles/components/radio/_radio-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/radio/_radio-theme.scss index f8bab4500ee..48550710bd2 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/radio/_radio-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/radio/_radio-theme.scss @@ -24,7 +24,7 @@ /// @param {Color} $hover-color [null] - The border and dot colors on hover. /// @param {Color} $fill-color-hover [null] - The checked border and dot colors on hover. /// @param {Color} $fill-hover-border-color [null] - The checked dot border color on hover. -/// @param {Color} $focus-outline-color [null] - TThe focus outlined color. +/// @param {Color} $focus-outline-color [null] - The focus outlined color. /// /// @requires $default-palette /// @requires $light-schema @@ -203,13 +203,13 @@ %radio-composite--x { &::before { border: $border-width $border-style var-get($theme, 'fill-color'); + background: var-get($theme, 'fill-color'); + transform: $scale; @if $bootstrap-theme { border-color: var-get($theme, 'fill-hover-border-color'); + background: var-get($theme, 'fill-hover-border-color'); } - - background: var-get($theme, 'fill-color-hover'); - transform: $scale; } &::after { @@ -223,7 +223,7 @@ %igx-radio-hover__composite { &::before { - background: var-get($theme, 'fill-color-hover'); + background: var-get($theme, 'hover-color'); transform: $radio-hover-scale; } } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-component.scss index dee856d4e57..af0d7589a9c 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-component.scss @@ -71,6 +71,10 @@ @extend %igx-switch--focused-checked !optional; } + @include mx(disabled, checked) { + @extend %igx-switch--disabled-checked !optional; + } + @include m(checked) { @include e(composite) { @extend %switch-composite--x !optional; diff --git a/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-theme.scss index d1f5c8426b0..5d02d8959cf 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/switch/_switch-theme.scss @@ -18,10 +18,13 @@ /// /// @param {Color} $thumb-on-color [null] - The color of the thumb when the switch is on. /// @param {Color} $track-on-color [null] - The color of the track when the switch is on. +/// @param {Color} $track-on-hover-color [null] - The color of the track when the switch is on and hovered. /// @param {Color} $thumb-off-color [null] - The color of the thumb when the switch is off. /// @param {Color} $track-off-color [null] - The color of the track when the switch is off. /// @param {Color} $thumb-disabled-color [null] - The color of the thumb when the switch is disabled. +/// @param {Color} $thumb-on-disabled-color [null] - The color of the thumb when the switch is on and disabled. /// @param {Color} $track-disabled-color [null] - The color of the track when the switch is disabled. +/// @param {Color} $track-on-disabled-color [null] - The color of the track when the switch is on and disabled. /// @param {Color} $label-color [null] - The color of the switch label /// @param {Color} $label-disabled-color [null] - The color of the switch label when the switch is disabled /// @param {box-shadow} $resting-shadow [null] - The shadow used for the thumb in resting state of the switch. @@ -36,7 +39,9 @@ /// @param {Color} $border-disabled-color [null] - The border color of the switch when it is disabled. /// @param {Color} $border-on-color [null] - The border color when the switch is on. /// @param {Color} $border-on-hover-color [null] - The border color when the switch is on and hovered. -/// @param {Color} $focus-outline-color [null] - TThe focus outlined color. +/// @param {Color} $focus-outline-color [null] - The focus outlined color. +/// @param {Color} $focus-outline-color-focused [null] - The focus outlined color for focused state. +/// @param {Color} $focus-fill-color [null] - The focus fill color. /// /// @requires $default-palette /// @requires $light-schema @@ -55,12 +60,15 @@ $thumb-on-color: null, $track-on-color: null, + $track-on-hover-color: null, $thumb-off-color: null, $track-off-color: null, $track-disabled-color: null, + $track-on-disabled-color: null, $thumb-disabled-color: null, + $thumb-on-disabled-color: null, $label-color: null, $label-disabled-color: null, @@ -78,6 +86,8 @@ $border-on-color: null, $border-on-hover-color: null, $focus-outline-color: null, + $focus-outline-color-focused: null, + $focus-fill-color: null ) { $name: 'igx-switch'; $switch-schema: (); @@ -123,12 +133,15 @@ thumb-on-color: $thumb-on-color, track-on-color: $track-on-color, + track-on-hover-color: $track-on-hover-color, thumb-off-color: $thumb-off-color, track-off-color: $track-off-color, track-disabled-color: $track-disabled-color, + track-on-disabled-color: $track-on-disabled-color, thumb-disabled-color: $thumb-disabled-color, + thumb-on-disabled-color: $thumb-on-disabled-color, label-color: $label-color, label-disabled-color: $label-disabled-color, @@ -146,6 +159,8 @@ border-on-color: $border-on-color, border-on-hover-color: $border-on-hover-color, focus-outline-color: $focus-outline-color, + focus-outline-color-focused: $focus-outline-color-focused, + focus-fill-color: $focus-fill-color )); } @@ -183,21 +198,21 @@ $switch-thumb-width: map.get(( material: 20px, fluent: 12px, - bootstrap: 8px, + bootstrap: 10px, indigo-design: 8px ), $variant); $switch-on-offset: map.get(( material: 1px, fluent: math.div($switch-thumb-width, 2), - bootstrap: math.div($switch-thumb-width, 2), + bootstrap: 4px, indigo-design: 7px ), $variant); $switch-off-offset: map.get(( material: -1px, fluent: math.div($switch-thumb-width, 3), - bootstrap: math.div($switch-thumb-width, 3), + bootstrap: math.div($switch-thumb-width, 4), indigo-design: math.div($switch-thumb-width, 3), ), $variant); @@ -279,9 +294,23 @@ border: 2px solid var-get($theme, 'border-color'); } - &:hover, - &:focus { - border-color: var-get($theme, 'border-hover-color'); + @if $variant != 'bootstrap' { + &:hover, + &:focus { + border-color: var-get($theme, 'border-hover-color'); + } + } + + @if $variant == 'fluent' { + &:hover { + %switch-composite-thumb { + background: var-get($theme, 'border-hover-color'); + } + + %switch-composite-thumb--x { + background: var-get($theme, 'thumb-on-color'); + } + } } } @@ -293,6 +322,12 @@ &:focus { border-color: var-get($theme, 'border-on-hover-color'); } + + @if $variant == 'fluent' { + &:hover { + background: var-get($theme, 'track-on-hover-color'); + } + } } %switch-composite--disabled { @@ -319,8 +354,10 @@ transform: translateX(#{rem(-1 * $switch-off-offset)}); } - &:hover { - box-shadow: $thumb-hover-shadow; + @if $variant != 'bootstrap' { + &:hover { + box-shadow: $thumb-hover-shadow; + } } } @@ -358,25 +395,31 @@ %igx-switch--focused { @if $variant == 'fluent' { - position: relative; - $focus-outline-offset-top: rem(2px); - $focus-outline-offset-left: rem(2px); - - &::after { - content: ''; - position: absolute; - top: -$focus-outline-offset-top; - left: -$focus-outline-offset-left; - box-shadow: 0 0 0 1px var-get($theme, 'focus-outline-color'); - width: calc(100% + (#{$focus-outline-offset-left} * 2)); - height: calc(100% + (#{$focus-outline-offset-top} * 2)); + %switch-composite { + position: relative; + $focus-outline-offset-top: rem(2px); + $focus-outline-offset-left: rem(2px); + + &::after { + content: ''; + position: absolute; + top: -$focus-outline-offset-top; + left: -$focus-outline-offset-left; + box-shadow: 0 0 0 1px var-get($theme, 'focus-outline-color'); + width: calc(100% + (#{$focus-outline-offset-left} * 2)); + height: calc(100% + (#{$focus-outline-offset-top} * 2)); + } } } @if $variant == 'bootstrap' { %switch-composite { - border-radius: var-get($theme, 'border-radius-thumb'); - box-shadow: 0 0 0 2px var-get($theme, 'focus-outline-color'); + border-color: var-get($theme, 'focus-fill-color'); + box-shadow: 0 0 0 4px var-get($theme, 'focus-outline-color'); + } + + %switch-composite-thumb { + background: var-get($theme, 'focus-fill-color'); } } @@ -389,6 +432,16 @@ } %igx-switch--focused-checked { + @if $variant == 'bootstrap' { + %switch-composite { + border-color: var-get($theme, 'border-on-color'); + } + + %switch-composite-thumb--x { + background: var-get($theme, 'thumb-on-color'); + } + } + @if $variant == 'indigo-design' { %switch-composite { box-shadow: 0 0 0 3px var-get($theme, 'focus-outline-color-focused'); @@ -396,6 +449,24 @@ } } + %igx-switch--disabled-checked { + @if $variant != 'indigo-design' { + %switch-composite--x { + background: var-get($theme, 'track-on-disabled-color'); + } + + %switch-composite-thumb--x { + background: var-get($theme, 'thumb-on-disabled-color'); + } + } + + @if $variant == 'bootstrap' or $variant == 'fluent'{ + %switch-composite--x { + border-color: var-get($theme, 'track-on-disabled-color'); + } + } + } + %switch-ripple--focused { background: var-get($theme, 'track-off-color'); transition: background .2s $ease-out-quad; diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/_palettes.scss b/projects/igniteui-angular/src/lib/core/styles/themes/_palettes.scss index 42c4bdebb1b..4adef9aafa3 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_palettes.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_palettes.scss @@ -239,8 +239,8 @@ $dark-fluent-excel-palette: $fluent-excel-dark-palette; /// @access private $fluent-word-palette: palette( - $primary: #2b579a, - $secondary: #2b579a, + $primary: #0078d4, + $secondary: #0078d4, $success: #107c10, $warn: #797673, $error: #a80000, @@ -289,10 +289,10 @@ $dark-fluent-word-palette: $fluent-word-dark-palette; /// @access private $bootstrap-palette: palette( - $primary: #007bff, + $primary: #0d6efd, $secondary: #6c757d, - $info: #17a2b8, - $success: #28a745, + $info: #0dcaf0, + $success: #198754, $warn: #ffc107, $error: #dc3545, $surface: #f8f9fa, @@ -314,12 +314,12 @@ $bootstrap-dark-palette: palette( /// Light bootstrap palette /// @type Map -/// @prop {Color} primary [#007bff] - The 500 variant of the `primary` color (default). +/// @prop {Color} primary [#0d6efd] - The 500 variant of the `primary` color (default). /// @prop {Color} secondary [#6c757d] - The 500 variant of the `secondary` color (default). /// @prop {Color} grays [#000] - The base color for the `grays` shades. /// @prop {Color} surface [#f8f9fa] - The color used as a background in components, such as cards, sheets, and menus. -/// @prop {Color} info [#17a2b8] - The `info` color. Default for every palette if not specified. -/// @prop {Color} success [#28a745] - The `success` color. Default for every palette if not specified. +/// @prop {Color} info [#0dcaf0] - The `info` color. Default for every palette if not specified. +/// @prop {Color} success [#198754] - The `success` color. Default for every palette if not specified. /// @prop {Color} warn [#ffc107] - The `warn` color. Default for every palette if not specified. /// @prop {Color} error [#dc3456] - The `error`. Default for every palette if not specified. /// @access public @@ -328,12 +328,12 @@ $light-bootstrap-palette: $bootstrap-palette; /// Dark bootstrap palette /// @type Map -/// @prop {Color} primary [#007bff] - The 500 variant of the `primary` color (default). +/// @prop {Color} primary [#0d6efd] - The 500 variant of the `primary` color (default). /// @prop {Color} secondary [#6c757d] - The 500 variant of the `secondary` color (default). /// @prop {Color} grays [#fff] - The base color for the `grays` shades. /// @prop {Color} surface [#333] - The color used as a background in components, such as cards, sheets, and menus. -/// @prop {Color} info [#17a2b8] - The `info` color. Default for every palette if not specified. -/// @prop {Color} success [#28a745] - The `success` color. Default for every palette if not specified. +/// @prop {Color} info [#0dcaf0] - The `info` color. Default for every palette if not specified. +/// @prop {Color} success [#198754] - The `success` color. Default for every palette if not specified. /// @prop {Color} warn [#ffc107] - The `warn` color. Default for every palette if not specified. /// @prop {Color} error [#dc3456] - The `error`. Default for every palette if not specified. /// @access public diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_button.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_button.scss index 2e917f89da4..03d50574304 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_button.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_button.scss @@ -108,40 +108,45 @@ $dark-button: ( /// @requires {Map} $material-base-button /// @requires {function} extend $fluent-base-button-dark: extend( - $fluent-base-button, - $material-base-button-dark, + ( + disabled-background: ( + color: ('grays', 50) + ), + + disabled-foreground: ( + color: ('grays', 100) + ), + ) ); -/// @prop {Map} hover-foreground [color: ('primary', 200)] - The text color of a flat button on hover. -/// @prop {Map} focus-background [color: ('grays', 100, .3)] - The focus background color of a flat button. /// @requires {Map} $fluent-flat-button /// @requires {Map} $fluent-base-button-dark /// @requires {function} extend $fluent-flat-button-dark: extend( $fluent-flat-button, - $fluent-base-button-dark, - ( - hover-foreground: ( - color: ('primary', 200) - ), - - focus-background: ( - color:('grays', 100, .3) - ), - ) + $fluent-base-button-dark ); /// @requires {Map} $fluent-raised-button /// @requires {Map} $fluent-base-button-dark /// @requires {function} extend -$fluent-raised-button-dark: extend($fluent-raised-button, $fluent-base-button-dark); +$fluent-raised-button-dark: extend( + $fluent-raised-button, + $fluent-base-button-dark, + () +); /// @requires {Map} $fluent-fab-button /// @requires {Map} $fluent-base-button-dark /// @requires {function} extend -$fluent-fab-button-dark: extend($fluent-fab-button, $fluent-base-button-dark); +$fluent-fab-button-dark: extend( + $fluent-fab-button, + $fluent-base-button-dark, + () +); -/// @prop {Map} focus-background [color: ('grays', 100, .2)] - The focus background color of a icon button. +/// @prop {Map} hover-background [color: ('grays', 50)] - The hover background color of an icon button. +/// @prop {Map} active-background [color: ('grays', 100, .8)] - The active background color of a raised button. /// @requires {Map} $fluent-icon-button /// @requires {Map} $fluent-base-button-dark /// @requires {function} extend @@ -149,14 +154,19 @@ $fluent-icon-button-dark: extend( $fluent-icon-button, $fluent-base-button-dark, ( - focus-background: ( - color:('grays', 100, .2) + hover-background: ( + color: ('grays', 50) + ), + + active-background: ( + color: ('grays', 100, .8) ), ) ); -/// @prop {Map} hover-foreground [color: ('grays', 100, .3)] - The text color of a outlined button on hover. -/// @prop {Map} focus-background [color: ('grays', 100, .3)] - The focus background color of a outlined button. +/// @prop {Map} hover-foreground [color: ('grays', 50)] - The text color of a outlined button on hover. +/// @prop {Map} active-background [color: ('grays', 100, .8)] - The active background color of a raised button. +/// @prop {Map} active-foreground [contrast-color: ('grays' 100)] - The active text color of a raised button. /// @prop {Map} border-color [color: ('grays', 100)] - The border color of an outlined button. /// @requires {Map} $fluent-outlined-button /// @requires {Map} $fluent-base-button-dark @@ -165,12 +175,16 @@ $fluent-outlined-button-dark: extend( $fluent-outlined-button, $fluent-base-button-dark, ( - focus-background: ( - color:('grays', 100, .3) + hover-background: ( + color:('grays', 50) ), - hover-background: ( - color:('grays', 100, .3) + active-background: ( + color: ('grays', 100, .8) + ), + + active-foreground: ( + contrast-color: ('grays', 100) ), border-color: ( diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_radio.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_radio.scss index 2ae236f4695..b2cc9c19b8f 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_radio.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_radio.scss @@ -15,8 +15,19 @@ $dark-radio: $light-radio; /// Generates a dark fluent radio schema. /// @type {Map} +/// @property {Map} fill-hover-border-color [color: ('primary', 200)] - The text color used for the label text. +/// @property {Map} fill-color-hover [color: ('primary', 200)] - The checked border and dot colors on hover. +/// @requires {function} extend /// @requires $fluent-radio -$dark-fluent-radio: $fluent-radio; +$dark-fluent-radio: extend($fluent-radio, ( + fill-hover-border-color: ( + color: ('primary', 200) + ), + + fill-color-hover: ( + color: ('primary', 200) + ), +)); /// Generates a dark bootstrap radio schema. /// @type {Map} diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_switch.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_switch.scss index 6e96c50f2f4..744fbac8234 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_switch.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_switch.scss @@ -9,7 +9,7 @@ /// Generates a base dark switch schema. /// @type {Map} -/// @prop {Map} thumb-disabled-color [color: ('grays', 400)] - The color of the thumb when the switch is disabled. +/// @prop {Color} thumb-disabled-color [color: ('grays', 400)] - The color of the thumb when the switch is disabled. /// @see $default-palette $base-dark-switch: ( thumb-disabled-color: ( @@ -27,11 +27,14 @@ $dark-switch: extend($light-switch, $base-dark-switch); /// Generates a dark fluent switch schema based on a mix of $fluent-switch and $base-dark-switch. /// @type {Map} -/// @property {Map} border-color [color: ('grays', 500)] - The border color of the switch. -/// @property {Map} border-hover-color [color: ('grays', 800)] - The border color of the switch on hover. -/// @property {Map} border-disabled-color [color: ('grays', 200)] - The border color of the disabled switch. -/// @property {Map} track-disabled-color [color: ('grays', 200)] - The color of the track when the switch is disabled. -/// @property {Map} thumb-on-color [color: ('grays', 900)] - The color of the thumb when the switch is on. +/// @property {Color} border-color [color: ('grays', 500)] - The border color of the switch. +/// @property {Color} border-on-hover-color [color('primary', 200)] - The border color of the on-switch. +/// @property {Color} border-disabled-color [color: ('grays', 200)] - The border color of the disabled switch. +/// @property {Color} track-on-disabled-color [color: ('grays', 200)] - The color of the track when the switch is on and disabled. +/// @property {Color} track-on-hover-color [color: ('primary', 200)] - The color of the track when the switch is on and hovered. +/// @property {Color} thumb-off-color [color: ('grays', 500)] - The color of the thumb when the switch is off. +/// @property {Color} thumb-disabled-color [color: ('grays', 200)] - The color of the thumb when the switch is disabled. +/// /// @requires {function} extend /// @requires $fluent-switch /// @requires $base-dark-switch @@ -40,26 +43,35 @@ $dark-fluent-switch: extend($fluent-switch, $base-dark-switch, ( color: ('grays', 500) ), - border-hover-color:( - color: ('grays', 800) + thumb-off-color: ( + color: ('grays', 500) + ), + + border-on-hover-color:( + color: ('primary', 200) + ), + + track-on-hover-color:( + color: ('primary', 200) ), border-disabled-color:( color: ('grays', 200) ), - track-disabled-color: ( + thumb-disabled-color: ( color: ('grays', 200) ), - thumb-on-color: ( - color: ('grays', 900) + track-on-disabled-color:( + color: ('grays', 200) ), )); /// Generates a dark bootstrap switch schema based on a mix of $bootstrap-switch and $base-dark-switch. /// @type {Map} -/// @property {Map} thumb-on-color [color: ('grays', 900)] - The color of the thumb when the switch is on. +/// @property {Color} thumb-on-color [color: ('grays', 900)] - The color of the thumb when the switch is on. +/// @property {Color} thumb-on-disabled-color [color: ('grays', 900)] - The color of the thumb when the switch is on and disabled. /// @requires {function} extend /// @requires $bootstrap-switch /// @requires $base-dark-switch @@ -69,22 +81,26 @@ $dark-bootstrap-switch: extend( thumb-on-color: ( color: ('grays', 900) ), + + thumb-on-disabled-color: ( + color: ('grays', 900) + ), ) ); /// Generates a dark indigo switch schema. /// @type {Map} -/// @property {Map} thumb-on-color [color: 'surface'] - The color of the thumb when the switch is on. -/// @property {Map} track-on-color [color: ('grays', 900)] - The color of the track when the switch is on. -/// @property {Map} thumb-off-color [color: ('grays', 700)] - The color of the thumb when the switch is off. -/// @property {Map} track-off-color [transparent] - The color of the track when the switch is off. +/// @property {Color} thumb-on-color [color: 'surface'] - The color of the thumb when the switch is on. +/// @property {Color} track-on-color [color: ('grays', 900)] - The color of the track when the switch is on. +/// @property {Color} thumb-off-color [color: ('grays', 700)] - The color of the thumb when the switch is off. +/// @property {Color} track-off-color [transparent] - The color of the track when the switch is off. /// /// @property {Color} border-color [color: ('grays', 700)] - The border color of the switch. /// @property {Color} border-hover-color [color: ('grays', 700)] - The border color of the switch on hover. /// @property {Color} border-disabled-color [color: ('grays', 400)] - The border color of the disabled switch. /// @property {Color} border-on-color [color: ('grays', 900)] - The border color of the on-switch. /// @property {Color} border-on-hover-color [color: ('grays', 900)] - The border color of the on-switch. -/// @property {Map} label-disabled-color [color: ('grays', 400)] - The color of the switch label when the switch is disabled +/// @property {Color} label-disabled-color [color: ('grays', 400)] - The color of the switch label when the switch is disabled /// @requires {function} extend /// @requires $indigo-switch $dark-indigo-switch: extend( diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_button.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_button.scss index 24235a75f2f..9e192c1d367 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_button.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_button.scss @@ -12,8 +12,8 @@ /// @prop {Color} shadow-color [transparent] - The shadow color of the button. /// @prop {Color} border-color [transparent] - The outline color of the button. /// @prop {Color} disabled-border-color [transparent] - The disabled border color of the button. -/// @prop {Color} disabled-background [color: ('grays', 300)] - The disabled background color of the button. -/// @prop {Color} disabled-foreground [color: ('grays', 500)] - The disabled foreground color of the button. +/// @prop {Map} disabled-background [color: ('grays', 300)] - The disabled background color of the button. +/// @prop {Map} disabled-foreground [color: ('grays', 500)] - The disabled foreground color of the button. $material-base-button: ( variant: 'material', @@ -39,6 +39,8 @@ $material-base-button: ( /// @prop {Map} hover-foreground [color: ('secondary', 500)] - The hover text color of a flat button. /// @prop {Map} focus-background [color: ('secondary', 400, .12)] - The focus background color of a flat button. /// @prop {Map} focus-foreground [color: ('secondary', 500)] - The focus text color of a flat button. +/// @prop {Map} active-background [color: ('secondary', 400, .12)] - The active background color of a flat button. +/// @prop {Map} active-foreground [color: ('secondary', 500)] - The active text color of a flat button. /// @prop {Number} flat-border-radius [.2] - The border radius used for flat button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {function} extend /// @requires {Map} $material-base-button @@ -72,6 +74,14 @@ $material-flat-button: extend( focus-foreground: ( color: ('secondary', 500) ), + + active-background: ( + color: ('secondary', 400, .12), + ), + + active-foreground: ( + color: ('secondary', 500) + ), ) ); @@ -102,6 +112,8 @@ $material-outlined-button: extend( /// @prop {Map} hover-foreground [contrast-color: ('secondary', 300)] - The hover text color of a raised button. /// @prop {Map} focus-background [color: ('secondary', 300)] - The focus background color of a raised button. /// @prop {Map} focus-foreground [contrast-color: ('secondary', 300)] - The focus text color of a raised button. +/// @prop {Map} active-background [color: ('secondary', 300)] - The active background color of a raised button. +/// @prop {Map} active-foreground [contrast-color: ('secondary', 300)] - The active text color of a raised button. /// @prop {Number} resting-elevation [2] - The elevation level, between 0-24, to be used for the resting state. /// @prop {Number} hover-elevation [4] - The elevation level, between 0-24, to be used for the hover state. /// @prop {Number} focus-elevation [8] - The elevation level, between 0-24, to be used for the focus state. @@ -140,6 +152,14 @@ $material-raised-button: extend( focus-foreground: ( contrast-color: ('secondary', 300) ), + + active-background: ( + color: ('secondary', 300) + ), + + active-foreground: ( + contrast-color: ('secondary', 300) + ), ) ); @@ -168,6 +188,8 @@ $material-fab-button: extend( /// @prop {Map} hover-foreground [color: ('grays', 800)] - The hover icon color of an icon button. /// @prop {Map} focus-background [color: ('grays', 400] - The focus background color an icon button. /// @prop {Map} focus-foreground [color: ('grays', 800)] - The focus icon color of an icon button. +/// @prop {Map} active-background [color: ('grays', 400] - The active background color an icon button. +/// @prop {Map} active-foreground [color: ('grays', 800)] - The active icon color of an icon button. /// @prop {Number} resting-elevation [0] - The elevation level, between 0-24, to be used for the resting state. /// @prop {Number} hover-elevation [0] - The elevation level, between 0-24, to be used for the hover state. /// @prop {Number} focus-elevation [6] - The elevation level, between 0-24, to be used for the focus state. @@ -204,6 +226,14 @@ $material-icon-button: extend( focus-foreground: ( color: ('grays', 800) ), + + active-background: ( + color: ('grays', 400) + ), + + active-foreground: ( + color: ('grays', 800) + ), ) ); @@ -226,21 +256,31 @@ $light-button: ( /// @type {Map} /// @requires {Map} $material-base-button /// @requires {Map} $flat-elevation-button +/// @prop {Map} disabled-background [color: ('grays', 200)] - The disabled background color of the button. +/// @prop {Map} disabled-foreground [color: ('grays', 400)] - The disabled foreground color of the button. /// @requires {function} extend $fluent-base-button: extend( $material-base-button, $flat-elevation-button, ( variant: 'fluent', + + disabled-background: ( + color: ('grays', 200) + ), + + disabled-foreground: ( + color: ('grays', 400) + ), ) ); /// @type {Map} +/// @prop {Map} border-color [color: ('grays', 800)] - The border color of an outlined button. /// @prop {Map} foreground [color: ('grays', 900)] - The idle text color of a flat button. /// @prop {Color} hover-background [transparent] - The hover background color of a flat button. /// @prop {Map} hover-foreground [color: ('primary', 500)] - The hover text color of a flat button. -/// @prop {Map} focus-background [color:('grays', 100)] - The focus background color of a flat button. -/// @prop {Map} focus-foreground [color: ('primary', 500)] - The focus text color of a flat button. +/// @prop {Map} active-foreground [color: ('grays', 900)] - The active text color of a flat button. /// @prop {Number} border-radius [0] - The border radius used for flat button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-flat-button /// @requires {Map} $fluent-base-button @@ -251,6 +291,10 @@ $fluent-flat-button: extend( $fluent-base-button, $square-shape-button, ( + border-color: ( + color: ('grays', 800) + ), + foreground: ( color: ('grays', 900) ), @@ -261,12 +305,8 @@ $fluent-flat-button: extend( color: ('primary', 500) ), - focus-background: ( - color:('grays', 100) - ), - - focus-foreground: ( - color: ('primary', 500) + active-foreground: ( + color: ('grays', 900) ), ) ); @@ -278,6 +318,10 @@ $fluent-flat-button: extend( /// @prop {Map} hover-foreground [color: ('grays', 900)] - The hover text color of an outlined button. /// @prop {Map} focus-background [color: ('grays', 100)] - The focus background color of an outlined button. /// @prop {Map} focus-foreground [color: ('grays', 900)] - The focus text color of an outlined button. +/// @prop {Map} active-background [color: ('grays', 100)] - The active background color of an outlined button. +/// @prop {Map} active-foreground [color: ('grays', 900)] - The active text color of an outlined button. +/// @prop {Map} disabled-background [color: ('grays', 200)] - The disabled background color of the button. +/// @prop {Map} disabled-foreground [color: ('grays', 400, .5)] - The disabled foreground color of the button. /// @prop {Number} border-radius [.1] - The border radius used for outlined button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-outlined-button /// @requires {Map} $fluent-base-button @@ -297,7 +341,7 @@ $fluent-outlined-button: extend( ), hover-background: ( - color: ('grays', 100) + color: ('grays', 200) ), hover-foreground: ( @@ -311,16 +355,35 @@ $fluent-outlined-button: extend( focus-foreground: ( color: ('grays', 900) ), + + active-background: ( + color: ('grays', 300) + ), + + active-foreground: ( + contrast-color: ('grays', 300) + ), + + disabled-background: ( + color: ('grays', 200) + ), + + disabled-foreground: ( + color: ('grays', 400, .5) + ), ) ); /// @type {Map} +/// @prop {Map} border-color [contrast: ('grays', 900)] - The border color of an outlined button. /// @prop {Map} background [color: ('primary', 500)] - The background color of an raised button. -/// @prop {Color} foreground [#fff] - The idle text color of a raised button. +/// @prop {Map} foreground [contrast-color: ('primary', 900] - The idle text color of a raised button. /// @prop {Map} hover-background [color: ('primary', 600)] - The hover background of a raised button. -/// @prop {Color} hover-foreground [#fff] - The hover text color of a raised button. +/// @prop {Map} hover-foreground [contrast-color: ('primary', 900)] - The hover text color of a raised button. /// @prop {Map} focus-background [color: ('primary', 600)] - The focus background color of a raised button. -/// @prop {Color} focus-foreground [#fff] - The focus text color of a raised button. +/// @prop {Map} focus-foreground [contrast-color: ('primary', 900)] - The focus text color of a raised button. +/// @prop {Map} active-background [color: ('primary', 800)] - The active background color of a raised button. +/// @prop {Map} active-foreground [contrast-color: ('primary', 900)] - The active text color of a raised button. /// @prop {Number} resting-elevation [0] - The elevation level, between 0-24, to be used for the resting state. /// @prop {Number} hover-elevation [0] - The elevation level, between 0-24, to be used for the hover state. /// @prop {Number} focus-elevation [0] - The elevation level, between 0-24, to be used for the focus state. @@ -334,12 +397,16 @@ $fluent-raised-button: extend( $fluent-base-button, $fluent-shape-button, ( + border-color: ( + contrast-color: ('grays', 900) + ), + background: ( color: ('primary', 500) ), foreground: ( - contrast-color: ('primary', 500) + contrast-color: ('primary', 900) ), hover-background: ( @@ -347,7 +414,7 @@ $fluent-raised-button: extend( ), hover-foreground: ( - contrast-color: ('primary', 600) + contrast-color: ('primary', 900) ), focus-background: ( @@ -355,12 +422,21 @@ $fluent-raised-button: extend( ), focus-foreground: ( - contrast-color: ('primary', 600) + contrast-color: ('primary', 900) + ), + + active-background: ( + color: ('primary', 800) + ), + + active-foreground: ( + contrast-color: ('primary', 900) ), ) ); /// @type {Map} +/// @prop {Map} border-color [contrast: ('grays', 900)] - The border color of an outlined button. /// @prop {Number} resting-elevation [0] - The elevation level, between 0-24, to be used for the resting state. /// @prop {Number} hover-elevation [0] - The elevation level, between 0-24, to be used for the hover state. /// @prop {Number} focus-elevation [0] - The elevation level, between 0-24, to be used for the focus state. @@ -375,12 +451,20 @@ $fluent-fab-button: extend( $fluent-raised-button, ( selector: '[igxButton="fab"], .igx-button--fab', + + border-color: ( + contrast-color: ('grays', 900) + ), ) ); /// @type {Map} -/// @prop {Map} hover-background [color: ('grays', 50)] - The hover background color of an icon button. -/// @prop {Map} focus-background [color: ('grays', 100)] - The focus icon color of an icon button. +/// @prop {Map} hover-background [color: ('grays', 200)] - The hover background color of an icon button. +/// @prop {Map} hover-foreground [contrast-color: ('primary')] - The hover text color of a raised button. +/// @prop {Color} focus-background [transparent] - The focus icon color of an icon button. +/// @prop {Map} focus-foreground [contrast-color: ('primary')] - The focus text color of a raised button. +/// @prop {Map} active-background [color: ('grays', 300)] - The active background color of a raised button. +/// @prop {Map} active-foreground [contrast-color: ('primary')] - The active text color of a raised button. /// @prop {Number} border-radius [0] - The border radius used for icon button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-icon-button /// @requires {Map} $fluent-base-button @@ -391,13 +475,47 @@ $fluent-icon-button: extend( $fluent-base-button, $square-shape-button, ( + foreground: ( + color: ('primary') + ), + + border-color: ( + color: ('grays', 500) + ), + hover-background: ( - color: ('grays', 50) + color: ('grays', 200) ), - focus-background: ( + hover-foreground: ( + color: ('primary') + ), + + focus-foreground: ( + color: ('primary') + ), + + focus-background: transparent, + + active-background: ( + color: ('grays', 300) + ), + + active-foreground: ( + color: ('primary') + ), + + disabled-background: ( color: ('grays', 100) ), + + disabled-foreground: ( + color: ('grays', 400) + ), + + disabled-border-color: ( + color: ('grays', 300) + ), ) ); @@ -447,8 +565,6 @@ $bootstrap-base-button: extend( /// @prop {Color} hover-background [transparent] - The hover background color of a flat button. /// @prop {Map} focus-background [color: ('grays', 100)] - The focus background color of a flat button. /// @prop {Number} border-radius [.2] - The border radius used for flat button. Can be a fraction between 0 and 1, pixels, or percent. -/// @prop {Map} disabled-foreground [contrast-color: ('primary', 500, .65)] - The disabled foreground color of the button. - /// @requires {Map} $material-flat-button /// @requires {Map} $bootstrap-base-button /// @requires {function} extend @@ -473,10 +589,6 @@ $bootstrap-flat-button: extend( focus-background: ( color: ('grays', 200) ), - - disabled-foreground: ( - color: ('primary', 500, .65) - ), ) ); @@ -487,8 +599,7 @@ $bootstrap-flat-button: extend( /// @prop {Map} hover-background [color: ('primary', 500)] - The hover background color of an outlined button. /// @prop {Map} focus-background [color: ('primary', 500)] - The focus background color of an outlined button. /// @prop {Map} border-color [color: ('primary', 500)] - The border color of an outlined button. -/// @prop {Map} disabled-border-color [color: ('primary', 500, .65)] - The disabled focused border color of the button. -/// @prop {Map} disabled-foreground [contrast-color: ('primary', 500, .65)] - The disabled foreground color of the button. +/// @prop {Map} disabled-border-color [color: ('primary', 500), lighten: 35%] - The disabled focused border color of the button. /// @prop {Map} shadow-color [color: ('primary', 200)] - The shadow color of the button. /// @prop {Number} border-radius [.2] - The border radius used for outlined button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-outlined-button @@ -522,12 +633,8 @@ $bootstrap-outlined-button: extend( color: ('primary', 500) ), - disabled-foreground: ( - color: ('primary', 500, .65) - ), - disabled-border-color: ( - color: ('primary', 500, .65) + color: ('primary', 50) ), shadow-color: ( @@ -544,8 +651,6 @@ $bootstrap-outlined-button: extend( /// @prop {Map} hover-background [color: ('primary', 600)] - The hover background color of an raised button. /// @prop {Map} focus-background [color: ('primary', 600)] - The focus background color of an raised button. /// @prop {Map} shadow-color [color: ('primary', 200)] - The shadow color of the button. -/// @prop {Map} disabled-foreground [contrast-color: ('primary', 900)] - The disabled foreground color of the button. -/// @prop {Map} disabled-background [color: ('primary', 500, .65)] - The disabled background color of the button. /// @prop {Number} border-radius [.2] - The border radius used for outlined button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-raised-button /// @requires {Map} $bootstrap-base-button @@ -581,14 +686,6 @@ $bootstrap-raised-button: extend( shadow-color: ( color: ('primary', 200) ), - - disabled-foreground: ( - contrast-color: ('primary', 900) - ), - - disabled-background: ( - color: ('primary', 500, .65) - ), ) ); @@ -599,8 +696,6 @@ $bootstrap-raised-button: extend( /// @prop {Map} background [color: ('primary', 500)] - The background color of a raised button. /// @prop {Map} hover-background [color: ('primary', 600)] - The hover background color of an raised button. /// @prop {Map} focus-background [color: ('primary', 600)] - The focus background color of an raised button. -/// @prop {Map} disabled-foreground [contrast-color: ('primary', 900)] - The disabled foreground color of the button. -/// @prop {Map} disabled-background [color: ('primary', 500, .65)] - The disabled background color of the button. /// @requires {Map} $material-fab-button /// @requires {Map} $bootstrap-base-button /// @requires {Map} $bootstrap-raised-button @@ -630,14 +725,6 @@ $bootstrap-fab-button: extend( focus-background: ( color: ('primary', 600) ), - - disabled-foreground: ( - contrast-color: ('primary', 900) - ), - - disabled-background: ( - color: ('primary', 500, .65) - ), ) ); @@ -647,7 +734,6 @@ $bootstrap-fab-button: extend( /// @prop {Map} focus-background [color: ('primary', 600)] - The focus background color of an icon button. /// @prop {Map} focus-foreground [contrast-color: ('primary', 600)] - The focus text color of an icon button. /// @prop {Map} shadow-color [color: ('primary', 200)] - The shadow color of the button. -/// @prop {Map} disabled-foreground [contrast-color: ('grays', 800, .65)] - The disabled foreground color of the button. /// @prop {Number} border-radius [.2] - The border radius used for outlined button. Can be a fraction between 0 and 1, pixels, or percent. /// @requires {Map} $material-icon-button /// @requires {Map} $bootstrap-base-button @@ -677,10 +763,6 @@ $bootstrap-icon-button: extend( shadow-color: ( color: ('primary', 200) ), - - disabled-foreground: ( - contrast-color: ('primary', 800, .65) - ), ) ); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_checkbox.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_checkbox.scss index f543445aca0..3dacf0e46e2 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_checkbox.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_checkbox.scss @@ -10,11 +10,12 @@ /// Generates a light checkbox schema. /// @type {Map} /// @prop {Color} tick-color [color: ('grays', 50)] - The checked mark color. -/// @prop {Map} label-color [color: ('grays', 900)]- The text color used for the label text. -/// @prop {Map} empty-color [color: ('grays', 600)] - The unchecked border color. -/// @prop {Map} fill-color [color: ('secondary', 500)] - The checked border and fill colors. -/// @prop {Map} disabled-color [color: ('grays', 400)] - The disabled border and fill colors. -/// @prop {Map} disabled-color-label [color: ('grays', 400)] - The disabled color of the label. +/// @prop {Color} label-color [color: ('grays', 900)]- The text color used for the label text. +/// @prop {Color} empty-color [color: ('grays', 600)] - The unchecked border color. +/// @prop {Color} fill-color [color: ('secondary', 500)] - The checked border and fill colors. +/// @prop {Color} disabled-color [color: ('grays', 400)] - The disabled border and fill colors. +/// @prop {Color} disabled-indeterminate-color [color: ('secondary', 200)] - The disabled border and fill colors in indeterminate state. +/// @prop {Color} disabled-color-label [color: ('grays', 400)] - The disabled color of the label. /// @prop {Number} border-radius [.2] - The border radius used for checkbox. Can be a fraction between 0 and 1, pixels, or percent. /// @prop {Number} border-radius-ripple [1] - The border radius used for checkbox ripple. Can be a fraction between 0 and 1, pixels, or percent. /// @@ -46,6 +47,10 @@ $light-checkbox: extend( color: ('grays', 400) ), + disabled-indeterminate-color: ( + color: ('secondary', 100) + ), + disabled-color-label: ( color: ('grays', 400) ) @@ -56,7 +61,7 @@ $light-checkbox: extend( /// @type {Map} /// @prop {Map} empty-color [color: ('grays', 900)] - The unchecked border color. /// @prop {Map} fill-color [color: ('primary', 500)] - The checked border and fill colors. -/// @prop {Map} disabled-color [color: ('grays', 300)] - The disabled border and fill colors. +/// @prop {Map} disabled-color [color: ('grays', 400)] - The disabled border and fill colors. /// @prop {Number} border-radius [2px] - The border radius used for checkbox. Can be a fraction between 0 and 1, pixels, or percent. /// @prop {Number} border-radius-ripple [2px] - The border radius used for checkbox ripple. Can be a fraction between 0 and 1, pixels, or percent. /// @property {Map} focus-outline-color [color: ('grays', 800)] - The focus outlined color. @@ -83,7 +88,7 @@ $fluent-checkbox: extend( ), disabled-color: ( - color: ('grays', 300) + color: ('grays', 400) ), ) ); @@ -92,7 +97,7 @@ $fluent-checkbox: extend( /// @type {Map} /// @prop {Map} fill-color [color: ('primary', 500)] - The checked border and fill colors. /// @prop {Map} empty-color [color: ('grays', 500)] - The unchecked border color. -/// @prop {Map} disabled-color [color: ('grays', 300)] - The disabled border and fill colors. +/// @prop {Map} disabled-color [color: ('primary', 200)] - The disabled border and fill colors. /// @prop {Number} border-radius [4px] - The border radius used for checkbox. Can be a fraction between 0 and 1, pixels, or percent. /// @prop {Number} border-radius-ripple [4px] - The border radius used for checkbox ripple. Can be a fraction between 0 and 1, pixels, or percent. /// @property {Map} focus-outline-color [color: ('primary', 200)] - The focus outlined color. @@ -119,7 +124,7 @@ $bootstrap-checkbox: extend( ), disabled-color: ( - color: ('grays', 300) + color: ('primary', 200) ), ) ); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_radio.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_radio.scss index f63cb1b4f6e..96886e893f9 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_radio.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_radio.scss @@ -56,12 +56,12 @@ $light-radio: ( /// Generates a fluent radio schema. /// @type {Map} /// -/// @property {Map} hover-color [color: ('grays', 800)] - The text color used for the label text. -/// @property {Map} fill-hover-border-color [color: ('secondary', 600)] - The checked dot border color on hover. -/// @property {Map} empty-color [color: ('grays', 800)] - The unchecked border color. -/// @property {Map} fill-color [color: ('secondary', 300)] - The checked border and dot colors. -/// @property {Map} fill-color-hover [color: ('secondary',3500)] - The checked border and dot colors on hover. -/// @property {Map} focus-outline-color [color: ('grays', 800)] - The focus outlined color. +/// @property {Color} hover-color [color: ('grays', 700)] - The border and dot colors on hover. +/// @property {Color} fill-hover-border-color [color: ('primary', 900)] - The checked dot border color on hover. +/// @property {Color} empty-color [color: ('grays', 900)] - The unchecked border color. +/// @property {Color} fill-color [color: ('primary', 500)] - The checked border and dot colors. +/// @property {Color} fill-color-hover [color: ('primary',900)] - The checked border and dot colors on hover. +/// @property {Color} focus-outline-color [color: ('grays', 700)] - The focus outlined color. /// /// @requires {function} extend /// @requires {Map} $light-radio @@ -71,27 +71,27 @@ $fluent-radio: extend( variant: 'fluent', focus-outline-color: ( - color: ('grays', 800) + color: ('grays', 700) ), hover-color: ( - color: ('grays', 800) + color: ('grays', 700) ), fill-hover-border-color: ( - color: ('secondary', 600) + color: ('primary', 900) ), empty-color: ( - color: ('grays', 800) + color: ('grays', 900) ), fill-color: ( - color: ('secondary', 300) + color: ('primary', 500) ), fill-color-hover: ( - color: ('secondary', 300) + color: ('primary', 900) ), ) ); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_switch.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_switch.scss index 91c962e9942..228ab6f99a0 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_switch.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_switch.scss @@ -11,14 +11,16 @@ /// Generates a light switch schema. /// @type {Map} /// -/// @property {Map} thumb-on-color [color: ('secondary', 500)] - The color of the thumb when the switch is on. -/// @property {Map} track-on-color [color: ('secondary', 200)] - The color of the track when the switch is on. -/// @property {Map} thumb-off-color [color: ('grays', 200)] - The color of the thumb when the switch is off. -/// @property {Map} track-off-color [color: ('grays', 600)] - The color of the track when the switch is off. -/// @property {Map} thumb-disabled-color [color: ('grays', 400)] - The color of the thumb when the switch is disabled. -/// @property {Map} track-disabled-color [color: ('grays', 300)] - The color of the track when the switch is disabled. -/// @property {Map} label-color [color: ('grays', 900)] - The color of the switch label -/// @property {Map} label-disabled-color [color: ('grays', 400)] - The color of the switch label when the switch is disabled +/// @property {Color} thumb-on-color [color: ('secondary', 500)] - The color of the thumb when the switch is on. +/// @property {Color} track-on-color [color: ('secondary', 200)] - The color of the track when the switch is on. +/// @property {Color} thumb-off-color [color: ('grays', 50)] - The color of the thumb when the switch is off. +/// @property {Color} track-off-color [color: ('grays', 500)] - The color of the track when the switch is off. +/// @property {Color} thumb-disabled-color [color: ('grays', 200)] - The color of the thumb when the switch is disabled. +/// @property {Color} thumb-on-disabled-color [color: ('secondary', 100)] - The color of the thumb when the switch is on and disabled. +/// @property {Color} track-disabled-color [color: ('grays', 300)] - The color of the track when the switch is disabled. +/// @property {Color} track-on-disabled-color [color: ('secondary', 50)] - The color of the track when the switch is on and disabled. +/// @property {Color} label-color [color: ('grays', 900)] - The color of the switch label +/// @property {Color} label-disabled-color [color: ('grays', 400)] - The color of the switch label when the switch is disabled /// @property {Number} resting-elevation [2] - The elevation level, between 0-24, to be used for the resting state. /// @property {Number} hover-elevation [3] - The elevation level, between 0-24, to be used for the hover state. /// @property {Number} disabled-elevation [1] - The elevation level, between 0-24, to be used for the disabled state. @@ -58,19 +60,27 @@ $light-switch: extend( ), thumb-off-color: ( - color: ('grays', 200) + color: ('grays', 50) ), track-disabled-color: ( - color: ('grays', 300, .54) + color: ('grays', 300) + ), + + track-on-disabled-color: ( + color: ('secondary', 50) ), track-off-color: ( - color: ('grays', 600) + color: ('grays', 500) ), thumb-disabled-color: ( - color: ('grays', 400), + color: ('grays', 200), + ), + + thumb-on-disabled-color: ( + color: ('secondary', 100), ), label-color: ( @@ -86,17 +96,22 @@ $light-switch: extend( /// Generates a fluent switch schema. /// @type {Map} /// -/// @property {Map} border-color [color('grays', 500)] - The border color of the switch. -/// @property {Map} border-hover-color [color('grays', 800)] - The border color of the switch on hover. -/// @property {Map} border-disabled-color [color('grays', 300)] - The border color of the disabled switch. -/// @property {Map} border-on-color [color('primary', 500)] - The border color of the on-switch. -/// @property {Map} border-on-hover-color [color('primary', 500)] - The border color of the on-switch. +/// @property {Color} border-color [color('grays', 700)] - The border color of the switch. +/// @property {Color} border-hover-color [color('grays', 800)] - The border color of the switch on hover. +/// @property {Color} border-disabled-color [color('grays', 400)] - The border color of the disabled switch. +/// @property {Color} border-on-color [color('primary', 500)] - The border color of the on-switch. +/// @property {Color} border-on-hover-color [color('primary', 900)] - The border color of the on-switch. /// /// @property {Color} thumb-on-color [color: ('grays', 50)] - The color of the thumb when the switch is on. -/// @property {Map} thumb-off-color [color: ('grays', 900)] - The color of the thumb when the switch is off. -/// @property {Map} track-on-color [color: ('secondary', 500)] - The color of the track when the switch is on. +/// @property {Color} thumb-off-color [color: ('grays', 700)] - The color of the thumb when the switch is off. +/// @property {Color} track-on-color [color: ('primary', 500)] - The color of the track when the switch is on. /// @property {Color} track-off-color [transparent] - The color of the track when the switch is off. -/// @property {Map} focus-outline-color [color: ('grays', 800)] - The focus outlined color. +/// @property {Color} track-on-hover-color [color: ('primary', 900)] - The color of the track when the switch is on and hovered. +/// @property {Color} track-on-disabled-color [color: ('grays', 400)] - The color of the track when the switch is on and disabled. +/// @property {Color} track-disabled-color [transparent] - The color of the track when the switch is disabled. +/// @property {Color} thumb-disabled-color [color: ('grays', 400)] - The color of the thumb when the switch is disabled. +/// @property {Color} thumb-on-disabled-color [color: ('grays', 100)] - The color of the thumb when the switch is on and disabled. +/// @property {Color} focus-outline-color [color: ('grays', 700)] - The focus outlined color. /// /// @property {Number} border-radius-track [10px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. /// @@ -110,52 +125,77 @@ $fluent-switch: extend( variant: 'fluent', focus-outline-color: ( - color: ('grays', 800) + color: ('grays', 700) ), border-color: ( - color: ('grays', 500), + color: ('grays', 700), + ), + + thumb-off-color: ( + color: ('grays', 700), ), + track-off-color: transparent, + border-hover-color:( color: ('grays', 800), ), - border-disabled-color:( - color: ('grays', 300), + track-on-color: ( + color: ('primary', 500), ), border-on-color:( color: ('primary', 500) ), + track-on-hover-color:( + color: ('primary', 900) + ), + border-on-hover-color:( - color: ('primary', 500) + color: ('primary', 900) ), thumb-on-color: ( color: ('grays', 50) ), - thumb-off-color: ( - color: ('grays', 900), + track-on-disabled-color:( + color: ('grays', 400) ), - track-on-color: ( - color: ('secondary', 500), + thumb-disabled-color:( + color: ('grays', 400) ), - track-off-color: transparent, + thumb-on-disabled-color:( + color: ('grays', 100) + ), + + border-disabled-color:( + color: ('grays', 400), + ), + + track-disabled-color: transparent, ) ); /// Generates a bootstrap switch schema. /// @type {Map} -/// @property {Map} track-on-color [color: ('primary', 500)] - The color of the track when the switch is on. -/// @property {Map} border-disabled-color [color: ('grays', 300)] - The border color of the disabled switch. -/// @property {Number} border-radius-track [4px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. -/// @property {Number} border-radius-thumb [2px] - The border radius used for switch thumb. Can be a fraction between 0 and 1, pixels, or percent. -/// @property {Map} focus-outline-color [color: ('primary', 200)] - The focus outlined color. +/// @property {Color} focus-outline-color [color: ('primary', 100)] - The focus outlined color. +/// @property {Color} focus-fill-color [color: ('primary', 200)] - The focus fill color. +/// @property {Color} border-color [color('grays', 400)] - The border color of the switch. +/// @property {Color} border-disabled-color [color: ('grays', 300)] - The border color of the disabled switch. +/// @property {Color} thumb-off-color [color: ('grays', 400)] - The color of the thumb when the switch is off. +/// @property {Color} thumb-disabled-color [color: ('grays', 300)] - The color of the thumb when the switch is disabled. +/// @property {Color} track-on-color [color: ('primary', 500)] - The color of the track when the switch is on. +/// @property {Color} track-on-disabled-color [color: ('primary', 200)] - The color of the track when the switch is on and disabled. +/// @property {Color} thumb-on-disabled-color [color: ('grays', 50)] - The color of the thumb when the switch is on and disabled. +/// @property {Color} track-disabled-color [transparent] - The color of the track when the switch is disabled. +/// @property {Number} border-radius-track [32px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. +/// @property {Number} border-radius-thumb [32px] - The border radius used for switch thumb. Can be a fraction between 0 and 1, pixels, or percent. /// /// @requires {function} extend /// @requires {Map} $fluent-switch @@ -167,39 +207,63 @@ $bootstrap-switch: extend( variant: 'bootstrap', focus-outline-color: ( - color: ('primary', 200) + color: ('primary', 100), ), - track-on-color: ( - color: ('primary', 500), + focus-fill-color: ( + color: ('primary', 200), ), - border-disabled-color:( + border-color: ( + color: ('grays', 400), + ), + + border-disabled-color: ( color: ('grays', 300), ), + + thumb-off-color: ( + color: ('grays', 400), + ), + + thumb-disabled-color: ( + color: ('grays', 300), + ), + + thumb-on-disabled-color: ( + color: ('grays', 50) + ), + + track-on-color: ( + color: ('primary', 500), + ), + + track-on-disabled-color:( + color: ('primary', 200) + ) ) ); /// Generates an indigo switch schema. /// @type {Map} /// -/// @property {Map} thumb-on-color [color: ('grays', 50)] - The color of the thumb when the switch is on. -/// @property {Map} track-on-color [color: ('primary', 500)] - The color of the track when the switch is on. -/// @property {Map} thumb-off-color [color: ('grays', 600)] - The color of the thumb when the switch is off. -/// @property {Map} track-off-color [transparent] - The color of the track when the switch is off. -/// @property {Map} thumb-disabled-color [color: ('grays', 300)] - The color of the thumb when the switch is disabled. -/// @property {Map} track-disabled-color [transparent] - The color of the track when the switch is disabled. +/// @property {Color} thumb-on-color [color: ('grays', 50)] - The color of the thumb when the switch is on. +/// @property {Color} track-on-color [color: ('primary', 500)] - The color of the track when the switch is on. +/// @property {Color} thumb-off-color [color: ('grays', 600)] - The color of the thumb when the switch is off. +/// @property {Color} track-off-color [transparent] - The color of the track when the switch is off. +/// @property {Color} thumb-disabled-color [color: ('grays', 300)] - The color of the thumb when the switch is disabled. +/// @property {Color} track-disabled-color [transparent] - The color of the track when the switch is disabled. /// /// @property {Color} border-color [color: ('grays', 600)] - The border color of the switch. /// @property {Color} border-hover-color [color: ('grays', 600)] - The border color of the switch on hover. /// @property {Color} border-disabled-color [color: ('grays', 300)] - The border color of the disabled switch. /// @property {Color} border-on-color [color: ('primary', 500)] - The border color of the on-switch. /// @property {Color} border-on-hover-color [color: ('primary', 500)] - The border color of the on-switch. -/// @property {Map} label-disabled-color [color: ('grays', 300)] - The color of the switch label when the switch is disabled +/// @property {Color} label-disabled-color [color: ('grays', 300)] - The color of the switch label when the switch is disabled /// @property {Number} border-radius-track [8px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. /// @property {Number} border-radius-thumb [1] - The border radius used for switch thumb. Can be a fraction between 0 and 1, pixels, or percent. -/// @property {Map} focus-outline-color [color: ('grays', 300)] - The focus outlined color. -/// @property {Map} focus-outline-color-focused [color: ('primary', 200)] - The focus outlined color for focused state. +/// @property {Color} focus-outline-color [color: ('grays', 300)] - The focus outlined color. +/// @property {Color} focus-outline-color-focused [color: ('primary', 200)] - The focus outlined color for focused state. /// /// @requires {function} extend /// @requires $light-switch diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_switch.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_switch.scss index cb49f5b78cd..5b3e614952d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_switch.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_switch.scss @@ -41,11 +41,11 @@ $fluent-shape-switch: ( ); /// @type Map -/// @property {Number} border-radius-track [4px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. -/// @property {Number} border-radius-thumb [2px] - The border radius used for switch thumb. Can be a fraction between 0 and 1, pixels, or percent. +/// @property {Number} border-radius-track [32px] - The border radius used for switch track. Can be a fraction between 0 and 1, pixels, or percent. +/// @property {Number} border-radius-thumb [32px] - The border radius used for switch thumb. Can be a fraction between 0 and 1, pixels, or percent. $bootstrap-shape-switch: ( - border-radius-track: 4px, - border-radius-thumb: 2px, + border-radius-track: 32px, + border-radius-thumb: 32px, ); /// @type Map diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/scale-presets/_fluent.scss b/projects/igniteui-angular/src/lib/core/styles/typography/scale-presets/_fluent.scss index faaa4007628..ea43b8db6de 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/scale-presets/_fluent.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/scale-presets/_fluent.scss @@ -93,9 +93,9 @@ $fluent_subtitle-2: type-style( $fluent_button: type-style( $font-size: rem(14px), - $font-weight: 600, + $font-weight: 400, $line-height: rem(14px), - $text-transform: none, + $text-transform: capitalize, $margin-top: 0, $margin-bottom: 0 ); diff --git a/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts b/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts index a0f284d395d..99b1b39a437 100644 --- a/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts @@ -1,9 +1,10 @@ -import { PivotGridType } from '../grids/common/grid.interface'; +import { ColumnType, PivotGridType } from '../grids/common/grid.interface'; import { DEFAULT_PIVOT_KEYS, IPivotDimension, IPivotDimensionStrategy, IPivotGridRecord, IPivotKeys, IPivotValue, PivotDimensionType } from '../grids/pivot-grid/pivot-grid.interface'; import { PivotUtil } from '../grids/pivot-grid/pivot-util'; -import { FilteringStrategy } from './filtering-strategy'; +import { FilteringStrategy, IgxFilterItem } from './filtering-strategy'; import { cloneArray } from '../core/utils'; +import { IFilteringExpressionsTree } from './filtering-expressions-tree'; export class NoopPivotDimensionsStrategy implements IPivotDimensionStrategy { private static _instance: NoopPivotDimensionsStrategy = null; @@ -100,7 +101,6 @@ export class PivotColumnDimensionsStrategy implements IPivotDimensionStrategy { }) } }); - } this.applyAggregates(rec, columns, values, pivotKeys); } @@ -122,10 +122,6 @@ export class PivotColumnDimensionsStrategy implements IPivotDimensionStrategy { } return leafs; } - - private isLeaf(record, pivotKeys) { - return !(record[pivotKeys.records] && record[pivotKeys.records].some(r => r[pivotKeys.records])); - } } export class DimensionValuesFilteringStrategy extends FilteringStrategy { @@ -141,10 +137,53 @@ export class DimensionValuesFilteringStrategy extends FilteringStrategy { protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: PivotGridType): any { - const config = grid.pivotConfiguration; const allDimensions = grid.allDimensions; const enabledDimensions = allDimensions.filter(x => x && x.enabled); - const dim = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === fieldName); - return PivotUtil.extractValueFromDimension(dim, rec); + const dim :IPivotDimension = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === fieldName); + const value = dim.childLevel ? this._getDimensionValueHierarchy(dim, rec).map(x => `[` + x +`]`).join('.') : PivotUtil.extractValueFromDimension(dim, rec); + return value; + } + + public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { + const grid = (column.grid as any); + const enabledDimensions = grid.allDimensions.filter(x => x && x.enabled); + let data = column.grid.gridAPI.filterDataByExpressions(tree); + const dim = enabledDimensions.find(x => x.memberName === column.field); + const allValuesHierarchy = PivotUtil.getFieldsHierarchy( + data, + [dim], + PivotDimensionType.Column, + grid.pivotKeys + ); + const isNoop = grid.pivotConfiguration.columnStrategy instanceof NoopPivotDimensionsStrategy || grid.pivotConfiguration.rowStrategy instanceof NoopPivotDimensionsStrategy; + const items: IgxFilterItem[] = !isNoop ? this._getFilterItems(allValuesHierarchy, grid.pivotKeys) : [{value : ''}]; + return Promise.resolve(items); + } + + private _getFilterItems(hierarchy: Map, pivotKeys: IPivotKeys) : IgxFilterItem[] { + const items: IgxFilterItem[] = []; + hierarchy.forEach((value) => { + const val = value.value; + const path = val.split(pivotKeys.columnDimensionSeparator); + const hierarchicalValue = path.length > 1 ? path.map(x => `[` + x +`]`).join('.') : val; + const text = path[path.length -1]; + items.push({ + value: hierarchicalValue, + label: text, + children: this._getFilterItems(value.children, pivotKeys) + }); + }); + return items; + } + + private _getDimensionValueHierarchy(dim: IPivotDimension, rec: any) : string[] { + let path = []; + let value = PivotUtil.extractValueFromDimension(dim, rec); + path.push(value); + if (dim.childLevel) { + const childVals = this._getDimensionValueHierarchy(dim.childLevel, rec); + path = path.concat(childVals); + } + return path; } } diff --git a/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts index c033a290dd6..63ef36a3e75 100644 --- a/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts @@ -54,14 +54,14 @@ export class IgxCalendarContainerComponent { } /** @hidden */ -@NgModule({ - declarations: [IgxCalendarContainerComponent], - imports: [ - CommonModule, - IgxButtonModule, - IgxRippleModule, - IgxCalendarModule - ], - exports: [IgxCalendarContainerComponent] +@NgModule({ + declarations: [IgxCalendarContainerComponent], + imports: [ + CommonModule, + IgxButtonModule, + IgxRippleModule, + IgxCalendarModule + ], + exports: [IgxCalendarContainerComponent] }) export class IgxCalendarContainerModule { } diff --git a/projects/igniteui-angular/src/lib/directives/drag-drop/drag-drop.directive.ts b/projects/igniteui-angular/src/lib/directives/drag-drop/drag-drop.directive.ts index e577d848e0f..b862d1894ea 100644 --- a/projects/igniteui-angular/src/lib/directives/drag-drop/drag-drop.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/drag-drop/drag-drop.directive.ts @@ -1,4 +1,4 @@ -import { +import { Directive, ElementRef, EventEmitter, @@ -570,6 +570,7 @@ export class IgxDragDirective implements AfterContentInit, OnDestroy { protected _ghostStartY; protected _ghostHostX = 0; protected _ghostHostY = 0; + protected _dynamicGhostRef; protected _pointerDownId = null; protected _clicked = false; @@ -713,6 +714,11 @@ export class IgxDragDirective implements AfterContentInit, OnDestroy { if (this.ghost && this.ghostElement && this._removeOnDestroy) { this.ghostElement.parentNode.removeChild(this.ghostElement); this.ghostElement = null; + + if (this._dynamicGhostRef) { + this._dynamicGhostRef.destroy(); + this._dynamicGhostRef = null; + } } } @@ -1090,6 +1096,10 @@ export class IgxDragDirective implements AfterContentInit, OnDestroy { } this.ghostElement.parentNode.removeChild(this.ghostElement); this.ghostElement = null; + if (this._dynamicGhostRef) { + this._dynamicGhostRef.destroy(); + this._dynamicGhostRef = null; + } } else if (!this.ghost) { this.element.nativeElement.style.transitionProperty = ''; this.element.nativeElement.style.transitionDuration = '0.0s'; @@ -1126,10 +1136,9 @@ export class IgxDragDirective implements AfterContentInit, OnDestroy { return; } - let dynamicGhostRef; if (this.ghostTemplate) { - dynamicGhostRef = this.viewContainer.createEmbeddedView(this.ghostTemplate, this.ghostContext); - this.ghostElement = dynamicGhostRef.rootNodes[0]; + this._dynamicGhostRef = this.viewContainer.createEmbeddedView(this.ghostTemplate, this.ghostContext); + this.ghostElement = this._dynamicGhostRef.rootNodes[0]; } else { this.ghostElement = node ? node.cloneNode(true) : this.element.nativeElement.cloneNode(true); } @@ -1155,8 +1164,8 @@ export class IgxDragDirective implements AfterContentInit, OnDestroy { this.ghostCreate.emit(createEventArgs); if (createEventArgs.cancel) { this.ghostElement = null; - if (this.ghostTemplate && dynamicGhostRef) { - dynamicGhostRef.destroy(); + if (this.ghostTemplate && this._dynamicGhostRef) { + this._dynamicGhostRef.destroy(); } return; } diff --git a/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts b/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts index 15258bface2..8b43952ea50 100644 --- a/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts @@ -1464,6 +1464,9 @@ export class IgxGridForOfDirective extends IgxForOfDirective implements On @Input() public igxGridForOfUniqueSizeCache = false; + @Input() + public igxGridForOfVariableSizes = true; + public get igxGridForOf() { return this.igxForOf; } @@ -1494,6 +1497,12 @@ export class IgxGridForOfDirective extends IgxForOfDirective implements On return this.igxForSizePropName || 'height'; } + public recalcUpdateSizes() { + if (this.igxGridForOfVariableSizes) { + super.recalcUpdateSizes(); + } + } + /** * @hidden @internal * An event that is emitted after data has been changed but before the view is refreshed diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts index 8f1e93fb6fc..6984c832816 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts @@ -12,30 +12,30 @@ import { IgxSummaryTemplateDirective } from './templates.directive'; -@NgModule({ - declarations: [ - IgxFilterCellTemplateDirective, - IgxSummaryTemplateDirective, - IgxCellTemplateDirective, - IgxCellHeaderTemplateDirective, - IgxCellFooterTemplateDirective, - IgxCellEditorTemplateDirective, - IgxCollapsibleIndicatorTemplateDirective, - IgxColumnComponent, - IgxColumnGroupComponent, - IgxColumnLayoutComponent - ], - exports: [ - IgxFilterCellTemplateDirective, - IgxSummaryTemplateDirective, - IgxCellTemplateDirective, - IgxCellHeaderTemplateDirective, - IgxCellFooterTemplateDirective, - IgxCellEditorTemplateDirective, - IgxCollapsibleIndicatorTemplateDirective, - IgxColumnComponent, - IgxColumnGroupComponent, - IgxColumnLayoutComponent - ] +@NgModule({ + declarations: [ + IgxFilterCellTemplateDirective, + IgxSummaryTemplateDirective, + IgxCellTemplateDirective, + IgxCellHeaderTemplateDirective, + IgxCellFooterTemplateDirective, + IgxCellEditorTemplateDirective, + IgxCollapsibleIndicatorTemplateDirective, + IgxColumnComponent, + IgxColumnGroupComponent, + IgxColumnLayoutComponent + ], + exports: [ + IgxFilterCellTemplateDirective, + IgxSummaryTemplateDirective, + IgxCellTemplateDirective, + IgxCellHeaderTemplateDirective, + IgxCellFooterTemplateDirective, + IgxCellEditorTemplateDirective, + IgxCollapsibleIndicatorTemplateDirective, + IgxColumnComponent, + IgxColumnGroupComponent, + IgxColumnLayoutComponent + ] }) export class IgxGridColumnModule {} diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.html index 53962038668..3771b6d6605 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.html @@ -1,28 +1,28 @@ -
-

{{ esf.column.header || esf.column.field }}

-
- - - -
-
+
+

{{ esf.column.header || esf.column.field }}

+
+ + + +
+
diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.ts index 4072a4761f2..608e6990c95 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-header.component.ts @@ -1,49 +1,49 @@ -import { Component, Input } from '@angular/core'; -import { BaseFilteringComponent } from './base-filtering.component'; - -/** - * A component used for presenting Excel style header UI. - */ -@Component({ - selector: 'igx-excel-style-header', - templateUrl: './excel-style-header.component.html' -}) -export class IgxExcelStyleHeaderComponent { - /** - * Sets whether the column pinning icon should be shown in the header. - * Default value is `false`. - * - * @example - * ```html - * - * ``` - */ - @Input() - public showPinning: boolean; - - /** - * Sets whether the column selecting icon should be shown in the header. - * Default value is `false`. - * - * @example - * ```html - * - * ``` - */ - @Input() - public showSelecting: boolean; - - /** - * Sets whether the column hiding icon should be shown in the header. - * Default value is `false`. - * - * @example - * ```html - * - * ``` - */ - @Input() - public showHiding: boolean; - - constructor(public esf: BaseFilteringComponent) { } -} +import { Component, Input } from '@angular/core'; +import { BaseFilteringComponent } from './base-filtering.component'; + +/** + * A component used for presenting Excel style header UI. + */ +@Component({ + selector: 'igx-excel-style-header', + templateUrl: './excel-style-header.component.html' +}) +export class IgxExcelStyleHeaderComponent { + /** + * Sets whether the column pinning icon should be shown in the header. + * Default value is `false`. + * + * @example + * ```html + * + * ``` + */ + @Input() + public showPinning: boolean; + + /** + * Sets whether the column selecting icon should be shown in the header. + * Default value is `false`. + * + * @example + * ```html + * + * ``` + */ + @Input() + public showSelecting: boolean; + + /** + * Sets whether the column hiding icon should be shown in the header. + * Default value is `false`. + * + * @example + * ```html + * + * ``` + */ + @Input() + public showHiding: boolean; + + constructor(public esf: BaseFilteringComponent) { } +} diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/public_api.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/public_api.ts index 2726dafafa6..4de549833cf 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/public_api.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/public_api.ts @@ -1,11 +1,11 @@ -export * from './excel-style-clear-filters.component'; -export * from './excel-style-conditional-filter.component'; -export * from './excel-style-header.component'; -export * from './excel-style-hiding.component'; -export * from './excel-style-moving.component'; -export * from './excel-style-pinning.component'; -export * from './excel-style-search.component'; -export * from './excel-style-selecting.component'; -export * from './excel-style-sorting.component'; -export * from './grid.excel-style-filtering.component'; -export * from './excel-style-date-expression.component'; +export * from './excel-style-clear-filters.component'; +export * from './excel-style-conditional-filter.component'; +export * from './excel-style-header.component'; +export * from './excel-style-hiding.component'; +export * from './excel-style-moving.component'; +export * from './excel-style-pinning.component'; +export * from './excel-style-search.component'; +export * from './excel-style-selecting.component'; +export * from './excel-style-sorting.component'; +export * from './grid.excel-style-filtering.component'; +export * from './excel-style-date-expression.component'; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index a70f431b0f5..92f86cf8913 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4937,7 +4937,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ public get hasSummarizedColumns(): boolean { - return this.summaryService.hasSummarizedColumns; + const summarizedColumns = this.columnList.filter(col => col.hasSummary && !col.hidden); + return summarizedColumns.length > 0; } /** @@ -6219,12 +6220,14 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * @hidden */ protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) { - const list = this.columnList.toArray(); - this._reorderColumns(from, to, pos, list); - const newList = this._resetColumnList(list); - this.columnList.reset(newList); - this.columnList.notifyOnChanges(); - this._columns = this.columnList.toArray(); + Promise.resolve().then(() => { + const list = this.columnList.toArray(); + this._reorderColumns(from, to, pos, list); + const newList = this._resetColumnList(list); + this.columnList.reset(newList); + this.columnList.notifyOnChanges(); + this._columns = this.columnList.toArray(); + }); } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts index 5599a24a64e..cb7af853cbf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts @@ -45,80 +45,80 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive'; /** * @hidden */ -@NgModule({ - declarations: [ - IgxRowDirective, - IgxGridCellComponent, - IgxRowAddTextDirective, - IgxRowEditTemplateDirective, - IgxRowEditActionsDirective, - IgxRowEditTextDirective, - IgxRowEditTabStopDirective, - IgxGridBodyDirective, - IgxGridFooterComponent, - IgxAdvancedFilteringDialogComponent, - IgxRowExpandedIndicatorDirective, - IgxRowCollapsedIndicatorDirective, - IgxHeaderExpandIndicatorDirective, - IgxHeaderCollapseIndicatorDirective, - IgxExcelStyleHeaderIconDirective, - IgxSortAscendingHeaderIconDirective, - IgxSortDescendingHeaderIconDirective, - IgxSortHeaderIconDirective, - IgxGroupAreaDropDirective, - IgxGroupByMetaPipe - ], - exports: [ - IgxGridCellComponent, - IgxRowAddTextDirective, - IgxRowEditTemplateDirective, - IgxRowEditActionsDirective, - IgxRowEditTextDirective, - IgxRowEditTabStopDirective, - IgxGridBodyDirective, - IgxColumnActionsModule, - IgxGridColumnModule, - IgxGridHeadersModule, - IgxGridPipesModule, - IgxGridFilteringModule, - IgxGridExcelStyleFilteringModule, - IgxRowDragModule, - IgxPaginatorModule, - IgxGridFooterComponent, - IgxGridResizingModule, - IgxColumnMovingModule, - IgxGridSelectionModule, - IgxGridSummaryModule, - IgxGridToolbarModule, - IgxAdvancedFilteringDialogComponent, - IgxGridSharedModules, - IgxRowExpandedIndicatorDirective, - IgxRowCollapsedIndicatorDirective, - IgxHeaderExpandIndicatorDirective, - IgxHeaderCollapseIndicatorDirective, - IgxExcelStyleHeaderIconDirective, - IgxSortAscendingHeaderIconDirective, - IgxSortDescendingHeaderIconDirective, - IgxSortHeaderIconDirective, - IgxGroupAreaDropDirective, - IgxGroupByMetaPipe - ], - imports: [ - IgxGridColumnModule, - IgxGridHeadersModule, - IgxColumnMovingModule, - IgxGridResizingModule, - IgxGridSelectionModule, - IgxGridSummaryModule, - IgxGridToolbarModule, - IgxColumnActionsModule, - IgxGridPipesModule, - IgxGridFilteringModule, - IgxGridExcelStyleFilteringModule, - IgxRowDragModule, - IgxPaginatorModule, - IgxGridSharedModules, - IgxChipsModule - ] +@NgModule({ + declarations: [ + IgxRowDirective, + IgxGridCellComponent, + IgxRowAddTextDirective, + IgxRowEditTemplateDirective, + IgxRowEditActionsDirective, + IgxRowEditTextDirective, + IgxRowEditTabStopDirective, + IgxGridBodyDirective, + IgxGridFooterComponent, + IgxAdvancedFilteringDialogComponent, + IgxRowExpandedIndicatorDirective, + IgxRowCollapsedIndicatorDirective, + IgxHeaderExpandIndicatorDirective, + IgxHeaderCollapseIndicatorDirective, + IgxExcelStyleHeaderIconDirective, + IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, + IgxSortHeaderIconDirective, + IgxGroupAreaDropDirective, + IgxGroupByMetaPipe + ], + exports: [ + IgxGridCellComponent, + IgxRowAddTextDirective, + IgxRowEditTemplateDirective, + IgxRowEditActionsDirective, + IgxRowEditTextDirective, + IgxRowEditTabStopDirective, + IgxGridBodyDirective, + IgxColumnActionsModule, + IgxGridColumnModule, + IgxGridHeadersModule, + IgxGridPipesModule, + IgxGridFilteringModule, + IgxGridExcelStyleFilteringModule, + IgxRowDragModule, + IgxPaginatorModule, + IgxGridFooterComponent, + IgxGridResizingModule, + IgxColumnMovingModule, + IgxGridSelectionModule, + IgxGridSummaryModule, + IgxGridToolbarModule, + IgxAdvancedFilteringDialogComponent, + IgxGridSharedModules, + IgxRowExpandedIndicatorDirective, + IgxRowCollapsedIndicatorDirective, + IgxHeaderExpandIndicatorDirective, + IgxHeaderCollapseIndicatorDirective, + IgxExcelStyleHeaderIconDirective, + IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, + IgxSortHeaderIconDirective, + IgxGroupAreaDropDirective, + IgxGroupByMetaPipe + ], + imports: [ + IgxGridColumnModule, + IgxGridHeadersModule, + IgxColumnMovingModule, + IgxGridResizingModule, + IgxGridSelectionModule, + IgxGridSummaryModule, + IgxGridToolbarModule, + IgxColumnActionsModule, + IgxGridPipesModule, + IgxGridFilteringModule, + IgxGridExcelStyleFilteringModule, + IgxRowDragModule, + IgxPaginatorModule, + IgxGridSharedModules, + IgxChipsModule + ] }) export class IgxGridCommonModule { } diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts index 0ce31d5228a..1319fe4d48e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts @@ -1,279 +1,279 @@ -import { CellType, ColumnType, GridType, RowType } from './common/grid.interface'; -import { ISelectionNode } from './common/types'; -import { resolveNestedPath } from '../core/utils'; - -export class IgxGridCell implements CellType { - - /** - * Returns the grid containing the cell. - * - * @memberof IgxGridCell - */ - public grid: GridType; - private _row: RowType; - private _rowIndex: number; - private _column: ColumnType; - private _columnField: string; - - /** - * @hidden - */ - constructor( - grid: GridType, - row: number | RowType, - column: string | ColumnType) { - this.grid = grid; - if (typeof row === 'number') { - this._rowIndex = row; - } else { - this._row = row; - this._rowIndex = row.index; - } - if (typeof column === 'string') { - this._columnField = column; - } else { - this._column = column; - } - } - - /** - * Returns the row containing the cell. - * ```typescript - * let row = this.cell.row; - * ``` - * - * @memberof IgxGridCell - */ - public get row(): RowType { - return this._row || this.grid.createRow(this._rowIndex); - } - - /** - * Returns the column of the cell. - * ```typescript - * let column = this.cell.column; - * ``` - * - * @memberof IgxGridCell - */ - public get column(): ColumnType { - return this._column || this.grid.getColumnByName(this._columnField); - } - - /** - * Gets the current edit value while a cell is in edit mode. - * ```typescript - * let editValue = this.cell.editValue; - * ``` - * - * @memberof IgxGridCell - */ - public get editValue(): any { - if (this.isCellInEditMode()) { - return this.grid.crudService.cell.editValue; - } - } - - /** - * Sets the current edit value while a cell is in edit mode. - * Only for cell editing mode. - * ```typescript - * this.cell.editValue = value; - * ``` - * - * @memberof IgxGridCell - */ - public set editValue(value: any) { - if (this.isCellInEditMode()) { - this.grid.crudService.cell.editValue = value; - } - } - - /** - * Returns whether the cell is editable.. - * - * @memberof IgxGridCell - */ - public get editable(): boolean { - return this.column.editable && !this.row?.disabled; - } - - /** - * Gets the width of the cell. - * ```typescript - * let cellWidth = this.cell.width; - * ``` - * - * @memberof IgxGridCell - */ - public get width(): string { - return this.column.width; - } - - /** - * Returns the cell value. - * - * @memberof IgxGridCell - */ - public get value(): any { - // will return undefined for a column layout, because getCellByColumnVisibleIndex may return the column layout at that index. - // getCellByColumnVisibleIndex is deprecated and will be removed in future version - return this.column.field ? - this.column.hasNestedPath ? resolveNestedPath(this.row?.data, this.column.field) : this.row?.data[this.column.field] - : undefined; - } - - /** - * Updates the cell value. - * - * @memberof IgxGridCell - */ - public set value(val: any) { - this.update(val); - } - - /** - * Gets the cell id. - * A cell in the grid is identified by: - * - rowID - primaryKey data value or the whole rowData, if the primaryKey is omitted. - * - rowIndex - the row index - * - columnID - column index - * - * ```typescript - * let cellID = cell.id; - * ``` - * - * @memberof IgxGridCell - */ - public get id(): any { - const primaryKey = this.grid.primaryKey; - const rowID = primaryKey ? this.row?.data[primaryKey] : this.row?.data; - return { rowID, columnID: this.column.index, rowIndex: this._rowIndex || this.row?.index }; - } - - /** - * Returns if the row is currently in edit mode. - * - * @memberof IgxGridCell - */ - public get editMode(): boolean { - return this.isCellInEditMode(); - } - - /** - * Starts/ends edit mode for the cell. - * - * ```typescript - * cell.editMode = !cell.editMode; - * ``` - * - * @memberof IgxGridCell - */ - public set editMode(value: boolean) { - const isInEditMode = this.isCellInEditMode(); - if (!this.row || this.row?.deleted || isInEditMode === value) { - return; - } - if (this.editable && value) { - this.endEdit(); - // TODO possibly define similar method in gridAPI, which does not emit event - this.grid.crudService.enterEditMode(this); - } else { - this.grid.crudService.endCellEdit(); - } - this.grid.notifyChanges(); - } - - /** - * Gets whether the cell is selected. - * ```typescript - * let isSelected = this.cell.selected; - * ``` - * - * - * @memberof IgxGridCell - */ - public get selected(): boolean { - return this.grid.selectionService.selected(this.selectionNode); - } - - /** - * Selects/deselects the cell. - * ```typescript - * this.cell.selected = true. - * ``` - * - * - * @memberof IgxGridCell - */ - public set selected(val: boolean) { - const node = this.selectionNode; - if (val) { - this.grid.selectionService.add(node); - } else { - this.grid.selectionService.remove(node); - } - this.grid.notifyChanges(); - } - - public get active() { - const node = this.grid.navigation.activeNode; - return node ? node.row === this.row?.index && node.column === this.column.visibleIndex : false; - } - - - /** - * Updates the cell value. - * - * ```typescript - * cell.update(newValue); - * ``` - * - * @memberof IgxGridCell - */ - public update(val: any): void { - if (this.row?.deleted) { - return; - } - - this.endEdit(); - - const cell = this.isCellInEditMode() ? this.grid.crudService.cell : this.grid.crudService.createCell(this); - cell.editValue = val; - this.grid.gridAPI.update_cell(cell); - this.grid.crudService.endCellEdit(); - this.grid.notifyChanges(); - } - - protected get selectionNode(): ISelectionNode { - return { - row: this.row?.index, - column: this.column.columnLayoutChild ? this.column.parent.visibleIndex : this.column.visibleIndex, - layout: this.column.columnLayoutChild ? { - rowStart: this.column.rowStart, - colStart: this.column.colStart, - rowEnd: this.column.rowEnd, - colEnd: this.column.colEnd, - columnVisibleIndex: this.column.visibleIndex - } : null - }; - } - - private isCellInEditMode(): boolean { - if (this.grid.crudService.cellInEditMode) { - const cellInEditMode = this.grid.crudService.cell.id; - const isCurrentCell = cellInEditMode.rowID === this.id.rowID && - cellInEditMode.rowIndex === this.id.rowIndex && - cellInEditMode.columnID === this.id.columnID; - return isCurrentCell; - } - return false; - } - - private endEdit(): void { - if (!this.isCellInEditMode()) { - this.grid.gridAPI.update_cell(this.grid.crudService.cell); - this.grid.crudService.endCellEdit(); - } - } -} +import { CellType, ColumnType, GridType, RowType } from './common/grid.interface'; +import { ISelectionNode } from './common/types'; +import { resolveNestedPath } from '../core/utils'; + +export class IgxGridCell implements CellType { + + /** + * Returns the grid containing the cell. + * + * @memberof IgxGridCell + */ + public grid: GridType; + private _row: RowType; + private _rowIndex: number; + private _column: ColumnType; + private _columnField: string; + + /** + * @hidden + */ + constructor( + grid: GridType, + row: number | RowType, + column: string | ColumnType) { + this.grid = grid; + if (typeof row === 'number') { + this._rowIndex = row; + } else { + this._row = row; + this._rowIndex = row.index; + } + if (typeof column === 'string') { + this._columnField = column; + } else { + this._column = column; + } + } + + /** + * Returns the row containing the cell. + * ```typescript + * let row = this.cell.row; + * ``` + * + * @memberof IgxGridCell + */ + public get row(): RowType { + return this._row || this.grid.createRow(this._rowIndex); + } + + /** + * Returns the column of the cell. + * ```typescript + * let column = this.cell.column; + * ``` + * + * @memberof IgxGridCell + */ + public get column(): ColumnType { + return this._column || this.grid.getColumnByName(this._columnField); + } + + /** + * Gets the current edit value while a cell is in edit mode. + * ```typescript + * let editValue = this.cell.editValue; + * ``` + * + * @memberof IgxGridCell + */ + public get editValue(): any { + if (this.isCellInEditMode()) { + return this.grid.crudService.cell.editValue; + } + } + + /** + * Sets the current edit value while a cell is in edit mode. + * Only for cell editing mode. + * ```typescript + * this.cell.editValue = value; + * ``` + * + * @memberof IgxGridCell + */ + public set editValue(value: any) { + if (this.isCellInEditMode()) { + this.grid.crudService.cell.editValue = value; + } + } + + /** + * Returns whether the cell is editable.. + * + * @memberof IgxGridCell + */ + public get editable(): boolean { + return this.column.editable && !this.row?.disabled; + } + + /** + * Gets the width of the cell. + * ```typescript + * let cellWidth = this.cell.width; + * ``` + * + * @memberof IgxGridCell + */ + public get width(): string { + return this.column.width; + } + + /** + * Returns the cell value. + * + * @memberof IgxGridCell + */ + public get value(): any { + // will return undefined for a column layout, because getCellByColumnVisibleIndex may return the column layout at that index. + // getCellByColumnVisibleIndex is deprecated and will be removed in future version + return this.column.field ? + this.column.hasNestedPath ? resolveNestedPath(this.row?.data, this.column.field) : this.row?.data[this.column.field] + : undefined; + } + + /** + * Updates the cell value. + * + * @memberof IgxGridCell + */ + public set value(val: any) { + this.update(val); + } + + /** + * Gets the cell id. + * A cell in the grid is identified by: + * - rowID - primaryKey data value or the whole rowData, if the primaryKey is omitted. + * - rowIndex - the row index + * - columnID - column index + * + * ```typescript + * let cellID = cell.id; + * ``` + * + * @memberof IgxGridCell + */ + public get id(): any { + const primaryKey = this.grid.primaryKey; + const rowID = primaryKey ? this.row?.data[primaryKey] : this.row?.data; + return { rowID, columnID: this.column.index, rowIndex: this._rowIndex || this.row?.index }; + } + + /** + * Returns if the row is currently in edit mode. + * + * @memberof IgxGridCell + */ + public get editMode(): boolean { + return this.isCellInEditMode(); + } + + /** + * Starts/ends edit mode for the cell. + * + * ```typescript + * cell.editMode = !cell.editMode; + * ``` + * + * @memberof IgxGridCell + */ + public set editMode(value: boolean) { + const isInEditMode = this.isCellInEditMode(); + if (!this.row || this.row?.deleted || isInEditMode === value) { + return; + } + if (this.editable && value) { + this.endEdit(); + // TODO possibly define similar method in gridAPI, which does not emit event + this.grid.crudService.enterEditMode(this); + } else { + this.grid.crudService.endCellEdit(); + } + this.grid.notifyChanges(); + } + + /** + * Gets whether the cell is selected. + * ```typescript + * let isSelected = this.cell.selected; + * ``` + * + * + * @memberof IgxGridCell + */ + public get selected(): boolean { + return this.grid.selectionService.selected(this.selectionNode); + } + + /** + * Selects/deselects the cell. + * ```typescript + * this.cell.selected = true. + * ``` + * + * + * @memberof IgxGridCell + */ + public set selected(val: boolean) { + const node = this.selectionNode; + if (val) { + this.grid.selectionService.add(node); + } else { + this.grid.selectionService.remove(node); + } + this.grid.notifyChanges(); + } + + public get active() { + const node = this.grid.navigation.activeNode; + return node ? node.row === this.row?.index && node.column === this.column.visibleIndex : false; + } + + + /** + * Updates the cell value. + * + * ```typescript + * cell.update(newValue); + * ``` + * + * @memberof IgxGridCell + */ + public update(val: any): void { + if (this.row?.deleted) { + return; + } + + this.endEdit(); + + const cell = this.isCellInEditMode() ? this.grid.crudService.cell : this.grid.crudService.createCell(this); + cell.editValue = val; + this.grid.gridAPI.update_cell(cell); + this.grid.crudService.endCellEdit(); + this.grid.notifyChanges(); + } + + protected get selectionNode(): ISelectionNode { + return { + row: this.row?.index, + column: this.column.columnLayoutChild ? this.column.parent.visibleIndex : this.column.visibleIndex, + layout: this.column.columnLayoutChild ? { + rowStart: this.column.rowStart, + colStart: this.column.colStart, + rowEnd: this.column.rowEnd, + colEnd: this.column.colEnd, + columnVisibleIndex: this.column.visibleIndex + } : null + }; + } + + private isCellInEditMode(): boolean { + if (this.grid.crudService.cellInEditMode) { + const cellInEditMode = this.grid.crudService.cell.id; + const isCurrentCell = cellInEditMode.rowID === this.id.rowID && + cellInEditMode.rowIndex === this.id.rowIndex && + cellInEditMode.columnID === this.id.columnID; + return isCurrentCell; + } + return false; + } + + private endEdit(): void { + if (!this.isCellInEditMode()) { + this.grid.gridAPI.update_cell(this.grid.crudService.cell); + this.grid.crudService.endCellEdit(); + } + } +} diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts index 41955d5d0ba..f74dbec12fd 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts @@ -1,789 +1,789 @@ -import { IGroupByRecord } from '../data-operations/groupby-record.interface'; -import { IgxAddRow, IgxEditRow } from './common/crud.service'; -import { GridInstanceType, GridSummaryCalculationMode, GridSummaryPosition } from './common/enums'; -import { IgxGridCell } from './grid-public-cell'; -import { IgxSummaryResult } from './summaries/grid-summary'; -import { ITreeGridRecord } from './tree-grid/tree-grid.interfaces'; -import mergeWith from 'lodash.mergewith'; -import { CellType, GridServiceType, GridType, RowType } from './common/grid.interface'; - -abstract class BaseRow implements RowType { - public index: number; - /** - * The grid that contains the row. - */ - public grid: GridType; - protected _data?: any; - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex(): number { - return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); - } - - /** - * Gets the row key. - * A row in the grid is identified either by: - * - primaryKey data value, - * - the whole rowData, if the primaryKey is omitted. - * - * ```typescript - * let rowKey = row.key; - * ``` - */ - public get key(): any { - const data = this._data ?? this.grid.dataView[this.index]; - const primaryKey = this.grid.primaryKey; - return primaryKey ? data[primaryKey] : data; - } - - /** - * Gets if this represents add row UI - * - * ```typescript - * let isAddRow = row.addRowUI; - * ``` - */ - public get addRowUI(): boolean { - return !!this.grid.crudService.row && - this.grid.crudService.row.getClassName() === IgxAddRow.name && - this.grid.crudService.row.id === this.key; - } - - /** - * The data record that populates the row. - * - * ```typescript - * let rowData = row.data; - * ``` - */ - public get data(): any { - if (this.inEditMode) { - return mergeWith(this.grid.dataCloneStrategy.clone(this._data ?? this.grid.dataView[this.index]), - this.grid.transactions.getAggregatedValue(this.key, false), - (objValue, srcValue) => { - if (Array.isArray(srcValue)) { - return objValue = srcValue; - } - }); - } - return this._data ?? this.grid.dataView[this.index]; - } - - /** - * Returns if the row is currently in edit mode. - */ - public get inEditMode(): boolean { - if (this.grid.rowEditable) { - const editRowState = this.grid.crudService.row; - return (editRowState && editRowState.id === this.key) || false; - } else { - return false; - } - } - - /** - * Gets whether the row is pinned. - * Default value is `false`. - * ```typescript - * const isPinned = row.pinned; - * ``` - */ - public get pinned(): boolean { - return this.grid.isRecordPinned(this.data); - } - - /** - * Sets whether the row is pinned. - * Default value is `false`. - * ```typescript - * row.pinned = !row.pinned; - * ``` - */ - public set pinned(val: boolean) { - if (val) { - this.pin(); - } else { - this.unpin(); - } - } - - /** - * Gets the row expanded/collapsed state. - * - * ```typescript - * const isExpanded = row.expanded; - * ``` - */ - public get expanded(): boolean { - return this.grid.gridAPI.get_row_expansion_state(this.data); - } - - /** - * Expands/collapses the row. - * - * ```typescript - * row.expanded = true; - * ``` - */ - public set expanded(val: boolean) { - this.grid.gridAPI.set_row_expansion_state(this.key, val); - } - - /** - * Gets whether the row is selected. - * Default value is `false`. - * ```typescript - * row.selected = true; - * ``` - */ - public get selected(): boolean { - return this.grid.selectionService.isRowSelected(this.key); - } - - /** - * Sets whether the row is selected. - * Default value is `false`. - * ```typescript - * row.selected = !row.selected; - * ``` - */ - public set selected(val: boolean) { - if (val) { - this.grid.selectionService.selectRowsWithNoEvent([this.key]); - } else { - this.grid.selectionService.deselectRowsWithNoEvent([this.key]); - } - this.grid.cdr.markForCheck(); - } - - /** - * Returns if the row is in delete state. - */ - public get deleted(): boolean { - return this.grid.gridAPI.row_deleted_transaction(this.key); - } - - /** - * Returns if the row has child rows. Always return false for IgxGridRow. - */ - public get hasChildren(): boolean { - return false; - } - - public get disabled(): boolean { - return this.grid.isGhostRecord(this.data); - } - - /** - * Gets the rendered cells in the row component. - */ - public get cells(): CellType[] { - const res: CellType[] = []; - this.grid.columnList.forEach(col => { - const cell: CellType = new IgxGridCell(this.grid, this.index, col.field); - res.push(cell); - }); - return res; - } - - /** - * Pins the specified row. - * This method emits `onRowPinning` event. - * - * ```typescript - * // pin the selected row from the grid - * this.grid.selectedRows[0].pin(); - * ``` - */ - public pin(): boolean { - return this.grid.pinRow(this.key, this.index); - } - - /** - * Unpins the specified row. - * This method emits `onRowPinning` event. - * - * ```typescript - * // unpin the selected row from the grid - * this.grid.selectedRows[0].unpin(); - * ``` - */ - public unpin(): boolean { - return this.grid.unpinRow(this.key); - } - - /** - * Updates the specified row object and the data source record with the passed value. - * - * ```typescript - * // update the second selected row's value - * let newValue = "Apple"; - * this.grid.selectedRows[1].update(newValue); - * ``` - */ - public update(value: any): void { - const crudService = this.grid.crudService; - if (crudService.cellInEditMode && crudService.cell.id.rowID === this.key) { - this.grid.transactions.endPending(false); - } - const row = new IgxEditRow(this.key, this.index, this.data, this.grid); - this.grid.gridAPI.update_row(row, value); - this.grid.notifyChanges(); - } - - /** - * Removes the specified row from the grid's data source. - * This method emits `onRowDeleted` event. - * - * ```typescript - * // delete the third selected row from the grid - * this.grid.selectedRows[2].delete(); - * ``` - */ - public delete(): void { - this.grid.deleteRowById(this.key); - } -} - -export class IgxGridRow extends BaseRow implements RowType { - /** - * @hidden - */ - constructor( - public grid: GridType, - public index: number, data?: any - ) { - super(); - this._data = data && data.addRow && data.recordRef ? data.recordRef : data; - } - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex(): number { - if (this.grid.paginator) { - const precedingDetailRows = []; - const precedingGroupRows = []; - const firstRow = this.grid.dataView[0]; - const hasDetailRows = this.grid.expansionStates.size; - const hasGroupedRows = this.grid.groupingExpressions.length; - let precedingSummaryRows = 0; - const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); - - // from groupingFlatResult, resolve two other collections: - // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages - // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages - if (hasDetailRows || hasGroupedRows) { - this.grid.groupingFlatResult.forEach((r, ind) => { - const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; - if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { - precedingGroupRows.push(r); - } - if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && !this.grid.isGroupByRecord(r)) { - precedingDetailRows.push(r); - } - }); - } - - if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { - // if firstRow is a child of the last item in precedingGroupRows, - // then summaryRow for this given groupedRecord is rendered after firstRow, - // i.e. need to decrease firstRowInd to account for the above. - precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; - if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && - precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { - precedingSummaryRows += -1; - } - } - - return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; - } else { - return this.index; - } - } - - /** - * Returns the parent row, if grid is grouped. - */ - public get parent(): RowType { - let parent: IgxGroupByRow; - if (!this.grid.groupingExpressions.length) { - return undefined; - } - - let i = this.index - 1; - while (i >= 0 && !parent) { - const rec = this.grid.dataView[i]; - if (this.grid.isGroupByRecord(rec)) { - parent = new IgxGroupByRow(this.grid, i, rec); - } - i--; - } - return parent; - } -} - -export class IgxTreeGridRow extends BaseRow implements RowType { - /** - * @hidden - */ - constructor( - public grid: GridType, - public index: number, data?: any, private _treeRow?: ITreeGridRecord - ) { - super(); - this._data = data && data.addRow && data.recordRef ? data.recordRef : data; - } - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex(): number { - if (this.grid.hasSummarizedColumns && ((this.grid.paginator?.page || 0) > 0)) { - if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { - const firstRowIndex = this.grid.processedExpandedFlatData.indexOf(this.grid.dataView[0].data); - // firstRowIndex is based on data result after all pipes triggered, excluding summary pipe - const precedingSummaryRows = this.grid.summaryPosition === GridSummaryPosition.bottom ? - this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) : - this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) + 1; - // there is a summary row for each root record, so we calculate how many root records are rendered before the current row - return firstRowIndex + precedingSummaryRows + this.index; - } - } - return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); - } - - /** - * The data passed to the row component. - * - * ```typescript - * let selectedRowData = this.grid.selectedRows[0].data; - * ``` - */ - public get data(): any { - if (this.inEditMode) { - return mergeWith(this.grid.dataCloneStrategy.clone(this._data ?? this.grid.dataView[this.index]), - this.grid.transactions.getAggregatedValue(this.key, false), - (objValue, srcValue) => { - if (Array.isArray(srcValue)) { - return objValue = srcValue; - } - }); - } - const rec = this.grid.dataView[this.index]; - return this._data ? this._data : this.grid.isTreeRow(rec) ? rec.data : rec; - } - - /** - * Returns the child rows. - */ - public get children(): RowType[] { - const children: IgxTreeGridRow[] = []; - if (this.treeRow.expanded) { - this.treeRow.children.forEach((rec, i) => { - const row = new IgxTreeGridRow(this.grid, this.index + 1 + i, rec.data); - children.push(row); - }); - } - return children; - } - - /** - * Returns the parent row. - */ - public get parent(): RowType { - const row = this.grid.getRowByKey(this.treeRow.parent?.key); - return row; - } - - /** - * Returns true if child rows exist. Always return false for IgxGridRow. - */ - public get hasChildren(): boolean { - if (this.treeRow.children) { - return this.treeRow.children.length > 0; - } else { - return false; - } - } - - /** - * The `ITreeGridRecord` with metadata about the row in the context of the tree grid. - * - * ```typescript - * const rowParent = this.treeGrid.getRowByKey(1).treeRow.parent; - * ``` - */ - public get treeRow(): ITreeGridRecord { - return this._treeRow ?? this.grid.records.get(this.key); - } - - /** - * Gets whether the row is pinned. - * - * ```typescript - * let isPinned = row.pinned; - * ``` - */ - public get pinned(): boolean { - return this.grid.isRecordPinned(this); - } - - /** - * Sets whether the row is pinned. - * Default value is `false`. - * ```typescript - * row.pinned = !row.pinned; - * ``` - */ - public set pinned(val: boolean) { - if (val) { - this.pin(); - } else { - this.unpin(); - } - } - - /** - * Gets whether the row is expanded. - * - * ```typescript - * let esExpanded = row.expanded; - * ``` - */ - public get expanded(): boolean { - return this.grid.gridAPI.get_row_expansion_state(this.treeRow); - } - - /** - * Expands/collapses the row. - * - * ```typescript - * row.expanded = true; - * ``` - */ - public set expanded(val: boolean) { - this.grid.gridAPI.set_row_expansion_state(this.key, val); - } - - public get disabled(): boolean { - // TODO cell - return this.grid.isGhostRecord(this.data) ? this.treeRow.isFilteredOutParent === undefined : false; - } - - private getRootParent(row: ITreeGridRecord): ITreeGridRecord { - while (row.parent) { - row = row.parent; - } - return row; - } -} - -export class IgxHierarchicalGridRow extends BaseRow implements RowType { - /** - * @hidden - */ - constructor( - public grid: GridType, - public index: number, data?: any - ) { - super(); - this._data = data && data.addRow && data.recordRef ? data.recordRef : data; - } - - /** - * Returns true if row islands exist. - */ - public get hasChildren(): boolean { - return !!this.grid.childLayoutKeys.length; - } - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex() { - const firstRowInd = this.grid.filteredSortedData.indexOf(this.grid.dataView[0]); - const expandedRows = this.grid.filteredSortedData.filter((rec, ind) => { - const rowID = this.grid.primaryKey ? rec[this.grid.primaryKey] : rec; - return this.grid.expansionStates.get(rowID) && ind < firstRowInd; - }); - return firstRowInd + expandedRows.length + this.index; - } - - /** - * Gets the rendered cells in the row component. - */ - public get cells(): CellType[] { - const res: CellType[] = []; - this.grid.columnList.forEach(col => { - const cell: CellType = new IgxGridCell(this.grid, this.index, col.field); - res.push(cell); - }); - return res; - } -} - -export class IgxGroupByRow implements RowType { - /** - * Returns the row index. - */ - public index: number; - - /** - * The grid that contains the row. - */ - public grid: GridType; - - /** - * Returns always true, because this is in instance of an IgxGroupByRow. - */ - public isGroupByRow: boolean; - - /** - * The IGroupByRecord object, representing the group record, if the row is a GroupByRow. - */ - public get groupRow(): IGroupByRecord { - return this._groupRow ? this._groupRow : this.grid.dataView[this.index]; - } - - /** - * Returns the child rows. - */ - public get children(): RowType[] { - const children: IgxGridRow[] = []; - this.groupRow.records.forEach((rec, i) => { - const row = new IgxGridRow(this.grid, this.index + 1 + i, rec); - children.push(row); - }); - return children; - } - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex(): number { - if (this.grid.page) { - const precedingDetailRows = []; - const precedingGroupRows = []; - const firstRow = this.grid.dataView[0]; - const hasDetailRows = this.grid.expansionStates.size; - const hasGroupedRows = this.grid.groupingExpressions.length; - let precedingSummaryRows = 0; - const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); - - // from groupingFlatResult, resolve two other collections: - // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages - // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages - if (hasDetailRows || hasGroupedRows) { - this.grid.groupingFlatResult.forEach((r, ind) => { - const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; - if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { - precedingGroupRows.push(r); - } - if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && !this.grid.isGroupByRecord(r)) { - precedingDetailRows.push(r); - } - }); - } - - if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { - // if firstRow is a child of the last item in precedingGroupRows, - // then summaryRow for this given groupedRecord is rendered after firstRow, - // i.e. need to decrease firstRowInd to account for the above. - precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; - if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && - precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { - precedingSummaryRows += -1; - } - } - - return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; - } else { - return this.index; - } - } - - /** - * @hidden - */ - constructor(grid: GridType, index: number, private _groupRow?: IGroupByRecord) { - this.grid = grid; - this.index = index; - this.isGroupByRow = true; - } - - /** - * Gets whether the row is selected. - * Default value is `false`. - * ```typescript - * row.selected = true; - * ``` - */ - public get selected(): boolean { - return this.children.every(row => row.selected); - } - - /** - * Sets whether the row is selected. - * Default value is `false`. - * ```typescript - * row.selected = !row.selected; - * ``` - */ - public set selected(val: boolean) { - if (val) { - this.children.forEach(row => { - this.grid.selectionService.selectRowsWithNoEvent([row.key]); - }); - } else { - this.children.forEach(row => { - this.grid.selectionService.deselectRowsWithNoEvent([row.key]); - }); - } - this.grid.cdr.markForCheck(); - } - - /** - * Gets/sets whether the group row is expanded. - * ```typescript - * const groupRowExpanded = groupRow.expanded; - * ``` - */ - public get expanded(): boolean { - return this.grid.isExpandedGroup(this.groupRow); - } - - public set expanded(value: boolean) { - this.gridAPI.set_grouprow_expansion_state(this.groupRow, value); - } - - public isActive(): boolean { - return this.grid.navigation.activeNode ? this.grid.navigation.activeNode.row === this.index : false; - } - - /** - * Toggles the group row expanded/collapsed state. - * ```typescript - * groupRow.toggle() - * ``` - */ - public toggle(): void { - this.grid.toggleGroup(this.groupRow); - } - - private get gridAPI(): GridServiceType { - return this.grid.gridAPI as GridServiceType; - } -} - -export class IgxSummaryRow implements RowType { - /** - * Returns the row index. - */ - public index: number; - - /** - * The grid that contains the row. - */ - public grid: GridType; - - /** - * Returns always true, because this is in instance of an IgxGroupByRow. - */ - public isSummaryRow: boolean; - - - /** - * Returns the curent grid type - */ - private gridType: GridInstanceType; - - /** - * The IGroupByRecord object, representing the group record, if the row is a GroupByRow. - */ - public get summaries(): Map { - return this._summaries ? this._summaries : this.grid.dataView[this.index].summaries; - } - - /** - * Returns the view index calculated per the grid page. - */ - public get viewIndex(): number { - if (this.grid.hasSummarizedColumns && this.grid.page > 0) { - if (this.gridType === GridInstanceType.Grid) { - if (this.grid.page) { - const precedingDetailRows = []; - const precedingGroupRows = []; - const firstRow = this.grid.dataView[0]; - const hasDetailRows = this.grid.expansionStates.size; - const hasGroupedRows = this.grid.groupingExpressions.length; - let precedingSummaryRows = 0; - const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); - - // from groupingFlatResult, resolve two other collections: - // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages - // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages - if (hasDetailRows || hasGroupedRows) { - this.grid.groupingFlatResult.forEach((r, ind) => { - const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; - if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { - precedingGroupRows.push(r); - } - if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && - !this.grid.isGroupByRecord(r)) { - precedingDetailRows.push(r); - } - }); - } - - if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { - // if firstRow is a child of the last item in precedingGroupRows, - // then summaryRow for this given groupedRecord is rendered after firstRow, - // i.e. need to decrease firstRowInd to account for the above. - precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; - if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && - precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { - precedingSummaryRows += -1; - } - } - - return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; - } else { - return this.index; - } - } else if (this.gridType === GridInstanceType.TreeGrid) { - if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { - const firstRowIndex = this.grid.processedExpandedFlatData.indexOf(this.grid.dataView[0].data); - const precedingSummaryRows = this.grid.summaryPosition === GridSummaryPosition.bottom ? - this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) : - this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) + 1; - return firstRowIndex + precedingSummaryRows + this.index; - } - } - } - - return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); - } - - /** - * @hidden - */ - constructor( - grid: GridType, - index: number, private _summaries?: Map, type?: GridInstanceType - ) { - this.grid = grid; - this.index = index; - this.isSummaryRow = true; - this.gridType = type; - } - - private getRootParent(row: ITreeGridRecord): ITreeGridRecord { - while (row.parent) { - row = row.parent; - } - return row; - } -} +import { IGroupByRecord } from '../data-operations/groupby-record.interface'; +import { IgxAddRow, IgxEditRow } from './common/crud.service'; +import { GridInstanceType, GridSummaryCalculationMode, GridSummaryPosition } from './common/enums'; +import { IgxGridCell } from './grid-public-cell'; +import { IgxSummaryResult } from './summaries/grid-summary'; +import { ITreeGridRecord } from './tree-grid/tree-grid.interfaces'; +import mergeWith from 'lodash.mergewith'; +import { CellType, GridServiceType, GridType, RowType } from './common/grid.interface'; + +abstract class BaseRow implements RowType { + public index: number; + /** + * The grid that contains the row. + */ + public grid: GridType; + protected _data?: any; + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex(): number { + return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); + } + + /** + * Gets the row key. + * A row in the grid is identified either by: + * - primaryKey data value, + * - the whole rowData, if the primaryKey is omitted. + * + * ```typescript + * let rowKey = row.key; + * ``` + */ + public get key(): any { + const data = this._data ?? this.grid.dataView[this.index]; + const primaryKey = this.grid.primaryKey; + return primaryKey ? data[primaryKey] : data; + } + + /** + * Gets if this represents add row UI + * + * ```typescript + * let isAddRow = row.addRowUI; + * ``` + */ + public get addRowUI(): boolean { + return !!this.grid.crudService.row && + this.grid.crudService.row.getClassName() === IgxAddRow.name && + this.grid.crudService.row.id === this.key; + } + + /** + * The data record that populates the row. + * + * ```typescript + * let rowData = row.data; + * ``` + */ + public get data(): any { + if (this.inEditMode) { + return mergeWith(this.grid.dataCloneStrategy.clone(this._data ?? this.grid.dataView[this.index]), + this.grid.transactions.getAggregatedValue(this.key, false), + (objValue, srcValue) => { + if (Array.isArray(srcValue)) { + return objValue = srcValue; + } + }); + } + return this._data ?? this.grid.dataView[this.index]; + } + + /** + * Returns if the row is currently in edit mode. + */ + public get inEditMode(): boolean { + if (this.grid.rowEditable) { + const editRowState = this.grid.crudService.row; + return (editRowState && editRowState.id === this.key) || false; + } else { + return false; + } + } + + /** + * Gets whether the row is pinned. + * Default value is `false`. + * ```typescript + * const isPinned = row.pinned; + * ``` + */ + public get pinned(): boolean { + return this.grid.isRecordPinned(this.data); + } + + /** + * Sets whether the row is pinned. + * Default value is `false`. + * ```typescript + * row.pinned = !row.pinned; + * ``` + */ + public set pinned(val: boolean) { + if (val) { + this.pin(); + } else { + this.unpin(); + } + } + + /** + * Gets the row expanded/collapsed state. + * + * ```typescript + * const isExpanded = row.expanded; + * ``` + */ + public get expanded(): boolean { + return this.grid.gridAPI.get_row_expansion_state(this.data); + } + + /** + * Expands/collapses the row. + * + * ```typescript + * row.expanded = true; + * ``` + */ + public set expanded(val: boolean) { + this.grid.gridAPI.set_row_expansion_state(this.key, val); + } + + /** + * Gets whether the row is selected. + * Default value is `false`. + * ```typescript + * row.selected = true; + * ``` + */ + public get selected(): boolean { + return this.grid.selectionService.isRowSelected(this.key); + } + + /** + * Sets whether the row is selected. + * Default value is `false`. + * ```typescript + * row.selected = !row.selected; + * ``` + */ + public set selected(val: boolean) { + if (val) { + this.grid.selectionService.selectRowsWithNoEvent([this.key]); + } else { + this.grid.selectionService.deselectRowsWithNoEvent([this.key]); + } + this.grid.cdr.markForCheck(); + } + + /** + * Returns if the row is in delete state. + */ + public get deleted(): boolean { + return this.grid.gridAPI.row_deleted_transaction(this.key); + } + + /** + * Returns if the row has child rows. Always return false for IgxGridRow. + */ + public get hasChildren(): boolean { + return false; + } + + public get disabled(): boolean { + return this.grid.isGhostRecord(this.data); + } + + /** + * Gets the rendered cells in the row component. + */ + public get cells(): CellType[] { + const res: CellType[] = []; + this.grid.columnList.forEach(col => { + const cell: CellType = new IgxGridCell(this.grid, this.index, col.field); + res.push(cell); + }); + return res; + } + + /** + * Pins the specified row. + * This method emits `onRowPinning` event. + * + * ```typescript + * // pin the selected row from the grid + * this.grid.selectedRows[0].pin(); + * ``` + */ + public pin(): boolean { + return this.grid.pinRow(this.key, this.index); + } + + /** + * Unpins the specified row. + * This method emits `onRowPinning` event. + * + * ```typescript + * // unpin the selected row from the grid + * this.grid.selectedRows[0].unpin(); + * ``` + */ + public unpin(): boolean { + return this.grid.unpinRow(this.key); + } + + /** + * Updates the specified row object and the data source record with the passed value. + * + * ```typescript + * // update the second selected row's value + * let newValue = "Apple"; + * this.grid.selectedRows[1].update(newValue); + * ``` + */ + public update(value: any): void { + const crudService = this.grid.crudService; + if (crudService.cellInEditMode && crudService.cell.id.rowID === this.key) { + this.grid.transactions.endPending(false); + } + const row = new IgxEditRow(this.key, this.index, this.data, this.grid); + this.grid.gridAPI.update_row(row, value); + this.grid.notifyChanges(); + } + + /** + * Removes the specified row from the grid's data source. + * This method emits `onRowDeleted` event. + * + * ```typescript + * // delete the third selected row from the grid + * this.grid.selectedRows[2].delete(); + * ``` + */ + public delete(): void { + this.grid.deleteRowById(this.key); + } +} + +export class IgxGridRow extends BaseRow implements RowType { + /** + * @hidden + */ + constructor( + public grid: GridType, + public index: number, data?: any + ) { + super(); + this._data = data && data.addRow && data.recordRef ? data.recordRef : data; + } + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex(): number { + if (this.grid.paginator) { + const precedingDetailRows = []; + const precedingGroupRows = []; + const firstRow = this.grid.dataView[0]; + const hasDetailRows = this.grid.expansionStates.size; + const hasGroupedRows = this.grid.groupingExpressions.length; + let precedingSummaryRows = 0; + const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); + + // from groupingFlatResult, resolve two other collections: + // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages + // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages + if (hasDetailRows || hasGroupedRows) { + this.grid.groupingFlatResult.forEach((r, ind) => { + const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; + if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { + precedingGroupRows.push(r); + } + if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && !this.grid.isGroupByRecord(r)) { + precedingDetailRows.push(r); + } + }); + } + + if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { + // if firstRow is a child of the last item in precedingGroupRows, + // then summaryRow for this given groupedRecord is rendered after firstRow, + // i.e. need to decrease firstRowInd to account for the above. + precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; + if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && + precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { + precedingSummaryRows += -1; + } + } + + return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; + } else { + return this.index; + } + } + + /** + * Returns the parent row, if grid is grouped. + */ + public get parent(): RowType { + let parent: IgxGroupByRow; + if (!this.grid.groupingExpressions.length) { + return undefined; + } + + let i = this.index - 1; + while (i >= 0 && !parent) { + const rec = this.grid.dataView[i]; + if (this.grid.isGroupByRecord(rec)) { + parent = new IgxGroupByRow(this.grid, i, rec); + } + i--; + } + return parent; + } +} + +export class IgxTreeGridRow extends BaseRow implements RowType { + /** + * @hidden + */ + constructor( + public grid: GridType, + public index: number, data?: any, private _treeRow?: ITreeGridRecord + ) { + super(); + this._data = data && data.addRow && data.recordRef ? data.recordRef : data; + } + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex(): number { + if (this.grid.hasSummarizedColumns && ((this.grid.paginator?.page || 0) > 0)) { + if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { + const firstRowIndex = this.grid.processedExpandedFlatData.indexOf(this.grid.dataView[0].data); + // firstRowIndex is based on data result after all pipes triggered, excluding summary pipe + const precedingSummaryRows = this.grid.summaryPosition === GridSummaryPosition.bottom ? + this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) : + this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) + 1; + // there is a summary row for each root record, so we calculate how many root records are rendered before the current row + return firstRowIndex + precedingSummaryRows + this.index; + } + } + return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); + } + + /** + * The data passed to the row component. + * + * ```typescript + * let selectedRowData = this.grid.selectedRows[0].data; + * ``` + */ + public get data(): any { + if (this.inEditMode) { + return mergeWith(this.grid.dataCloneStrategy.clone(this._data ?? this.grid.dataView[this.index]), + this.grid.transactions.getAggregatedValue(this.key, false), + (objValue, srcValue) => { + if (Array.isArray(srcValue)) { + return objValue = srcValue; + } + }); + } + const rec = this.grid.dataView[this.index]; + return this._data ? this._data : this.grid.isTreeRow(rec) ? rec.data : rec; + } + + /** + * Returns the child rows. + */ + public get children(): RowType[] { + const children: IgxTreeGridRow[] = []; + if (this.treeRow.expanded) { + this.treeRow.children.forEach((rec, i) => { + const row = new IgxTreeGridRow(this.grid, this.index + 1 + i, rec.data); + children.push(row); + }); + } + return children; + } + + /** + * Returns the parent row. + */ + public get parent(): RowType { + const row = this.grid.getRowByKey(this.treeRow.parent?.key); + return row; + } + + /** + * Returns true if child rows exist. Always return false for IgxGridRow. + */ + public get hasChildren(): boolean { + if (this.treeRow.children) { + return this.treeRow.children.length > 0; + } else { + return false; + } + } + + /** + * The `ITreeGridRecord` with metadata about the row in the context of the tree grid. + * + * ```typescript + * const rowParent = this.treeGrid.getRowByKey(1).treeRow.parent; + * ``` + */ + public get treeRow(): ITreeGridRecord { + return this._treeRow ?? this.grid.records.get(this.key); + } + + /** + * Gets whether the row is pinned. + * + * ```typescript + * let isPinned = row.pinned; + * ``` + */ + public get pinned(): boolean { + return this.grid.isRecordPinned(this); + } + + /** + * Sets whether the row is pinned. + * Default value is `false`. + * ```typescript + * row.pinned = !row.pinned; + * ``` + */ + public set pinned(val: boolean) { + if (val) { + this.pin(); + } else { + this.unpin(); + } + } + + /** + * Gets whether the row is expanded. + * + * ```typescript + * let esExpanded = row.expanded; + * ``` + */ + public get expanded(): boolean { + return this.grid.gridAPI.get_row_expansion_state(this.treeRow); + } + + /** + * Expands/collapses the row. + * + * ```typescript + * row.expanded = true; + * ``` + */ + public set expanded(val: boolean) { + this.grid.gridAPI.set_row_expansion_state(this.key, val); + } + + public get disabled(): boolean { + // TODO cell + return this.grid.isGhostRecord(this.data) ? this.treeRow.isFilteredOutParent === undefined : false; + } + + private getRootParent(row: ITreeGridRecord): ITreeGridRecord { + while (row.parent) { + row = row.parent; + } + return row; + } +} + +export class IgxHierarchicalGridRow extends BaseRow implements RowType { + /** + * @hidden + */ + constructor( + public grid: GridType, + public index: number, data?: any + ) { + super(); + this._data = data && data.addRow && data.recordRef ? data.recordRef : data; + } + + /** + * Returns true if row islands exist. + */ + public get hasChildren(): boolean { + return !!this.grid.childLayoutKeys.length; + } + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex() { + const firstRowInd = this.grid.filteredSortedData.indexOf(this.grid.dataView[0]); + const expandedRows = this.grid.filteredSortedData.filter((rec, ind) => { + const rowID = this.grid.primaryKey ? rec[this.grid.primaryKey] : rec; + return this.grid.expansionStates.get(rowID) && ind < firstRowInd; + }); + return firstRowInd + expandedRows.length + this.index; + } + + /** + * Gets the rendered cells in the row component. + */ + public get cells(): CellType[] { + const res: CellType[] = []; + this.grid.columnList.forEach(col => { + const cell: CellType = new IgxGridCell(this.grid, this.index, col.field); + res.push(cell); + }); + return res; + } +} + +export class IgxGroupByRow implements RowType { + /** + * Returns the row index. + */ + public index: number; + + /** + * The grid that contains the row. + */ + public grid: GridType; + + /** + * Returns always true, because this is in instance of an IgxGroupByRow. + */ + public isGroupByRow: boolean; + + /** + * The IGroupByRecord object, representing the group record, if the row is a GroupByRow. + */ + public get groupRow(): IGroupByRecord { + return this._groupRow ? this._groupRow : this.grid.dataView[this.index]; + } + + /** + * Returns the child rows. + */ + public get children(): RowType[] { + const children: IgxGridRow[] = []; + this.groupRow.records.forEach((rec, i) => { + const row = new IgxGridRow(this.grid, this.index + 1 + i, rec); + children.push(row); + }); + return children; + } + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex(): number { + if (this.grid.page) { + const precedingDetailRows = []; + const precedingGroupRows = []; + const firstRow = this.grid.dataView[0]; + const hasDetailRows = this.grid.expansionStates.size; + const hasGroupedRows = this.grid.groupingExpressions.length; + let precedingSummaryRows = 0; + const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); + + // from groupingFlatResult, resolve two other collections: + // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages + // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages + if (hasDetailRows || hasGroupedRows) { + this.grid.groupingFlatResult.forEach((r, ind) => { + const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; + if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { + precedingGroupRows.push(r); + } + if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && !this.grid.isGroupByRecord(r)) { + precedingDetailRows.push(r); + } + }); + } + + if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { + // if firstRow is a child of the last item in precedingGroupRows, + // then summaryRow for this given groupedRecord is rendered after firstRow, + // i.e. need to decrease firstRowInd to account for the above. + precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; + if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && + precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { + precedingSummaryRows += -1; + } + } + + return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; + } else { + return this.index; + } + } + + /** + * @hidden + */ + constructor(grid: GridType, index: number, private _groupRow?: IGroupByRecord) { + this.grid = grid; + this.index = index; + this.isGroupByRow = true; + } + + /** + * Gets whether the row is selected. + * Default value is `false`. + * ```typescript + * row.selected = true; + * ``` + */ + public get selected(): boolean { + return this.children.every(row => row.selected); + } + + /** + * Sets whether the row is selected. + * Default value is `false`. + * ```typescript + * row.selected = !row.selected; + * ``` + */ + public set selected(val: boolean) { + if (val) { + this.children.forEach(row => { + this.grid.selectionService.selectRowsWithNoEvent([row.key]); + }); + } else { + this.children.forEach(row => { + this.grid.selectionService.deselectRowsWithNoEvent([row.key]); + }); + } + this.grid.cdr.markForCheck(); + } + + /** + * Gets/sets whether the group row is expanded. + * ```typescript + * const groupRowExpanded = groupRow.expanded; + * ``` + */ + public get expanded(): boolean { + return this.grid.isExpandedGroup(this.groupRow); + } + + public set expanded(value: boolean) { + this.gridAPI.set_grouprow_expansion_state(this.groupRow, value); + } + + public isActive(): boolean { + return this.grid.navigation.activeNode ? this.grid.navigation.activeNode.row === this.index : false; + } + + /** + * Toggles the group row expanded/collapsed state. + * ```typescript + * groupRow.toggle() + * ``` + */ + public toggle(): void { + this.grid.toggleGroup(this.groupRow); + } + + private get gridAPI(): GridServiceType { + return this.grid.gridAPI as GridServiceType; + } +} + +export class IgxSummaryRow implements RowType { + /** + * Returns the row index. + */ + public index: number; + + /** + * The grid that contains the row. + */ + public grid: GridType; + + /** + * Returns always true, because this is in instance of an IgxGroupByRow. + */ + public isSummaryRow: boolean; + + + /** + * Returns the curent grid type + */ + private gridType: GridInstanceType; + + /** + * The IGroupByRecord object, representing the group record, if the row is a GroupByRow. + */ + public get summaries(): Map { + return this._summaries ? this._summaries : this.grid.dataView[this.index].summaries; + } + + /** + * Returns the view index calculated per the grid page. + */ + public get viewIndex(): number { + if (this.grid.hasSummarizedColumns && this.grid.page > 0) { + if (this.gridType === GridInstanceType.Grid) { + if (this.grid.page) { + const precedingDetailRows = []; + const precedingGroupRows = []; + const firstRow = this.grid.dataView[0]; + const hasDetailRows = this.grid.expansionStates.size; + const hasGroupedRows = this.grid.groupingExpressions.length; + let precedingSummaryRows = 0; + const firstRowInd = this.grid.groupingFlatResult.indexOf(firstRow); + + // from groupingFlatResult, resolve two other collections: + // precedingGroupedRows -> use it to resolve summaryRow for each group in previous pages + // precedingDetailRows -> ise it to resolve the detail row for each expanded grid row in previous pages + if (hasDetailRows || hasGroupedRows) { + this.grid.groupingFlatResult.forEach((r, ind) => { + const rowID = this.grid.primaryKey ? r[this.grid.primaryKey] : r; + if (hasGroupedRows && ind < firstRowInd && this.grid.isGroupByRecord(r)) { + precedingGroupRows.push(r); + } + if (this.grid.expansionStates.get(rowID) && ind < firstRowInd && + !this.grid.isGroupByRecord(r)) { + precedingDetailRows.push(r); + } + }); + } + + if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { + // if firstRow is a child of the last item in precedingGroupRows, + // then summaryRow for this given groupedRecord is rendered after firstRow, + // i.e. need to decrease firstRowInd to account for the above. + precedingSummaryRows = precedingGroupRows.filter(gr => this.grid.isExpandedGroup(gr)).length; + if (this.grid.summaryPosition === GridSummaryPosition.bottom && precedingGroupRows.length && + precedingGroupRows[precedingGroupRows.length - 1].records.indexOf(firstRow) > -1) { + precedingSummaryRows += -1; + } + } + + return precedingDetailRows.length + precedingSummaryRows + firstRowInd + this.index; + } else { + return this.index; + } + } else if (this.gridType === GridInstanceType.TreeGrid) { + if (this.grid.summaryCalculationMode !== GridSummaryCalculationMode.rootLevelOnly) { + const firstRowIndex = this.grid.processedExpandedFlatData.indexOf(this.grid.dataView[0].data); + const precedingSummaryRows = this.grid.summaryPosition === GridSummaryPosition.bottom ? + this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) : + this.grid.rootRecords.indexOf(this.getRootParent(this.grid.dataView[0])) + 1; + return firstRowIndex + precedingSummaryRows + this.index; + } + } + } + + return this.index + ((this.grid.paginator?.page || 0) * (this.grid.paginator?.perPage || 0)); + } + + /** + * @hidden + */ + constructor( + grid: GridType, + index: number, private _summaries?: Map, type?: GridInstanceType + ) { + this.grid = grid; + this.index = index; + this.isSummaryRow = true; + this.gridType = type; + } + + private getRootParent(row: ITreeGridRecord): ITreeGridRecord { + while (row.parent) { + row = row.parent; + } + return row; + } +} diff --git a/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts index 8502e14f143..bc0987bd294 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts @@ -996,7 +996,7 @@ describe('IgxGrid - multi-column headers #grid', () => { expect(grid.getCellByColumn(0, 'City').value).toEqual('Berlin'); }); - it('Should pin column groups using indexes correctly.', () => { + it('Should pin column groups using indexes correctly.', fakeAsync(() => { fixture = TestBed.createComponent(StegosaurusGridComponent); fixture.detectChanges(); const ci = fixture.componentInstance; @@ -1018,6 +1018,7 @@ describe('IgxGrid - multi-column headers #grid', () => { // unpinning with index expect(grid.unpinColumn(ci.genInfoColGroup, 2)).toBe(true); + tick(); fixture.detectChanges(); const postUnpinningColList = [ci.idCol].concat(ci.postalCodeColList).concat(ci.cityColList) .concat(ci.countryColList).concat(ci.regionColList).concat(ci.genInfoColList) @@ -1027,18 +1028,21 @@ describe('IgxGrid - multi-column headers #grid', () => { // pinning to non-existent index expect(grid.pinColumn(ci.genInfoColGroup, 15)).toBe(false); + tick(); fixture.detectChanges(); testColumnsVisibleIndexes(postUnpinningColList); testColumnPinning(ci.genInfoColGroup, false); // pinning to negative index expect(grid.pinColumn(ci.genInfoColGroup, -15)).toBe(false); + tick(); fixture.detectChanges(); testColumnsVisibleIndexes(postUnpinningColList); testColumnPinning(ci.genInfoColGroup, false); // pinning with index expect(grid.pinColumn(ci.genInfoColGroup, 2)).toBe(true); + tick(); fixture.detectChanges(); const postPinningColList = [ci.idCol].concat(ci.postalCodeColList).concat(ci.genInfoColList) .concat(ci.cityColList).concat(ci.countryColList).concat(ci.regionColList) @@ -1048,14 +1052,18 @@ describe('IgxGrid - multi-column headers #grid', () => { // unpinning to non-existent index expect(grid.unpinColumn(ci.genInfoColGroup, 15)).toBe(false); + tick(); + fixture.detectChanges(); testColumnsVisibleIndexes(postPinningColList); testColumnPinning(ci.genInfoColGroup, true); // unpinning to negative index expect(grid.unpinColumn(ci.genInfoColGroup, -15)).toBe(false); + tick(); + fixture.detectChanges(); testColumnsVisibleIndexes(postPinningColList); testColumnPinning(ci.genInfoColGroup, true); - }); + })); it('Should initially pin the whole group when one column of the group is pinned', () => { fixture = TestBed.createComponent(ThreeGroupsThreeColumnsGridComponent); @@ -1074,7 +1082,7 @@ describe('IgxGrid - multi-column headers #grid', () => { grid = fixture.componentInstance.grid; })); - it('Should not allow moving group to another level via API.', () => { + it('Should not allow moving group to another level via API.', fakeAsync(() => { expect(grid.pinnedColumns.length).toEqual(0); expect(grid.unpinnedColumns.length).toEqual(16); expect(grid.rowList.first.cells.first.value).toMatch('ALFKI'); @@ -1095,6 +1103,7 @@ describe('IgxGrid - multi-column headers #grid', () => { // Try to move a group column to pinned area, where there is non group column const contName = grid.getColumnByName('ContactName'); grid.moveColumn(contName, colID); + tick(); fixture.detectChanges(); // pinning should be unsuccesfull ! @@ -1123,18 +1132,22 @@ describe('IgxGrid - multi-column headers #grid', () => { const contTitle = grid.getColumnByName('ContactTitle'); grid.moveColumn(colID, genGroup); + tick(); + fixture.detectChanges(); grid.moveColumn(compName, persDetails); - grid.moveColumn(contName, contTitle); + tick(); fixture.detectChanges(); + grid.moveColumn(contName, contTitle); + tick(); fixture.detectChanges(); expect(grid.rowList.first.cells.first.value).toMatch('Sales Representative'); expect(grid.rowList.first.cells.toArray()[1].value).toMatch('Maria Anders'); expect(grid.rowList.first.cells.toArray()[2].value).toMatch('Alfreds Futterkiste'); expect(grid.rowList.first.cells.toArray()[3].value).toMatch('ALFKI'); - }); + })); - it('Should move column group correctly. One level column groups.', () => { + it('Should move column group correctly. One level column groups.', fakeAsync(() => { fixture = TestBed.createComponent(ThreeGroupsThreeColumnsGridComponent); fixture.detectChanges(); const ci = fixture.componentInstance; @@ -1148,36 +1161,42 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving last to be first grid.moveColumn(ci.contactInfoColGroup, ci.genInfoColGroup, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); testColumnsOrder(contactInfoCols.concat(genInfoCols).concat(locCols)); // moving first to be last grid.moveColumn(ci.contactInfoColGroup, ci.locationColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoCols.concat(locCols).concat(contactInfoCols)); // moving inner to be last grid.moveColumn(ci.locationColGroup, ci.contactInfoColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoCols.concat(contactInfoCols).concat(locCols)); // moving inner to be first grid.moveColumn(ci.contactInfoColGroup, ci.genInfoColGroup, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); testColumnsOrder(contactInfoCols.concat(genInfoCols).concat(locCols)); // moving to the same spot, no change expected grid.moveColumn(ci.genInfoColGroup, ci.genInfoColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(contactInfoCols.concat(genInfoCols).concat(locCols)); // moving column group to the place of a column, no change expected grid.moveColumn(ci.genInfoColGroup, ci.countryCol); + tick(); fixture.detectChanges(); testColumnsOrder(contactInfoCols.concat(genInfoCols).concat(locCols)); - }); + })); - it('Should move columns within column groups. One level column groups.', () => { + it('Should move columns within column groups. One level column groups.', fakeAsync(() => { fixture = TestBed.createComponent(ThreeGroupsThreeColumnsGridComponent); fixture.detectChanges(); const ci = fixture.componentInstance; @@ -1188,48 +1207,55 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving last to be first grid.moveColumn(ci.postalCodeCol, ci.phoneCol, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.postalCodeCol, ci.phoneCol, ci.faxCol])); // moving first to be last grid.moveColumn(ci.postalCodeCol, ci.faxCol); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.phoneCol, ci.faxCol, ci.postalCodeCol])); // moving inner to be last grid.moveColumn(ci.faxCol, ci.postalCodeCol); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.phoneCol, ci.postalCodeCol, ci.faxCol])); // moving inner to be first grid.moveColumn(ci.postalCodeCol, ci.phoneCol, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.postalCodeCol, ci.phoneCol, ci.faxCol])); // moving to the sample spot, no change expected grid.moveColumn(ci.postalCodeCol, ci.postalCodeCol); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.postalCodeCol, ci.phoneCol, ci.faxCol])); // moving column to the place of its column group, no change expected grid.moveColumn(ci.postalCodeCol, ci.contactInfoColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.postalCodeCol, ci.phoneCol, ci.faxCol])); //// moving column to the place of a column group, no change expected grid.moveColumn(ci.postalCodeCol, ci.genInfoColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(genInfoAndLocCols.concat([ci.contactInfoColGroup, ci.postalCodeCol, ci.phoneCol, ci.faxCol])); - }); + })); - it('Should move columns and groups. Two level column groups.', () => { + it('Should move columns and groups. Two level column groups.', fakeAsync(() => { fixture = TestBed.createComponent(NestedColGroupsGridComponent); fixture.detectChanges(); const ci = fixture.componentInstance; @@ -1237,12 +1263,14 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving a two-level col grid.moveColumn(ci.phoneCol, ci.locationColGroup, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); testColumnsOrder([ci.contactInfoColGroup, ci.phoneCol, ci.locationColGroup, ci.countryCol, ci.genInfoColGroup, ci.companyNameCol, ci.cityCol]); // moving a three-level col grid.moveColumn(ci.cityCol, ci.contactInfoColGroup, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); const colsOrder = [ci.cityCol, ci.contactInfoColGroup, ci.phoneCol, ci.locationColGroup, ci.countryCol, ci.genInfoColGroup, ci.companyNameCol]; @@ -1250,28 +1278,34 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving between different groups, hould stay the same grid.moveColumn(ci.locationColGroup, ci.companyNameCol); + tick(); fixture.detectChanges(); testColumnsOrder(colsOrder); // moving between different levels, should stay the same grid.moveColumn(ci.countryCol, ci.phoneCol); + tick(); + fixture.detectChanges(); testColumnsOrder(colsOrder); // moving between different levels, should stay the same grid.moveColumn(ci.cityCol, ci.phoneCol); + tick(); fixture.detectChanges(); testColumnsOrder(colsOrder); grid.moveColumn(ci.genInfoColGroup, ci.companyNameCol); + tick(); fixture.detectChanges(); testColumnsOrder(colsOrder); grid.moveColumn(ci.locationColGroup, ci.contactInfoColGroup); + tick(); fixture.detectChanges(); testColumnsOrder(colsOrder); - }); + })); - it('Should move columns and groups. Pinning enabled.', () => { + it('Should move columns and groups. Pinning enabled.', fakeAsync(() => { fixture = TestBed.createComponent(StegosaurusGridComponent); fixture.detectChanges(); const ci = fixture.componentInstance; @@ -1287,6 +1321,7 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving group from unpinned to pinned ci.grid.moveColumn(ci.phoneColGroup, ci.idCol, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); let postMovingOrder = ci.phoneColList.concat([ci.idCol]).concat(ci.genInfoColList) .concat(ci.postalCodeColList).concat(ci.cityColList).concat(ci.countryColList) @@ -1297,6 +1332,7 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving sub group to different parent, should not be allowed ci.grid.moveColumn(ci.pDetailsColGroup, ci.regionCol); + tick(); fixture.detectChanges(); testColumnsVisibleIndexes(postMovingOrder); testColumnPinning(ci.pDetailsColGroup, true); @@ -1304,6 +1340,7 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving pinned group as firstly unpinned ci.grid.moveColumn(ci.idCol, ci.cityColGroup); + tick(); fixture.detectChanges(); ci.idCol.pinned = false; fixture.detectChanges(); @@ -1317,11 +1354,12 @@ describe('IgxGrid - multi-column headers #grid', () => { // moving column to different parent, shound not be allowed ci.grid.moveColumn(ci.postalCodeCol, ci.cityCol); + tick(); fixture.detectChanges(); testColumnsVisibleIndexes(postMovingOrder); testColumnPinning(ci.postalCodeCol, true); testColumnPinning(ci.cityCol, true); - }); + })); }); describe('Features integration tests: ', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/column-moving.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column-moving.spec.ts index 003a8904a82..ae74b1b7f8f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/column-moving.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/column-moving.spec.ts @@ -1,5 +1,5 @@ import { DebugElement } from '@angular/core'; -import { TestBed, fakeAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -51,28 +51,30 @@ describe('IgxGrid - Column Moving #grid', () => { grid.moving = true; })); - it('Should be able to reorder columns.', (() => { + it('Should be able to reorder columns.', fakeAsync(() => { let columnsList = grid.columnList; grid.moveColumn(columnsList.get(0), columnsList.get(2)); - + tick(); + fixture.detectChanges(); expect(columnsList.get(0).field).toEqual('Name'); expect(columnsList.get(1).field).toEqual('LastName'); expect(columnsList.get(2).field).toEqual('ID'); })); - it('Should be able to reorder columns programmatically.', () => { + it('Should be able to reorder columns programmatically.', fakeAsync(() => { let columnsList = grid.columnList; const column = columnsList.get(0) as IgxColumnComponent; column.move(2); + tick(); fixture.detectChanges(); columnsList = grid.columnList; expect(columnsList.get(0).field).toEqual('Name'); expect(columnsList.get(1).field).toEqual('LastName'); expect(columnsList.get(2).field).toEqual('ID'); - }); + })); - it('Should not reorder columns, if passed incorrect index.', () => { + it('Should not reorder columns, if passed incorrect index.', fakeAsync(() => { let columnsList = grid.columnList; expect(columnsList.get(0).field).toEqual('ID'); @@ -81,6 +83,7 @@ describe('IgxGrid - Column Moving #grid', () => { const column = columnsList.get(0) as IgxColumnComponent; column.move(-1); + tick(); fixture.detectChanges(); columnsList = grid.columnList; @@ -89,15 +92,16 @@ describe('IgxGrid - Column Moving #grid', () => { expect(columnsList.get(2).field).toEqual('LastName'); column.move(columnsList.length); + tick(); fixture.detectChanges(); columnsList = grid.columnList; expect(columnsList.get(0).field).toEqual('ID'); expect(columnsList.get(1).field).toEqual('Name'); expect(columnsList.get(2).field).toEqual('LastName'); - }); + })); - it('Should show hidden column on correct index', () => { + it('Should show hidden column on correct index', fakeAsync(() => { let columnsList = grid.columnList; const column = columnsList.get(0) as IgxColumnComponent; @@ -105,6 +109,7 @@ describe('IgxGrid - Column Moving #grid', () => { fixture.detectChanges(); column.move(2); + tick(); column.hidden = false; fixture.detectChanges(); @@ -112,23 +117,25 @@ describe('IgxGrid - Column Moving #grid', () => { expect(columnsList.get(0).field).toEqual('Name'); expect(columnsList.get(1).field).toEqual('LastName'); expect(columnsList.get(2).field).toEqual('ID'); - }); + })); - it('Should fire columnMovingEnd with correct values of event arguments.', () => { + it('Should fire columnMovingEnd with correct values of event arguments.', fakeAsync(() => { let columnsList = grid.columnList; const column = columnsList.get(0) as IgxColumnComponent; spyOn(grid.columnMovingEnd, 'emit').and.callThrough(); column.move(2); + tick(); + fixture.detectChanges(); columnsList = grid.columnList; const args = { source: grid.columnList.get(2), target: grid.columnList.get(1), cancel: false }; expect(grid.columnMovingEnd.emit).toHaveBeenCalledTimes(1); expect(grid.columnMovingEnd.emit).toHaveBeenCalledWith(args); - }); + })); - it('Should exit edit mode and commit the new value when column moving programmatically', () => { + it('Should exit edit mode and commit the new value when column moving programmatically', fakeAsync(() => { fixture.componentInstance.isEditable = true; fixture.detectChanges(); const cacheValue = grid.getCellByColumn(0, 'ID').value; @@ -152,15 +159,16 @@ describe('IgxGrid - Column Moving #grid', () => { const columnsList = grid.columnList; const column = columnsList.get(0) as IgxColumnComponent; column.move(2); + tick(); fixture.detectChanges(); // step 4 - verify cell has exited edit mode correctly expect(grid.columnList.get(2).field).toEqual('ID'); expect(grid.getCellByColumn(0, 'ID').editMode).toBe(false); expect(grid.getCellByColumn(0, 'ID').value).toBe(cacheValue); - }); + })); - it('Should preserve hidden columns order after columns are reordered programmatically', () => { + it('Should preserve hidden columns order after columns are reordered programmatically', fakeAsync(() => { // step 1 - hide a column fixture.componentInstance.isHidden = true; @@ -171,6 +179,7 @@ describe('IgxGrid - Column Moving #grid', () => { const columnsList = grid.columnList; const column = columnsList.get(2) as IgxColumnComponent; column.move(1); + tick(); fixture.detectChanges(); @@ -184,7 +193,7 @@ describe('IgxGrid - Column Moving #grid', () => { expect(grid.visibleColumns[0].field).toEqual('ID'); expect(grid.visibleColumns[1].field).toEqual('Name'); expect(grid.visibleColumns[2].field).toEqual('LastName'); - }); + })); it('Should not break vertical or horizontal scrolling after columns are reordered programmatically', (async () => { let columnsList = grid.columnList; @@ -192,6 +201,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 1 - move a column const column = columnsList.get(1) as IgxColumnComponent; column.move(2); + await wait(); fixture.detectChanges(); columnsList = grid.columnList; @@ -227,6 +237,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move a column const column = columnsList.get(0) as IgxColumnComponent; column.move(2); + await wait(); fixture.detectChanges(); fixture.detectChanges(); @@ -248,6 +259,7 @@ describe('IgxGrid - Column Moving #grid', () => { const column = columnsList.get(0) as IgxColumnComponent; column.move(1); + await wait(); fixture.detectChanges(); columnsList = grid.columnList; @@ -896,6 +908,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move that column and verify selection is preserved const column = columnsList.get(0) as IgxColumnComponent; column.move(2); + await wait(); fixture.detectChanges(); columnsList = grid.columnList; @@ -996,6 +1009,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 1 - move a column const column = columnsList.get(0) as IgxColumnComponent; column.move(2); + await wait(); fixture.detectChanges(); // step 2 - navigate to page 2 and verify correct column order @@ -1071,6 +1085,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move that column const column = columnsList.get(0) as IgxColumnComponent; column.move(3); + await wait(); fixture.detectChanges(); fixture.detectChanges(); @@ -1115,6 +1130,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move a pinned column const column = grid.getColumnByName('ID'); column.move(2); + await wait(); fixture.detectChanges(); // step 3 - verify pinned columns are reordered correctly @@ -1214,6 +1230,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move that column and verify selection is preserved const column = columnsList.get(4) as IgxColumnComponent; column.move(2); + await wait(); fixture.detectChanges(); // step 3 - unpin that column programmatically and verify correct order @@ -1291,6 +1308,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - move that column and verify selection is preserved const column = grid.getColumnByName('ContactName') as IgxColumnComponent; column.move(1); + await wait(); fixture.detectChanges(); // step 3 - verify column is still unpinned @@ -1356,6 +1374,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - drag/drop a pinned column among unpinned columns const column = grid.getColumnByName('ID') as IgxColumnComponent; column.move(1); + await wait(); fixture.detectChanges(); // step 3 - verify column is unpinned at the correct place @@ -1429,6 +1448,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 1 - move level 0 column to first position let column = grid.getColumnByName('ID'); column.move(0); + await wait(); fixture.detectChanges(); let columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1438,6 +1458,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 2 - try moving level 0 column into column group // not possible column.move(3); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1448,6 +1469,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 3 - try moving level 0 column into column group // not possible column.move(5); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1458,6 +1480,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 4 - try moving level 0 column between two column groups column.move(4); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1469,6 +1492,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 5 - move level 0 column to last position column.move(8); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1479,6 +1503,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 6 - move last column between two column groups column.move(4); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1492,6 +1517,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 7 - move level 1 column in the group column = grid.getColumnByName('Address'); column.move(5); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1501,6 +1527,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 8 - move level 1 column outside the group // not possible column.move(4); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1511,6 +1538,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 9 - move level 2 column outsuide the group column = grid.getColumnByName('ContactName'); column.move(0); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1522,6 +1550,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 10 - move level 2 column inside the group column = grid.getColumnByName('ContactTitle'); column.move(2); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1533,6 +1562,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 11 - move level 2 column inside the group column = grid.getColumnByName('Missing'); column.move(8); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1544,6 +1574,7 @@ describe('IgxGrid - Column Moving #grid', () => { column = grid.getColumnByName('CompanyName'); column.move(1); + await wait(); fixture.detectChanges(); expect(columnsList[0].field).toEqual('CompanyName'); @@ -1551,6 +1582,7 @@ describe('IgxGrid - Column Moving #grid', () => { expect(columnsList[2].field).toEqual('ContactName'); column.move(2); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1562,6 +1594,7 @@ describe('IgxGrid - Column Moving #grid', () => { it('MCH - should not move group column to last position', (async () => { let column = grid.getColumnByName('Missing'); column.move(3); + await wait(); fixture.detectChanges(); let columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1571,6 +1604,7 @@ describe('IgxGrid - Column Moving #grid', () => { column = grid.getColumnByName('CompanyName').topLevelParent; column.move(8); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1583,6 +1617,7 @@ describe('IgxGrid - Column Moving #grid', () => { let column = grid.getColumnByName('Missing'); column.move(3); + await wait(); fixture.detectChanges(); let columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1592,6 +1627,7 @@ describe('IgxGrid - Column Moving #grid', () => { column = grid.getColumnByName('CompanyName').topLevelParent; column.move(6); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); @@ -1611,6 +1647,7 @@ describe('IgxGrid - Column Moving #grid', () => { // step 1 - move level 0 column to first position const column = grid.getColumnByName('CompanyName'); column.move(8); + await wait(); fixture.detectChanges(); columnsList = grid.columnList.filter((col) => !(col instanceof IgxColumnGroupComponent)); diff --git a/projects/igniteui-angular/src/lib/grids/grid/column-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column-selection.spec.ts index db5b48db39b..450de2e5511 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/column-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/column-selection.spec.ts @@ -1090,17 +1090,18 @@ describe('IgxGrid - Column Selection #grid', () => { GridSelectionFunctions.verifyColumnAndCellsSelected(colProductID); }); - it('Moving: Verify that when move a column, it stays selected', () => { + it('Moving: Verify that when move a column, it stays selected', fakeAsync(() => { colProductID.selected = true; fix.detectChanges(); grid.moveColumn(colProductID, colProductName); + tick(); fix.detectChanges(); GridSelectionFunctions.verifyColumnAndCellsSelected(colProductID); GridSelectionFunctions.verifyColumnAndCellsSelected(colProductName, false); expect(colProductID.visibleIndex).toEqual(1); - }); + })); it('Paging: Verify column stays selected when change page', fakeAsync(() => { colProductName.selected = true; diff --git a/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts index 3dcd6f488aa..a7158070563 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts @@ -160,7 +160,7 @@ describe('IgxGrid - Column properties #grid', () => { } }); - it('should reflect the column in the DOM based on its index', () => { + it('should reflect the column in the DOM based on its index', fakeAsync(() => { const fix = TestBed.createComponent(ColumnCellFormatterComponent); fix.detectChanges(); @@ -176,6 +176,7 @@ describe('IgxGrid - Column properties #grid', () => { // Swap columns grid.moveColumn(grid.columnList.first, grid.columnList.last); + tick(); fix.detectChanges(); expect(grid.columnList.first.field).toMatch('IsEmployed'); @@ -184,7 +185,7 @@ describe('IgxGrid - Column properties #grid', () => { headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); expect(headers[0].nativeElement.textContent).toMatch('IsEmployed'); expect(headers[1].nativeElement.textContent).toMatch('Name'); - }); + })); it('should support adding and removing columns through a declared iterable', fakeAsync(/** columnList.changes rAF */() => { const fix = TestBed.createComponent(ColumnsFromIterableComponent); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-add-row.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-add-row.spec.ts index 1d03fe73928..dbd2450d6b6 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-add-row.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-add-row.spec.ts @@ -1,6 +1,6 @@ import { IgxGridModule, IgxGridComponent } from './public_api'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { TestBed, fakeAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { DebugElement } from '@angular/core'; import { GridFunctions, GridSummaryFunctions } from '../../test-utils/grid-functions.spec'; @@ -936,7 +936,7 @@ describe('IgxGrid - Row Adding #grid', () => { gridContent = GridFunctions.getGridContent(fixture); })); - it('Should exit add row mode when moving a column', () => { + it('Should exit add row mode when moving a column', fakeAsync(() => { spyOn(grid.gridAPI.crudService, 'endEdit').and.callThrough(); const dataLength = grid.data.length; const row = grid.rowList.first; @@ -949,12 +949,13 @@ describe('IgxGrid - Row Adding #grid', () => { expect(grid.rowEditingOverlay.collapsed).toEqual(false); grid.moveColumn(grid.columnList.get(1), grid.columnList.get(2)); + tick(); fixture.detectChanges(); expect(grid.gridAPI.crudService.endEdit).toHaveBeenCalled(); expect(grid.data.length).toBe(dataLength); expect(grid.rowEditingOverlay.collapsed).toEqual(true); - }); + })); it('Should exit add row mode when pinning/unpinning a column', () => { spyOn(grid.gridAPI.crudService, 'endEdit').and.callThrough(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-selection.spec.ts index 85157f78a1f..a7253c4de48 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-selection.spec.ts @@ -2266,7 +2266,7 @@ describe('IgxGrid - Cell selection #grid', () => { expect(grid.getSelectedData()).toEqual(selectedData); }); - it('Moving: selection should not change when move columns inside selected range', () => { + it('Moving: selection should not change when move columns inside selected range', fakeAsync(() => { const firstCell = grid.gridAPI.get_cell_by_index(2, 'ParentID'); const secondCell = grid.gridAPI.get_cell_by_index(4, 'HireDate'); GridSelectionFunctions.selectCellsRangeNoWait(fix, firstCell, secondCell); @@ -2282,6 +2282,7 @@ describe('IgxGrid - Cell selection #grid', () => { GridSelectionFunctions.verifyCellsRegionSelected(grid, 2, 4, 1, 3); expect(grid.getSelectedData()).toEqual(selectedData); grid.moveColumn(grid.getColumnByName('ParentID'), grid.getColumnByName('HireDate')); + tick(); fix.detectChanges(); GridSelectionFunctions.verifySelectedRange(grid, 2, 4, 1, 3); @@ -2291,6 +2292,7 @@ describe('IgxGrid - Cell selection #grid', () => { fix.detectChanges(); grid.moveColumn(grid.getColumnByName('ParentID'), grid.getColumnByName('ID'), DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); const newSelectedData = [ { ID: 317, Name: 'Monica Reyes', HireDate: new Date('Sep 18, 2014') }, @@ -2300,7 +2302,7 @@ describe('IgxGrid - Cell selection #grid', () => { GridSelectionFunctions.verifySelectedRange(grid, 2, 4, 1, 3); GridSelectionFunctions.verifyCellsRegionSelected(grid, 2, 4, 1, 3); expect(grid.getSelectedData()).toEqual(newSelectedData); - }); + })); it('Summaries: selection range should not change when enable/disable summaries', (async () => { grid.height = '600px'; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-collapsible-columns.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-collapsible-columns.spec.ts index b28495540b1..4676bc0f9d6 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-collapsible-columns.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-collapsible-columns.spec.ts @@ -1,4 +1,4 @@ -import { TestBed, fakeAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { IgxGridModule } from './grid.module'; import { IgxGridComponent } from './grid.component'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -554,7 +554,7 @@ describe('IgxGrid - multi-column headers #grid', () => { GridFunctions.verifyGroupIsExpanded(fixture, addressInf, true, true); }); - it('Moving: Verify that expanded state is preserved when move column group', () => { + it('Moving: Verify that expanded state is preserved when move column group', fakeAsync(() => { const generalInf = GridFunctions.getColGroup(grid, 'General Information'); expect(addressInf.expanded).toBeTruthy(); @@ -562,22 +562,25 @@ describe('IgxGrid - multi-column headers #grid', () => { expect(generalInf.visibleIndex).toBe(1); grid.moveColumn(generalInf, addressInf, DropPosition.AfterDropTarget); + tick(); fixture.detectChanges(); expect(addressInf.expanded).toBeTruthy(); expect(generalInf.collapsible).toBeFalsy(); expect(generalInf.visibleIndex).toBe(3); addressInf.expanded = false; + tick(); fixture.detectChanges(); expect(addressInf.expanded).toBeFalsy(); grid.moveColumn(generalInf, addressInf, DropPosition.BeforeDropTarget); + tick(); fixture.detectChanges(); expect(addressInf.expanded).toBeFalsy(); expect(generalInf.collapsible).toBeFalsy(); expect(generalInf.visibleIndex).toBe(1); - }); + })); it('Moving: Verify moving column inside the group', () => { const postalCode = grid.getColumnByName('PostalCode'); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index d2e499c6c2c..57d415792bf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -2187,7 +2187,7 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { // Filtering + Moving it('should move chip under the correct column when column is moved and filter row should open for correct column.', - () => { + fakeAsync(() => { grid.filter('ProductName', 'Angular', IgxStringFilteringOperand.instance().condition('contains')); fix.detectChanges(); @@ -2195,6 +2195,7 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const stringCol = grid.getColumnByName('ProductName'); const numberCol = grid.getColumnByName('Downloads'); grid.moveColumn(stringCol, numberCol); + tick(); fix.detectChanges(); // check UI in filter cell is correct after moving @@ -2202,7 +2203,7 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { expect(GridFunctions.getChipText(filteringCells[2])).toEqual('Angular'); expect(GridFunctions.getChipText(filteringCells[1])).toEqual('Filter'); - }); + })); // Filtering + Hiding it('should not display filter cell for hidden columns and chips should show under correct column.', fakeAsync(() => { @@ -3095,6 +3096,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const moveRight = GridFunctions.getExcelStyleFilteringMoveButtons(fix)[1]; moveLeft.click(); + tick(); fix.detectChanges(); expect(grid.columnList.get(2).field).toBe('ProductName'); @@ -5708,6 +5710,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const moveRight = GridFunctions.getExcelStyleFilteringMoveButtons(fix)[1]; moveLeft.click(); + tick(); fix.detectChanges(); expect(grid.columnList.get(2).field).toBe('ProductName'); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-mrl-keyboard-nav.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-mrl-keyboard-nav.spec.ts index 5c08495ca80..4346a97e0da 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-mrl-keyboard-nav.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-mrl-keyboard-nav.spec.ts @@ -1,5 +1,5 @@ import { Component, ViewChild } from '@angular/core'; -import { TestBed, ComponentFixture, fakeAsync } from '@angular/core/testing'; +import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxGridModule, IGridCellEventArgs } from './public_api'; @@ -965,7 +965,7 @@ describe('IgxGrid Multi Row Layout - Keyboard navigation #grid', () => { }); describe('Column Moving Integration', () => { - it('tab navigation should follow correct sequence if a column is moved.', () => { + it('tab navigation should follow correct sequence if a column is moved.', fakeAsync(() => { fix.componentInstance.colGroups = [{ group: 'group1', // row span 3 @@ -996,6 +996,7 @@ describe('IgxGrid Multi Row Layout - Keyboard navigation #grid', () => { const col1 = grid.getColumnByName('group3'); const col2 = grid.getColumnByName('group1'); grid.moveColumn(col2, col1); + tick(); fix.detectChanges(); // check visible indexes are correct @@ -1006,7 +1007,7 @@ describe('IgxGrid Multi Row Layout - Keyboard navigation #grid', () => { expect(grid.getCellByColumn(0, 'City').column.visibleIndex).toBe(4); expect(grid.getCellByColumn(0, 'ContactTitle').column.visibleIndex).toBe(5); expect(grid.getCellByColumn(0, 'PostalCode').column.visibleIndex).toBe(6); - }); + })); }); describe('Pinning integration', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index 5f2c9a92d1b..690c13a972c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -1401,7 +1401,7 @@ describe('IgxGrid - Row Editing #grid', () => { ['Count', 'Earliest', 'Latest'], ['10', 'Jan 1, 1901', 'Dec 25, 2025']); })); - it(`Moving: Should exit edit mode when moving a column`, () => { + it(`Moving: Should exit edit mode when moving a column`, fakeAsync(() => { grid.moving = true; const column = grid.columnList.filter(c => c.field === 'ProductName')[0]; const targetColumn = grid.columnList.filter(c => c.field === 'ProductID')[0]; @@ -1417,14 +1417,14 @@ describe('IgxGrid - Row Editing #grid', () => { expect(cell.editMode).toEqual(true); expect(grid.rowEditingOverlay.collapsed).toEqual(false); grid.moveColumn(column, targetColumn); - + tick(); fix.detectChanges(); expect(cell.editMode).toBeFalsy(); expect(grid.gridAPI.crudService.endEdit).toHaveBeenCalled(); expect(grid.gridAPI.crudService.endEdit).toHaveBeenCalledWith(false); expect(grid.rowEditingOverlay.collapsed).toEqual(true); - }); + })); it(`Pinning: Should exit edit mode when pinning/unpinning a column`, () => { spyOn(grid.gridAPI.crudService, 'endEdit').and.callThrough(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts index e64877a135a..91eb04611ee 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts @@ -614,13 +614,14 @@ describe('IgxGrid - Summaries #grid', () => { ['Count', 'Earliest', 'Latest'], ['10', 'May 17, 1990', 'Dec 25, 2025']); })); - it('Moving: should move summaries when move column', () => { + it('Moving: should move summaries when move column', fakeAsync(() => { grid.moving = true; const colUnitsInStock = grid.getColumnByName('UnitsInStock'); const colProductID = grid.getColumnByName('ProductID'); fix.detectChanges(); grid.moveColumn(colUnitsInStock, colProductID, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); const summaryRow = fix.debugElement.query(By.css(SUMMARY_ROW)); @@ -631,7 +632,7 @@ describe('IgxGrid - Summaries #grid', () => { GridSummaryFunctions.verifyColumnSummaries(summaryRow, 3, ['Count'], ['10']); GridSummaryFunctions.verifyColumnSummaries(summaryRow, 4, ['Count', 'Earliest', 'Latest'], ['10', 'May 17, 1990', 'Dec 25, 2025']); - }); + })); it('Hiding: should hide summary row when a column which has summary is hidded', fakeAsync(() => { grid.getColumnByName('ProductName').hasSummary = false; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.master-detail.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.master-detail.spec.ts index 715b977a371..9fa96150a9b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.master-detail.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.master-detail.spec.ts @@ -785,19 +785,21 @@ describe('IgxGrid Master Detail #grid', () => { fix.detectChanges(); })); - it('Should keep the expand/collapse icon in the first column, even when moving a column in first place.', () => { + it('Should keep the expand/collapse icon in the first column, even when moving a column in first place.', fakeAsync(() => { grid.moveColumn(grid.columnList.last, grid.columnList.first); + tick(); fix.detectChanges(); expect(grid.rowList.first.cells.first instanceof IgxGridExpandableCellComponent).toBeTruthy(); - }); + })); - it('Should keep the expand/collapse icon in the first column, even when moving a column out of first place.', () => { + it('Should keep the expand/collapse icon in the first column, even when moving a column out of first place.', fakeAsync(() => { grid.moveColumn(grid.columnList.first, grid.columnList.last); + tick(); fix.detectChanges(); expect(grid.rowList.first.cells.first instanceof IgxGridExpandableCellComponent).toBeTruthy(); - }); + })); }); describe('Cell Selection', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pagination.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pagination.spec.ts index bdcb10659f8..eb6221eb73c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pagination.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pagination.spec.ts @@ -84,7 +84,7 @@ describe('IgxGrid - Grid Paging #grid', () => { it('should paginate data API', () => { fix.detectChanges(); - // Goto page 3 through API and listen for event + // Goto page 3 through API and listen for event spyOn(grid.pagingDone, 'emit'); grid.paginate(2); @@ -197,6 +197,7 @@ describe('IgxGrid - Grid Paging #grid', () => { expect(grid.nativeElement.querySelectorAll('.igx-paginator > select').length).toEqual(0); }); + it('change paging pages per page API', (async () => { fix.detectChanges(); grid.height = '300px'; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts index 340fc78bd21..2f46267024f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts @@ -586,10 +586,11 @@ describe('IgxGrid - Column Pinning #grid', () => { GridFunctions.verifyUnpinnedAreaWidth(grid, scrBarMainSection.nativeElement.offsetWidth, false); }); - it('should pin an unpinned column when drag/drop it among pinned columns.', () => { + it('should pin an unpinned column when drag/drop it among pinned columns.', fakeAsync(() => { // move 'ID' column to the pinned area grid.moveColumn(grid.getColumnByName('ID'), grid.getColumnByName('ContactName'), DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); // verify column is pinned at the correct place @@ -597,7 +598,7 @@ describe('IgxGrid - Column Pinning #grid', () => { expect(grid.pinnedColumns[1].field).toEqual('ID'); expect(grid.pinnedColumns[2].field).toEqual('ContactName'); expect(grid.getColumnByName('ID').pinned).toBeTruthy(); - }); + })); it('should correctly pin columns with their summaries to end.', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts index 40cffe98bdb..4794cdab2b3 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts @@ -590,16 +590,18 @@ describe('IgxGrid - search API #grid - ', () => { verifyActiveHighlight(0); })); - it('Active highlight should be preserved when a column is moved', () => { + it('Active highlight should be preserved when a column is moved', fakeAsync(() => { grid.findNext('casey'); const columns = grid.columnList.toArray(); grid.moveColumn(columns[0], columns[1]); + tick(); + fix.detectChanges(); const highlights = getHighlights(); expect(highlights.length).toBe(1); verifyActiveHighlight(0); - }); + })); it('Should exit edit mode and search a cell', () => { const cell = grid.getCellByColumn(0, 'Name'); diff --git a/projects/igniteui-angular/src/lib/grids/grid/row-drag.directive.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/row-drag.directive.spec.ts index 249a7f252d9..30e3db64df1 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/row-drag.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/row-drag.directive.spec.ts @@ -1,5 +1,5 @@ import { Component, ViewChild, DebugElement, QueryList } from '@angular/core'; -import { TestBed, ComponentFixture, fakeAsync } from '@angular/core/testing'; +import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -508,9 +508,10 @@ describe('Row Drag Tests #grid', () => { fixture.detectChanges(); verifyDragAndDropRowCellValues(1, 0); }); - it('should be able to drag grid row when column moving is enabled', () => { + it('should be able to drag grid row when column moving is enabled', fakeAsync(() => { const dragGridColumns = dragGrid.columnList.toArray(); dragGrid.moveColumn(dragGridColumns[0], dragGridColumns[2]); + tick(); fixture.detectChanges(); dragIndicatorElement = dragIndicatorElements[2].nativeElement; @@ -548,7 +549,7 @@ describe('Row Drag Tests #grid', () => { expect(dropRowCells[2].value).toEqual(dragRowCells[1].value); expect(dropRowCells[3].value).toEqual(dragRowCells[3].value); expect(dropRowCells[4].value).toEqual(dragRowCells[4].value); - }); + })); it('should be able to drag grid row when column pinning is enabled', () => { dragGrid.pinColumn('ProductName'); fixture.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/grids/grouping/group-by-area.directive.ts b/projects/igniteui-angular/src/lib/grids/grouping/group-by-area.directive.ts index a7a5c7d0565..2904deda203 100644 --- a/projects/igniteui-angular/src/lib/grids/grouping/group-by-area.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grouping/group-by-area.directive.ts @@ -1,194 +1,194 @@ -import { - Directive, - ElementRef, - EventEmitter, - HostBinding, - Input, - Output, - Pipe, - PipeTransform, - QueryList, - TemplateRef, - ViewChildren -} from '@angular/core'; -import { IChipsAreaReorderEventArgs, IgxChipComponent } from '../../chips/public_api'; -import { DisplayDensity } from '../../core/displayDensity'; -import { PlatformUtil } from '../../core/utils'; -import { IGroupingExpression } from '../../data-operations/grouping-expression.interface'; -import { SortingDirection } from '../../data-operations/sorting-strategy'; -import { GridType } from '../common/grid.interface'; -import { IgxColumnMovingDragDirective } from '../moving/moving.drag.directive'; - -/** - * An internal component representing a base group-by drop area. - * - * @hidden @internal - */ - @Directive() -export abstract class IgxGroupByAreaDirective { - /** - * The drop area template if provided by the parent grid. - * Otherwise, uses the default internal one. - */ - @Input() - public dropAreaTemplate: TemplateRef; - - @Input() - public density: DisplayDensity = DisplayDensity.comfortable; - - @HostBinding('class.igx-grid-grouparea') - public defaultClass = true; - - @HostBinding('class.igx-grid-grouparea--cosy') - public get cosyStyle() { - return this.density === 'cosy'; - } - - @HostBinding('class.igx-grid-grouparea--compact') - public get compactStyle() { - return this.density === 'compact'; - } - - /** The parent grid containing the component. */ - @Input() - public grid: GridType; - - /** - * The group-by expressions provided by the parent grid. - */ - @Input() - public get expressions(): IGroupingExpression[] { - return this._expressions; - } - - public set expressions(value: IGroupingExpression[]) { - this._expressions = value; - this.chipExpressions = this._expressions; - this.expressionsChanged(); - this.expressionsChange.emit(this._expressions); - } - - /** - * The default message for the default drop area template. - * Obviously, if another template is provided, this is ignored. - */ - @Input() - public get dropAreaMessage(): string { - return this._dropAreaMessage ?? this.grid.resourceStrings.igx_grid_groupByArea_message; - } - - public set dropAreaMessage(value: string) { - this._dropAreaMessage = value; - } - - @Output() - public expressionsChange = new EventEmitter(); - - @ViewChildren(IgxChipComponent) - public chips: QueryList; - - public chipExpressions: IGroupingExpression[]; - - /** The native DOM element. Used in sizing calculations. */ - public get nativeElement() { - return this.ref.nativeElement; - } - - private _expressions: IGroupingExpression[] = []; - private _dropAreaMessage: string; - - constructor(private ref: ElementRef, protected platform: PlatformUtil) { } - - - public get dropAreaVisible(): boolean { - return (this.grid.columnInDrag && this.grid.columnInDrag.groupable) || - !this.expressions.length; - } - - public handleKeyDown(id: string, event: KeyboardEvent) { - if (this.platform.isActivationKey(event)) { - this.updateSorting(id); - } - } - - public handleClick(id: string) { - if (!this.grid.getColumnByName(id).groupable) { - return; - } - this.updateSorting(id); - } - - public onDragDrop(event) { - const drag: IgxColumnMovingDragDirective = event.detail.owner; - if (drag instanceof IgxColumnMovingDragDirective) { - const column = drag.column; - if (!this.grid.columnList.find(c => c === column)) { - return; - } - - const isGrouped = this.expressions.findIndex((item) => item.fieldName === column.field) !== -1; - if (column.groupable && !isGrouped && !column.columnGroup && !!column.field) { - const groupingExpression = { - fieldName: column.field, - dir: SortingDirection.Asc, - ignoreCase: column.sortingIgnoreCase, - strategy: column.sortStrategy, - groupingComparer: column.groupingComparer - }; - - this.groupBy(groupingExpression); - } - } - } - - protected getReorderedExpressions(chipsArray: IgxChipComponent[]) { - const newExpressions = []; - - chipsArray.forEach(chip => { - const expr = this.expressions.find(item => item.fieldName === chip.id); - - // disallow changing order if there are columns with groupable: false - if (!this.grid.getColumnByName(expr.fieldName)?.groupable) { - return; - } - - newExpressions.push(expr); - }); - - return newExpressions; - } - - protected updateSorting(id: string) { - const expr = this.grid.sortingExpressions.find(e => e.fieldName === id); - expr.dir = 3 - expr.dir; - this.grid.sort(expr); - } - - protected expressionsChanged() { - } - - public abstract handleReorder(event: IChipsAreaReorderEventArgs); - - public abstract handleMoveEnd(); - - public abstract groupBy(expression: IGroupingExpression); - - public abstract clearGrouping(name: string); - -} - -/** - * A pipe to circumvent the use of getters/methods just to get some additional - * information from the grouping expression and pass it to the chip representing - * that expression. - * - * @hidden @internal - */ -@Pipe({ name: 'igxGroupByMeta' }) -export class IgxGroupByMetaPipe implements PipeTransform { - - public transform(key: string, grid: GridType) { - const column = grid.getColumnByName(key); - return { groupable: column.groupable, title: column.header || key }; - } -} +import { + Directive, + ElementRef, + EventEmitter, + HostBinding, + Input, + Output, + Pipe, + PipeTransform, + QueryList, + TemplateRef, + ViewChildren +} from '@angular/core'; +import { IChipsAreaReorderEventArgs, IgxChipComponent } from '../../chips/public_api'; +import { DisplayDensity } from '../../core/displayDensity'; +import { PlatformUtil } from '../../core/utils'; +import { IGroupingExpression } from '../../data-operations/grouping-expression.interface'; +import { SortingDirection } from '../../data-operations/sorting-strategy'; +import { GridType } from '../common/grid.interface'; +import { IgxColumnMovingDragDirective } from '../moving/moving.drag.directive'; + +/** + * An internal component representing a base group-by drop area. + * + * @hidden @internal + */ + @Directive() +export abstract class IgxGroupByAreaDirective { + /** + * The drop area template if provided by the parent grid. + * Otherwise, uses the default internal one. + */ + @Input() + public dropAreaTemplate: TemplateRef; + + @Input() + public density: DisplayDensity = DisplayDensity.comfortable; + + @HostBinding('class.igx-grid-grouparea') + public defaultClass = true; + + @HostBinding('class.igx-grid-grouparea--cosy') + public get cosyStyle() { + return this.density === 'cosy'; + } + + @HostBinding('class.igx-grid-grouparea--compact') + public get compactStyle() { + return this.density === 'compact'; + } + + /** The parent grid containing the component. */ + @Input() + public grid: GridType; + + /** + * The group-by expressions provided by the parent grid. + */ + @Input() + public get expressions(): IGroupingExpression[] { + return this._expressions; + } + + public set expressions(value: IGroupingExpression[]) { + this._expressions = value; + this.chipExpressions = this._expressions; + this.expressionsChanged(); + this.expressionsChange.emit(this._expressions); + } + + /** + * The default message for the default drop area template. + * Obviously, if another template is provided, this is ignored. + */ + @Input() + public get dropAreaMessage(): string { + return this._dropAreaMessage ?? this.grid.resourceStrings.igx_grid_groupByArea_message; + } + + public set dropAreaMessage(value: string) { + this._dropAreaMessage = value; + } + + @Output() + public expressionsChange = new EventEmitter(); + + @ViewChildren(IgxChipComponent) + public chips: QueryList; + + public chipExpressions: IGroupingExpression[]; + + /** The native DOM element. Used in sizing calculations. */ + public get nativeElement() { + return this.ref.nativeElement; + } + + private _expressions: IGroupingExpression[] = []; + private _dropAreaMessage: string; + + constructor(private ref: ElementRef, protected platform: PlatformUtil) { } + + + public get dropAreaVisible(): boolean { + return (this.grid.columnInDrag && this.grid.columnInDrag.groupable) || + !this.expressions.length; + } + + public handleKeyDown(id: string, event: KeyboardEvent) { + if (this.platform.isActivationKey(event)) { + this.updateSorting(id); + } + } + + public handleClick(id: string) { + if (!this.grid.getColumnByName(id).groupable) { + return; + } + this.updateSorting(id); + } + + public onDragDrop(event) { + const drag: IgxColumnMovingDragDirective = event.detail.owner; + if (drag instanceof IgxColumnMovingDragDirective) { + const column = drag.column; + if (!this.grid.columnList.find(c => c === column)) { + return; + } + + const isGrouped = this.expressions.findIndex((item) => item.fieldName === column.field) !== -1; + if (column.groupable && !isGrouped && !column.columnGroup && !!column.field) { + const groupingExpression = { + fieldName: column.field, + dir: SortingDirection.Asc, + ignoreCase: column.sortingIgnoreCase, + strategy: column.sortStrategy, + groupingComparer: column.groupingComparer + }; + + this.groupBy(groupingExpression); + } + } + } + + protected getReorderedExpressions(chipsArray: IgxChipComponent[]) { + const newExpressions = []; + + chipsArray.forEach(chip => { + const expr = this.expressions.find(item => item.fieldName === chip.id); + + // disallow changing order if there are columns with groupable: false + if (!this.grid.getColumnByName(expr.fieldName)?.groupable) { + return; + } + + newExpressions.push(expr); + }); + + return newExpressions; + } + + protected updateSorting(id: string) { + const expr = this.grid.sortingExpressions.find(e => e.fieldName === id); + expr.dir = 3 - expr.dir; + this.grid.sort(expr); + } + + protected expressionsChanged() { + } + + public abstract handleReorder(event: IChipsAreaReorderEventArgs); + + public abstract handleMoveEnd(); + + public abstract groupBy(expression: IGroupingExpression); + + public abstract clearGrouping(name: string); + +} + +/** + * A pipe to circumvent the use of getters/methods just to get some additional + * information from the grouping expression and pass it to the chip representing + * that expression. + * + * @hidden @internal + */ +@Pipe({ name: 'igxGroupByMeta' }) +export class IgxGroupByMetaPipe implements PipeTransform { + + public transform(key: string, grid: GridType) { + const column = grid.getColumnByName(key); + return { groupable: column.groupable, title: column.header || key }; + } +} diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts index 20a98556ab0..38d9f90859e 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts @@ -1,36 +1,36 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { IgxGridModule } from '../grid/grid.module'; -import { IgxChildGridRowComponent, IgxHierarchicalGridComponent } from './hierarchical-grid.component'; -import { IgxHierarchicalRowComponent } from './hierarchical-row.component'; -import { IgxGridHierarchicalPipe, IgxGridHierarchicalPagingPipe } from './hierarchical-grid.pipes'; -import { IgxRowIslandComponent } from './row-island.component'; -import { IgxHierarchicalGridCellComponent } from './hierarchical-cell.component'; - -/** - * @hidden - */ -@NgModule({ - declarations: [ - IgxHierarchicalGridComponent, - IgxHierarchicalRowComponent, - IgxRowIslandComponent, - IgxChildGridRowComponent, - IgxHierarchicalGridCellComponent, - IgxGridHierarchicalPipe, - IgxGridHierarchicalPagingPipe - ], - exports: [ - IgxGridModule, - IgxHierarchicalGridComponent, - IgxHierarchicalRowComponent, - IgxHierarchicalGridCellComponent, - IgxRowIslandComponent, - IgxChildGridRowComponent - ], - imports: [ - IgxGridModule, - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class IgxHierarchicalGridModule { -} +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { IgxGridModule } from '../grid/grid.module'; +import { IgxChildGridRowComponent, IgxHierarchicalGridComponent } from './hierarchical-grid.component'; +import { IgxHierarchicalRowComponent } from './hierarchical-row.component'; +import { IgxGridHierarchicalPipe, IgxGridHierarchicalPagingPipe } from './hierarchical-grid.pipes'; +import { IgxRowIslandComponent } from './row-island.component'; +import { IgxHierarchicalGridCellComponent } from './hierarchical-cell.component'; + +/** + * @hidden + */ +@NgModule({ + declarations: [ + IgxHierarchicalGridComponent, + IgxHierarchicalRowComponent, + IgxRowIslandComponent, + IgxChildGridRowComponent, + IgxHierarchicalGridCellComponent, + IgxGridHierarchicalPipe, + IgxGridHierarchicalPagingPipe + ], + exports: [ + IgxGridModule, + IgxHierarchicalGridComponent, + IgxHierarchicalRowComponent, + IgxHierarchicalGridCellComponent, + IgxRowIslandComponent, + IgxChildGridRowComponent + ], + imports: [ + IgxGridModule, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class IgxHierarchicalGridModule { +} diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-data-selector.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-data-selector.component.html index a996f620c86..fdbec88c7c6 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-data-selector.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-data-selector.component.html @@ -86,9 +86,8 @@
(dragMove)="onItemDragMove($event)" (dragEnd)="onItemDragEnd($event)" (dropped)="onItemDropped($event, panel.type)" - *igxFor=" + *ngFor=" let item of this.grid[panel.dataKey]; - scrollOrientation: 'vertical'; let index " [id]="item[panel.itemKey]" diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-aggregate.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-aggregate.ts index 53f6e4af14d..892ddf26882 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-aggregate.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-aggregate.ts @@ -173,7 +173,7 @@ export class IgxPivotDateAggregate extends IgxPivotAggregate { * * @memberof IgxPivotDateAggregate */ - public static latest(members: number[]) { + public static latest(members: any[]) { return IgxDateSummaryOperand.latest(members); } @@ -186,7 +186,7 @@ export class IgxPivotDateAggregate extends IgxPivotAggregate { * * @memberof IgxPivotDateAggregate */ - public static earliest(members: number[]) { + public static earliest(members: any[]) { return IgxDateSummaryOperand.earliest(members); } } @@ -225,7 +225,7 @@ export class IgxPivotTimeAggregate extends IgxPivotAggregate { * * @memberof IgxPivotTimeAggregate */ - public static latestTime(members: number[]) { + public static latestTime(members: any[]) { return IgxTimeSummaryOperand.latestTime(members); } @@ -238,7 +238,7 @@ export class IgxPivotTimeAggregate extends IgxPivotAggregate { * * @memberof IgxPivotTimeAggregate */ - public static earliestTime(members: number[]) { + public static earliestTime(members: any[]) { return IgxTimeSummaryOperand.earliestTime(members); } } diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-keyboard-nav.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-keyboard-nav.spec.ts index 8ac251eb8b8..c246276d3ad 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-keyboard-nav.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid-keyboard-nav.spec.ts @@ -52,6 +52,16 @@ describe('IgxPivotGrid - Keyboard navigation #pivotGrid', () => { GridFunctions.verifyHeaderIsFocused(secondCell.parent); activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); expect(activeCells.length).toBe(1); + + // should do nothing if wrong key is pressed + UIInteractions.simulateClickAndSelectEvent(firstCell); + fixture.detectChanges(); + GridFunctions.verifyHeaderIsFocused(firstCell.parent); + activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); + expect(activeCells.length).toBe(1); + UIInteractions.triggerKeyDownEvtUponElem('h', firstCell.nativeElement); + fixture.detectChanges(); + GridFunctions.verifyHeaderIsFocused(firstCell.parent); }); it('should not go outside of the boundaries of the row dimensions content', () => { @@ -119,6 +129,39 @@ describe('IgxPivotGrid - Keyboard navigation #pivotGrid', () => { expect(activeCells.length).toBe(1); }); + it('should allow navigating from any to first row headers(Ctrl + ArrowUp)', () => { + // Ctrl + arrowup + let allGroups = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const thirdCell = allGroups.filter(x => x.componentInstance.column.field === 'ProductCategory')[2] + UIInteractions.simulateClickAndSelectEvent(thirdCell); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', thirdCell.nativeElement, true, false, false, true); + fixture.detectChanges(); + + allGroups = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const firstCell = allGroups[0]; + GridFunctions.verifyHeaderIsFocused(firstCell.parent); + let activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); + expect(activeCells.length).toBe(1); + + // just arrow up + UIInteractions.simulateClickAndSelectEvent(thirdCell); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', thirdCell.nativeElement, true, false, false, false); + fixture.detectChanges(); + allGroups = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const secondCell = allGroups.filter(x => x.componentInstance.column.field === 'ProductCategory')[1]; + GridFunctions.verifyHeaderIsFocused(secondCell.parent); + activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); + expect(activeCells.length).toBe(1); + + }); + it('should allow navigating between column headers', () => { let firstHeader = fixture.debugElement.queryAll( By.css(`${PIVOT_HEADER_ROW} ${HEADER_CELL_CSS_CLASS}`))[0]; @@ -133,7 +176,7 @@ describe('IgxPivotGrid - Keyboard navigation #pivotGrid', () => { UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', pivotGrid.theadRow.nativeElement); fixture.detectChanges(); - + const secondHeader = fixture.debugElement.queryAll( By.css(`${PIVOT_HEADER_ROW} ${HEADER_CELL_CSS_CLASS}`))[1]; GridFunctions.verifyHeaderIsFocused(secondHeader.parent); @@ -191,4 +234,33 @@ describe('IgxPivotGrid - Keyboard navigation #pivotGrid', () => { activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); expect(activeCells.length).toBe(1); }); + + it('should allow navigating within the cells of the body', async () => { + const cell = pivotGrid.rowList.first.cells.first; + GridFunctions.focusFirstCell(fixture, pivotGrid); + fixture.detectChanges(); + expect(pivotGrid.navigation.activeNode.row).toBeUndefined(); + expect(pivotGrid.navigation.activeNode.column).toBeUndefined(); + + UIInteractions.simulateClickAndSelectEvent(cell.nativeElement); + fixture.detectChanges(); + + GridFunctions.focusFirstCell(fixture, pivotGrid); + fixture.detectChanges(); + expect(pivotGrid.navigation.activeNode.row).toBeDefined(); + expect(pivotGrid.navigation.activeNode.column).toBeDefined(); + + let activeCells = fixture.debugElement.queryAll(By.css(`.igx-grid__td--active`)); + expect(activeCells.length).toBe(1); + expect(cell.column.field).toEqual('Stanley-UnitsSold'); + + const gridContent = GridFunctions.getGridContent(fixture); + UIInteractions.triggerEventHandlerKeyDown('arrowright', gridContent); + await wait(30); + fixture.detectChanges(); + + activeCells = fixture.debugElement.queryAll(By.css(`.igx-grid__td--active`)); + expect(activeCells.length).toBe(1); + expect(activeCells[0].componentInstance.column.field).toEqual('Stanley-UnitPrice') + }); }); diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html index 7fa85295b20..a3a05419774 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html @@ -41,6 +41,7 @@ let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]='verticalScroll' [igxForContainerSize]='calcHeight' [igxForItemSize]="hasColumnLayouts ? rowHeight * multiRowLayoutRowSize + 1 : renderedRowHeight" + [igxGridForOfVariableSizes]='false' #verticalScrollContainer> void) { - const enabledDimensions = this.allDimensions.filter(x => x && x.enabled); - const dim = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === column.field); - if (dim) { - this.getDimensionData(dim, exprTree, uniqueValues => done(uniqueValues)); - } - } - /** * Gets the full list of dimensions. * @@ -944,33 +934,6 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni return (config.rows || []).concat((config.columns || [])).concat(config.filters || []).filter(x => x !== null && x !== undefined); } - /** - * @hidden @internal - */ - public getDimensionData(dim: IPivotDimension, - dimExprTree: IFilteringExpressionsTree, - done: (colVals: any[]) => void) { - let columnValues = []; - const data = this.gridAPI.get_data(); - const state = { - expressionsTree: dimExprTree, - strategy: this.filterStrategy || new DimensionValuesFilteringStrategy(), - advancedFilteringExpressionsTree: this.advancedFilteringExpressionsTree - }; - const filtered = FilterUtil.filter(data, state, this); - const allValuesHierarchy = PivotUtil.getFieldsHierarchy( - filtered, - [dim], - PivotDimensionType.Column, - this.pivotKeys - ); - const flatData = Array.from(allValuesHierarchy.values()); - // Note: Once ESF supports tree view, we should revert this back. - columnValues = flatData.map(record => record['value']); - done(columnValues); - return; - } - /** @hidden @internal */ public createFilterESF(dropdown: any, column: ColumnType, options: OverlaySettings, shouldReatach: boolean) { options.outlet = this.outlet; @@ -1904,10 +1867,10 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } protected generateDimensionColumns(): IgxColumnComponent[] { - const leafFields = PivotUtil.flatten(this.allDimensions, 0).filter(x => !x.childLevel).map(x => x.memberName); + const rootFields = this.allDimensions.map(x => x.memberName); const columns = []; const factory = this.resolver.resolveComponentFactory(IgxColumnComponent); - leafFields.forEach((field) => { + rootFields.forEach((field) => { const ref = factory.create(this.viewRef.injector); ref.instance.field = field; ref.changeDetectorRef.detectChanges(); @@ -1969,16 +1932,8 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } currentFields.forEach((value) => { let shouldGenerate = true; - if (value.dimension && value.dimension.filter) { - const state = { - expressionsTree: value.dimension.filter.filteringOperands[0], - strategy: this.filterStrategy || new DimensionValuesFilteringStrategy(), - advancedFilteringExpressionsTree: this.advancedFilteringExpressionsTree - }; - const filtered = FilterUtil.filter(cloneArray(value.records), state, this); - if (filtered.length === 0) { - shouldGenerate = false; - } + if (data.length === 0) { + shouldGenerate = false; } if (shouldGenerate && (value.children == null || value.children.length === 0 || value.children.size === 0)) { const col = this.createColumnForDimension(value, data, parent, this.hasMultipleValues); @@ -2074,13 +2029,6 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni return cols; } - /** - * @hidden @internal - */ - public getPropName(dim: IPivotDimension) { - return !!dim ?? dim.memberName + this.pivotKeys.rowDimensionSeparator + 'height'; - } - /** * @hidden @internal */ diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts index 4bf4d510960..f040db86f63 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts @@ -1,7 +1,7 @@ import { NoopPivotDimensionsStrategy } from '../../data-operations/pivot-strategy'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxPivotDateDimension } from './pivot-grid-dimensions'; -import { IgxPivotNumericAggregate } from './pivot-grid-aggregate'; +import { IgxPivotAggregate, IgxPivotDateAggregate, IgxPivotNumericAggregate, IgxPivotTimeAggregate } from './pivot-grid-aggregate'; import { IPivotConfiguration } from './pivot-grid.interface'; import { IgxPivotAutoTransform, IgxPivotColumnPipe, IgxPivotRowExpansionPipe, IgxPivotRowPipe } from './pivot-grid.pipes'; import { PivotGridFunctions } from '../../test-utils/pivot-grid-functions.spec'; @@ -293,6 +293,37 @@ describe('Pivot pipes #pivotGrid', () => { { 'All-USA-UnitsSold': 240, 'All-USA-UnitPrice': 18.13, 'All-UnitsSold': 240, 'All-UnitPrice': 18.13 }]); }); + it('should return correct values for each pivot aggregation type', () => { + // check each aggregator has correct aggregations + expect(IgxPivotAggregate.aggregators().map(x => x.key)).toEqual(['COUNT']); + expect(IgxPivotNumericAggregate.aggregators().map(x => x.key)).toEqual(['COUNT', 'MIN', 'MAX', 'SUM', 'AVG']); + expect(IgxPivotDateAggregate.aggregators().map(x => x.key)).toEqual(['COUNT', 'LATEST', 'EARLIEST']); + expect(IgxPivotTimeAggregate.aggregators().map(x => x.key)).toEqual(['COUNT', 'LATEST', 'EARLIEST']); + + // check aggregations are applied correctly + expect(IgxPivotAggregate.count([1, 2, 3])).toEqual(3); + + expect(IgxPivotNumericAggregate.count([1, 2, 3])).toEqual(3); + expect(IgxPivotNumericAggregate.min([1, 2, 3])).toEqual(1); + expect(IgxPivotNumericAggregate.max([1, 2, 3])).toEqual(3); + expect(IgxPivotNumericAggregate.sum([1, 2, 3])).toEqual(6); + expect(IgxPivotNumericAggregate.average([1, 2, 3])).toEqual(2); + + expect(IgxPivotDateAggregate.latest(['01/01/2021', '01/01/2022', '02/01/2021'])).toEqual('01/01/2022'); + expect(IgxPivotDateAggregate.earliest(['01/01/2021', '01/01/2022', '02/01/2021'])).toEqual('01/01/2021'); + + + expect(IgxPivotTimeAggregate.latestTime(['01/01/2021 8:00', '01/01/2021 1:00', '01/01/2021 22:00'])).toEqual(new Date('01/01/2021 22:00')); + expect(IgxPivotTimeAggregate.earliestTime(['01/01/2021 8:00', '01/01/2021 1:00', '01/01/2021 22:00'])).toEqual(new Date('01/01/2021 1:00')); + + // check localization can be changed + IgxPivotTimeAggregate.resourceStrings = { + igx_grid_pivot_aggregate_time_earliest: 'Earliest Custom Time' + }; + + expect(IgxPivotTimeAggregate.aggregators().find(x => x.key === 'EARLIEST').label).toEqual('Earliest Custom Time'); + }); + it('allow setting NoopPivotDimensionsStrategy for rows/columns', () => { const preprocessedData = [ { diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index 9cb6d0ae8d6..a06e1cc8c04 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -5,13 +5,13 @@ import { FilteringExpressionsTree, FilteringLogic, IgxPivotGridComponent, IgxPiv import { IgxChipComponent } from '../../chips/chip.component'; import { IgxChipsAreaComponent } from '../../chips/chips-area.component'; import { DefaultPivotSortingStrategy } from '../../data-operations/pivot-sort-strategy'; -import { DimensionValuesFilteringStrategy } from '../../data-operations/pivot-strategy'; +import { DimensionValuesFilteringStrategy, NoopPivotDimensionsStrategy } from '../../data-operations/pivot-strategy'; import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec'; import { PivotGridFunctions } from '../../test-utils/pivot-grid-functions.spec'; import { IgxPivotGridTestBaseComponent, IgxPivotGridTestComplexHierarchyComponent, IgxTotalSaleAggregate } from '../../test-utils/pivot-grid-samples.spec'; -import { UIInteractions } from '../../test-utils/ui-interactions.spec'; +import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; import { IgxPivotDateAggregate, IgxPivotNumericAggregate } from './pivot-grid-aggregate'; import { IgxPivotDateDimension } from './pivot-grid-dimensions'; import { IPivotGridRecord, PivotDimensionType } from './pivot-grid.interface'; @@ -175,17 +175,30 @@ describe('IgxPivotGrid #pivotGrid', () => { it('should remove value from chip', () => { const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.values[1].displayName = 'Units Price'; + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges(); + expect(pivotGrid.columns.length).toBe(9); expect(pivotGrid.values.length).toBe(2); const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); - const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; + let rowChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); + let removeIcon = rowChip.querySelectorAll('igx-icon')[3]; removeIcon.click(); fixture.detectChanges(); expect(pivotGrid.pivotConfiguration.values[0].enabled).toBeFalse(); expect(pivotGrid.values.length).toBe(1); expect(pivotGrid.columns.length).not.toBe(9); + + // should remove the second one as well + rowChip = headerRow.querySelector('igx-chip[id="Units Price"]'); + removeIcon = rowChip.querySelectorAll('igx-icon')[3]; + removeIcon.click(); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.values[1].enabled).toBeFalse(); + expect(pivotGrid.values.length).toBe(0); + expect(pivotGrid.columns.length).toBe(3) }); it('should remove filter dimension from chip', () => { @@ -219,6 +232,53 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(pivotGrid.rowList.length).toBe(5); }); + it('should correctly remove chip from filters dropdown', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration = { + columns: [], + rows: [ + { + memberName: 'SellerName', + enabled: true + } + ], + filters: [ + { + memberName: 'Date', + enabled: true + }, + { + memberName: 'ProductCategory', + enabled: true + }, + { + memberName: 'Country', + enabled: true + } + ] + }; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[4]; + expect(dropdownIcon).not.toBeUndefined(); + expect(headerRow.querySelector('igx-badge').innerText).toBe('2'); + dropdownIcon.click(); + fixture.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[0]; + const chip = excelMenu.querySelectorAll('igx-chip')[0]; + const removeIcon = chip.querySelectorAll('igx-icon')[1]; + removeIcon.click(); + fixture.detectChanges(); + + const filtersChip = headerRow.querySelector('igx-chip[id="Date"]'); + expect(filtersChip).toBeDefined(); + expect(headerRow.querySelector('igx-chip[id="ProductCategory"]')).toBeNull(); + }); + it('should collapse column with 1 value dimension', () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.pivotConfiguration.values.pop(); @@ -291,6 +351,21 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(value[1]).toBeFalse(); }); + it('should collapse row', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + + expect(pivotGrid.rowList.length).toEqual(5); + expect(pivotGrid.expansionStates.size).toEqual(0); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-row-dimension-content'); + const header = headerRow.querySelector('igx-pivot-row-dimension-header'); + const expander = header.querySelectorAll('igx-icon')[0]; + expander.click(); + fixture.detectChanges(); + expect(pivotGrid.rowList.length).toEqual(1); + expect(pivotGrid.expansionStates.size).toEqual(1); + expect(pivotGrid.expansionStates.get('All')).toBeFalse(); + }); + it('should display aggregations when no row dimensions are enabled', () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.pivotConfiguration.columns = [ @@ -421,9 +496,94 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(pivotGrid.columnDimensions.length).toEqual(0); }); + it('should change display density', fakeAsync(() => { + const pivotGrid = fixture.componentInstance.pivotGrid; + const minWidthComf = '80'; + const minWidthSupercompact = '56'; + const cellHeightComf = 50; + const cellHeightSuperCompact = 24; + + pivotGrid.superCompactMode = true; + tick(); + fixture.detectChanges(); + + expect(pivotGrid.displayDensity).toBe('compact') + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.minWidth).toBe(minWidthSupercompact); + expect(pivotGrid.rowList.first.cellHeight).toBe(cellHeightSuperCompact); + + pivotGrid.superCompactMode = false; + fixture.detectChanges(); + + pivotGrid.displayDensity = 'comfortable'; + tick(); + fixture.detectChanges(); + + expect(pivotGrid.displayDensity).toBe('comfortable') + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.minWidth).toBe(minWidthComf); + expect(pivotGrid.rowList.first.cellHeight).toBe(cellHeightComf); + })); + + it('should render correct grid with noop strategies', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.data = [ + { + AllProducts: 'All Products', All: 2127, 'Bulgaria': 774, 'USA': 829, 'Uruguay': 524, 'AllProducts_records': [ + { ProductCategory: 'Clothing', All: 1523, 'Bulgaria': 774, 'USA': 296, 'Uruguay': 456, }, + { ProductCategory: 'Bikes', All: 68, 'Uruguay': 68 }, + { ProductCategory: 'Accessories', All: 293, 'USA': 293 }, + { ProductCategory: 'Components', All: 240, 'USA': 240 } + ] + } + ]; + + pivotGrid.pivotConfiguration = { + columnStrategy: NoopPivotDimensionsStrategy.instance(), + rowStrategy: NoopPivotDimensionsStrategy.instance(), + columns: [ + { + memberName: 'Country', + enabled: true + }, + ] + , + rows: [ + { + memberFunction: () => 'All', + memberName: 'AllProducts', + enabled: true, + width: '25%', + childLevel: { + memberName: 'ProductCategory', + enabled: true + } + } + ], + values: [ + { + member: 'UnitsSold', + aggregate: { + aggregator: IgxPivotNumericAggregate.sum, + key: 'sum', + label: 'Sum' + }, + enabled: true + }, + ], + filters: null + }; + + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges(); + + expect(pivotGrid.rowList.first.cells.toArray().map(x => x.value)).toEqual([2127, 774, 829, 524]); + }); + describe('IgxPivotGrid Features #pivotGrid', () => { - it('should show excel style filtering via dimension chip.', () => { + it('should show excel style filtering via dimension chip.', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; expect(pivotGrid.filterStrategy).toBeInstanceOf(DimensionValuesFilteringStrategy); const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; @@ -433,35 +593,51 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); filterIcon.click(); + await wait(100); fixture.detectChanges(); const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); + const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); - // should show and should display correct checkboxes. + // should show Select All checkbox expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); - expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); + + // expand tree hierarchy + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 0); + await wait(100); + fixture.detectChanges(); + // should show correct tree items + const treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fixture, excelMenu, 'igx-pivot-grid'); + expect(treeItems.length).toBe(5); + + expect(treeItems[1].innerText).toBe('Clothing'); + expect(treeItems[2].innerText).toBe('Bikes'); + expect(treeItems[3].innerText).toBe('Accessories'); + expect(treeItems[4].innerText).toBe('Components'); }); - it('should filter rows via excel style filtering dimension chip.', () => { + it('should filter rows via excel style filtering dimension chip.', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); const rowChip = headerRow.querySelector('igx-chip[id="All"]'); const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; filterIcon.click(); + await wait(100); fixture.detectChanges(); - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; - const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + // expand tree hierarchy + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 0); + await wait(100); + fixture.detectChanges(); + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + const checkboxes = GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-tree-grid'); // uncheck Accessories - checkboxes[1].click(); + checkboxes[4].click(); fixture.detectChanges(); // uncheck Bikes - checkboxes[2].click(); + checkboxes[3].click(); fixture.detectChanges(); // Click 'apply' button to apply filter. @@ -478,12 +654,13 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(rowDimensionHeaders).toEqual(expectedHeaders); }); - it('should filter columns via excel style filtering dimension chip.', () => { + it('should filter columns via excel style filtering dimension chip.', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); const rowChip = headerRow.querySelector('igx-chip[id="Country"]'); const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; filterIcon.click(); + await wait(100); fixture.detectChanges(); const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); @@ -493,7 +670,7 @@ describe('IgxPivotGrid #pivotGrid', () => { fixture.detectChanges(); // uncheck Uruguay - checkboxes[2].click(); + checkboxes[3].click(); fixture.detectChanges(); @@ -507,7 +684,7 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(colHeaders).toEqual(expected); }); - it('should show filters chips', () => { + it('should show filters chips', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.pivotConfiguration.filters = [{ memberName: 'SellerName', @@ -523,19 +700,20 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); filterIcon.click(); + await wait(100); fixture.detectChanges(); const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); // should show and should display correct checkboxes. expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Stanley'); expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Lydia'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); }); - it('should show filters in chips dropdown button', () => { + it('should show filters in chips dropdown button', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.pivotConfiguration.filters = [ { @@ -556,6 +734,7 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); dropdownIcon.click(); + await wait(100); fixture.detectChanges(); const chips = excelMenu.querySelectorAll('igx-chip'); @@ -569,10 +748,10 @@ describe('IgxPivotGrid #pivotGrid', () => { // should show and should display correct checkboxes. expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Stanley'); expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Lydia'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); // switch to the `ProductCategory` filters const chipAreaElement = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); @@ -582,18 +761,19 @@ describe('IgxPivotGrid #pivotGrid', () => { id: chips[1].id } }); + await wait(500); fixture.detectChanges(); esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); }); - it('should be able to filter from chips dropdown button', () => { + it('should be able to filter from chips dropdown button', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.pivotConfiguration.filters = [ { @@ -614,16 +794,16 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); dropdownIcon.click(); + await wait(100); fixture.detectChanges(); const checkBoxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); - // uncheck David - checkBoxes[1].click(); + checkBoxes[4].click(); fixture.detectChanges(); - // uncheck Elisa - checkBoxes[5].click(); + // uncheck Lydia + checkBoxes[3].click(); fixture.detectChanges(); // Click 'apply' button to apply filter. @@ -667,6 +847,162 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(filtersChip).not.toBeUndefined(); }); + it('should show complex tree and allow filtering for Date dimension', async () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.rows = [new IgxPivotDateDimension( + { + memberName: 'Date', + enabled: true + }, + { + months: true, + quarters: true, + years: true, + fullDate: true, + total: true + } + )]; + + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="AllPeriods"]'); + const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; + filterIcon.click(); + await wait(100); + fixture.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + + // expand tree hierarchy + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 0); + await wait(100); + fixture.detectChanges(); + // should show correct tree items + let treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fixture, excelMenu, 'igx-pivot-grid'); + + expect(treeItems.length).toBe(4); + expect(treeItems[0].querySelector('.igx-tree-node__content').textContent).toBe('All Periods'); + expect(treeItems[1].querySelector('.igx-tree-node__content').textContent).toBe('2021'); + expect(treeItems[2].querySelector('.igx-tree-node__content').textContent).toBe('2019'); + expect(treeItems[3].querySelector('.igx-tree-node__content').textContent).toBe('2020'); + + + // expand tree hierarchy 2021 + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 1); + await wait(100); + fixture.detectChanges(); + + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fixture, excelMenu, 'igx-pivot-grid'); + + expect(treeItems.length).toBe(7); + expect(treeItems[0].querySelector('.igx-tree-node__content').textContent).toBe('All Periods'); + expect(treeItems[1].querySelector('.igx-tree-node__content').textContent).toBe('2021'); + expect(treeItems[2].querySelector('.igx-tree-node__content').textContent).toBe('Q1'); + expect(treeItems[3].querySelector('.igx-tree-node__content').textContent).toBe('Q2'); + expect(treeItems[4].querySelector('.igx-tree-node__content').textContent).toBe('Q4'); + expect(treeItems[5].querySelector('.igx-tree-node__content').textContent).toBe('2019'); + expect(treeItems[6].querySelector('.igx-tree-node__content').textContent).toBe('2020'); + + // expand tree hierarchy Q1 + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 2); + await wait(100); + fixture.detectChanges(); + + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fixture, excelMenu, 'igx-pivot-grid'); + expect(treeItems.length).toBe(8); + expect(treeItems[0].querySelector('.igx-tree-node__content').textContent).toBe('All Periods'); + expect(treeItems[1].querySelector('.igx-tree-node__content').textContent).toBe('2021'); + expect(treeItems[2].querySelector('.igx-tree-node__content').textContent).toBe('Q1'); + expect(treeItems[3].querySelector('.igx-tree-node__content').textContent).toBe('January'); + expect(treeItems[4].querySelector('.igx-tree-node__content').textContent).toBe('Q2'); + expect(treeItems[5].querySelector('.igx-tree-node__content').textContent).toBe('Q4'); + expect(treeItems[6].querySelector('.igx-tree-node__content').textContent).toBe('2019'); + expect(treeItems[7].querySelector('.igx-tree-node__content').textContent).toBe('2020'); + + // expand tree hierarchy January + GridFunctions.clickExcelTreeNodeExpandIcon(fixture, 3); + await wait(100); + fixture.detectChanges(); + + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fixture, excelMenu, 'igx-pivot-grid'); + expect(treeItems.length).toBe(9); + expect(treeItems[0].querySelector('.igx-tree-node__content').textContent).toBe('All Periods'); + expect(treeItems[1].querySelector('.igx-tree-node__content').textContent).toBe('2021'); + expect(treeItems[2].querySelector('.igx-tree-node__content').textContent).toBe('Q1'); + expect(treeItems[3].querySelector('.igx-tree-node__content').textContent).toBe('January'); + expect(treeItems[4].querySelector('.igx-tree-node__content').textContent).toBe('01/01/2021'); + expect(treeItems[5].querySelector('.igx-tree-node__content').textContent).toBe('Q2'); + expect(treeItems[6].querySelector('.igx-tree-node__content').textContent).toBe('Q4'); + expect(treeItems[7].querySelector('.igx-tree-node__content').textContent).toBe('2019'); + expect(treeItems[8].querySelector('.igx-tree-node__content').textContent).toBe('2020'); + + + const checkBoxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + // uncheck Q1 + checkBoxes[3].click(); + fixture.detectChanges(); + + // uncheck Q2 + checkBoxes[6].click(); + fixture.detectChanges(); + + // uncheck 2019 + checkBoxes[8].click(); + fixture.detectChanges(); + + // uncheck 2020 + checkBoxes[9].click(); + fixture.detectChanges(); + + // Click 'apply' button to apply filter. + GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + fixture.detectChanges(); + + // check rows + const rows = pivotGrid.rowList.toArray(); + expect(rows.length).toBe(5); + const expectedHeaders = ['All Periods', '2021', 'Q4', 'December', '12/08/2021']; + const rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); + expect(rowDimensionHeaders).toEqual(expectedHeaders); + }); + + it('should do nothing on filtering pointer down', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [ + { + memberName: 'Date', + enabled: true + }, + { + memberName: 'ProductCategory', + enabled: true + } + ]; + + pivotGrid.pivotConfiguration.rows = [{ + memberName: 'SellerName', + enabled: true + }]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow = fixture.debugElement.queryAll( + By.directive(IgxPivotHeaderRowComponent))[0].componentInstance; + const filtersChip = headerRow.nativeElement.querySelector('igx-chip[id="Date"]'); + expect(filtersChip).not.toBeUndefined(); + const filterIcon = filtersChip.querySelectorAll('igx-icon')[1]; + + spyOn(headerRow, 'onFilteringIconPointerDown').and.callThrough(); + filterIcon.dispatchEvent(new Event('pointerdown')); + expect(headerRow.onFilteringIconPointerDown).toHaveBeenCalledTimes(1); + }); + it('should apply sorting for dimension via row chip', () => { fixture.detectChanges(); const pivotGrid = fixture.componentInstance.pivotGrid; @@ -692,8 +1028,8 @@ describe('IgxPivotGrid #pivotGrid', () => { // should have emitted event expect(pivotGrid.dimensionsSortingExpressionsChange.emit).toHaveBeenCalledTimes(2); const expectedExpressions: ISortingExpression[] = [ - { dir: SortingDirection.Desc, fieldName: 'All', strategy: DefaultPivotSortingStrategy.instance()}, - { dir: SortingDirection.Desc, fieldName: 'ProductCategory', strategy: DefaultPivotSortingStrategy.instance()}, + { dir: SortingDirection.Desc, fieldName: 'All', strategy: DefaultPivotSortingStrategy.instance() }, + { dir: SortingDirection.Desc, fieldName: 'ProductCategory', strategy: DefaultPivotSortingStrategy.instance() }, ]; expect(pivotGrid.dimensionsSortingExpressionsChange.emit).toHaveBeenCalledWith(expectedExpressions); }); @@ -824,6 +1160,60 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(columnValues).toEqual(expectedOrder); }); + it('should sort date values', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.height = '700px'; + pivotGrid.width = '1000px'; + pivotGrid.pivotConfiguration.columns = [ + { + memberName: 'Date', + enabled: true, + dataType: 'date' + } + ]; + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'AllSeller', + memberFunction: () => 'All Sellers', + enabled: true, + childLevel: { + enabled: true, + memberName: 'SellerName' + } + } + ]; + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges(); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const colChip = headerRow.querySelector('igx-chip[id="Date"]'); + + // sort asc + colChip.click(); + fixture.detectChanges(); + + let colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + let expected = ['01/05/2019', '01/06/2020', '02/19/2020', '05/12/2020', '01/01/2021', '04/07/2021', '12/08/2021'] + expect(colHeaders).toEqual(expected); + + // sort desc + colChip.click(); + fixture.detectChanges(); + + colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + expected = ['12/08/2021', '04/07/2021', '01/01/2021', '05/12/2020', '02/19/2020', '01/06/2020', '01/05/2019']; + expect(colHeaders).toEqual(expected); + + //remove sort + colChip.click(); + fixture.detectChanges(); + + colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + expected = ['01/01/2021', '01/05/2019', '01/06/2020', '04/07/2021', '12/08/2021', '05/12/2020', '02/19/2020'] + expect(colHeaders).toEqual(expected); + + }); + it('should allow changing default aggregation via value chip drop-down.', () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.width = '1500px'; @@ -1027,6 +1417,13 @@ describe('IgxPivotGrid #pivotGrid', () => { owner: colChip2 }, colChipArea, PivotDimensionType.Column); pivotGrid.cdr.detectChanges(); + + headerRow.onDimDragLeave({ + owner: colChip2 + }); + expect((colChip2.nativeElement.previousElementSibling as any).style.visibility).toBe('hidden'); + expect((colChip2.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); + //check chip order is updated. expect(colChipArea.chipsList.toArray()[0].id).toBe(colChip2.id); expect(colChipArea.chipsList.toArray()[1].id).toBe(colChip1.id); @@ -1040,10 +1437,10 @@ describe('IgxPivotGrid #pivotGrid', () => { const pivotGrid = fixture.componentInstance.pivotGrid; fixture.detectChanges(); const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const valuesChipArea: IgxChipsAreaComponent = chipAreas[2].componentInstance; - const valChip1 = valuesChipArea.chipsList.toArray()[0]; - const valChip2 = valuesChipArea.chipsList.toArray()[1]; + let chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + let valuesChipArea: IgxChipsAreaComponent = chipAreas[2].componentInstance; + let valChip1 = valuesChipArea.chipsList.toArray()[0]; + let valChip2 = valuesChipArea.chipsList.toArray()[1]; // move first chip over the second one headerRow.onDimDragOver({ @@ -1067,12 +1464,58 @@ describe('IgxPivotGrid #pivotGrid', () => { owner: valChip2 }, valuesChipArea); pivotGrid.cdr.detectChanges(); + fixture.detectChanges(); + //check chip order is updated. expect(valuesChipArea.chipsList.toArray()[0].id).toBe(valChip2.id); expect(valuesChipArea.chipsList.toArray()[1].id).toBe(valChip1.id); // check dimension order is updated. expect(pivotGrid.pivotConfiguration.values.map(x => x.member)).toEqual(['UnitPrice', 'UnitsSold']); + // should be able to move on the opposite side + chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + valuesChipArea = chipAreas[2].componentInstance; + valChip1 = valuesChipArea.chipsList.toArray()[0]; + valChip2 = valuesChipArea.chipsList.toArray()[1]; + headerRow.onDimDragOver({ + dragChip: { + id: 'UnitsSold', + data: { pivotArea: 'value' } + }, + owner: valChip1, + originalEvent: { + offsetX: -100 + } + }); + fixture.detectChanges(); + + headerRow.onValueDrop({ + dragChip: valChip2, + owner: valChip1 + }, valuesChipArea); + pivotGrid.cdr.detectChanges(); + fixture.detectChanges(); + //check chip order is updated. + expect(valuesChipArea.chipsList.toArray()[0].id).toBe(valChip2.id); + expect(valuesChipArea.chipsList.toArray()[1].id).toBe(valChip1.id); + // check dimension order is updated. + expect(pivotGrid.pivotConfiguration.values.map(x => x.member)).toEqual(['UnitsSold', 'UnitPrice']); + + //should not be able to drag value to row + headerRow.onDimDragOver({ + dragChip: { + id: 'UnitsSold', + data: { pivotArea: 'value' } + }, + owner: valChip2, + originalEvent: { + offsetX: 100 + } + }, PivotDimensionType.Row); + fixture.detectChanges(); + + expect(pivotGrid.pivotConfiguration.values.map(x => x.member)).toEqual(['UnitsSold', 'UnitPrice']); + expect(pivotGrid.pivotConfiguration.rows.length).toBe(1); }); it('should allow moving dimension between rows, columns and filters.', () => { const pivotGrid = fixture.componentInstance.pivotGrid; @@ -1242,6 +1685,7 @@ describe('IgxPivotGrid #pivotGrid', () => { it('should select/deselect the correct row', () => { fixture.detectChanges(); const pivotGrid = fixture.componentInstance.pivotGrid; + expect(pivotGrid.selectedRows).toEqual([]); const pivotRows = GridFunctions.getPivotRows(fixture); const row = pivotRows[2].componentInstance; const rowHeaders = fixture.debugElement.queryAll( @@ -1432,6 +1876,134 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); })); + + it('should update grid after resizing with double click', fakeAsync(() => { + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); + + const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[3]).nativeElement; + + // Resize first column + UIInteractions.simulateMouseEvent('dblclick', headerResArea, 100, 0); + tick(200); + fixture.detectChanges(); + + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(parseFloat(rowHeaders[0].componentInstance.column.width)).toBeGreaterThan(200); + expect(parseFloat(rowHeaders[3].componentInstance.column.width)).toBeGreaterThan(200); + + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); + })); + + it('should update grid after resizing to equal min width', fakeAsync(() => { + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); + + const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[3]).nativeElement; + + // Resize first column + UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); + tick(200); + fixture.detectChanges(); + + const resizer = GridFunctions.getResizer(fixture).nativeElement; + expect(resizer).toBeDefined(); + UIInteractions.simulateMouseEvent('mousemove', resizer, -400, 5); + UIInteractions.simulateMouseEvent('mouseup', resizer, -400, 5); + fixture.detectChanges(); + + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const minWdith = parseFloat(rowHeaders[0].componentInstance.column.minWidth); + expect(parseFloat(rowHeaders[0].componentInstance.column.width)).toEqual(minWdith); + expect(parseFloat(rowHeaders[3].componentInstance.column.width)).toEqual(minWdith); + + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); + })); + + it('should update grid after resizing with percentages', fakeAsync(() => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.width = '1000px'; + pivotGrid.pivotConfiguration.rows[0].width = '20%'; + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges; + + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(parseFloat(rowHeaders[0].componentInstance.column.width)).toBeGreaterThan(150); + expect(parseFloat(rowHeaders[3].componentInstance.column.width)).toBeGreaterThan(150); + expect(pivotGrid.pivotConfiguration.rows[0].width).toEqual('20%'); + + const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[3]).nativeElement; + + // Resize first column + UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); + tick(200); + fixture.detectChanges(); + + const resizer = GridFunctions.getResizer(fixture).nativeElement; + expect(resizer).toBeDefined(); + UIInteractions.simulateMouseEvent('mousemove', resizer, -100, 5); + UIInteractions.simulateMouseEvent('mouseup', resizer, -100, 5); + fixture.detectChanges(); + + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(parseFloat(rowHeaders[0].componentInstance.column.width)).toBeLessThan(150); + expect(parseFloat(rowHeaders[3].componentInstance.column.width)).toBeLessThan(150); + // less than 10% + expect(parseFloat(pivotGrid.pivotConfiguration.rows[0].width)).toBeLessThan(10); + + + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + })); + + + it('Should not expand columns if collapsed after sorting', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.width = '1600px'; + fixture.detectChanges(); + pivotGrid.pivotConfiguration.columns = [ + pivotGrid.pivotConfiguration.rows[1] + ]; + pivotGrid.pivotConfiguration.rows.pop(); + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges(); + + expect(pivotGrid.columns.length).toBe(16); + expect(pivotGrid.rowList.first.cells.length).toBe(8); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const header = headerRow.querySelector('igx-grid-header-group'); + const expander = header.querySelectorAll('igx-icon')[0]; + expander.click(); + fixture.detectChanges(); + expect(pivotGrid.columnGroupStates.size).toBe(1); + expect(pivotGrid.rowList.first.cells.length).toBe(2); + + const colChip = headerRow.querySelector('igx-chip[id="AllProducts"]'); + + // sort + colChip.click(); + fixture.detectChanges(); + + expect(pivotGrid.columnGroupStates.size).toBe(1); + expect(pivotGrid.rowList.first.cells.length).toBe(2); + }); }); describe('IgxPivotGrid APIs #pivotGrid', () => { @@ -1457,7 +2029,14 @@ describe('IgxPivotGrid #pivotGrid', () => { pivotGrid = fixture.componentInstance.pivotGrid; })); - it('should allow inserting new dimension at index.', () => { + + it('should allow inserting new dimension.', () => { + //insert wtihout index + pivotGrid.insertDimensionAt({ memberName: 'Date', enabled: true }, PivotDimensionType.Row); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.rows[2].memberName).toBe('Date'); + + // At Index // insert in rows pivotGrid.insertDimensionAt({ memberName: 'SellerName', enabled: true }, PivotDimensionType.Row, 1); fixture.detectChanges(); @@ -1627,8 +2206,8 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(first).toBe('All Cities'); }); - it('should allow inserting new value at index.', () => { - const value = { + it('should allow inserting new value.', () => { + let value = { member: 'Date', aggregate: { aggregator: IgxPivotDateAggregate.latest, @@ -1637,11 +2216,31 @@ describe('IgxPivotGrid #pivotGrid', () => { }, enabled: true }; + // At Index pivotGrid.insertValueAt(value, 1); fixture.detectChanges(); expect(pivotGrid.values.length).toBe(3); expect(pivotGrid.values[1].member).toBe('Date'); expect(pivotGrid.columns.length).toBe(20); + + // With no Index + pivotGrid.pivotConfiguration.values = undefined; + pivotGrid.notifyDimensionChange(true); + fixture.detectChanges(); + pivotGrid.insertValueAt({ + member: 'Date', + displayName: 'DateNew', + aggregate: { + aggregator: IgxPivotDateAggregate.earliest, + key: 'EARLIEST', + label: 'Earliest' + }, + enabled: true + }); + expect(pivotGrid.values.length).toBe(1); + expect(pivotGrid.values[0].member).toBe('Date'); + expect(pivotGrid.values[0].displayName).toBe('DateNew'); + expect(pivotGrid.columns.length).toBe(5); }); it('should allow removing value.', () => { @@ -1672,6 +2271,22 @@ describe('IgxPivotGrid #pivotGrid', () => { it('should allow moving value.', () => { const val = pivotGrid.pivotConfiguration.values[0]; + + //should do nothing if value is not present in the configuration + pivotGrid.moveValue({ + member: 'NotPresent', + enabled:true, + aggregate: { + aggregator: () => {}, + key: 'Test', + label: 'test' + } + }); + fixture.detectChanges(); + expect(pivotGrid.values.length).toBe(2); + expect(pivotGrid.values[0].member).toBe('UnitsSold'); + expect(pivotGrid.values[1].member).toBe('AmountOfSale'); + // move after pivotGrid.moveValue(val, 1); fixture.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts index 40d7ca9bf44..9e9be47f5ba 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts @@ -273,7 +273,7 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem */ public getAreaHeight(area: IgxChipsAreaComponent) { const chips = area.chipsList; - return chips && chips.length > 0 ? chips.first.nativeElement.clientHeight : 0; + return chips && chips.length > 0 ? chips.first.nativeElement.offsetHeight : 0; } /** @@ -310,26 +310,10 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem public filterRemoved(event: IBaseChipEventArgs) { const filter = this.grid.pivotConfiguration.filters.find(x => x.memberName === event.owner.id); this.grid.toggleDimension(filter); - if (this.isFiltersButton && this.filterDropdownDimensions.has(filter)) { - const selectedChip = this.dropdownChips.chipsList.find(x => x.selected); - if (!selectedChip || selectedChip.id === event.owner.id) { - this.dropdownChips.chipsList.first.selected = true; - } - this.filterDropdownDimensions.delete(filter) - if (this.filterDropdownDimensions.size === 0) { - this.grid.filteringService.hideESF(); - } else { - this.onFiltersAreaDropdownClick({ target: this.filtersButton.el.nativeElement }, undefined, false); - } + if (this.filterDropdownDimensions.size > 0) { + this.onFiltersAreaDropdownClick({ target: this.filtersButton.el.nativeElement }, undefined, false); } else { - if (this.filterAreaDimensions.has(filter)) { - this.filterAreaDimensions.delete(filter) - this.grid.filteringService.hideESF(); - } else if (this.filterDropdownDimensions.size > 0) { - this.onFiltersAreaDropdownClick({ target: this.filtersButton.el.nativeElement }, undefined, false); - } else { - this.grid.filteringService.hideESF(); - } + this.grid.filteringService.hideESF(); } } @@ -359,15 +343,7 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem event.stopPropagation(); event.preventDefault(); let dim = dimension; - let col; - while (dim) { - col = this.grid.dimensionDataColumns.find(x => x.field === dim.memberName || x.field === dim.member); - if (col) { - break; - } else { - dim = dim.childLevel; - } - } + const col = this.grid.dimensionDataColumns.find(x => x.field === dim.memberName || x.field === dim.member); this.grid.filteringService.toggleFilterDropdown(event.target, col); } @@ -377,15 +353,7 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem */ public onSummaryClick(eventArgs, value: IPivotValue, dropdown: IgxDropDownComponent, chip: IgxChipComponent) { this._subMenuOverlaySettings.target = eventArgs.currentTarget; - if (dropdown.collapsed) { - this.updateDropDown(value, dropdown, chip); - } else { - // close for previous chip - dropdown.close(); - dropdown.closed.pipe(first()).subscribe(() => { - this.updateDropDown(value, dropdown, chip); - }); - } + this.updateDropDown(value, dropdown, chip); } /** @@ -393,15 +361,7 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem */ public onFiltersAreaDropdownClick(event, dimension?, shouldReattach = true) { let dim = dimension || this.filterDropdownDimensions.values().next().value; - let col; - while (dim) { - col = this.grid.dimensionDataColumns.find(x => x.field === dim.memberName || x.field === dim.member); - if (col) { - break; - } else { - dim = dim.childLevel; - } - } + const col = this.grid.dimensionDataColumns.find(x => x.field === dim.memberName || x.field === dim.member); if (shouldReattach) { this.dropdownChips.chipsList.forEach(chip => { chip.selected = false @@ -551,12 +511,6 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem this.onAreaDragLeave(event, area); } - protected getDimensionsType(dimension: IPivotDimension) { - const isColumn = !!this.grid.pivotConfiguration.columns?.find(x => x && x.memberName === dimension.memberName); - const isRow = !!this.grid.pivotConfiguration.rows?.find(x => x && x.memberName === dimension.memberName); - return isColumn ? PivotDimensionType.Column : isRow ? PivotDimensionType.Row : PivotDimensionType.Filter; - } - protected updateDropDown(value: IPivotValue, dropdown: IgxDropDownComponent, chip: IgxChipComponent) { this.value = value; dropdown.width = chip.nativeElement.clientWidth + 'px'; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html index 1911b856316..9271eb60afe 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html @@ -12,7 +12,7 @@ -
+
{{ getExpandState() ? 'expand_more' : 'chevron_right'}} {{column.header}} @@ -21,7 +21,7 @@ -
+
{{column.header}} diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts index cf672b64ac4..b918bbf4da7 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts @@ -131,10 +131,6 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon return this.dimension.level; } - public get rowSpan() { - return this.rowData.rowSpan || 1; - } - protected extractFromDimensions() { const col = this.extractFromDimension(this.dimension, this.rowData); const prevDims = []; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-header-group.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-header-group.component.html index 7d409191c6a..6f1a2adc64d 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-header-group.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-header-group.component.html @@ -1,28 +1,3 @@ - - -
- - - - -
- -
- - {{column.header}} @@ -32,53 +7,6 @@ {{column.expanded ? 'expand_more' : 'chevron_right'}} - - -
- -
- - -
-
- - -
-
- - - - -
- -
- { - if (key.indexOf(pivotKeys.rowDimensionSeparator + pivotKeys.level) !== -1 && - key.indexOf(pivotKeys.level + pivotKeys.rowDimensionSeparator) === -1 && - key.indexOf(pivotKeys.records) === -1) { - total += rec[key] || 0; - } - }); - return total; - } - - public static flattenColumnHierarchy(hierarchies: any, values: IPivotValue[], pivotKeys: IPivotKeys) { - const flatData = []; - hierarchies.forEach((h, key) => { - const obj = {}; - const multipleValues = values.length > 1; - for (const value of values) { - if (h[pivotKeys.aggregations]) { - if (multipleValues) { - obj[key + pivotKeys.columnDimensionSeparator + value.member] = h[pivotKeys.aggregations][value.member]; - } else { - obj[key] = h[pivotKeys.aggregations][value.member]; - } - } - obj[pivotKeys.records] = h[pivotKeys.records]; - flatData.push(obj); - if (h[pivotKeys.children]) { - const records = this.flattenColumnHierarchy(h[pivotKeys.children], values, pivotKeys); - for (const record of records) { - delete record[pivotKeys.records]; - const childKeys = Object.keys(record); - for (const childKey of childKeys) { - obj[childKey] = record[childKey]; - } - } - } - } - }); - - return flatData; - } - public static buildExpressionTree(config: IPivotConfiguration) { const allDimensions = (config.rows || []).concat((config.columns || [])).concat(config.filters || []).filter(x => x !== null && x !== undefined); const enabledDimensions = allDimensions.filter(x => x && x.enabled); diff --git a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts index fea7377506f..2537b3cc30c 100644 --- a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts @@ -443,7 +443,7 @@ describe('IgxGridState - input properties #grid', () => { expect(grid.pinnedRows[0].key).toBe(1); expect(grid.pinnedRows[1].key).toBe(3); }); - + it('setState should correctly restore grid moving state from string', () => { const fix = TestBed.createComponent(IgxGridStateComponent); fix.detectChanges(); @@ -460,7 +460,7 @@ describe('IgxGridState - input properties #grid', () => { expect(grid.moving).toBeFalsy(); gridState = state.getState(true, 'moving'); expect(gridState).toBe(movingState); - + }); it('setState should correctly restore grid moving state from object', () => { @@ -470,7 +470,7 @@ describe('IgxGridState - input properties #grid', () => { const state = fix.componentInstance.state; const movingState = '{"moving":false}'; const initialState = '{"moving":true}'; - const movingStateObject = JSON.parse(movingState); + const movingStateObject = JSON.parse(movingState); let gridState = state.getState(true, 'moving'); expect(gridState).toBe(initialState); @@ -590,6 +590,31 @@ describe('IgxGridState - input properties #grid', () => { gridState = state.getState(true, 'expansion'); expect(gridState).toBe(expansionState); }); + + // fit('createExpressionsTreeFromObject should return null when columns are still not resolved', () => { + // const fix = TestBed.createComponent(IgxGridStateComponent); + // fix.detectChanges(); + // fix.componentInstance.ngOnInit(); + // fix.detectChanges(); + // const grid = fix.componentInstance.grid; + // // grid.columnList = new QueryList(); + // const state = fix.componentInstance.state; + // // eslint-disable-next-line max-len + // + // const advFilteringState = '{"advancedFiltering":{"filteringOperands":[{"fieldName":"InStock","condition":{"name":"true","isUnary":true,"iconName":"is-true"},"searchVal":null,"ignoreCase":true},{"fieldName":"ProductID","condition":{"name":"greaterThan","isUnary":false,"iconName":"greater-than"},"searchVal":"3","ignoreCase":true}],"operator":0,"type":1}}'; + // const initialState = '{"advancedFiltering":{}}'; + // const advFilteringStateObject = JSON.parse(advFilteringState); + // + // let gridState = state.getState(true, 'advancedFiltering'); + // expect(gridState).toBe(initialState); + // + // state.setState(advFilteringStateObject); + // gridState = state.getState(false, 'advancedFiltering') as IGridState; + // + // let areColumnsResolved = grid.columnList.length > 0 || !!gridState.columns + // + // expect(areColumnsResolved).toBe(false); + // }); }); class HelperFunctions { diff --git a/projects/igniteui-angular/src/lib/grids/state.directive.ts b/projects/igniteui-angular/src/lib/grids/state.directive.ts index 74935e9e6ff..7a9b5ef343e 100644 --- a/projects/igniteui-angular/src/lib/grids/state.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/state.directive.ts @@ -553,8 +553,10 @@ export class IgxGridStateDirective { let dataType: string; if (this.currGrid.columnList.length > 0) { dataType = this.currGrid.columnList.find(c => c.field === expr.fieldName).dataType; - } else { + } else if (this.state.columns) { dataType = this.state.columns.find(c => c.field === expr.fieldName).dataType; + } else { + return null; } // when ESF, values are stored in Set. // First those values are converted to an array before returning string in the stringifyCallback diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-crud.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-crud.spec.ts index c5aeb29287b..810493b5d48 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-crud.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-crud.spec.ts @@ -367,11 +367,12 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyRowsCount(fix, 3, 10); }); - it('should support updating a child tree-cell through the treeGrid API', () => { + it('should support updating a child tree-cell through the treeGrid API', fakeAsync(() => { // Test prerequisites: move 'Age' column so it becomes the tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'Age')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); verifyCellValue(fix, 6, 'Age', '25'); @@ -383,13 +384,14 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyCellValue(fix, 6, 'Age', '18'); verifyRowsCount(fix, 3, 10); - }); + })); - it('should support updating a child tree-cell through the cellObject API', () => { + it('should support updating a child tree-cell through the cellObject API', fakeAsync(() => { // Test prerequisites: move 'Age' column so it becomes the tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'Age')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); @@ -402,7 +404,7 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyCellValue(fix, 6, 'Age', '18'); verifyRowsCount(fix, 3, 10); - }); + })); }); describe('Primary/Foreign key', () => { @@ -514,11 +516,12 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyTreeGridRecordsCount(fix, 4, 8); // Root rows count increment with 1 due to the row update. }); - it('should support updating a child tree-cell through the treeGrid API', () => { + it('should support updating a child tree-cell through the treeGrid API', fakeAsync(() => { // Test prerequisites: move 'Name' column so it becomes the tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'Name')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); verifyCellValue(fix, 3, 'Name', 'Debra Morton'); @@ -530,13 +533,14 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyCellValue(fix, 3, 'Name', 'Michael Myers'); verifyRowsCount(fix, 8, 8); - }); + })); - it('should support updating a child tree-cell through the cellObject API', () => { + it('should support updating a child tree-cell through the cellObject API', fakeAsync(() => { // Test prerequisites: move 'Name' column so it becomes the tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'Name')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); verifyCellValue(fix, 3, 'Name', 'Debra Morton'); @@ -548,7 +552,7 @@ describe('IgxTreeGrid - CRUD #tGrid', () => { verifyCellValue(fix, 3, 'Name', 'Michael Myers'); verifyRowsCount(fix, 8, 8); - }); + })); }); }); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-indentation.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-indentation.spec.ts index 03c961e6a10..4fa744f88eb 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-indentation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-indentation.spec.ts @@ -169,7 +169,7 @@ describe('IgxTreeGrid - Indentation #tGrid', () => { TreeGridFunctions.verifyRowIndentationLevel(treeGrid.getRowByIndex(9), rows[9], 1); })); - it('should change cell content alignment of tree-column with number dataType when it is no longer tree-column', () => { + it('should change cell content alignment of tree-column with number dataType when it is no longer tree-column', fakeAsync(() => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4); verifyCellsContentAlignment(fix, 'ID', true); // Verify cells of 'ID' are left-aligned. @@ -177,6 +177,7 @@ describe('IgxTreeGrid - Indentation #tGrid', () => { const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; let targetColumn = treeGrid.columnList.filter(c => c.field === 'Age')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4); @@ -185,11 +186,12 @@ describe('IgxTreeGrid - Indentation #tGrid', () => { // Moving 'ID' column targetColumn = treeGrid.columnList.filter(c => c.field === 'Name')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4); verifyCellsContentAlignment(fix, 'ID', true); // Verify cells of 'ID' are left-aligned. - }); + })); }); describe('Primary/Foreign key', () => { @@ -334,16 +336,16 @@ describe('IgxTreeGrid - Indentation #tGrid', () => { const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; let targetColumn = treeGrid.columnList.filter(c => c.field === 'Age')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); - tick(16); TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 5); verifyCellsContentAlignment(fix, 'ID', false); // Verify cells of 'ID' are right-aligned. // Moving 'ID' column targetColumn = treeGrid.columnList.filter(c => c.field === 'ParentID')[0]; treeGrid.moveColumn(sourceColumn, targetColumn, DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); - tick(16); TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5); verifyCellsContentAlignment(fix, 'ID', true); // Verify cells of 'ID' are left-aligned. })); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts index 3bb7ed62aa5..d869af25ea9 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts @@ -138,17 +138,18 @@ describe('IgxTreeGrid - Integration #tGrid', () => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 3); }); - it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', () => { + it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', fakeAsync(() => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4); // Move tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'HireDate')[0]; treeGrid.moveColumn(sourceColumn, targetColumn); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4); - }); + })); it('(UI) should transform a non-tree column into a tree column when moving the original tree-column through', (async () => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4); @@ -161,6 +162,7 @@ describe('IgxTreeGrid - Integration #tGrid', () => { await wait(); UIInteractions.simulatePointerEvent('pointermove', header, 490, 30); UIInteractions.simulatePointerEvent('pointerup', header, 490, 30); + await wait() fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4); @@ -251,17 +253,18 @@ describe('IgxTreeGrid - Integration #tGrid', () => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4); }); - it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', () => { + it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', fakeAsync(() => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5); // Move tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'JobTitle')[0]; treeGrid.moveColumn(sourceColumn, targetColumn); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 5); - }); + })); it('(UI) should transform a non-tree column into a tree column when moving the original tree-column through', (async () => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5); @@ -274,6 +277,7 @@ describe('IgxTreeGrid - Integration #tGrid', () => { await wait(); UIInteractions.simulatePointerEvent('pointermove', header, 490, 30); UIInteractions.simulatePointerEvent('pointerup', header, 490, 30); + await wait() fix.detectChanges(); TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 5); @@ -1282,17 +1286,18 @@ describe('IgxTreeGrid - Integration #tGrid', () => { TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'ID', 3); }); - it('(API) Should transform a non-tree column into a tree column when moving it first and both are part of the same group', () => { + it('(API) Should transform a non-tree column into a tree column when moving it first and both are part of the same group', fakeAsync(() => { TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'ID', 4); // Move tree-column const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0]; const targetColumn = treeGrid.columnList.filter(c => c.field === 'Name')[0]; treeGrid.moveColumn(sourceColumn, targetColumn); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'Name', 4); - }); + })); it('(UI) Should transform a non-tree column into a tree column when moving it first within a group', (async () => { TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'ID', 4); @@ -1305,22 +1310,24 @@ describe('IgxTreeGrid - Integration #tGrid', () => { await wait(); UIInteractions.simulatePointerEvent('pointermove', header, 420, 90); UIInteractions.simulatePointerEvent('pointerup', header, 420, 90); + await wait() fix.detectChanges(); TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'Name', 4); })); - it('(API) Should transform a non-tree column of a column group to a tree column when its group is moved first', () => { + it('(API) Should transform a non-tree column of a column group to a tree column when its group is moved first', fakeAsync(() => { TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'ID', 4); // Move group-column const sourceColumn = treeGrid.columnList.filter(c => c.header === 'General Information')[0]; const targetColumn = treeGrid.columnList.filter(c => c.header === 'Additional Information')[0]; treeGrid.moveColumn(sourceColumn, targetColumn); + tick(); fix.detectChanges(); TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'HireDate', 4); - }); + })); it('(UI) Should transform a non-tree column of a column group to a tree column when its group is moved first', (async () => { TreeGridFunctions.verifyTreeColumnInMultiColHeaders(fix, 'ID', 4); @@ -1557,7 +1564,7 @@ describe('IgxTreeGrid - Integration #tGrid', () => { expect(wrongChipPosition.nativeElement.getElementsByClassName('igx-grid__td--pinned-chip').length).toBe(0); }); - it('pinned chip should always be in the first column', () => { + it('pinned chip should always be in the first column', fakeAsync(() => { const rowToPin = treeGrid.getRowByIndex(0); const primaryKey = treeGrid.primaryKey; @@ -1566,11 +1573,12 @@ describe('IgxTreeGrid - Integration #tGrid', () => { const thirdColumnField = treeGrid.columnList.get(2).field; treeGrid.moveColumn(treeGrid.columnList.get(2), treeGrid.columnList.get(0), DropPosition.BeforeDropTarget); + tick(); fix.detectChanges(); const pinnedChipExpectedPosition = treeGrid.gridAPI.get_cell_by_index(1, thirdColumnField); expect(pinnedChipExpectedPosition.nativeElement.getElementsByClassName('igx-grid__td--pinned-chip').length).toBe(1); - }); + })); it('should expand/collapse a pinned row with children', () => { let rows = TreeGridFunctions.getAllRows(fix); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts index 53bb92a75f7..117d427e8e7 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts @@ -1,171 +1,171 @@ -import { parseDate, resolveNestedPath } from '../../core/utils'; -import { DataUtil } from '../../data-operations/data-util'; -import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; -import { BaseFilteringStrategy, IgxFilterItem } from '../../data-operations/filtering-strategy'; -import { SortingDirection } from '../../data-operations/sorting-strategy'; -import { ColumnType, GridType } from '../common/grid.interface'; -import { IgxTreeGridAPIService } from './tree-grid-api.service'; -import { ITreeGridRecord } from './tree-grid.interfaces'; - -export class TreeGridFilteringStrategy extends BaseFilteringStrategy { - - constructor(public hierarchicalFilterFields?: string[]) { - super(); - } - - public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, - advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { - return this.filterImpl(data, expressionsTree, advancedExpressionsTree, undefined, grid); - } - - protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { - const column = grid?.getColumnByName(fieldName); - const hierarchicalRecord = rec as ITreeGridRecord; - let value = this.isHierarchicalFilterField(fieldName) ? - this.getHierarchicalFieldValue(hierarchicalRecord, fieldName) : - resolveNestedPath(hierarchicalRecord.data, fieldName); - - value = column?.formatter && this.shouldFormatFilterValues(column) ? - column.formatter(value, rec.data) : - value && (isDate || isTime) ? parseDate(value) : value; - - return value; - } - - private getHierarchicalFieldValue(record: ITreeGridRecord, field: string) { - const value = resolveNestedPath(record.data, field); - - return record.parent ? - `${this.getHierarchicalFieldValue(record.parent, field)}${value ? `.[${value}]` : ''}` : - `[${value}]`; - } - - private filterImpl(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, - advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] { - let i: number; - let rec: ITreeGridRecord; - const len = data.length; - const res: ITreeGridRecord[] = []; - if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) { - return data; - } - for (i = 0; i < len; i++) { - rec = DataUtil.cloneTreeGridRecord(data[i]); - rec.parent = parent; - if (rec.children) { - const filteredChildren = this.filterImpl(rec.children, expressionsTree, advancedExpressionsTree, rec, grid); - rec.children = filteredChildren.length > 0 ? filteredChildren : null; - } - - if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) { - res.push(rec); - } else if (rec.children && rec.children.length > 0) { - rec.isFilteredOutParent = true; - res.push(rec); - } - } - return res; - } - - private isHierarchicalFilterField(field: string) { - return this.hierarchicalFilterFields && this.hierarchicalFilterFields.indexOf(field) !== -1; - } - - public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { - if (!this.isHierarchicalFilterField(column.field)) { - return super.getFilterItems(column, tree); - } - - let data = (column.grid.gridAPI as IgxTreeGridAPIService).filterTreeDataByExpressions(tree); - data = DataUtil.treeGridSort( - data, - [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }], - column.grid.sortStrategy, - null, - column.grid); - - const items = this.getHierarchicalFilterItems(data, column); - - - return Promise.resolve(items); - } - - private getHierarchicalFilterItems(records: ITreeGridRecord[], column: ColumnType, parent?: IgxFilterItem): IgxFilterItem[] { - return records?.map(record => { - let value = resolveNestedPath(record.data, column.field); - const applyFormatter = column.formatter && this.shouldFormatFilterValues(column); - - value = applyFormatter ? - column.formatter(value, record.data) : - value; - - const hierarchicalValue = parent ? - (value || value === 0) ? `${parent.value}.[${value}]` : value : - `[${value}]`; - - const filterItem: IgxFilterItem = { value: hierarchicalValue }; - filterItem.label = this.getFilterItemLabel(column, value, !applyFormatter, record.data); - filterItem.children = this.getHierarchicalFilterItems(record.children, column, filterItem); - return filterItem; - }); - } -} - -export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringStrategy { - /** - * Creates a new instance of FormattedValuesFilteringStrategy. - * - * @param fields An array of column field names that should be formatted. - * If omitted the values of all columns which has formatter will be formatted. - */ - constructor(private fields?: string[]) { - super(); - } - - protected shouldFormatFilterValues(column: ColumnType): boolean { - return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); - } -} - -export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilteringStrategy { - public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, - advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { - return this.filterImplementation(data, expressionsTree, advancedExpressionsTree, undefined, grid); - } - - private filterImplementation(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, - advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] { - let i: number; - let rec: ITreeGridRecord; - const len = data.length; - const res: ITreeGridRecord[] = []; - if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) { - return data; - } - for (i = 0; i < len; i++) { - rec = DataUtil.cloneTreeGridRecord(data[i]); - rec.parent = parent; - if (rec.children) { - const filteredChildren = this.filterImplementation(rec.children, expressionsTree, advancedExpressionsTree, rec, grid); - rec.children = filteredChildren.length > 0 ? filteredChildren : null; - } - if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) { - res.push(rec); - } else if (rec.children && rec.children.length > 0) { - rec = this.setCorrectLevelToFilteredRecords(rec); - res.push(...rec.children); - } - } - return res; - } - - private setCorrectLevelToFilteredRecords(rec: ITreeGridRecord): ITreeGridRecord { - if (rec.children && rec.children.length > 0) { - rec.children.map(child => { - child.level = child.level - 1; - return this.setCorrectLevelToFilteredRecords(child); - }); - } - return rec; - } -} +import { parseDate, resolveNestedPath } from '../../core/utils'; +import { DataUtil } from '../../data-operations/data-util'; +import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; +import { BaseFilteringStrategy, IgxFilterItem } from '../../data-operations/filtering-strategy'; +import { SortingDirection } from '../../data-operations/sorting-strategy'; +import { ColumnType, GridType } from '../common/grid.interface'; +import { IgxTreeGridAPIService } from './tree-grid-api.service'; +import { ITreeGridRecord } from './tree-grid.interfaces'; + +export class TreeGridFilteringStrategy extends BaseFilteringStrategy { + + constructor(public hierarchicalFilterFields?: string[]) { + super(); + } + + public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, + advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { + return this.filterImpl(data, expressionsTree, advancedExpressionsTree, undefined, grid); + } + + protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { + const column = grid?.getColumnByName(fieldName); + const hierarchicalRecord = rec as ITreeGridRecord; + let value = this.isHierarchicalFilterField(fieldName) ? + this.getHierarchicalFieldValue(hierarchicalRecord, fieldName) : + resolveNestedPath(hierarchicalRecord.data, fieldName); + + value = column?.formatter && this.shouldFormatFilterValues(column) ? + column.formatter(value, rec.data) : + value && (isDate || isTime) ? parseDate(value) : value; + + return value; + } + + private getHierarchicalFieldValue(record: ITreeGridRecord, field: string) { + const value = resolveNestedPath(record.data, field); + + return record.parent ? + `${this.getHierarchicalFieldValue(record.parent, field)}${value ? `.[${value}]` : ''}` : + `[${value}]`; + } + + private filterImpl(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, + advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] { + let i: number; + let rec: ITreeGridRecord; + const len = data.length; + const res: ITreeGridRecord[] = []; + if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) { + return data; + } + for (i = 0; i < len; i++) { + rec = DataUtil.cloneTreeGridRecord(data[i]); + rec.parent = parent; + if (rec.children) { + const filteredChildren = this.filterImpl(rec.children, expressionsTree, advancedExpressionsTree, rec, grid); + rec.children = filteredChildren.length > 0 ? filteredChildren : null; + } + + if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) { + res.push(rec); + } else if (rec.children && rec.children.length > 0) { + rec.isFilteredOutParent = true; + res.push(rec); + } + } + return res; + } + + private isHierarchicalFilterField(field: string) { + return this.hierarchicalFilterFields && this.hierarchicalFilterFields.indexOf(field) !== -1; + } + + public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { + if (!this.isHierarchicalFilterField(column.field)) { + return super.getFilterItems(column, tree); + } + + let data = (column.grid.gridAPI as IgxTreeGridAPIService).filterTreeDataByExpressions(tree); + data = DataUtil.treeGridSort( + data, + [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }], + column.grid.sortStrategy, + null, + column.grid); + + const items = this.getHierarchicalFilterItems(data, column); + + + return Promise.resolve(items); + } + + private getHierarchicalFilterItems(records: ITreeGridRecord[], column: ColumnType, parent?: IgxFilterItem): IgxFilterItem[] { + return records?.map(record => { + let value = resolveNestedPath(record.data, column.field); + const applyFormatter = column.formatter && this.shouldFormatFilterValues(column); + + value = applyFormatter ? + column.formatter(value, record.data) : + value; + + const hierarchicalValue = parent ? + (value || value === 0) ? `${parent.value}.[${value}]` : value : + `[${value}]`; + + const filterItem: IgxFilterItem = { value: hierarchicalValue }; + filterItem.label = this.getFilterItemLabel(column, value, !applyFormatter, record.data); + filterItem.children = this.getHierarchicalFilterItems(record.children, column, filterItem); + return filterItem; + }); + } +} + +export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringStrategy { + /** + * Creates a new instance of FormattedValuesFilteringStrategy. + * + * @param fields An array of column field names that should be formatted. + * If omitted the values of all columns which has formatter will be formatted. + */ + constructor(private fields?: string[]) { + super(); + } + + protected shouldFormatFilterValues(column: ColumnType): boolean { + return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); + } +} + +export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilteringStrategy { + public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, + advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { + return this.filterImplementation(data, expressionsTree, advancedExpressionsTree, undefined, grid); + } + + private filterImplementation(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, + advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] { + let i: number; + let rec: ITreeGridRecord; + const len = data.length; + const res: ITreeGridRecord[] = []; + if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) { + return data; + } + for (i = 0; i < len; i++) { + rec = DataUtil.cloneTreeGridRecord(data[i]); + rec.parent = parent; + if (rec.children) { + const filteredChildren = this.filterImplementation(rec.children, expressionsTree, advancedExpressionsTree, rec, grid); + rec.children = filteredChildren.length > 0 ? filteredChildren : null; + } + if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) { + res.push(rec); + } else if (rec.children && rec.children.length > 0) { + rec = this.setCorrectLevelToFilteredRecords(rec); + res.push(...rec.children); + } + } + return res; + } + + private setCorrectLevelToFilteredRecords(rec: ITreeGridRecord): ITreeGridRecord { + if (rec.children && rec.children.length > 0) { + rec.children.map(child => { + child.level = child.level - 1; + return this.setCorrectLevelToFilteredRecords(child); + }); + } + return rec; + } +} diff --git a/projects/igniteui-angular/src/lib/input-group/input-group.component.html b/projects/igniteui-angular/src/lib/input-group/input-group.component.html index b14f3661f60..8b954f0b52b 100644 --- a/projects/igniteui-angular/src/lib/input-group/input-group.component.html +++ b/projects/igniteui-angular/src/lib/input-group/input-group.component.html @@ -21,7 +21,7 @@ - + -
-
-
- - - -
-
-
-
-
- -
-
- - - +
+ +
+
+
+ + + +
+
+
+
+
+ +
+
+ + +
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.component.ts b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.component.ts index 00976a05db2..6b8d181fdf5 100644 --- a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.component.ts +++ b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.component.ts @@ -1,337 +1,337 @@ -import { AnimationBuilder } from '@angular/animations'; -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostBinding, Input, NgZone, OnDestroy, ViewChild } from '@angular/core'; -import { getResizeObserver, mkenum } from '../../core/utils'; -import { IgxDirectionality } from '../../services/direction/directionality'; -import { IgxTabsBase } from '../tabs.base'; -import { IgxTabsDirective } from '../tabs.directive'; - -export const IgxTabsAlignment = mkenum({ - start: 'start', - end: 'end', - center: 'center', - justify: 'justify' -}); - -/** @hidden */ -enum TabScrollButtonStyle { - Visible = 'visible', - Hidden = 'hidden', - NotDisplayed = 'not_displayed' -} - -export type IgxTabsAlignment = (typeof IgxTabsAlignment)[keyof typeof IgxTabsAlignment]; - -/** @hidden */ -let NEXT_TAB_ID = 0; - -/** - * Tabs component is used to organize or switch between similar data sets. - * - * @igxModule IgxTabsModule - * - * @igxTheme igx-tabs-theme - * - * @igxKeywords tabs - * - * @igxGroup Layouts - * - * @remarks - * The Ignite UI for Angular Tabs component places tabs at the top and allows for scrolling when there are multiple tab items on the screen. - * - * @example - * ```html - * - * - * - * folder - * Tab 1 - * - * - * Content 1 - * - * - * ... - * - * ``` - */ -@Component({ - selector: 'igx-tabs', - templateUrl: 'tabs.component.html', - providers: [{ provide: IgxTabsBase, useExisting: IgxTabsComponent }] -}) - -export class IgxTabsComponent extends IgxTabsDirective implements AfterViewInit, OnDestroy { - - /** - * An @Input property which determines the tab alignment. Defaults to `start`. - */ - @Input() - public get tabAlignment(): string | IgxTabsAlignment { - return this._tabAlignment; - }; - - public set tabAlignment(value: string | IgxTabsAlignment) { - this._tabAlignment = value; - requestAnimationFrame(() => { - this.updateScrollButtons(); - this.realignSelectedIndicator(); - }); - } - - /** @hidden */ - @ViewChild('headerContainer', { static: true }) - public headerContainer: ElementRef; - - /** @hidden */ - @ViewChild('viewPort', { static: true }) - public viewPort: ElementRef; - - /** @hidden */ - @ViewChild('itemsWrapper', { static: true }) - public itemsWrapper: ElementRef; - - /** @hidden */ - @ViewChild('itemsContainer', { static: true }) - public itemsContainer: ElementRef; - - /** @hidden */ - @ViewChild('selectedIndicator') - public selectedIndicator: ElementRef; - - /** @hidden */ - @ViewChild('scrollPrevButton') - public scrollPrevButton: ElementRef; - - /** @hidden */ - @ViewChild('scrollNextButton') - public scrollNextButton: ElementRef; - - /** @hidden */ - @HostBinding('class.igx-tabs') - public defaultClass = true; - - /** @hidden */ - public offset = 0; - - /** @hidden */ - protected componentName = 'igx-tabs'; - - private _tabAlignment: string | IgxTabsAlignment = 'start'; - private _resizeObserver: ResizeObserver; - - constructor(builder: AnimationBuilder, cdr: ChangeDetectorRef, private ngZone: NgZone, public dir: IgxDirectionality) { - super(builder, cdr, dir); - } - - - /** @hidden @internal */ - public ngAfterViewInit(): void { - super.ngAfterViewInit(); - - this.ngZone.runOutsideAngular(() => { - this._resizeObserver = new (getResizeObserver())(() => { - this.updateScrollButtons(); - this.realignSelectedIndicator(); - }); - this._resizeObserver.observe(this.headerContainer.nativeElement); - this._resizeObserver.observe(this.viewPort.nativeElement); - }); - } - - /** @hidden @internal */ - public ngOnDestroy(): void { - super.ngOnDestroy(); - - this.ngZone.runOutsideAngular(() => { - this._resizeObserver?.disconnect(); - }); - } - - /** @hidden */ - public scrollPrev() { - this.scroll(false); - } - - /** @hidden */ - public scrollNext() { - this.scroll(true); - } - - /** @hidden */ - public realignSelectedIndicator() { - if (this.selectedIndex >= 0 && this.selectedIndex < this.items.length) { - const header = this.items.get(this.selectedIndex).headerComponent.nativeElement; - this.alignSelectedIndicator(header, 0); - } - } - - /** @hidden */ - public resolveHeaderScrollClasses() { - return { - 'igx-tabs__header-scroll--start': this.tabAlignment === 'start', - 'igx-tabs__header-scroll--end': this.tabAlignment === 'end', - 'igx-tabs__header-scroll--center': this.tabAlignment === 'center', - 'igx-tabs__header-scroll--justify': this.tabAlignment === 'justify', - }; - } - - /** @hidden */ - protected scrollTabHeaderIntoView() { - if (this.selectedIndex >= 0) { - const tabItems = this.items.toArray(); - const tabHeaderNativeElement = tabItems[this.selectedIndex].headerComponent.nativeElement; - - // Scroll left if there is need - if (this.getElementOffset(tabHeaderNativeElement) < this.offset) { - this.scrollElement(tabHeaderNativeElement, false); - } - - // Scroll right if there is need - const viewPortOffsetWidth = this.viewPort.nativeElement.offsetWidth; - const delta = (this.getElementOffset(tabHeaderNativeElement) + tabHeaderNativeElement.offsetWidth) - (viewPortOffsetWidth + this.offset); - - // Fix for IE 11, a difference is accumulated from the widths calculations - if (delta > 1) { - this.scrollElement(tabHeaderNativeElement, true); - } - - this.alignSelectedIndicator(tabHeaderNativeElement); - } else { - this.hideSelectedIndicator(); - } - } - - /** @hidden */ - protected getNextTabId() { - return NEXT_TAB_ID++; - } - - /** @hidden */ - protected onItemChanges() { - super.onItemChanges(); - - Promise.resolve().then(() => { - this.updateScrollButtons(); - }); - } - - private alignSelectedIndicator(element: HTMLElement, duration = 0.3): void { - if (this.selectedIndicator) { - this.selectedIndicator.nativeElement.style.visibility = 'visible'; - this.selectedIndicator.nativeElement.style.transitionDuration = duration > 0 ? `${duration}s` : 'initial'; - this.selectedIndicator.nativeElement.style.width = `${element.offsetWidth}px`; - this.selectedIndicator.nativeElement.style.transform = `translate(${element.offsetLeft}px)`; - } - } - - private hideSelectedIndicator(): void { - if (this.selectedIndicator) { - this.selectedIndicator.nativeElement.style.visibility = 'hidden'; - } - } - - private scroll(scrollNext: boolean): void { - const tabsArray = this.items.toArray(); - - for (let index = 0; index < tabsArray.length; index++) { - const tab = tabsArray[index]; - const element = tab.headerComponent.nativeElement; - if (scrollNext) { - if (element.offsetWidth + this.getElementOffset(element) > this.viewPort.nativeElement.offsetWidth + this.offset) { - this.scrollElement(element, scrollNext); - break; - } - } else { - if (this.getElementOffset(element) >= this.offset) { - this.scrollElement(tabsArray[index - 1].headerComponent.nativeElement, scrollNext); - break; - } - } - } - } - - private scrollElement(element: any, scrollNext: boolean): void { - const viewPortWidth = this.viewPort.nativeElement.offsetWidth; - - this.offset = (scrollNext) ? element.offsetWidth + this.getElementOffset(element) - viewPortWidth : this.getElementOffset(element); - this.viewPort.nativeElement.scrollLeft = this.getOffset(this.offset); - this.updateScrollButtons(); - } - - private updateScrollButtons() { - const itemsContainerWidth = this.getTabItemsContainerWidth(); - - const scrollPrevButtonStyle = this.resolveLeftScrollButtonStyle(itemsContainerWidth); - this.setScrollButtonStyle(this.scrollPrevButton.nativeElement, scrollPrevButtonStyle); - - const scrollNextButtonStyle = this.resolveRightScrollButtonStyle(itemsContainerWidth); - this.setScrollButtonStyle(this.scrollNextButton.nativeElement, scrollNextButtonStyle); - } - - private setScrollButtonStyle(button: HTMLElement, buttonStyle: TabScrollButtonStyle) { - if (buttonStyle === TabScrollButtonStyle.Visible) { - button.style.visibility = 'visible'; - button.style.display = ''; - } else if (buttonStyle === TabScrollButtonStyle.Hidden) { - button.style.visibility = 'hidden'; - button.style.display = ''; - } else if (buttonStyle === TabScrollButtonStyle.NotDisplayed) { - button.style.display = 'none'; - } - } - private resolveLeftScrollButtonStyle(itemsContainerWidth: number): TabScrollButtonStyle { - const headerContainerWidth = this.headerContainer.nativeElement.offsetWidth; - const offset = this.offset; - - if (offset === 0) { - // Fix for IE 11, a difference is accumulated from the widths calculations. - if (itemsContainerWidth - headerContainerWidth <= 1) { - return TabScrollButtonStyle.NotDisplayed; - } - return TabScrollButtonStyle.Hidden; - } else { - return TabScrollButtonStyle.Visible; - } - } - - private resolveRightScrollButtonStyle(itemsContainerWidth: number): TabScrollButtonStyle { - const viewPortWidth = this.viewPort.nativeElement.offsetWidth; - const headerContainerWidth = this.headerContainer.nativeElement.offsetWidth; - const offset = this.offset; - const total = offset + viewPortWidth; - - // Fix for IE 11, a difference is accumulated from the widths calculations. - if (itemsContainerWidth - headerContainerWidth <= 1 && offset === 0) { - return TabScrollButtonStyle.NotDisplayed; - } - - if (itemsContainerWidth > total) { - return TabScrollButtonStyle.Visible; - } else { - return TabScrollButtonStyle.Hidden; - } - } - - private getTabItemsContainerWidth() { - // We use this hacky way to get the width of the itemsContainer, - // because there is inconsistency in IE we cannot use offsetWidth or scrollOffset. - const itemsContainerChildrenCount = this.itemsContainer.nativeElement.children.length; - let itemsContainerWidth = 0; - - if (itemsContainerChildrenCount > 1) { - const lastTab = this.itemsContainer.nativeElement.children[itemsContainerChildrenCount - 1] as HTMLElement; - itemsContainerWidth = this.getElementOffset(lastTab) + lastTab.offsetWidth; - } - - return itemsContainerWidth; - } - - private getOffset(offset: number): number { - return this.dir.rtl ? -offset : offset; - } - - private getElementOffset(element: HTMLElement): number { - return this.dir.rtl ? this.itemsWrapper.nativeElement.offsetWidth - element.offsetLeft - element.offsetWidth : element.offsetLeft; - } -} - +import { AnimationBuilder } from '@angular/animations'; +import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostBinding, Input, NgZone, OnDestroy, ViewChild } from '@angular/core'; +import { getResizeObserver, mkenum } from '../../core/utils'; +import { IgxDirectionality } from '../../services/direction/directionality'; +import { IgxTabsBase } from '../tabs.base'; +import { IgxTabsDirective } from '../tabs.directive'; + +export const IgxTabsAlignment = mkenum({ + start: 'start', + end: 'end', + center: 'center', + justify: 'justify' +}); + +/** @hidden */ +enum TabScrollButtonStyle { + Visible = 'visible', + Hidden = 'hidden', + NotDisplayed = 'not_displayed' +} + +export type IgxTabsAlignment = (typeof IgxTabsAlignment)[keyof typeof IgxTabsAlignment]; + +/** @hidden */ +let NEXT_TAB_ID = 0; + +/** + * Tabs component is used to organize or switch between similar data sets. + * + * @igxModule IgxTabsModule + * + * @igxTheme igx-tabs-theme + * + * @igxKeywords tabs + * + * @igxGroup Layouts + * + * @remarks + * The Ignite UI for Angular Tabs component places tabs at the top and allows for scrolling when there are multiple tab items on the screen. + * + * @example + * ```html + * + * + * + * folder + * Tab 1 + * + * + * Content 1 + * + * + * ... + * + * ``` + */ +@Component({ + selector: 'igx-tabs', + templateUrl: 'tabs.component.html', + providers: [{ provide: IgxTabsBase, useExisting: IgxTabsComponent }] +}) + +export class IgxTabsComponent extends IgxTabsDirective implements AfterViewInit, OnDestroy { + + /** + * An @Input property which determines the tab alignment. Defaults to `start`. + */ + @Input() + public get tabAlignment(): string | IgxTabsAlignment { + return this._tabAlignment; + }; + + public set tabAlignment(value: string | IgxTabsAlignment) { + this._tabAlignment = value; + requestAnimationFrame(() => { + this.updateScrollButtons(); + this.realignSelectedIndicator(); + }); + } + + /** @hidden */ + @ViewChild('headerContainer', { static: true }) + public headerContainer: ElementRef; + + /** @hidden */ + @ViewChild('viewPort', { static: true }) + public viewPort: ElementRef; + + /** @hidden */ + @ViewChild('itemsWrapper', { static: true }) + public itemsWrapper: ElementRef; + + /** @hidden */ + @ViewChild('itemsContainer', { static: true }) + public itemsContainer: ElementRef; + + /** @hidden */ + @ViewChild('selectedIndicator') + public selectedIndicator: ElementRef; + + /** @hidden */ + @ViewChild('scrollPrevButton') + public scrollPrevButton: ElementRef; + + /** @hidden */ + @ViewChild('scrollNextButton') + public scrollNextButton: ElementRef; + + /** @hidden */ + @HostBinding('class.igx-tabs') + public defaultClass = true; + + /** @hidden */ + public offset = 0; + + /** @hidden */ + protected componentName = 'igx-tabs'; + + private _tabAlignment: string | IgxTabsAlignment = 'start'; + private _resizeObserver: ResizeObserver; + + constructor(builder: AnimationBuilder, cdr: ChangeDetectorRef, private ngZone: NgZone, public dir: IgxDirectionality) { + super(builder, cdr, dir); + } + + + /** @hidden @internal */ + public ngAfterViewInit(): void { + super.ngAfterViewInit(); + + this.ngZone.runOutsideAngular(() => { + this._resizeObserver = new (getResizeObserver())(() => { + this.updateScrollButtons(); + this.realignSelectedIndicator(); + }); + this._resizeObserver.observe(this.headerContainer.nativeElement); + this._resizeObserver.observe(this.viewPort.nativeElement); + }); + } + + /** @hidden @internal */ + public ngOnDestroy(): void { + super.ngOnDestroy(); + + this.ngZone.runOutsideAngular(() => { + this._resizeObserver?.disconnect(); + }); + } + + /** @hidden */ + public scrollPrev() { + this.scroll(false); + } + + /** @hidden */ + public scrollNext() { + this.scroll(true); + } + + /** @hidden */ + public realignSelectedIndicator() { + if (this.selectedIndex >= 0 && this.selectedIndex < this.items.length) { + const header = this.items.get(this.selectedIndex).headerComponent.nativeElement; + this.alignSelectedIndicator(header, 0); + } + } + + /** @hidden */ + public resolveHeaderScrollClasses() { + return { + 'igx-tabs__header-scroll--start': this.tabAlignment === 'start', + 'igx-tabs__header-scroll--end': this.tabAlignment === 'end', + 'igx-tabs__header-scroll--center': this.tabAlignment === 'center', + 'igx-tabs__header-scroll--justify': this.tabAlignment === 'justify', + }; + } + + /** @hidden */ + protected scrollTabHeaderIntoView() { + if (this.selectedIndex >= 0) { + const tabItems = this.items.toArray(); + const tabHeaderNativeElement = tabItems[this.selectedIndex].headerComponent.nativeElement; + + // Scroll left if there is need + if (this.getElementOffset(tabHeaderNativeElement) < this.offset) { + this.scrollElement(tabHeaderNativeElement, false); + } + + // Scroll right if there is need + const viewPortOffsetWidth = this.viewPort.nativeElement.offsetWidth; + const delta = (this.getElementOffset(tabHeaderNativeElement) + tabHeaderNativeElement.offsetWidth) - (viewPortOffsetWidth + this.offset); + + // Fix for IE 11, a difference is accumulated from the widths calculations + if (delta > 1) { + this.scrollElement(tabHeaderNativeElement, true); + } + + this.alignSelectedIndicator(tabHeaderNativeElement); + } else { + this.hideSelectedIndicator(); + } + } + + /** @hidden */ + protected getNextTabId() { + return NEXT_TAB_ID++; + } + + /** @hidden */ + protected onItemChanges() { + super.onItemChanges(); + + Promise.resolve().then(() => { + this.updateScrollButtons(); + }); + } + + private alignSelectedIndicator(element: HTMLElement, duration = 0.3): void { + if (this.selectedIndicator) { + this.selectedIndicator.nativeElement.style.visibility = 'visible'; + this.selectedIndicator.nativeElement.style.transitionDuration = duration > 0 ? `${duration}s` : 'initial'; + this.selectedIndicator.nativeElement.style.width = `${element.offsetWidth}px`; + this.selectedIndicator.nativeElement.style.transform = `translate(${element.offsetLeft}px)`; + } + } + + private hideSelectedIndicator(): void { + if (this.selectedIndicator) { + this.selectedIndicator.nativeElement.style.visibility = 'hidden'; + } + } + + private scroll(scrollNext: boolean): void { + const tabsArray = this.items.toArray(); + + for (let index = 0; index < tabsArray.length; index++) { + const tab = tabsArray[index]; + const element = tab.headerComponent.nativeElement; + if (scrollNext) { + if (element.offsetWidth + this.getElementOffset(element) > this.viewPort.nativeElement.offsetWidth + this.offset) { + this.scrollElement(element, scrollNext); + break; + } + } else { + if (this.getElementOffset(element) >= this.offset) { + this.scrollElement(tabsArray[index - 1].headerComponent.nativeElement, scrollNext); + break; + } + } + } + } + + private scrollElement(element: any, scrollNext: boolean): void { + const viewPortWidth = this.viewPort.nativeElement.offsetWidth; + + this.offset = (scrollNext) ? element.offsetWidth + this.getElementOffset(element) - viewPortWidth : this.getElementOffset(element); + this.viewPort.nativeElement.scrollLeft = this.getOffset(this.offset); + this.updateScrollButtons(); + } + + private updateScrollButtons() { + const itemsContainerWidth = this.getTabItemsContainerWidth(); + + const scrollPrevButtonStyle = this.resolveLeftScrollButtonStyle(itemsContainerWidth); + this.setScrollButtonStyle(this.scrollPrevButton.nativeElement, scrollPrevButtonStyle); + + const scrollNextButtonStyle = this.resolveRightScrollButtonStyle(itemsContainerWidth); + this.setScrollButtonStyle(this.scrollNextButton.nativeElement, scrollNextButtonStyle); + } + + private setScrollButtonStyle(button: HTMLElement, buttonStyle: TabScrollButtonStyle) { + if (buttonStyle === TabScrollButtonStyle.Visible) { + button.style.visibility = 'visible'; + button.style.display = ''; + } else if (buttonStyle === TabScrollButtonStyle.Hidden) { + button.style.visibility = 'hidden'; + button.style.display = ''; + } else if (buttonStyle === TabScrollButtonStyle.NotDisplayed) { + button.style.display = 'none'; + } + } + private resolveLeftScrollButtonStyle(itemsContainerWidth: number): TabScrollButtonStyle { + const headerContainerWidth = this.headerContainer.nativeElement.offsetWidth; + const offset = this.offset; + + if (offset === 0) { + // Fix for IE 11, a difference is accumulated from the widths calculations. + if (itemsContainerWidth - headerContainerWidth <= 1) { + return TabScrollButtonStyle.NotDisplayed; + } + return TabScrollButtonStyle.Hidden; + } else { + return TabScrollButtonStyle.Visible; + } + } + + private resolveRightScrollButtonStyle(itemsContainerWidth: number): TabScrollButtonStyle { + const viewPortWidth = this.viewPort.nativeElement.offsetWidth; + const headerContainerWidth = this.headerContainer.nativeElement.offsetWidth; + const offset = this.offset; + const total = offset + viewPortWidth; + + // Fix for IE 11, a difference is accumulated from the widths calculations. + if (itemsContainerWidth - headerContainerWidth <= 1 && offset === 0) { + return TabScrollButtonStyle.NotDisplayed; + } + + if (itemsContainerWidth > total) { + return TabScrollButtonStyle.Visible; + } else { + return TabScrollButtonStyle.Hidden; + } + } + + private getTabItemsContainerWidth() { + // We use this hacky way to get the width of the itemsContainer, + // because there is inconsistency in IE we cannot use offsetWidth or scrollOffset. + const itemsContainerChildrenCount = this.itemsContainer.nativeElement.children.length; + let itemsContainerWidth = 0; + + if (itemsContainerChildrenCount > 1) { + const lastTab = this.itemsContainer.nativeElement.children[itemsContainerChildrenCount - 1] as HTMLElement; + itemsContainerWidth = this.getElementOffset(lastTab) + lastTab.offsetWidth; + } + + return itemsContainerWidth; + } + + private getOffset(offset: number): number { + return this.dir.rtl ? -offset : offset; + } + + private getElementOffset(element: HTMLElement): number { + return this.dir.rtl ? this.itemsWrapper.nativeElement.offsetWidth - element.offsetLeft - element.offsetWidth : element.offsetLeft; + } +} + diff --git a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.directives.ts b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.directives.ts index cd41313c01b..4d361fefdc1 100644 --- a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.directives.ts +++ b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.directives.ts @@ -1,11 +1,11 @@ -import { Directive } from '@angular/core'; - -@Directive({ - selector: 'igx-tab-header-label,[igxTabHeaderLabel]' -}) -export class IgxTabHeaderLabelDirective { } - -@Directive({ - selector: 'igx-tab-header-icon,[igxTabHeaderIcon]' -}) -export class IgxTabHeaderIconDirective { } +import { Directive } from '@angular/core'; + +@Directive({ + selector: 'igx-tab-header-label,[igxTabHeaderLabel]' +}) +export class IgxTabHeaderLabelDirective { } + +@Directive({ + selector: 'igx-tab-header-icon,[igxTabHeaderIcon]' +}) +export class IgxTabHeaderIconDirective { } diff --git a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.module.ts b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.module.ts index ea22e2f0cc7..adce4e86ac2 100644 --- a/projects/igniteui-angular/src/lib/tabs/tabs/tabs.module.ts +++ b/projects/igniteui-angular/src/lib/tabs/tabs/tabs.module.ts @@ -1,36 +1,36 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { IgxRippleModule } from '../../directives/ripple/ripple.directive'; -import { IgxIconModule } from '../../icon/public_api'; -import { IgxTabHeaderComponent } from './tab-header.component'; -import { IgxTabHeaderIconDirective, IgxTabHeaderLabelDirective } from './tabs.directives'; -import { IgxTabItemComponent } from './tab-item.component'; -import { IgxTabContentComponent } from './tab-content.component'; -import { IgxTabsComponent } from './tabs.component'; -import { IgxPrefixModule } from '../../directives/prefix/prefix.directive'; -import { IgxSuffixModule } from '../../directives/suffix/suffix.directive'; - -/** @hidden */ -@NgModule({ - declarations: [ - IgxTabsComponent, - IgxTabItemComponent, - IgxTabHeaderComponent, - IgxTabContentComponent, - IgxTabHeaderLabelDirective, - IgxTabHeaderIconDirective - ], - exports: [ - IgxTabsComponent, - IgxTabItemComponent, - IgxTabHeaderComponent, - IgxTabContentComponent, - IgxTabHeaderLabelDirective, - IgxTabHeaderIconDirective, - IgxPrefixModule, - IgxSuffixModule - ], - imports: [CommonModule, IgxIconModule, IgxRippleModule, IgxPrefixModule, IgxSuffixModule] -}) -export class IgxTabsModule { -} +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { IgxRippleModule } from '../../directives/ripple/ripple.directive'; +import { IgxIconModule } from '../../icon/public_api'; +import { IgxTabHeaderComponent } from './tab-header.component'; +import { IgxTabHeaderIconDirective, IgxTabHeaderLabelDirective } from './tabs.directives'; +import { IgxTabItemComponent } from './tab-item.component'; +import { IgxTabContentComponent } from './tab-content.component'; +import { IgxTabsComponent } from './tabs.component'; +import { IgxPrefixModule } from '../../directives/prefix/prefix.directive'; +import { IgxSuffixModule } from '../../directives/suffix/suffix.directive'; + +/** @hidden */ +@NgModule({ + declarations: [ + IgxTabsComponent, + IgxTabItemComponent, + IgxTabHeaderComponent, + IgxTabContentComponent, + IgxTabHeaderLabelDirective, + IgxTabHeaderIconDirective + ], + exports: [ + IgxTabsComponent, + IgxTabItemComponent, + IgxTabHeaderComponent, + IgxTabContentComponent, + IgxTabHeaderLabelDirective, + IgxTabHeaderIconDirective, + IgxPrefixModule, + IgxSuffixModule + ], + imports: [CommonModule, IgxIconModule, IgxRippleModule, IgxPrefixModule, IgxSuffixModule] +}) +export class IgxTabsModule { +} diff --git a/src/app/button/button.sample.css b/src/app/button/button.sample.css index 6a292f7622b..089372ce21c 100644 --- a/src/app/button/button.sample.css +++ b/src/app/button/button.sample.css @@ -14,7 +14,7 @@ } .display-density { - grid-template-columns: repeat(auto-fill, minmax(400px, 2fr)); + grid-template-columns: repeat(auto-fill, minmax(450px, 2fr)); } .button-sample { diff --git a/src/app/button/button.sample.html b/src/app/button/button.sample.html index 9dd1357f4da..61e0785962b 100644 --- a/src/app/button/button.sample.html +++ b/src/app/button/button.sample.html @@ -3,16 +3,16 @@

Flat Buttons

- +
- Red Sox + Red Sox
- +
- +
@@ -27,13 +27,13 @@

Flat Buttons

Raised Buttons

- +
- +
- +
@@ -51,13 +51,13 @@

Raised Buttons

Outlined Buttons

- +
- +
- +
@@ -75,22 +75,22 @@

Outlined Buttons

Floating Action Buttons

-
-
-
-
@@ -111,32 +111,32 @@

Floating Action Buttons

Icon Buttons

-
-
-
-
-
-
@@ -151,28 +151,35 @@

Buttons based on Display Density

- +
- +
-
- +
-
- diff --git a/src/app/card/card.sample.scss b/src/app/card/card.sample.scss index 368b57cb541..3faf8f112c4 100644 --- a/src/app/card/card.sample.scss +++ b/src/app/card/card.sample.scss @@ -223,14 +223,14 @@ display: inline-flex; text-decoration: none; margin-left: 20px; - color: #007bff; + color: #0d6efd; &:first-of-type { margin-left: 0; } &:hover { - color: #0056b3; + color: #0a58ca; } } diff --git a/src/app/grid-cell-api/grid-cell-api.sample.css b/src/app/grid-cell-api/grid-cell-api.sample.css index f814d60a525..824423498ba 100644 --- a/src/app/grid-cell-api/grid-cell-api.sample.css +++ b/src/app/grid-cell-api/grid-cell-api.sample.css @@ -1,76 +1,76 @@ -.sample-buttons { - margin-top: 24px; -} - -[igxButton] { - margin-right: 8px; - margin-bottom: 8px; -} - -.density-chooser { - margin-bottom: 16px; - max-width: 900px; -} - -.references { - font-size: .75em; -} - -.references>p { - margin: 0; - letter-spacing: initial; - line-height: initial; - font-size: 1em; -} - -.sample-column { - width: 60%; - margin: 8px; - flex-flow: ''; -} - -.log-wrapper { - width: 30%; -} - -.clearBtn { - top: 3px; - margin-left: 20px; -} - -.logContainer { - padding: 0.2rem 0.4rem; -} - -:host { - width: 100%; -} - -.sample-column { - display: flex; - flex-flow: row wrap !important; - width: 100%; -} - -.grid-wrapper { - width: 60%; -} - -.clearBtn { - top: 3px; - margin-left: 20px; -} - -.selected-data-area { - overflow-y: auto; - max-height: 550px; - width: 100%; - height: 100%; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); - margin-top: 8px; -} - -.highlight { - text-align: center; - margin-bottom: 0.4rem; +.sample-buttons { + margin-top: 24px; +} + +[igxButton] { + margin-right: 8px; + margin-bottom: 8px; +} + +.density-chooser { + margin-bottom: 16px; + max-width: 900px; +} + +.references { + font-size: .75em; +} + +.references>p { + margin: 0; + letter-spacing: initial; + line-height: initial; + font-size: 1em; +} + +.sample-column { + width: 60%; + margin: 8px; + flex-flow: ''; +} + +.log-wrapper { + width: 30%; +} + +.clearBtn { + top: 3px; + margin-left: 20px; +} + +.logContainer { + padding: 0.2rem 0.4rem; +} + +:host { + width: 100%; +} + +.sample-column { + display: flex; + flex-flow: row wrap !important; + width: 100%; +} + +.grid-wrapper { + width: 60%; +} + +.clearBtn { + top: 3px; + margin-left: 20px; +} + +.selected-data-area { + overflow-y: auto; + max-height: 550px; + width: 100%; + height: 100%; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); + margin-top: 8px; +} + +.highlight { + text-align: center; + margin-bottom: 0.4rem; } \ No newline at end of file diff --git a/src/app/grid-cell-api/grid-cell-api.sample.html b/src/app/grid-cell-api/grid-cell-api.sample.html index 6af6f69b693..6cad8da28fe 100644 --- a/src/app/grid-cell-api/grid-cell-api.sample.html +++ b/src/app/grid-cell-api/grid-cell-api.sample.html @@ -1,303 +1,303 @@ -
-
igxGrid
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- One digit indicates Column Index. Two digits indicates rowIndex, colIndex
- -
- - -
-
-
- - - - -
-
Country: {{dataItem.Country}}
-
City: {{dataItem.City}}
-
Address: {{dataItem.Address}}
-
-
- - - - - - - - - -
-
-
-
-
-
- -
-
-
-
- -
-
IgxTreeGrid Hierarchical Data
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- One digit indicates Column Index. Two digits indicates rowIndex, colIndex
- -
- - -
-
-
- - - - - - - - - - - - -
-
-
-
-
- -
-
-
- -
-
IgxTreeGrid Flat Data
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- One digit indicates Column Index. Two digits indicates rowIndex, colIndex
- -
- - -
-
-
- - - - - - - - - - - - -
-
-
-
-
- -
-
-
- -
-
igxHierarchicalGrid
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- One digit indicates Column Index. Two digits indicates rowIndex, colIndex
- -
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-
-
+
+
igxGrid
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ One digit indicates Column Index. Two digits indicates rowIndex, colIndex
+ +
+ + +
+
+
+ + + + +
+
Country: {{dataItem.Country}}
+
City: {{dataItem.City}}
+
Address: {{dataItem.Address}}
+
+
+ + + + + + + + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
IgxTreeGrid Hierarchical Data
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ One digit indicates Column Index. Two digits indicates rowIndex, colIndex
+ +
+ + +
+
+
+ + + + + + + + + + + + +
+
+
+
+
+ +
+
+
+ +
+
IgxTreeGrid Flat Data
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ One digit indicates Column Index. Two digits indicates rowIndex, colIndex
+ +
+ + +
+
+
+ + + + + + + + + + + + +
+
+
+
+
+ +
+
+
+ +
+
igxHierarchicalGrid
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ One digit indicates Column Index. Two digits indicates rowIndex, colIndex
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+
diff --git a/src/app/grid-cell-api/grid-cell-api.sample.ts b/src/app/grid-cell-api/grid-cell-api.sample.ts index cf9c10d5219..e747876d8b0 100644 --- a/src/app/grid-cell-api/grid-cell-api.sample.ts +++ b/src/app/grid-cell-api/grid-cell-api.sample.ts @@ -1,367 +1,367 @@ -import { Component, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { - IgxGridComponent, - IgxTreeGridComponent, - IgxHierarchicalGridComponent, - CellType -} from 'igniteui-angular'; -import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; - -@Component({ - selector: 'app-grid-cell-api-sample', - styleUrls: ['grid-cell-api.sample.css'], - templateUrl: 'grid-cell-api.sample.html', - // providers: [ - // { provide: IgxGridTransaction, useClass: IgxTransactionService } - // ], -}) - -export class GridCellAPISampleComponent implements OnInit { - @ViewChild('grid', { static: true }) - private grid: IgxGridComponent; - - public data2: any; - public data: any[]; - public treeGridHierData: any[]; - public hierarchicalData: any[]; - public columns: any[]; - public hColumns: any[]; - public treeGridHierColumns: any[]; - public treeColumns: any[]; - public treeData: any[]; - - public index = '00'; - public tIndex = '00'; - public tHIndex = '00'; - public hIndex = '00'; - - public cellKey = 'ALFKI'; - public tCellKey = '0'; - public tHCellKey = 'ALFKI'; - public hCellKey = '0'; - - public columnField = 'ContactName'; - public tcolumnField = 'Salary'; - public tHcolumnField = 'ContactName'; - public hcolumnField = 'ProductName'; - - public selectedCell: CellType; - - constructor(private renderer: Renderer2) { } - - public ngOnInit(): void { - this.columns = [ - { field: 'ID', width: '200px', hidden: false }, - { field: 'CompanyName', header: 'Company Name', width: '200px', groupable: true }, - { field: 'ContactName', width: '200px', pinned: false, groupable: true }, - { field: 'ContactTitle', width: '300px', pinned: false, groupable: true }, - { field: 'Address', width: '250px' }, - { field: 'City', width: '200px' }, - { field: 'Region', width: '300px' }, - { field: 'PostalCode', width: '150px' }, - { field: 'Phone', width: '200px' }, - { field: 'Fax', width: '200px' } - ]; - - this.hColumns = [ - { field: 'ID', width: '200px' }, - { field: 'ChildLevels', width: '200px' }, - { field: 'ProductName', width: '200px' }, - { field: 'Col1', width: '200px' }, - { field: 'Col2', width: '200px' }, - { field: 'Col3', width: '200px' }, - { field: 'childData', width: '200px' }, - { field: 'childData2', width: '200px' }, - { field: 'hasChild', width: '200px' } - ]; - - this.treeGridHierColumns = [ - { field: 'ID', width: 200, resizable: true, pinned: true }, - { field: 'CompanyName', width: 150, resizable: true }, - { field: 'ContactName', width: 150, resizable: true }, - { field: 'ContactTitle', width: 150, resizable: true }, - { field: 'Address', width: 150, resizable: true}, - { field: 'City', width: 150, resizable: true, summary: true }, - { field: 'Region', width: 150, resizable: true }, - { field: 'PostalCode', width: 150, resizable: true }, - { field: 'Phone', width: 150, resizable: true }, - { field: 'Fax', width: 150, resizable: true } - ]; - this.treeGridHierData = HIERARCHICAL_SAMPLE_DATA.slice(0); - - this.data = [ - /* eslint-disable max-len */ - { ID: 'ALFKI', CompanyName: 'Alfreds Futterkiste', ContactName: 'Maria Anders', ContactTitle: 'Sales Representative', Address: 'Obere Str. 57', City: 'Berlin', Region: null, PostalCode: '12209', Country: 'Germany', Phone: '030-0074321', Fax: '030-0076545' }, - { ID: 'ANATR', CompanyName: 'Ana Trujillo Emparedados y helados', ContactName: 'Ana Trujillo', ContactTitle: 'Owner', Address: 'Avda. de la Constitución 2222', City: 'México D.F.', Region: null, PostalCode: '05021', Country: 'Mexico', Phone: '(5) 555-4729', Fax: '(5) 555-3745' }, - { ID: 'ANTON', CompanyName: 'Antonio Moreno Taquería', ContactName: 'Antonio Moreno', ContactTitle: 'Owner', Address: 'Mataderos 2312', City: 'México D.F.', Region: null, PostalCode: '05023', Country: 'Mexico', Phone: '(5) 555-3932', Fax: null }, - { ID: 'AROUT', CompanyName: 'Around the Horn', ContactName: 'Thomas Hardy', ContactTitle: 'Sales Representative', Address: '120 Hanover Sq.', City: 'London', Region: null, PostalCode: 'WA1 1DP', Country: 'UK', Phone: '(171) 555-7788', Fax: '(171) 555-6750' }, - { ID: 'BERGS', CompanyName: 'Berglunds snabbköp', ContactName: 'Christina Berglund', ContactTitle: 'Order Administrator', Address: 'Berguvsvägen 8', City: 'Luleå', Region: null, PostalCode: 'S-958 22', Country: 'Sweden', Phone: '0921-12 34 65', Fax: '0921-12 34 67' }, - { ID: 'BLAUS', CompanyName: 'Blauer See Delikatessen', ContactName: 'Hanna Moos', ContactTitle: 'Sales Representative', Address: 'Forsterstr. 57', City: 'Mannheim', Region: null, PostalCode: '68306', Country: 'Germany', Phone: '0621-08460', Fax: '0621-08924' }, - { ID: 'BLONP', CompanyName: 'Blondesddsl père et fils', ContactName: 'Frédérique Citeaux', ContactTitle: 'Marketing Manager', Address: '24, place Kléber', City: 'Strasbourg', Region: null, PostalCode: '67000', Country: 'France', Phone: '88.60.15.31', Fax: '88.60.15.32' }, - { ID: 'BOLID', CompanyName: 'Bólido Comidas preparadas', ContactName: 'Martín Sommer', ContactTitle: 'Owner', Address: 'C/ Araquil, 67', City: 'Madrid', Region: null, PostalCode: '28023', Country: 'Spain', Phone: '(91) 555 22 82', Fax: '(91) 555 91 99' }, - { ID: 'BONAP', CompanyName: 'Bon app\'', ContactName: 'Laurence Lebihan', ContactTitle: 'Owner', Address: '12, rue des Bouchers', City: 'Marseille', Region: null, PostalCode: '13008', Country: 'France', Phone: '91.24.45.40', Fax: '91.24.45.41' }, - { ID: 'BOTTM', CompanyName: 'Bottom-Dollar Markets', ContactName: 'Elizabeth Lincoln', ContactTitle: 'Accounting Manager', Address: '23 Tsawassen Blvd.', City: 'Tsawassen', Region: 'BC', PostalCode: 'T2F 8M4', Country: 'Canada', Phone: '(604) 555-4729', Fax: '(604) 555-3745' }, - { ID: 'BSBEV', CompanyName: 'B\'s Beverages', ContactName: 'Victoria Ashworth', ContactTitle: 'Sales Representative', Address: 'Fauntleroy Circus', City: 'London', Region: null, PostalCode: 'EC2 5NT', Country: 'UK', Phone: '(171) 555-1212', Fax: null }, - { ID: 'CACTU', CompanyName: 'Cactus Comidas para llevar', ContactName: 'Patricio Simpson', ContactTitle: 'Sales Agent', Address: 'Cerrito 333', City: 'Buenos Aires', Region: null, PostalCode: '1010', Country: 'Argentina', Phone: '(1) 135-5555', Fax: '(1) 135-4892' }, - { ID: 'CENTC', CompanyName: 'Centro comercial Moctezuma', ContactName: 'Francisco Chang', ContactTitle: 'Marketing Manager', Address: 'Sierras de Granada 9993', City: 'México D.F.', Region: null, PostalCode: '05022', Country: 'Mexico', Phone: '(5) 555-3392', Fax: '(5) 555-7293' }, - { ID: 'CHOPS', CompanyName: 'Chop-suey Chinese', ContactName: 'Yang Wang', ContactTitle: 'Owner', Address: 'Hauptstr. 29', City: 'Bern', Region: null, PostalCode: '3012', Country: 'Switzerland', Phone: '0452-076545', Fax: null }, - { ID: 'COMMI', CompanyName: 'Comércio Mineiro', ContactName: 'Pedro Afonso', ContactTitle: 'Sales Associate', Address: 'Av. dos Lusíadas, 23', City: 'Sao Paulo', Region: 'SP', PostalCode: '05432-043', Country: 'Brazil', Phone: '(11) 555-7647', Fax: null }, - { ID: 'CONSH', CompanyName: 'Consolidated Holdings', ContactName: 'Elizabeth Brown', ContactTitle: 'Sales Representative', Address: 'Berkeley Gardens 12 Brewery', City: 'London', Region: null, PostalCode: 'WX1 6LT', Country: 'UK', Phone: '(171) 555-2282', Fax: '(171) 555-9199' }, - { ID: 'DRACD', CompanyName: 'Drachenblut Delikatessen', ContactName: 'Sven Ottlieb', ContactTitle: 'Order Administrator', Address: 'Walserweg 21', City: 'Aachen', Region: null, PostalCode: '52066', Country: 'Germany', Phone: '0241-039123', Fax: '0241-059428' }, - { ID: 'DUMON', CompanyName: 'Du monde entier', ContactName: 'Janine Labrune', ContactTitle: 'Owner', Address: '67, rue des Cinquante Otages', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.67.88.88', Fax: '40.67.89.89' }, - { ID: 'EASTC', CompanyName: 'Eastern Connection', ContactName: 'Ann Devon', ContactTitle: 'Sales Agent', Address: '35 King George', City: 'London', Region: null, PostalCode: 'WX3 6FW', Country: 'UK', Phone: '(171) 555-0297', Fax: '(171) 555-3373' }, - { ID: 'ERNSH', CompanyName: 'Ernst Handel', ContactName: 'Roland Mendel', ContactTitle: 'Sales Manager', Address: 'Kirchgasse 6', City: 'Graz', Region: null, PostalCode: '8010', Country: 'Austria', Phone: '7675-3425', Fax: '7675-3426' }, - { ID: 'FAMIA', CompanyName: 'Familia Arquibaldo', ContactName: 'Aria Cruz', ContactTitle: 'Marketing Assistant', Address: 'Rua Orós, 92', City: 'Sao Paulo', Region: 'SP', PostalCode: '05442-030', Country: 'Brazil', Phone: '(11) 555-9857', Fax: null }, - { ID: 'FISSA', CompanyName: 'FISSA Fabrica Inter. Salchichas S.A.', ContactName: 'Diego Roel', ContactTitle: 'Accounting Manager', Address: 'C/ Moralzarzal, 86', City: 'Madrid', Region: null, PostalCode: '28034', Country: 'Spain', Phone: '(91) 555 94 44', Fax: '(91) 555 55 93' }, - { ID: 'FOLIG', CompanyName: 'Folies gourmandes', ContactName: 'Martine Rancé', ContactTitle: 'Assistant Sales Agent', Address: '184, chaussée de Tournai', City: 'Lille', Region: null, PostalCode: '59000', Country: 'France', Phone: '20.16.10.16', Fax: '20.16.10.17' }, - { ID: 'FOLKO', CompanyName: 'Folk och fä HB', ContactName: 'Maria Larsson', ContactTitle: 'Owner', Address: 'Åkergatan 24', City: 'Bräcke', Region: null, PostalCode: 'S-844 67', Country: 'Sweden', Phone: '0695-34 67 21', Fax: null }, - { ID: 'FRANK', CompanyName: 'Frankenversand', ContactName: 'Peter Franken', ContactTitle: 'Marketing Manager', Address: 'Berliner Platz 43', City: 'München', Region: null, PostalCode: '80805', Country: 'Germany', Phone: '089-0877310', Fax: '089-0877451' }, - { ID: 'FRANR', CompanyName: 'France restauration', ContactName: 'Carine Schmitt', ContactTitle: 'Marketing Manager', Address: '54, rue Royale', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.32.21.21', Fax: '40.32.21.20' }, - { ID: 'FRANS', CompanyName: 'Franchi S.p.A.', ContactName: 'Paolo Accorti', ContactTitle: 'Sales Representative', Address: 'Via Monte Bianco 34', City: 'Torino', Region: null, PostalCode: '10100', Country: 'Italy', Phone: '011-4988260', Fax: '011-4988261' } - ]; - this.hierarchicalData = this.generateDataUneven(100, 3); - - // treegrid cols and data - this.treeColumns = [ - { field: 'employeeID', label: 'ID', width: 200, resizable: true, dataType: 'number', hasSummary: false }, - { field: 'Salary', label: 'Salary', width: 200, resizable: true, dataType: 'number', hasSummary: true }, - { field: 'firstName', label: 'First Name', width: 300, resizable: true, dataType: 'string', hasSummary: false }, - { field: 'lastName', label: 'Last Name', width: 150, resizable: true, dataType: 'string', hasSummary: false }, - { field: 'Title', label: 'Title', width: 200, resizable: true, dataType: 'string', hasSummary: true } - ]; - this.treeData = [ - { Salary: 2500, employeeID: 0, PID: -1, firstName: 'Andrew', lastName: 'Fuller', Title: 'Vice President, Sales' }, - { Salary: 3500, employeeID: 1, PID: -1, firstName: 'Jonathan', lastName: 'Smith', Title: 'Human resources' }, - { Salary: 1500, employeeID: 2, PID: -1, firstName: 'Nancy', lastName: 'Davolio', Title: 'CFO' }, - { Salary: 2500, employeeID: 3, PID: -1, firstName: 'Steven', lastName: 'Buchanan', Title: 'CTO' }, - // sub of ID 0 - { Salary: 2500, employeeID: 4, PID: 0, firstName: 'Janet', lastName: 'Leverling', Title: 'Sales Manager' }, - { Salary: 3500, employeeID: 5, PID: 0, firstName: 'Laura', lastName: 'Callahan', Title: 'Inside Sales Coordinator' }, - { Salary: 1500, employeeID: 6, PID: 0, firstName: 'Margaret', lastName: 'Peacock', Title: 'Sales Representative' }, - { Salary: 2500, employeeID: 7, PID: 0, firstName: 'Michael', lastName: 'Suyama', Title: 'Sales Representative' }, - // sub of ID 4 - { Salary: 2500, employeeID: 8, PID: 4, firstName: 'Anne', lastName: 'Dodsworth', Title: 'Sales Representative' }, - { Salary: 3500, employeeID: 9, PID: 4, firstName: 'Danielle', lastName: 'Davis', Title: 'Sales Representative' }, - { Salary: 1500, employeeID: 10, PID: 4, firstName: 'Robert', lastName: 'King', Title: 'Sales Representative' }, - // sub of ID 2 - { Salary: 2500, employeeID: 11, PID: 2, firstName: 'Peter', lastName: 'Lewis', Title: 'Chief Accountant' }, - { Salary: 3500, employeeID: 12, PID: 2, firstName: 'Ryder', lastName: 'Zenaida', Title: 'Accountant' }, - { Salary: 1500, employeeID: 13, PID: 2, firstName: 'Wang', lastName: 'Mercedes', Title: 'Accountant' }, - // sub of ID 3 - { Salary: 1500, employeeID: 14, PID: 3, firstName: 'Theodore', lastName: 'Zia', Title: 'Software Architect' }, - { Salary: 4500, employeeID: 15, PID: 3, firstName: 'Lacota', lastName: 'Mufutau', Title: 'Product Manager' }, - // sub of ID 16 - { Salary: 2500, employeeID: 16, PID: 15, firstName: 'Jin', lastName: 'Elliott', Title: 'Product Owner' }, - { Salary: 3500, employeeID: 17, PID: 15, firstName: 'Armand', lastName: 'Ross', Title: 'Product Owner' }, - { Salary: 1500, employeeID: 18, PID: 15, firstName: 'Dane', lastName: 'Rodriquez', Title: 'Team Leader' }, - // sub of ID 19 - { Salary: 2500, employeeID: 19, PID: 18, firstName: 'Declan', lastName: 'Lester', Title: 'Senior Software Developer' }, - { Salary: 3500, employeeID: 20, PID: 18, firstName: 'Bernard', lastName: 'Jarvis', Title: 'Senior Software Developer' }, - { Salary: 1500, employeeID: 21, PID: 18, firstName: 'Jason', lastName: 'Clark', Title: 'QA' }, - { Salary: 1500, employeeID: 22, PID: 18, firstName: 'Mark', lastName: 'Young', Title: 'QA' }, - // sub of ID 20 - { Salary: 1500, employeeID: 23, PID: 20, firstName: 'Jeremy', lastName: 'Donaldson', Title: 'Software Developer' } - ]; - /* eslint-enable max-len */ - } - - public updateCell(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, - logger: HTMLElement) { - const indxs = this.getIndices(indices); - const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); - cell.update('New Value'); - this.logState(grid, indices, logger); - } - - public select(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, logger: HTMLElement) { - const indxs = this.getIndices(indices); - const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); - cell.selected = !cell.selected; - this.selectedCell = cell; - this.logState(grid, indices, logger); - } - - public setEditMode(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, - logger: HTMLElement) { - const indxs = this.getIndices(indices); - const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); - cell.editMode = !cell.editMode; - this.logState(grid, indices, logger); - } - - public generateDataUneven(count: number, level: number, parendID: string = null) { - const prods = []; - const currLevel = level; - let children; - for (let i = 0; i < count; i++) { - const rowID = parendID ? parendID + i : i.toString(); - if (level > 0) { - // Have child grids for row with even id less rows by not multiplying by 2 - children = this.generateDataUneven(((i % 2) + 1) * Math.round(count / 3), currLevel - 1, rowID); - } - prods.push({ - ID: rowID, - ChildLevels: currLevel, - ProductName: 'Product: A' + i, - Col1: i, - Col2: i, - Col3: i, - childData: children, - childData2: children, - hasChild: true - }); - } - return prods; - } - - public clearLog(logger: HTMLElement) { - const elements = logger.querySelectorAll('p'); - - elements.forEach(element => { - this.renderer.removeChild(logger, element); - }); - } - - public logState(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, logger: HTMLElement) { - this.clearLog(logger); - const indxs = this.getIndices(indices); - const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); - let state: string; - let states: string[]; - - if (cell) { - state = ` - value: ${cell.value}, - selected: ${cell.selected}, - editable: ${cell.editable}, - editMode: ${cell.editMode}, - editValue: ${cell.editValue}, - -----------------------------, - colIndex: ${cell.column.index}, - visibleColIndex: ${cell.column.visibleIndex}, - colField: ${cell.column.field}, - -----------------------------, - rowIndex: ${cell.row.index}, - rowViewIndex: ${cell.row.viewIndex}, - rowKey: ${cell.row.key}, - rowData: ${cell.row.data}, - -----------------------------, - gridId: ${cell.grid.id}, - id: ${cell.id}, - width: ${cell.width}`; - states = state.split(','); - } else { - states = [`Cell is: ${cell}`]; - } - - const createElem = this.renderer.createElement('p'); - - states.forEach(st => { - const text = this.renderer.createText(st); - this.renderer.appendChild(createElem, text); - this.renderer.appendChild(createElem, this.renderer.createElement('br')); - }); - - this.renderer.insertBefore(logger, createElem, logger.children[0]); - } - - public logStateByKey(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, key: any, - field: string, logger: HTMLElement) { - this.clearLog(logger); - const cell = grid.getCellByKey(key, field); - let state: string; - let states: string[]; - - if (cell) { - state = ` - value: ${cell.value}, - selected: ${cell.selected}, - editable: ${cell.editable}, - editMode: ${cell.editMode}, - editValue: ${cell.editValue}, - -----------------------------, - colIndex: ${cell.column.index}, - visibleColIndex: ${cell.column.visibleIndex}, - colField: ${cell.column.field}, - -----------------------------, - rowIndex: ${cell.row.index}, - rowViewIndex: ${cell.row.viewIndex}, - rowKey: ${cell.row.key}, - rowData: ${cell.row.data}, - -----------------------------, - gridId: ${cell.grid.id}, - cellID: ${cell.id}, - width: ${cell.width}`; - - states = state.split(','); - } else { - states = [`Cell is: ${cell}`]; - } - - const createElem = this.renderer.createElement('p'); - - states.forEach(st => { - const text = this.renderer.createText(st); - this.renderer.appendChild(createElem, text); - this.renderer.appendChild(createElem, this.renderer.createElement('br')); - }); - - this.renderer.insertBefore(logger, createElem, logger.children[0]); - } - - public logStateByColumn(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, - field: string, logger: HTMLElement) { - this.clearLog(logger); - const indxs = this.getIndices(indices); - const cell = grid.getCellByColumn(indxs[0], field); - let state: string; - let states: string[]; - - if (cell) { - state = ` - value: ${cell.value}, - selected: ${cell.selected}, - editable: ${cell.editable}, - editMode: ${cell.editMode}, - editValue: ${cell.editValue}, - -----------------------------, - colIndex: ${cell.column.index}, - visibleColIndex: ${cell.column.visibleIndex}, - colField: ${cell.column.field}, - -----------------------------, - rowIndex: ${cell.row.index}, - rowViewIndex: ${cell.row.viewIndex}, - rowKey: ${cell.row.key}, - rowData: ${cell.row.data}, - -----------------------------, - gridId: ${cell.grid.id}, - cellID: ${cell.id}, - width: ${cell.width}`; - - states = state.split(','); - } else { - states = [`Cell is: ${cell}`]; - } - - const createElem = this.renderer.createElement('p'); - - states.forEach(st => { - const text = this.renderer.createText(st); - this.renderer.appendChild(createElem, text); - this.renderer.appendChild(createElem, this.renderer.createElement('br')); - }); - - this.renderer.insertBefore(logger, createElem, logger.children[0]); - } - - private getIndices(indices: string): number[] { - if (indices.length === 1) { - indices = `0${indices}`; - } - let nums: number[] = indices.split('').map(n => parseInt(n, 10)); - if (nums.length === 0) { - nums = [0, 0]; - } - return nums; - } -} +import { Component, OnInit, Renderer2, ViewChild } from '@angular/core'; +import { + IgxGridComponent, + IgxTreeGridComponent, + IgxHierarchicalGridComponent, + CellType +} from 'igniteui-angular'; +import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; + +@Component({ + selector: 'app-grid-cell-api-sample', + styleUrls: ['grid-cell-api.sample.css'], + templateUrl: 'grid-cell-api.sample.html', + // providers: [ + // { provide: IgxGridTransaction, useClass: IgxTransactionService } + // ], +}) + +export class GridCellAPISampleComponent implements OnInit { + @ViewChild('grid', { static: true }) + private grid: IgxGridComponent; + + public data2: any; + public data: any[]; + public treeGridHierData: any[]; + public hierarchicalData: any[]; + public columns: any[]; + public hColumns: any[]; + public treeGridHierColumns: any[]; + public treeColumns: any[]; + public treeData: any[]; + + public index = '00'; + public tIndex = '00'; + public tHIndex = '00'; + public hIndex = '00'; + + public cellKey = 'ALFKI'; + public tCellKey = '0'; + public tHCellKey = 'ALFKI'; + public hCellKey = '0'; + + public columnField = 'ContactName'; + public tcolumnField = 'Salary'; + public tHcolumnField = 'ContactName'; + public hcolumnField = 'ProductName'; + + public selectedCell: CellType; + + constructor(private renderer: Renderer2) { } + + public ngOnInit(): void { + this.columns = [ + { field: 'ID', width: '200px', hidden: false }, + { field: 'CompanyName', header: 'Company Name', width: '200px', groupable: true }, + { field: 'ContactName', width: '200px', pinned: false, groupable: true }, + { field: 'ContactTitle', width: '300px', pinned: false, groupable: true }, + { field: 'Address', width: '250px' }, + { field: 'City', width: '200px' }, + { field: 'Region', width: '300px' }, + { field: 'PostalCode', width: '150px' }, + { field: 'Phone', width: '200px' }, + { field: 'Fax', width: '200px' } + ]; + + this.hColumns = [ + { field: 'ID', width: '200px' }, + { field: 'ChildLevels', width: '200px' }, + { field: 'ProductName', width: '200px' }, + { field: 'Col1', width: '200px' }, + { field: 'Col2', width: '200px' }, + { field: 'Col3', width: '200px' }, + { field: 'childData', width: '200px' }, + { field: 'childData2', width: '200px' }, + { field: 'hasChild', width: '200px' } + ]; + + this.treeGridHierColumns = [ + { field: 'ID', width: 200, resizable: true, pinned: true }, + { field: 'CompanyName', width: 150, resizable: true }, + { field: 'ContactName', width: 150, resizable: true }, + { field: 'ContactTitle', width: 150, resizable: true }, + { field: 'Address', width: 150, resizable: true}, + { field: 'City', width: 150, resizable: true, summary: true }, + { field: 'Region', width: 150, resizable: true }, + { field: 'PostalCode', width: 150, resizable: true }, + { field: 'Phone', width: 150, resizable: true }, + { field: 'Fax', width: 150, resizable: true } + ]; + this.treeGridHierData = HIERARCHICAL_SAMPLE_DATA.slice(0); + + this.data = [ + /* eslint-disable max-len */ + { ID: 'ALFKI', CompanyName: 'Alfreds Futterkiste', ContactName: 'Maria Anders', ContactTitle: 'Sales Representative', Address: 'Obere Str. 57', City: 'Berlin', Region: null, PostalCode: '12209', Country: 'Germany', Phone: '030-0074321', Fax: '030-0076545' }, + { ID: 'ANATR', CompanyName: 'Ana Trujillo Emparedados y helados', ContactName: 'Ana Trujillo', ContactTitle: 'Owner', Address: 'Avda. de la Constitución 2222', City: 'México D.F.', Region: null, PostalCode: '05021', Country: 'Mexico', Phone: '(5) 555-4729', Fax: '(5) 555-3745' }, + { ID: 'ANTON', CompanyName: 'Antonio Moreno Taquería', ContactName: 'Antonio Moreno', ContactTitle: 'Owner', Address: 'Mataderos 2312', City: 'México D.F.', Region: null, PostalCode: '05023', Country: 'Mexico', Phone: '(5) 555-3932', Fax: null }, + { ID: 'AROUT', CompanyName: 'Around the Horn', ContactName: 'Thomas Hardy', ContactTitle: 'Sales Representative', Address: '120 Hanover Sq.', City: 'London', Region: null, PostalCode: 'WA1 1DP', Country: 'UK', Phone: '(171) 555-7788', Fax: '(171) 555-6750' }, + { ID: 'BERGS', CompanyName: 'Berglunds snabbköp', ContactName: 'Christina Berglund', ContactTitle: 'Order Administrator', Address: 'Berguvsvägen 8', City: 'Luleå', Region: null, PostalCode: 'S-958 22', Country: 'Sweden', Phone: '0921-12 34 65', Fax: '0921-12 34 67' }, + { ID: 'BLAUS', CompanyName: 'Blauer See Delikatessen', ContactName: 'Hanna Moos', ContactTitle: 'Sales Representative', Address: 'Forsterstr. 57', City: 'Mannheim', Region: null, PostalCode: '68306', Country: 'Germany', Phone: '0621-08460', Fax: '0621-08924' }, + { ID: 'BLONP', CompanyName: 'Blondesddsl père et fils', ContactName: 'Frédérique Citeaux', ContactTitle: 'Marketing Manager', Address: '24, place Kléber', City: 'Strasbourg', Region: null, PostalCode: '67000', Country: 'France', Phone: '88.60.15.31', Fax: '88.60.15.32' }, + { ID: 'BOLID', CompanyName: 'Bólido Comidas preparadas', ContactName: 'Martín Sommer', ContactTitle: 'Owner', Address: 'C/ Araquil, 67', City: 'Madrid', Region: null, PostalCode: '28023', Country: 'Spain', Phone: '(91) 555 22 82', Fax: '(91) 555 91 99' }, + { ID: 'BONAP', CompanyName: 'Bon app\'', ContactName: 'Laurence Lebihan', ContactTitle: 'Owner', Address: '12, rue des Bouchers', City: 'Marseille', Region: null, PostalCode: '13008', Country: 'France', Phone: '91.24.45.40', Fax: '91.24.45.41' }, + { ID: 'BOTTM', CompanyName: 'Bottom-Dollar Markets', ContactName: 'Elizabeth Lincoln', ContactTitle: 'Accounting Manager', Address: '23 Tsawassen Blvd.', City: 'Tsawassen', Region: 'BC', PostalCode: 'T2F 8M4', Country: 'Canada', Phone: '(604) 555-4729', Fax: '(604) 555-3745' }, + { ID: 'BSBEV', CompanyName: 'B\'s Beverages', ContactName: 'Victoria Ashworth', ContactTitle: 'Sales Representative', Address: 'Fauntleroy Circus', City: 'London', Region: null, PostalCode: 'EC2 5NT', Country: 'UK', Phone: '(171) 555-1212', Fax: null }, + { ID: 'CACTU', CompanyName: 'Cactus Comidas para llevar', ContactName: 'Patricio Simpson', ContactTitle: 'Sales Agent', Address: 'Cerrito 333', City: 'Buenos Aires', Region: null, PostalCode: '1010', Country: 'Argentina', Phone: '(1) 135-5555', Fax: '(1) 135-4892' }, + { ID: 'CENTC', CompanyName: 'Centro comercial Moctezuma', ContactName: 'Francisco Chang', ContactTitle: 'Marketing Manager', Address: 'Sierras de Granada 9993', City: 'México D.F.', Region: null, PostalCode: '05022', Country: 'Mexico', Phone: '(5) 555-3392', Fax: '(5) 555-7293' }, + { ID: 'CHOPS', CompanyName: 'Chop-suey Chinese', ContactName: 'Yang Wang', ContactTitle: 'Owner', Address: 'Hauptstr. 29', City: 'Bern', Region: null, PostalCode: '3012', Country: 'Switzerland', Phone: '0452-076545', Fax: null }, + { ID: 'COMMI', CompanyName: 'Comércio Mineiro', ContactName: 'Pedro Afonso', ContactTitle: 'Sales Associate', Address: 'Av. dos Lusíadas, 23', City: 'Sao Paulo', Region: 'SP', PostalCode: '05432-043', Country: 'Brazil', Phone: '(11) 555-7647', Fax: null }, + { ID: 'CONSH', CompanyName: 'Consolidated Holdings', ContactName: 'Elizabeth Brown', ContactTitle: 'Sales Representative', Address: 'Berkeley Gardens 12 Brewery', City: 'London', Region: null, PostalCode: 'WX1 6LT', Country: 'UK', Phone: '(171) 555-2282', Fax: '(171) 555-9199' }, + { ID: 'DRACD', CompanyName: 'Drachenblut Delikatessen', ContactName: 'Sven Ottlieb', ContactTitle: 'Order Administrator', Address: 'Walserweg 21', City: 'Aachen', Region: null, PostalCode: '52066', Country: 'Germany', Phone: '0241-039123', Fax: '0241-059428' }, + { ID: 'DUMON', CompanyName: 'Du monde entier', ContactName: 'Janine Labrune', ContactTitle: 'Owner', Address: '67, rue des Cinquante Otages', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.67.88.88', Fax: '40.67.89.89' }, + { ID: 'EASTC', CompanyName: 'Eastern Connection', ContactName: 'Ann Devon', ContactTitle: 'Sales Agent', Address: '35 King George', City: 'London', Region: null, PostalCode: 'WX3 6FW', Country: 'UK', Phone: '(171) 555-0297', Fax: '(171) 555-3373' }, + { ID: 'ERNSH', CompanyName: 'Ernst Handel', ContactName: 'Roland Mendel', ContactTitle: 'Sales Manager', Address: 'Kirchgasse 6', City: 'Graz', Region: null, PostalCode: '8010', Country: 'Austria', Phone: '7675-3425', Fax: '7675-3426' }, + { ID: 'FAMIA', CompanyName: 'Familia Arquibaldo', ContactName: 'Aria Cruz', ContactTitle: 'Marketing Assistant', Address: 'Rua Orós, 92', City: 'Sao Paulo', Region: 'SP', PostalCode: '05442-030', Country: 'Brazil', Phone: '(11) 555-9857', Fax: null }, + { ID: 'FISSA', CompanyName: 'FISSA Fabrica Inter. Salchichas S.A.', ContactName: 'Diego Roel', ContactTitle: 'Accounting Manager', Address: 'C/ Moralzarzal, 86', City: 'Madrid', Region: null, PostalCode: '28034', Country: 'Spain', Phone: '(91) 555 94 44', Fax: '(91) 555 55 93' }, + { ID: 'FOLIG', CompanyName: 'Folies gourmandes', ContactName: 'Martine Rancé', ContactTitle: 'Assistant Sales Agent', Address: '184, chaussée de Tournai', City: 'Lille', Region: null, PostalCode: '59000', Country: 'France', Phone: '20.16.10.16', Fax: '20.16.10.17' }, + { ID: 'FOLKO', CompanyName: 'Folk och fä HB', ContactName: 'Maria Larsson', ContactTitle: 'Owner', Address: 'Åkergatan 24', City: 'Bräcke', Region: null, PostalCode: 'S-844 67', Country: 'Sweden', Phone: '0695-34 67 21', Fax: null }, + { ID: 'FRANK', CompanyName: 'Frankenversand', ContactName: 'Peter Franken', ContactTitle: 'Marketing Manager', Address: 'Berliner Platz 43', City: 'München', Region: null, PostalCode: '80805', Country: 'Germany', Phone: '089-0877310', Fax: '089-0877451' }, + { ID: 'FRANR', CompanyName: 'France restauration', ContactName: 'Carine Schmitt', ContactTitle: 'Marketing Manager', Address: '54, rue Royale', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.32.21.21', Fax: '40.32.21.20' }, + { ID: 'FRANS', CompanyName: 'Franchi S.p.A.', ContactName: 'Paolo Accorti', ContactTitle: 'Sales Representative', Address: 'Via Monte Bianco 34', City: 'Torino', Region: null, PostalCode: '10100', Country: 'Italy', Phone: '011-4988260', Fax: '011-4988261' } + ]; + this.hierarchicalData = this.generateDataUneven(100, 3); + + // treegrid cols and data + this.treeColumns = [ + { field: 'employeeID', label: 'ID', width: 200, resizable: true, dataType: 'number', hasSummary: false }, + { field: 'Salary', label: 'Salary', width: 200, resizable: true, dataType: 'number', hasSummary: true }, + { field: 'firstName', label: 'First Name', width: 300, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'lastName', label: 'Last Name', width: 150, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'Title', label: 'Title', width: 200, resizable: true, dataType: 'string', hasSummary: true } + ]; + this.treeData = [ + { Salary: 2500, employeeID: 0, PID: -1, firstName: 'Andrew', lastName: 'Fuller', Title: 'Vice President, Sales' }, + { Salary: 3500, employeeID: 1, PID: -1, firstName: 'Jonathan', lastName: 'Smith', Title: 'Human resources' }, + { Salary: 1500, employeeID: 2, PID: -1, firstName: 'Nancy', lastName: 'Davolio', Title: 'CFO' }, + { Salary: 2500, employeeID: 3, PID: -1, firstName: 'Steven', lastName: 'Buchanan', Title: 'CTO' }, + // sub of ID 0 + { Salary: 2500, employeeID: 4, PID: 0, firstName: 'Janet', lastName: 'Leverling', Title: 'Sales Manager' }, + { Salary: 3500, employeeID: 5, PID: 0, firstName: 'Laura', lastName: 'Callahan', Title: 'Inside Sales Coordinator' }, + { Salary: 1500, employeeID: 6, PID: 0, firstName: 'Margaret', lastName: 'Peacock', Title: 'Sales Representative' }, + { Salary: 2500, employeeID: 7, PID: 0, firstName: 'Michael', lastName: 'Suyama', Title: 'Sales Representative' }, + // sub of ID 4 + { Salary: 2500, employeeID: 8, PID: 4, firstName: 'Anne', lastName: 'Dodsworth', Title: 'Sales Representative' }, + { Salary: 3500, employeeID: 9, PID: 4, firstName: 'Danielle', lastName: 'Davis', Title: 'Sales Representative' }, + { Salary: 1500, employeeID: 10, PID: 4, firstName: 'Robert', lastName: 'King', Title: 'Sales Representative' }, + // sub of ID 2 + { Salary: 2500, employeeID: 11, PID: 2, firstName: 'Peter', lastName: 'Lewis', Title: 'Chief Accountant' }, + { Salary: 3500, employeeID: 12, PID: 2, firstName: 'Ryder', lastName: 'Zenaida', Title: 'Accountant' }, + { Salary: 1500, employeeID: 13, PID: 2, firstName: 'Wang', lastName: 'Mercedes', Title: 'Accountant' }, + // sub of ID 3 + { Salary: 1500, employeeID: 14, PID: 3, firstName: 'Theodore', lastName: 'Zia', Title: 'Software Architect' }, + { Salary: 4500, employeeID: 15, PID: 3, firstName: 'Lacota', lastName: 'Mufutau', Title: 'Product Manager' }, + // sub of ID 16 + { Salary: 2500, employeeID: 16, PID: 15, firstName: 'Jin', lastName: 'Elliott', Title: 'Product Owner' }, + { Salary: 3500, employeeID: 17, PID: 15, firstName: 'Armand', lastName: 'Ross', Title: 'Product Owner' }, + { Salary: 1500, employeeID: 18, PID: 15, firstName: 'Dane', lastName: 'Rodriquez', Title: 'Team Leader' }, + // sub of ID 19 + { Salary: 2500, employeeID: 19, PID: 18, firstName: 'Declan', lastName: 'Lester', Title: 'Senior Software Developer' }, + { Salary: 3500, employeeID: 20, PID: 18, firstName: 'Bernard', lastName: 'Jarvis', Title: 'Senior Software Developer' }, + { Salary: 1500, employeeID: 21, PID: 18, firstName: 'Jason', lastName: 'Clark', Title: 'QA' }, + { Salary: 1500, employeeID: 22, PID: 18, firstName: 'Mark', lastName: 'Young', Title: 'QA' }, + // sub of ID 20 + { Salary: 1500, employeeID: 23, PID: 20, firstName: 'Jeremy', lastName: 'Donaldson', Title: 'Software Developer' } + ]; + /* eslint-enable max-len */ + } + + public updateCell(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, + logger: HTMLElement) { + const indxs = this.getIndices(indices); + const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); + cell.update('New Value'); + this.logState(grid, indices, logger); + } + + public select(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, logger: HTMLElement) { + const indxs = this.getIndices(indices); + const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); + cell.selected = !cell.selected; + this.selectedCell = cell; + this.logState(grid, indices, logger); + } + + public setEditMode(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, + logger: HTMLElement) { + const indxs = this.getIndices(indices); + const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); + cell.editMode = !cell.editMode; + this.logState(grid, indices, logger); + } + + public generateDataUneven(count: number, level: number, parendID: string = null) { + const prods = []; + const currLevel = level; + let children; + for (let i = 0; i < count; i++) { + const rowID = parendID ? parendID + i : i.toString(); + if (level > 0) { + // Have child grids for row with even id less rows by not multiplying by 2 + children = this.generateDataUneven(((i % 2) + 1) * Math.round(count / 3), currLevel - 1, rowID); + } + prods.push({ + ID: rowID, + ChildLevels: currLevel, + ProductName: 'Product: A' + i, + Col1: i, + Col2: i, + Col3: i, + childData: children, + childData2: children, + hasChild: true + }); + } + return prods; + } + + public clearLog(logger: HTMLElement) { + const elements = logger.querySelectorAll('p'); + + elements.forEach(element => { + this.renderer.removeChild(logger, element); + }); + } + + public logState(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, logger: HTMLElement) { + this.clearLog(logger); + const indxs = this.getIndices(indices); + const cell = grid.getCellByColumnVisibleIndex(indxs[0], indxs[1]); + let state: string; + let states: string[]; + + if (cell) { + state = ` + value: ${cell.value}, + selected: ${cell.selected}, + editable: ${cell.editable}, + editMode: ${cell.editMode}, + editValue: ${cell.editValue}, + -----------------------------, + colIndex: ${cell.column.index}, + visibleColIndex: ${cell.column.visibleIndex}, + colField: ${cell.column.field}, + -----------------------------, + rowIndex: ${cell.row.index}, + rowViewIndex: ${cell.row.viewIndex}, + rowKey: ${cell.row.key}, + rowData: ${cell.row.data}, + -----------------------------, + gridId: ${cell.grid.id}, + id: ${cell.id}, + width: ${cell.width}`; + states = state.split(','); + } else { + states = [`Cell is: ${cell}`]; + } + + const createElem = this.renderer.createElement('p'); + + states.forEach(st => { + const text = this.renderer.createText(st); + this.renderer.appendChild(createElem, text); + this.renderer.appendChild(createElem, this.renderer.createElement('br')); + }); + + this.renderer.insertBefore(logger, createElem, logger.children[0]); + } + + public logStateByKey(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, key: any, + field: string, logger: HTMLElement) { + this.clearLog(logger); + const cell = grid.getCellByKey(key, field); + let state: string; + let states: string[]; + + if (cell) { + state = ` + value: ${cell.value}, + selected: ${cell.selected}, + editable: ${cell.editable}, + editMode: ${cell.editMode}, + editValue: ${cell.editValue}, + -----------------------------, + colIndex: ${cell.column.index}, + visibleColIndex: ${cell.column.visibleIndex}, + colField: ${cell.column.field}, + -----------------------------, + rowIndex: ${cell.row.index}, + rowViewIndex: ${cell.row.viewIndex}, + rowKey: ${cell.row.key}, + rowData: ${cell.row.data}, + -----------------------------, + gridId: ${cell.grid.id}, + cellID: ${cell.id}, + width: ${cell.width}`; + + states = state.split(','); + } else { + states = [`Cell is: ${cell}`]; + } + + const createElem = this.renderer.createElement('p'); + + states.forEach(st => { + const text = this.renderer.createText(st); + this.renderer.appendChild(createElem, text); + this.renderer.appendChild(createElem, this.renderer.createElement('br')); + }); + + this.renderer.insertBefore(logger, createElem, logger.children[0]); + } + + public logStateByColumn(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, indices: string, + field: string, logger: HTMLElement) { + this.clearLog(logger); + const indxs = this.getIndices(indices); + const cell = grid.getCellByColumn(indxs[0], field); + let state: string; + let states: string[]; + + if (cell) { + state = ` + value: ${cell.value}, + selected: ${cell.selected}, + editable: ${cell.editable}, + editMode: ${cell.editMode}, + editValue: ${cell.editValue}, + -----------------------------, + colIndex: ${cell.column.index}, + visibleColIndex: ${cell.column.visibleIndex}, + colField: ${cell.column.field}, + -----------------------------, + rowIndex: ${cell.row.index}, + rowViewIndex: ${cell.row.viewIndex}, + rowKey: ${cell.row.key}, + rowData: ${cell.row.data}, + -----------------------------, + gridId: ${cell.grid.id}, + cellID: ${cell.id}, + width: ${cell.width}`; + + states = state.split(','); + } else { + states = [`Cell is: ${cell}`]; + } + + const createElem = this.renderer.createElement('p'); + + states.forEach(st => { + const text = this.renderer.createText(st); + this.renderer.appendChild(createElem, text); + this.renderer.appendChild(createElem, this.renderer.createElement('br')); + }); + + this.renderer.insertBefore(logger, createElem, logger.children[0]); + } + + private getIndices(indices: string): number[] { + if (indices.length === 1) { + indices = `0${indices}`; + } + let nums: number[] = indices.split('').map(n => parseInt(n, 10)); + if (nums.length === 0) { + nums = [0, 0]; + } + return nums; + } +} diff --git a/src/app/grid-events/grid-events.component.html b/src/app/grid-events/grid-events.component.html index b2231d88743..f50f700c7df 100644 --- a/src/app/grid-events/grid-events.component.html +++ b/src/app/grid-events/grid-events.component.html @@ -1,83 +1,83 @@ -
- cancel sorting - cancel filtering - cancel pinning - cancel resizing - cancel columnSelectionChanging - cancel hiding -
- - -
- - -
- -
-
-
- - - - - - - - Really advanced filtering - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- Events execution sequence - -
-
-
-
-
-
-
+
+ cancel sorting + cancel filtering + cancel pinning + cancel resizing + cancel columnSelectionChanging + cancel hiding +
+ + +
+ + +
+ +
+
+
+ + + + + + + + Really advanced filtering + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ Events execution sequence + +
+
+
+
+
+
+
diff --git a/src/app/grid-events/grid-events.component.scss b/src/app/grid-events/grid-events.component.scss index 40cb020490a..282479e40df 100644 --- a/src/app/grid-events/grid-events.component.scss +++ b/src/app/grid-events/grid-events.component.scss @@ -1,41 +1,41 @@ -:host { - width: 100%; -} - -.sample-wrapper { - display: flex; - flex-flow: row wrap; - width: 100%; -} - -.grid-wrapper { - width: 60%; - margin: 8px; -} -.log-wrapper { - width: 35%; -} - -.clearBtn { - top: 3px; - margin-left: 20px; -} - -.selected-data-area{ - overflow-y: auto; - max-height: 550px; - width: 100%; - height: 100%; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); - margin-top: 8px; -} - -.logContainer { - padding: 0.2rem 0.4rem; -} - -.highlight { - text-align: center; - margin-bottom: 0.4rem; -} - +:host { + width: 100%; +} + +.sample-wrapper { + display: flex; + flex-flow: row wrap; + width: 100%; +} + +.grid-wrapper { + width: 60%; + margin: 8px; +} +.log-wrapper { + width: 35%; +} + +.clearBtn { + top: 3px; + margin-left: 20px; +} + +.selected-data-area{ + overflow-y: auto; + max-height: 550px; + width: 100%; + height: 100%; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); + margin-top: 8px; +} + +.logContainer { + padding: 0.2rem 0.4rem; +} + +.highlight { + text-align: center; + margin-bottom: 0.4rem; +} + diff --git a/src/app/grid-events/grid-events.component.ts b/src/app/grid-events/grid-events.component.ts index 76f306aaec9..3fb51d356cd 100644 --- a/src/app/grid-events/grid-events.component.ts +++ b/src/app/grid-events/grid-events.component.ts @@ -1,127 +1,127 @@ -import { Component, ViewChild, ElementRef, Renderer2 } from '@angular/core'; -import { IgxGridComponent, - ISortingExpression, IPinColumnEventArgs, - IColumnResizeEventArgs, IColumnSelectionEventArgs, IPageEventArgs, ISortingEventArgs, - IFilteringEventArgs, IgxStringFilteringOperand, IColumnMovingEndEventArgs, - IColumnMovingEventArgs, IColumnMovingStartEventArgs, IPinColumnCancellableEventArgs, - IColumnVisibilityChangingEventArgs, - IFilteringExpressionsTree, - IColumnVisibilityChangedEventArgs -} from 'igniteui-angular'; -import { data } from '../shared/data'; - -@Component({ - selector: 'app-grid-events', - styleUrls: ['grid-events.component.scss'], - templateUrl: 'grid-events.component.html' -}) -export class GridEventsComponent { - - @ViewChild('grid1', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; - @ViewChild('logger') public logger: ElementRef; - - public $sorting = false; - public $filtering = false; - public $paging = false; - public $pinning = false; - public $resizing = false; - public $columnSelectionChanging = false; - public $hiding = false; - public $moving = false; - public localData: any[]; - - constructor(private renderer: Renderer2) { - this.localData = data; - } - - public filter(term) { - this.grid.filter('ProductName', term, IgxStringFilteringOperand.instance().condition('contains')); - } - - public filterGlobal(term) { - this.grid.filterGlobal(term, IgxStringFilteringOperand.instance().condition('contains')); - } - - public columnMovingStart(event: IColumnMovingStartEventArgs) { - console.log('event' + event); - this.logAnEvent('=> columnMovingStart'); - } - public columnMoving(event: IColumnMovingEventArgs) { - event.cancel = this.$moving; - this.logAnEvent(event.cancel ? '=> columnMoving cancelled' : '=> columnMoving'); - } - public columnMovingEnd(event: IColumnMovingEndEventArgs) { - console.log('event' + event); - this.logAnEvent('=> columnMovingEnd'); - } - - public onSorting(event: ISortingEventArgs) { - event.cancel = this.$sorting; - this.logAnEvent('=> sorting', event.cancel); - } - public sortingDone(event: ISortingExpression | ISortingExpression []) { - console.log('event' + event); - this.logAnEvent(`=> sortingDone`); - } - - public onFiltering(event: IFilteringEventArgs) { - event.cancel = this.$filtering; - this.logAnEvent('=> filtering', event.cancel); - } - public filteringDone(event: IFilteringExpressionsTree) { - console.log('event' + event); - this.logAnEvent(`=> filteringDone`); - } - public pagingDone(event: IPageEventArgs) { - console.log('event' + event); - this.logAnEvent(`=> pagingDone`); - } - - public columnPin(event: IPinColumnCancellableEventArgs) { - event.cancel = this.$pinning; - this.logAnEvent('=> columnPin', event.cancel); - } - public columnPinned(event: IPinColumnEventArgs) { - console.log('event' + event); - this.logAnEvent(`=> columnPinned`); - } - - public columnVisibilityChanging(event: IColumnVisibilityChangingEventArgs ) { - event.cancel = this.$hiding; - this.logAnEvent('=> columnVisibilityChanging', event.cancel); - } - public columnVisibilityChanged(event: IColumnVisibilityChangedEventArgs) { - console.log('event' + event); - this.logAnEvent(`=> columnVisibilityChanged`); - } - - public columnResized(event: IColumnResizeEventArgs) { - console.log('event' + event); - this.logAnEvent(`=> columnResized`); - } - - public columnSelectionChanging(event: IColumnSelectionEventArgs) { - event.cancel = this.$columnSelectionChanging; - this.logAnEvent('=> columnSelectionChanging', event.cancel); - } - - public clearLog() { - const elements = this.logger.nativeElement.querySelectorAll('p'); - for (const element of elements) { - this.renderer.removeChild(this.logger.nativeElement, element); - } - } - - private logAnEvent(msg: string, cancelled?: boolean) { - const createElem = this.renderer.createElement('p'); - if (cancelled) { - msg = msg.concat(': cancelled '); - } - - const text = this.renderer.createText(msg); - this.renderer.appendChild(createElem, text); - const container = this.logger.nativeElement; - this.renderer.insertBefore(container, createElem, container.children[0]); - } -} - +import { Component, ViewChild, ElementRef, Renderer2 } from '@angular/core'; +import { IgxGridComponent, + ISortingExpression, IPinColumnEventArgs, + IColumnResizeEventArgs, IColumnSelectionEventArgs, IPageEventArgs, ISortingEventArgs, + IFilteringEventArgs, IgxStringFilteringOperand, IColumnMovingEndEventArgs, + IColumnMovingEventArgs, IColumnMovingStartEventArgs, IPinColumnCancellableEventArgs, + IColumnVisibilityChangingEventArgs, + IFilteringExpressionsTree, + IColumnVisibilityChangedEventArgs +} from 'igniteui-angular'; +import { data } from '../shared/data'; + +@Component({ + selector: 'app-grid-events', + styleUrls: ['grid-events.component.scss'], + templateUrl: 'grid-events.component.html' +}) +export class GridEventsComponent { + + @ViewChild('grid1', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; + @ViewChild('logger') public logger: ElementRef; + + public $sorting = false; + public $filtering = false; + public $paging = false; + public $pinning = false; + public $resizing = false; + public $columnSelectionChanging = false; + public $hiding = false; + public $moving = false; + public localData: any[]; + + constructor(private renderer: Renderer2) { + this.localData = data; + } + + public filter(term) { + this.grid.filter('ProductName', term, IgxStringFilteringOperand.instance().condition('contains')); + } + + public filterGlobal(term) { + this.grid.filterGlobal(term, IgxStringFilteringOperand.instance().condition('contains')); + } + + public columnMovingStart(event: IColumnMovingStartEventArgs) { + console.log('event' + event); + this.logAnEvent('=> columnMovingStart'); + } + public columnMoving(event: IColumnMovingEventArgs) { + event.cancel = this.$moving; + this.logAnEvent(event.cancel ? '=> columnMoving cancelled' : '=> columnMoving'); + } + public columnMovingEnd(event: IColumnMovingEndEventArgs) { + console.log('event' + event); + this.logAnEvent('=> columnMovingEnd'); + } + + public onSorting(event: ISortingEventArgs) { + event.cancel = this.$sorting; + this.logAnEvent('=> sorting', event.cancel); + } + public sortingDone(event: ISortingExpression | ISortingExpression []) { + console.log('event' + event); + this.logAnEvent(`=> sortingDone`); + } + + public onFiltering(event: IFilteringEventArgs) { + event.cancel = this.$filtering; + this.logAnEvent('=> filtering', event.cancel); + } + public filteringDone(event: IFilteringExpressionsTree) { + console.log('event' + event); + this.logAnEvent(`=> filteringDone`); + } + public pagingDone(event: IPageEventArgs) { + console.log('event' + event); + this.logAnEvent(`=> pagingDone`); + } + + public columnPin(event: IPinColumnCancellableEventArgs) { + event.cancel = this.$pinning; + this.logAnEvent('=> columnPin', event.cancel); + } + public columnPinned(event: IPinColumnEventArgs) { + console.log('event' + event); + this.logAnEvent(`=> columnPinned`); + } + + public columnVisibilityChanging(event: IColumnVisibilityChangingEventArgs ) { + event.cancel = this.$hiding; + this.logAnEvent('=> columnVisibilityChanging', event.cancel); + } + public columnVisibilityChanged(event: IColumnVisibilityChangedEventArgs) { + console.log('event' + event); + this.logAnEvent(`=> columnVisibilityChanged`); + } + + public columnResized(event: IColumnResizeEventArgs) { + console.log('event' + event); + this.logAnEvent(`=> columnResized`); + } + + public columnSelectionChanging(event: IColumnSelectionEventArgs) { + event.cancel = this.$columnSelectionChanging; + this.logAnEvent('=> columnSelectionChanging', event.cancel); + } + + public clearLog() { + const elements = this.logger.nativeElement.querySelectorAll('p'); + for (const element of elements) { + this.renderer.removeChild(this.logger.nativeElement, element); + } + } + + private logAnEvent(msg: string, cancelled?: boolean) { + const createElem = this.renderer.createElement('p'); + if (cancelled) { + msg = msg.concat(': cancelled '); + } + + const text = this.renderer.createText(msg); + this.renderer.appendChild(createElem, text); + const container = this.logger.nativeElement; + this.renderer.insertBefore(container, createElem, container.children[0]); + } +} + diff --git a/src/app/grid-finjs/controllers.component.html b/src/app/grid-finjs/controllers.component.html index e046f820c27..7399b8287a4 100644 --- a/src/app/grid-finjs/controllers.component.html +++ b/src/app/grid-finjs/controllers.component.html @@ -1,33 +1,33 @@ -
-
-
-
- Dark -
-
- - Grouped -
-
- Toolbar -
-
- - -
-
- - -
-
-
- -
-
-
- Feeding {{volume}} records every {{frequency / 1000}} sec. - {{volume}} records updated. - Feeding {{volume}} records every {{frequency / 1000}} sec. - ~{{volume/5}} records updated. -
-
+
+
+
+
+ Dark +
+
+ + Grouped +
+
+ Toolbar +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ Feeding {{volume}} records every {{frequency / 1000}} sec. + {{volume}} records updated. + Feeding {{volume}} records every {{frequency / 1000}} sec. + ~{{volume/5}} records updated. +
+
diff --git a/src/app/grid-finjs/controllers.component.scss b/src/app/grid-finjs/controllers.component.scss index 060abe0606b..204c7605de2 100644 --- a/src/app/grid-finjs/controllers.component.scss +++ b/src/app/grid-finjs/controllers.component.scss @@ -1,63 +1,63 @@ -:host ::ng-deep { - .fin-dark-theme { - .finjs-slider, - .sample-toolbar { - color: rgba(255, 255, 255, 0.87); - } - } - - .controls-holder { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - width: 100%; - padding-bottom: 15px; - } - .switches { - display: flex; - justify-content: space-between; - align-items: center; - flex: 1 0 0%; - padding-right: 20px; - font-size: 0.9rem; - } - .control-item { - padding-right: 20px; - label { - color: var(--igx-surface-500-contrast); - } - } - .igx-slider, - .igx-slider--disabled { - height: 24px; - } - .finjs-slider { - width: 40%; - min-width: 145px; - } - .finjs-play-controls { - width: 45%; - min-width: 620px; - margin-top: 10px; - } - .sample-toolbar { - height: 20px; - font-size: 0.8rem; - line-height: 20px; - /* position: absolute; */ - /* bottom: 10px; */ - /* left: 10px; */ - margin-top: 11px; - } - .igx-button--icon { - width: 2rem; - height: 2rem; - } -} - -.controls-wrapper { - display: flex; - justify-content: center; - position: relative; +:host ::ng-deep { + .fin-dark-theme { + .finjs-slider, + .sample-toolbar { + color: rgba(255, 255, 255, 0.87); + } + } + + .controls-holder { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + width: 100%; + padding-bottom: 15px; + } + .switches { + display: flex; + justify-content: space-between; + align-items: center; + flex: 1 0 0%; + padding-right: 20px; + font-size: 0.9rem; + } + .control-item { + padding-right: 20px; + label { + color: var(--igx-surface-500-contrast); + } + } + .igx-slider, + .igx-slider--disabled { + height: 24px; + } + .finjs-slider { + width: 40%; + min-width: 145px; + } + .finjs-play-controls { + width: 45%; + min-width: 620px; + margin-top: 10px; + } + .sample-toolbar { + height: 20px; + font-size: 0.8rem; + line-height: 20px; + /* position: absolute; */ + /* bottom: 10px; */ + /* left: 10px; */ + margin-top: 11px; + } + .igx-button--icon { + width: 2rem; + height: 2rem; + } +} + +.controls-wrapper { + display: flex; + justify-content: center; + position: relative; } \ No newline at end of file diff --git a/src/app/grid-finjs/controllers.component.ts b/src/app/grid-finjs/controllers.component.ts index 6de2f43dca3..39e09f747f1 100644 --- a/src/app/grid-finjs/controllers.component.ts +++ b/src/app/grid-finjs/controllers.component.ts @@ -1,106 +1,106 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { IgxButtonGroupComponent, IgxSliderComponent, -} from 'igniteui-angular'; -import { timer } from 'rxjs'; -import { debounce } from 'rxjs/operators'; - -@Component({ - selector: 'app-finjs-controllers', - styleUrls: ['./controllers.component.scss'], - templateUrl: './controllers.component.html' -}) -export class ControllerComponent implements OnInit, OnDestroy { - @ViewChild('buttonGroup1', { static: true }) public playButtons: IgxButtonGroupComponent; - @ViewChild('slider1', { static: true }) public volumeSlider: IgxSliderComponent; - @ViewChild('slider2', { static: true }) public intervalSlider: IgxSliderComponent; - - @Output() public switchChanged = new EventEmitter(); - @Output() public volumeChanged = new EventEmitter(); - @Output() public playAction = new EventEmitter(); - - public darkTheme = true; - public volume = 1000; - public frequency = 500; - public controls = [ - { - disabled: false, - icon: 'update', - label: 'LIVE PRICES', - selected: false - }, - { - disabled: false, - icon: 'update', - label: 'LIVE ALL PRICES', - selected: false - }, - { - disabled: true, - icon: 'stop', - label: 'Stop', - selected: false - } - ]; - - private subscription; - private selectedButton; - private volumeChanged$; - - public get buttonSelected(): number { - return this.selectedButton || this.selectedButton === 0 ? this.selectedButton : -1; - } - - public ngOnInit() { - this.volumeChanged$ = this.volumeSlider.valueChange.pipe(debounce(() => timer(200))); - this.volumeChanged$.subscribe(() => this.volumeChanged.emit(this.volumeSlider.value)); - } - - public onButtonSelected(event: any) { - switch (event.index) { - case 0: { - this.disableOtherButtons(event.index, true); - this.playAction.emit({ action: 'playRandom'}); - break; - } - case 1: { - this.disableOtherButtons(event.index, true); - this.playAction.emit({ action: 'playAll'}); - break; - } - case 2: { - this.disableOtherButtons(event.index, false); - this.playAction.emit({ action: 'stop'}); - break; - } - default: - { - break; - } - } - } - - public onChange(action: string, event: any) { - this.switchChanged.emit({action, value: event.checked }); - } - - public ngOnDestroy() { - this.volumeChanged$.unsubscribe(); - } - - private disableOtherButtons(ind: number, disableButtons: boolean) { - if (this.subscription) { - this.subscription.unsubscribe(); - } - this.volumeSlider.disabled = disableButtons; - this.intervalSlider.disabled = disableButtons; - this.selectedButton = ind; - this.playButtons.buttons.forEach((button, index) => { - if (index === 2) { - button.disabled = !disableButtons; - } else { - this.playButtons.buttons[0].disabled = disableButtons; - this.playButtons.buttons[1].disabled = disableButtons; - } - }); - } -} +import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { IgxButtonGroupComponent, IgxSliderComponent, +} from 'igniteui-angular'; +import { timer } from 'rxjs'; +import { debounce } from 'rxjs/operators'; + +@Component({ + selector: 'app-finjs-controllers', + styleUrls: ['./controllers.component.scss'], + templateUrl: './controllers.component.html' +}) +export class ControllerComponent implements OnInit, OnDestroy { + @ViewChild('buttonGroup1', { static: true }) public playButtons: IgxButtonGroupComponent; + @ViewChild('slider1', { static: true }) public volumeSlider: IgxSliderComponent; + @ViewChild('slider2', { static: true }) public intervalSlider: IgxSliderComponent; + + @Output() public switchChanged = new EventEmitter(); + @Output() public volumeChanged = new EventEmitter(); + @Output() public playAction = new EventEmitter(); + + public darkTheme = true; + public volume = 1000; + public frequency = 500; + public controls = [ + { + disabled: false, + icon: 'update', + label: 'LIVE PRICES', + selected: false + }, + { + disabled: false, + icon: 'update', + label: 'LIVE ALL PRICES', + selected: false + }, + { + disabled: true, + icon: 'stop', + label: 'Stop', + selected: false + } + ]; + + private subscription; + private selectedButton; + private volumeChanged$; + + public get buttonSelected(): number { + return this.selectedButton || this.selectedButton === 0 ? this.selectedButton : -1; + } + + public ngOnInit() { + this.volumeChanged$ = this.volumeSlider.valueChange.pipe(debounce(() => timer(200))); + this.volumeChanged$.subscribe(() => this.volumeChanged.emit(this.volumeSlider.value)); + } + + public onButtonSelected(event: any) { + switch (event.index) { + case 0: { + this.disableOtherButtons(event.index, true); + this.playAction.emit({ action: 'playRandom'}); + break; + } + case 1: { + this.disableOtherButtons(event.index, true); + this.playAction.emit({ action: 'playAll'}); + break; + } + case 2: { + this.disableOtherButtons(event.index, false); + this.playAction.emit({ action: 'stop'}); + break; + } + default: + { + break; + } + } + } + + public onChange(action: string, event: any) { + this.switchChanged.emit({action, value: event.checked }); + } + + public ngOnDestroy() { + this.volumeChanged$.unsubscribe(); + } + + private disableOtherButtons(ind: number, disableButtons: boolean) { + if (this.subscription) { + this.subscription.unsubscribe(); + } + this.volumeSlider.disabled = disableButtons; + this.intervalSlider.disabled = disableButtons; + this.selectedButton = ind; + this.playButtons.buttons.forEach((button, index) => { + if (index === 2) { + button.disabled = !disableButtons; + } else { + this.playButtons.buttons[0].disabled = disableButtons; + this.playButtons.buttons[1].disabled = disableButtons; + } + }); + } +} diff --git a/src/app/grid-finjs/main.component.html b/src/app/grid-finjs/main.component.html index a7aaad0a0e7..d84b507a1fb 100644 --- a/src/app/grid-finjs/main.component.html +++ b/src/app/grid-finjs/main.component.html @@ -1,9 +1,9 @@ -
- - - - +
+ + + +
\ No newline at end of file diff --git a/src/app/grid-finjs/main.component.scss b/src/app/grid-finjs/main.component.scss index 4cab541555f..ae02ecfa680 100644 --- a/src/app/grid-finjs/main.component.scss +++ b/src/app/grid-finjs/main.component.scss @@ -1,22 +1,22 @@ -.grid__wrapper { - position: relative; - width: 100%; - height: 100%; - min-height: 100%; - display: flex; - flex-direction: column; - top: 0; - left: 0; - padding: 15px; - display: flex; - flex-direction: column; -} - -:host ::ng-deep { - .fin-dark-theme { - .finjs-slider, - .sample-toolbar { - color: rgba(255, 255, 255, 0.87); - } - } +.grid__wrapper { + position: relative; + width: 100%; + height: 100%; + min-height: 100%; + display: flex; + flex-direction: column; + top: 0; + left: 0; + padding: 15px; + display: flex; + flex-direction: column; +} + +:host ::ng-deep { + .fin-dark-theme { + .finjs-slider, + .sample-toolbar { + color: rgba(255, 255, 255, 0.87); + } + } } \ No newline at end of file diff --git a/src/app/grid-finjs/main.component.ts b/src/app/grid-finjs/main.component.ts index 3c2ec19fce5..70f20b91bc8 100644 --- a/src/app/grid-finjs/main.component.ts +++ b/src/app/grid-finjs/main.component.ts @@ -1,91 +1,91 @@ -import { Component, EventEmitter, OnDestroy, Output, ViewChild } from '@angular/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { LocalService } from '../shared/local.service'; -import { ControllerComponent } from './controllers.component'; -import { GridFinJSComponent } from './grid-finjs.component'; - -@Component({ - providers: [LocalService], - selector: 'app-finjs-main', - styleUrls: ['./main.component.scss'], - templateUrl: './main.component.html' -}) -export class MainComponent implements OnDestroy { - @ViewChild('grid', { static: true }) public finGrid: GridFinJSComponent; - @ViewChild('controllers', { static: true }) public controller: ControllerComponent; - - @Output() public switch = new EventEmitter(); - @Output() public recordsVolume = new EventEmitter(); - @Output() public frequencyTimer = new EventEmitter(); - @Output() public player = new EventEmitter(); - - public volume = 1000; - public frequency = 500; - public darkTheme = true; - public data$: any; - private destroy$ = new Subject(); - private _timer; - - constructor(public finService: LocalService) { - this.finService.getFinancialData(this.volume); - this.data$ = this.finService.records.pipe(takeUntil(this.destroy$)); - } - - public ngOnDestroy() { - this.destroy$.next(true); - this.destroy$.complete(); - this.stopFeed(); - } - - public onSwitchChanged(event: any) { - switch (event.action) { - case 'toolbar': { - this.finGrid.showToolbar = event.value; - break; - } - case 'grouped': { - this.finGrid.toggleGrouping(event.value); - break; - } - default: - { - break; - } - } - } - - public onVolumeChanged(volume: any) { - this.finGrid.grid.deselectAllRows(); - this.finService.getFinancialData(volume); - } - - public onPlayAction(event: any) { - switch (event.action) { - case 'playAll': { - const currData = this.finGrid.grid.filteredSortedData ?? this.finGrid.data; - this._timer = setInterval(() => this.finService.updateAllPriceValues(currData), this.controller.frequency); - break; - } - case 'playRandom': { - const currData = this.finGrid.data; - this._timer = setInterval(() => this.finService.updateRandomPriceValues(currData), this.controller.frequency); - break; - } - case 'stop': { - this.stopFeed(); - break; - } - default: - { - break; - } - } - } - - public stopFeed() { - if (this._timer) { - clearInterval(this._timer); - } - } -} +import { Component, EventEmitter, OnDestroy, Output, ViewChild } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { LocalService } from '../shared/local.service'; +import { ControllerComponent } from './controllers.component'; +import { GridFinJSComponent } from './grid-finjs.component'; + +@Component({ + providers: [LocalService], + selector: 'app-finjs-main', + styleUrls: ['./main.component.scss'], + templateUrl: './main.component.html' +}) +export class MainComponent implements OnDestroy { + @ViewChild('grid', { static: true }) public finGrid: GridFinJSComponent; + @ViewChild('controllers', { static: true }) public controller: ControllerComponent; + + @Output() public switch = new EventEmitter(); + @Output() public recordsVolume = new EventEmitter(); + @Output() public frequencyTimer = new EventEmitter(); + @Output() public player = new EventEmitter(); + + public volume = 1000; + public frequency = 500; + public darkTheme = true; + public data$: any; + private destroy$ = new Subject(); + private _timer; + + constructor(public finService: LocalService) { + this.finService.getFinancialData(this.volume); + this.data$ = this.finService.records.pipe(takeUntil(this.destroy$)); + } + + public ngOnDestroy() { + this.destroy$.next(true); + this.destroy$.complete(); + this.stopFeed(); + } + + public onSwitchChanged(event: any) { + switch (event.action) { + case 'toolbar': { + this.finGrid.showToolbar = event.value; + break; + } + case 'grouped': { + this.finGrid.toggleGrouping(event.value); + break; + } + default: + { + break; + } + } + } + + public onVolumeChanged(volume: any) { + this.finGrid.grid.deselectAllRows(); + this.finService.getFinancialData(volume); + } + + public onPlayAction(event: any) { + switch (event.action) { + case 'playAll': { + const currData = this.finGrid.grid.filteredSortedData ?? this.finGrid.data; + this._timer = setInterval(() => this.finService.updateAllPriceValues(currData), this.controller.frequency); + break; + } + case 'playRandom': { + const currData = this.finGrid.data; + this._timer = setInterval(() => this.finService.updateRandomPriceValues(currData), this.controller.frequency); + break; + } + case 'stop': { + this.stopFeed(); + break; + } + default: + { + break; + } + } + } + + public stopFeed() { + if (this._timer) { + clearInterval(this._timer); + } + } +} diff --git a/src/app/grid-row-api/grid-row-api.sample.css b/src/app/grid-row-api/grid-row-api.sample.css index f814d60a525..824423498ba 100644 --- a/src/app/grid-row-api/grid-row-api.sample.css +++ b/src/app/grid-row-api/grid-row-api.sample.css @@ -1,76 +1,76 @@ -.sample-buttons { - margin-top: 24px; -} - -[igxButton] { - margin-right: 8px; - margin-bottom: 8px; -} - -.density-chooser { - margin-bottom: 16px; - max-width: 900px; -} - -.references { - font-size: .75em; -} - -.references>p { - margin: 0; - letter-spacing: initial; - line-height: initial; - font-size: 1em; -} - -.sample-column { - width: 60%; - margin: 8px; - flex-flow: ''; -} - -.log-wrapper { - width: 30%; -} - -.clearBtn { - top: 3px; - margin-left: 20px; -} - -.logContainer { - padding: 0.2rem 0.4rem; -} - -:host { - width: 100%; -} - -.sample-column { - display: flex; - flex-flow: row wrap !important; - width: 100%; -} - -.grid-wrapper { - width: 60%; -} - -.clearBtn { - top: 3px; - margin-left: 20px; -} - -.selected-data-area { - overflow-y: auto; - max-height: 550px; - width: 100%; - height: 100%; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); - margin-top: 8px; -} - -.highlight { - text-align: center; - margin-bottom: 0.4rem; +.sample-buttons { + margin-top: 24px; +} + +[igxButton] { + margin-right: 8px; + margin-bottom: 8px; +} + +.density-chooser { + margin-bottom: 16px; + max-width: 900px; +} + +.references { + font-size: .75em; +} + +.references>p { + margin: 0; + letter-spacing: initial; + line-height: initial; + font-size: 1em; +} + +.sample-column { + width: 60%; + margin: 8px; + flex-flow: ''; +} + +.log-wrapper { + width: 30%; +} + +.clearBtn { + top: 3px; + margin-left: 20px; +} + +.logContainer { + padding: 0.2rem 0.4rem; +} + +:host { + width: 100%; +} + +.sample-column { + display: flex; + flex-flow: row wrap !important; + width: 100%; +} + +.grid-wrapper { + width: 60%; +} + +.clearBtn { + top: 3px; + margin-left: 20px; +} + +.selected-data-area { + overflow-y: auto; + max-height: 550px; + width: 100%; + height: 100%; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); + margin-top: 8px; +} + +.highlight { + text-align: center; + margin-bottom: 0.4rem; } \ No newline at end of file diff --git a/src/app/grid-row-api/grid-row-api.sample.html b/src/app/grid-row-api/grid-row-api.sample.html index b204da36ebd..daf52b40760 100644 --- a/src/app/grid-row-api/grid-row-api.sample.html +++ b/src/app/grid-row-api/grid-row-api.sample.html @@ -1,267 +1,267 @@ -
-
igxGrid
-
-
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
-
-
- - -
-
Country: {{dataItem.Country}}
-
City: {{dataItem.City}}
-
Address: {{dataItem.Address}}
-
-
- - - -
- {{dragIcon}}{{countIcon}} -
-
- - - - - - - - - -
-
-
-
-
-
- -
-
-
-
- -
-
IgxTreeGrid Hierarchical Data
-
-
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
-
-
- - - - - - - - - - ======= - - - -
-
-
-
-
- -
-
-
- -
-
IgxTreeGrid Flat Data
-
-
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
-
-
- - - - - - - - - - - - -
-
-
-
-
- -
-
-
- -
-
igxHierarchicalGrid
-
-
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-
-
+
+
igxGrid
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + +
+
Country: {{dataItem.Country}}
+
City: {{dataItem.City}}
+
Address: {{dataItem.Address}}
+
+
+ + + +
+ {{dragIcon}}{{countIcon}} +
+
+ + + + + + + + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
IgxTreeGrid Hierarchical Data
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + + + + + + + + + ======= + + + +
+
+
+
+
+ +
+
+
+ +
+
IgxTreeGrid Flat Data
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + + + + + + + + + + + +
+
+
+
+
+ +
+
+
+ +
+
igxHierarchicalGrid
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+
diff --git a/src/app/grid-row-api/grid-row-api.sample.ts b/src/app/grid-row-api/grid-row-api.sample.ts index 5bfde56e20a..42987b7f547 100644 --- a/src/app/grid-row-api/grid-row-api.sample.ts +++ b/src/app/grid-row-api/grid-row-api.sample.ts @@ -1,323 +1,323 @@ -import { Component, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { - IgxGridComponent, - RowType, - IgxTreeGridComponent, - IgxHierarchicalGridComponent, - IPinningConfig, - RowPinningPosition, - GridSummaryCalculationMode, - GridSummaryPosition -} from 'igniteui-angular'; -import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; - -@Component({ - selector: 'app-grid-row-api-sample', - styleUrls: ['grid-row-api.sample.css'], - templateUrl: 'grid-row-api.sample.html' -}) - -export class GridRowAPISampleComponent implements OnInit { - @ViewChild('grid', { static: true }) - private grid: IgxGridComponent; - - @ViewChild('targetGrid', { static: true }) - private targetGrid: IgxGridComponent; - - @ViewChild('treeGridHier', { static: true }) - private treeGridHier: IgxTreeGridComponent; - - @ViewChild('hGrid', { static: true }) - private hGrid: IgxTreeGridComponent; - public countIcon = 'drag_indicator'; - public dragIcon = 'arrow_right_alt'; - public data2: any; - public data: any[]; - public treeGridHierData: any[]; - public hierarchicalData: any[]; - public columns: any[]; - public hColumns: any[]; - public treeGridHierColumns: any[]; - public treeColumns: any[]; - public treeData: any[]; - - public index = 0; - public tIndex = 0; - public tHIndex = 0; - public hIndex = 0; - - public key = ''; - public tKey = ''; - public tHKey = ''; - public hKey = ''; - - public pinningConfig: IPinningConfig = { rows: RowPinningPosition.Top }; - - constructor(private renderer: Renderer2) { } - - public ngOnInit(): void { - this.grid.summaryCalculationMode = GridSummaryCalculationMode.childLevelsOnly; - this.grid.summaryPosition = GridSummaryPosition.bottom; - - this.treeGridHier.summaryCalculationMode = GridSummaryCalculationMode.childLevelsOnly; - this.columns = [ - { field: 'ID', width: '200px', hidden: true }, - { field: 'CompanyName', width: '200px', groupable: true }, - { field: 'ContactName', width: '200px', pinned: false, groupable: true }, - { field: 'ContactTitle', width: '300px', pinned: false, groupable: true }, - { field: 'Address', width: '250px' }, - { field: 'City', width: '200px' }, - { field: 'Region', width: '300px' }, - { field: 'PostalCode', width: '150px' }, - { field: 'Phone', width: '200px' }, - { field: 'Fax', width: '200px' } - ]; - - this.hColumns = [ - { field: 'ID', width: '200px' }, - { field: 'ChildLevels', width: '200px' }, - { field: 'ProductName', width: '200px' }, - { field: 'Col1', width: '200px' }, - { field: 'Col2', width: '200px' }, - { field: 'Col3', width: '200px' }, - { field: 'childData', width: '200px' }, - { field: 'childData2', width: '200px' }, - { field: 'hasChild', width: '200px' } - ]; - - this.treeGridHierColumns = [ - { field: 'ID', width: 200, resizable: true, pinned: true }, - { field: 'CompanyName', width: 150, resizable: true }, - { field: 'ContactName', width: 150, resizable: true }, - { field: 'ContactTitle', width: 150, resizable: true }, - { field: 'Address', width: 150, resizable: true }, - { field: 'City', width: 150, resizable: true, summary: true }, - { field: 'Region', width: 150, resizable: true }, - { field: 'PostalCode', width: 150, resizable: true }, - { field: 'Phone', width: 150, resizable: true }, - { field: 'Fax', width: 150, resizable: true } - ]; - this.treeGridHierData = HIERARCHICAL_SAMPLE_DATA.slice(0); - - this.data = [ - /* eslint-disable max-len */ - { ID: 'ALFKI', CompanyName: 'Alfreds Futterkiste', ContactName: 'Maria Anders', ContactTitle: 'Sales Representative', Address: 'Obere Str. 57', City: 'Berlin', Region: null, PostalCode: '12209', Country: 'Germany', Phone: '030-0074321', Fax: '030-0076545' }, - { ID: 'ANATR', CompanyName: 'Ana Trujillo Emparedados y helados', ContactName: 'Ana Trujillo', ContactTitle: 'Owner', Address: 'Avda. de la Constitución 2222', City: 'México D.F.', Region: null, PostalCode: '05021', Country: 'Mexico', Phone: '(5) 555-4729', Fax: '(5) 555-3745' }, - { ID: 'ANTON', CompanyName: 'Antonio Moreno Taquería', ContactName: 'Antonio Moreno', ContactTitle: 'Owner', Address: 'Mataderos 2312', City: 'México D.F.', Region: null, PostalCode: '05023', Country: 'Mexico', Phone: '(5) 555-3932', Fax: null }, - { ID: 'AROUT', CompanyName: 'Around the Horn', ContactName: 'Thomas Hardy', ContactTitle: 'Sales Representative', Address: '120 Hanover Sq.', City: 'London', Region: null, PostalCode: 'WA1 1DP', Country: 'UK', Phone: '(171) 555-7788', Fax: '(171) 555-6750' }, - { ID: 'BERGS', CompanyName: 'Berglunds snabbköp', ContactName: 'Christina Berglund', ContactTitle: 'Order Administrator', Address: 'Berguvsvägen 8', City: 'Luleå', Region: null, PostalCode: 'S-958 22', Country: 'Sweden', Phone: '0921-12 34 65', Fax: '0921-12 34 67' }, - { ID: 'BLAUS', CompanyName: 'Blauer See Delikatessen', ContactName: 'Hanna Moos', ContactTitle: 'Sales Representative', Address: 'Forsterstr. 57', City: 'Mannheim', Region: null, PostalCode: '68306', Country: 'Germany', Phone: '0621-08460', Fax: '0621-08924' }, - { ID: 'BLONP', CompanyName: 'Blondesddsl père et fils', ContactName: 'Frédérique Citeaux', ContactTitle: 'Marketing Manager', Address: '24, place Kléber', City: 'Strasbourg', Region: null, PostalCode: '67000', Country: 'France', Phone: '88.60.15.31', Fax: '88.60.15.32' }, - { ID: 'BOLID', CompanyName: 'Bólido Comidas preparadas', ContactName: 'Martín Sommer', ContactTitle: 'Owner', Address: 'C/ Araquil, 67', City: 'Madrid', Region: null, PostalCode: '28023', Country: 'Spain', Phone: '(91) 555 22 82', Fax: '(91) 555 91 99' }, - { ID: 'BONAP', CompanyName: 'Bon app\'', ContactName: 'Laurence Lebihan', ContactTitle: 'Owner', Address: '12, rue des Bouchers', City: 'Marseille', Region: null, PostalCode: '13008', Country: 'France', Phone: '91.24.45.40', Fax: '91.24.45.41' }, - { ID: 'BOTTM', CompanyName: 'Bottom-Dollar Markets', ContactName: 'Elizabeth Lincoln', ContactTitle: 'Accounting Manager', Address: '23 Tsawassen Blvd.', City: 'Tsawassen', Region: 'BC', PostalCode: 'T2F 8M4', Country: 'Canada', Phone: '(604) 555-4729', Fax: '(604) 555-3745' }, - { ID: 'BSBEV', CompanyName: 'B\'s Beverages', ContactName: 'Victoria Ashworth', ContactTitle: 'Sales Representative', Address: 'Fauntleroy Circus', City: 'London', Region: null, PostalCode: 'EC2 5NT', Country: 'UK', Phone: '(171) 555-1212', Fax: null }, - { ID: 'CACTU', CompanyName: 'Cactus Comidas para llevar', ContactName: 'Patricio Simpson', ContactTitle: 'Sales Agent', Address: 'Cerrito 333', City: 'Buenos Aires', Region: null, PostalCode: '1010', Country: 'Argentina', Phone: '(1) 135-5555', Fax: '(1) 135-4892' }, - { ID: 'CENTC', CompanyName: 'Centro comercial Moctezuma', ContactName: 'Francisco Chang', ContactTitle: 'Marketing Manager', Address: 'Sierras de Granada 9993', City: 'México D.F.', Region: null, PostalCode: '05022', Country: 'Mexico', Phone: '(5) 555-3392', Fax: '(5) 555-7293' }, - { ID: 'CHOPS', CompanyName: 'Chop-suey Chinese', ContactName: 'Yang Wang', ContactTitle: 'Owner', Address: 'Hauptstr. 29', City: 'Bern', Region: null, PostalCode: '3012', Country: 'Switzerland', Phone: '0452-076545', Fax: null }, - { ID: 'COMMI', CompanyName: 'Comércio Mineiro', ContactName: 'Pedro Afonso', ContactTitle: 'Sales Associate', Address: 'Av. dos Lusíadas, 23', City: 'Sao Paulo', Region: 'SP', PostalCode: '05432-043', Country: 'Brazil', Phone: '(11) 555-7647', Fax: null }, - { ID: 'CONSH', CompanyName: 'Consolidated Holdings', ContactName: 'Elizabeth Brown', ContactTitle: 'Sales Representative', Address: 'Berkeley Gardens 12 Brewery', City: 'London', Region: null, PostalCode: 'WX1 6LT', Country: 'UK', Phone: '(171) 555-2282', Fax: '(171) 555-9199' }, - { ID: 'DRACD', CompanyName: 'Drachenblut Delikatessen', ContactName: 'Sven Ottlieb', ContactTitle: 'Order Administrator', Address: 'Walserweg 21', City: 'Aachen', Region: null, PostalCode: '52066', Country: 'Germany', Phone: '0241-039123', Fax: '0241-059428' }, - { ID: 'DUMON', CompanyName: 'Du monde entier', ContactName: 'Janine Labrune', ContactTitle: 'Owner', Address: '67, rue des Cinquante Otages', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.67.88.88', Fax: '40.67.89.89' }, - { ID: 'EASTC', CompanyName: 'Eastern Connection', ContactName: 'Ann Devon', ContactTitle: 'Sales Agent', Address: '35 King George', City: 'London', Region: null, PostalCode: 'WX3 6FW', Country: 'UK', Phone: '(171) 555-0297', Fax: '(171) 555-3373' }, - { ID: 'ERNSH', CompanyName: 'Ernst Handel', ContactName: 'Roland Mendel', ContactTitle: 'Sales Manager', Address: 'Kirchgasse 6', City: 'Graz', Region: null, PostalCode: '8010', Country: 'Austria', Phone: '7675-3425', Fax: '7675-3426' }, - { ID: 'FAMIA', CompanyName: 'Familia Arquibaldo', ContactName: 'Aria Cruz', ContactTitle: 'Marketing Assistant', Address: 'Rua Orós, 92', City: 'Sao Paulo', Region: 'SP', PostalCode: '05442-030', Country: 'Brazil', Phone: '(11) 555-9857', Fax: null }, - { ID: 'FISSA', CompanyName: 'FISSA Fabrica Inter. Salchichas S.A.', ContactName: 'Diego Roel', ContactTitle: 'Accounting Manager', Address: 'C/ Moralzarzal, 86', City: 'Madrid', Region: null, PostalCode: '28034', Country: 'Spain', Phone: '(91) 555 94 44', Fax: '(91) 555 55 93' }, - { ID: 'FOLIG', CompanyName: 'Folies gourmandes', ContactName: 'Martine Rancé', ContactTitle: 'Assistant Sales Agent', Address: '184, chaussée de Tournai', City: 'Lille', Region: null, PostalCode: '59000', Country: 'France', Phone: '20.16.10.16', Fax: '20.16.10.17' }, - { ID: 'FOLKO', CompanyName: 'Folk och fä HB', ContactName: 'Maria Larsson', ContactTitle: 'Owner', Address: 'Åkergatan 24', City: 'Bräcke', Region: null, PostalCode: 'S-844 67', Country: 'Sweden', Phone: '0695-34 67 21', Fax: null }, - { ID: 'FRANK', CompanyName: 'Frankenversand', ContactName: 'Peter Franken', ContactTitle: 'Marketing Manager', Address: 'Berliner Platz 43', City: 'München', Region: null, PostalCode: '80805', Country: 'Germany', Phone: '089-0877310', Fax: '089-0877451' }, - { ID: 'FRANR', CompanyName: 'France restauration', ContactName: 'Carine Schmitt', ContactTitle: 'Marketing Manager', Address: '54, rue Royale', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.32.21.21', Fax: '40.32.21.20' }, - { ID: 'FRANS', CompanyName: 'Franchi S.p.A.', ContactName: 'Paolo Accorti', ContactTitle: 'Sales Representative', Address: 'Via Monte Bianco 34', City: 'Torino', Region: null, PostalCode: '10100', Country: 'Italy', Phone: '011-4988260', Fax: '011-4988261' } - ]; - this.hierarchicalData = this.generateDataUneven(100, 3); - - // treegrid cols and data - this.treeColumns = [ - { field: 'employeeID', label: 'ID', width: 200, resizable: true, dataType: 'number', hasSummary: false }, - { field: 'Salary', label: 'Salary', width: 200, resizable: true, dataType: 'number', hasSummary: true }, - { field: 'firstName', label: 'First Name', width: 300, resizable: true, dataType: 'string', hasSummary: false }, - { field: 'lastName', label: 'Last Name', width: 150, resizable: true, dataType: 'string', hasSummary: false }, - { field: 'Title', label: 'Title', width: 200, resizable: true, dataType: 'string', hasSummary: true } - ]; - this.treeData = [ - { Salary: 2500, employeeID: 0, PID: -1, firstName: 'Andrew', lastName: 'Fuller', Title: 'Vice President, Sales' }, - { Salary: 3500, employeeID: 1, PID: -1, firstName: 'Jonathan', lastName: 'Smith', Title: 'Human resources' }, - { Salary: 1500, employeeID: 2, PID: -1, firstName: 'Nancy', lastName: 'Davolio', Title: 'CFO' }, - { Salary: 2500, employeeID: 3, PID: -1, firstName: 'Steven', lastName: 'Buchanan', Title: 'CTO' }, - // sub of ID 0 - { Salary: 2500, employeeID: 4, PID: 0, firstName: 'Janet', lastName: 'Leverling', Title: 'Sales Manager' }, - { Salary: 3500, employeeID: 5, PID: 0, firstName: 'Laura', lastName: 'Callahan', Title: 'Inside Sales Coordinator' }, - { Salary: 1500, employeeID: 6, PID: 0, firstName: 'Margaret', lastName: 'Peacock', Title: 'Sales Representative' }, - { Salary: 2500, employeeID: 7, PID: 0, firstName: 'Michael', lastName: 'Suyama', Title: 'Sales Representative' }, - // sub of ID 4 - { Salary: 2500, employeeID: 8, PID: 4, firstName: 'Anne', lastName: 'Dodsworth', Title: 'Sales Representative' }, - { Salary: 3500, employeeID: 9, PID: 4, firstName: 'Danielle', lastName: 'Davis', Title: 'Sales Representative' }, - { Salary: 1500, employeeID: 10, PID: 4, firstName: 'Robert', lastName: 'King', Title: 'Sales Representative' }, - // sub of ID 2 - { Salary: 2500, employeeID: 11, PID: 2, firstName: 'Peter', lastName: 'Lewis', Title: 'Chief Accountant' }, - { Salary: 3500, employeeID: 12, PID: 2, firstName: 'Ryder', lastName: 'Zenaida', Title: 'Accountant' }, - { Salary: 1500, employeeID: 13, PID: 2, firstName: 'Wang', lastName: 'Mercedes', Title: 'Accountant' }, - // sub of ID 3 - { Salary: 1500, employeeID: 14, PID: 3, firstName: 'Theodore', lastName: 'Zia', Title: 'Software Architect' }, - { Salary: 4500, employeeID: 15, PID: 3, firstName: 'Lacota', lastName: 'Mufutau', Title: 'Product Manager' }, - // sub of ID 16 - { Salary: 2500, employeeID: 16, PID: 15, firstName: 'Jin', lastName: 'Elliott', Title: 'Product Owner' }, - { Salary: 3500, employeeID: 17, PID: 15, firstName: 'Armand', lastName: 'Ross', Title: 'Product Owner' }, - { Salary: 1500, employeeID: 18, PID: 15, firstName: 'Dane', lastName: 'Rodriquez', Title: 'Team Leader' }, - // sub of ID 19 - { Salary: 2500, employeeID: 19, PID: 18, firstName: 'Declan', lastName: 'Lester', Title: 'Senior Software Developer' }, - { Salary: 3500, employeeID: 20, PID: 18, firstName: 'Bernard', lastName: 'Jarvis', Title: 'Senior Software Developer' }, - { Salary: 1500, employeeID: 21, PID: 18, firstName: 'Jason', lastName: 'Clark', Title: 'QA' }, - { Salary: 1500, employeeID: 22, PID: 18, firstName: 'Mark', lastName: 'Young', Title: 'QA' }, - // sub of ID 20 - { Salary: 1500, employeeID: 23, PID: 20, firstName: 'Jeremy', lastName: 'Donaldson', Title: 'Software Developer' } - ]; - /* eslint-enable max-len */ - } - - public togglePinning(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, - byIndex: boolean, index: number, key: any) { - const row: RowType = byIndex ? grid.getRowByIndex(index) : grid.getRowByKey(key); - if (row.pinned) { - row.unpin(); - } else { - row.pin(); - } - } - - - public deleteRow(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { - const row = grid.getRowByIndex(index); - row.delete(); - } - - public toggle(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { - const row = grid.getRowByIndex(index); - row.expanded = !row.expanded; - } - - public select(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { - const row = grid.getRowByIndex(index); - row.selected = !row.selected; - } - - public selectChildren(grid: IgxGridComponent | IgxTreeGridComponent, index: number) { - const row = grid.getRowByIndex(index); - const children = row.children; - children.forEach(ch => { - ch.selected = !ch.selected; - }); - } - - public selectParent(grid: IgxGridComponent | IgxTreeGridComponent, index: number) { - const row = grid.getRowByIndex(index); - const parent = row.parent; - parent.selected = !parent.selected; - if (parent) { - parent.selected = !parent.selected; - } - } - - public generateDataUneven(count: number, level: number, parendID: string = null) { - const prods = []; - const currLevel = level; - let children; - for (let i = 0; i < count; i++) { - const rowID = parendID ? parendID + i : i.toString(); - if (level > 0) { - // Have child grids for row with even id less rows by not multiplying by 2 - children = this.generateDataUneven(((i % 2) + 1) * Math.round(count / 3), currLevel - 1, rowID); - } - prods.push({ - ID: rowID, - ChildLevels: currLevel, - ProductName: 'Product: A' + i, - Col1: i, - Col2: i, - Col3: i, - childData: children, - childData2: children, - hasChild: true - }); - } - return prods; - } - - public clearLog(logger: HTMLElement) { - const elements = logger.querySelectorAll('p'); - - elements.forEach(element => { - this.renderer.removeChild(logger, element); - }); - } - - public logState(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number, logger: HTMLElement) { - this.clearLog(logger); - const row = grid.getRowByIndex(index); - const state = ` - index: ${row.index}, - viewIndex: ${row.viewIndex}, - -----------------------------, - isSummaryRow: ${row.isSummaryRow}, - summaries: ${row.summaries}, - -----------------------------, - isGroupByRow: ${row.isGroupByRow}, - groupByRow: ${row.groupRow?.value}, - -----------------------------, - parent: ${row.parent}, - expanded: ${row.expanded}, - key: ${row.key}, - pinned: ${row.pinned}, - deleted: ${row.deleted}, - inEditMode: ${row.inEditMode}, - selected: ${row.selected}, - hasChildren: ${row.hasChildren}, - disabled: ${row.disabled}, - --------------------------------, - cells.length: ${row.cells?.length}`; - // firstCell: ${row.cells[0].value}, - // lastCell: ${row.cells[row.cells.length - 1].value}`; - - const states = state.split(','); - const createElem = this.renderer.createElement('p'); - - states.forEach(st => { - const text = this.renderer.createText(st); - this.renderer.appendChild(createElem, text); - this.renderer.appendChild(createElem, this.renderer.createElement('br')); - }); - - this.renderer.insertBefore(logger, createElem, logger.children[0]); - } - - public onRowDragEnd(args) { - args.animation = true; - } - - public onDropAllowed(args) { - let selected = false; - const ids = this.grid.selectedRows; - const selectedRowData = this.grid.data.filter((record) => ids.includes(record.ID)); - selectedRowData.forEach((rowData) => { - selected = true; - this.targetGrid.addRow(rowData); - this.grid.deleteRow(rowData.ID); - }); - if (selected === false) { - this.targetGrid.addRow(args.dragData.data); - // this.grid.deleteRow(args.dragData.key); - } - } - - public onEnter() { - this.dragIcon = 'add'; - } - public onRowDragStart() { - const count = this.grid.selectedRows.length || 1; - this.countIcon = `filter_${count > 9 ? '9_plus' : `${count}`}`; - } - public onLeave() { - this.onRowDragStart(); - this.dragIcon = 'arrow_right_alt'; - } -} +import { Component, OnInit, Renderer2, ViewChild } from '@angular/core'; +import { + IgxGridComponent, + RowType, + IgxTreeGridComponent, + IgxHierarchicalGridComponent, + IPinningConfig, + RowPinningPosition, + GridSummaryCalculationMode, + GridSummaryPosition +} from 'igniteui-angular'; +import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; + +@Component({ + selector: 'app-grid-row-api-sample', + styleUrls: ['grid-row-api.sample.css'], + templateUrl: 'grid-row-api.sample.html' +}) + +export class GridRowAPISampleComponent implements OnInit { + @ViewChild('grid', { static: true }) + private grid: IgxGridComponent; + + @ViewChild('targetGrid', { static: true }) + private targetGrid: IgxGridComponent; + + @ViewChild('treeGridHier', { static: true }) + private treeGridHier: IgxTreeGridComponent; + + @ViewChild('hGrid', { static: true }) + private hGrid: IgxTreeGridComponent; + public countIcon = 'drag_indicator'; + public dragIcon = 'arrow_right_alt'; + public data2: any; + public data: any[]; + public treeGridHierData: any[]; + public hierarchicalData: any[]; + public columns: any[]; + public hColumns: any[]; + public treeGridHierColumns: any[]; + public treeColumns: any[]; + public treeData: any[]; + + public index = 0; + public tIndex = 0; + public tHIndex = 0; + public hIndex = 0; + + public key = ''; + public tKey = ''; + public tHKey = ''; + public hKey = ''; + + public pinningConfig: IPinningConfig = { rows: RowPinningPosition.Top }; + + constructor(private renderer: Renderer2) { } + + public ngOnInit(): void { + this.grid.summaryCalculationMode = GridSummaryCalculationMode.childLevelsOnly; + this.grid.summaryPosition = GridSummaryPosition.bottom; + + this.treeGridHier.summaryCalculationMode = GridSummaryCalculationMode.childLevelsOnly; + this.columns = [ + { field: 'ID', width: '200px', hidden: true }, + { field: 'CompanyName', width: '200px', groupable: true }, + { field: 'ContactName', width: '200px', pinned: false, groupable: true }, + { field: 'ContactTitle', width: '300px', pinned: false, groupable: true }, + { field: 'Address', width: '250px' }, + { field: 'City', width: '200px' }, + { field: 'Region', width: '300px' }, + { field: 'PostalCode', width: '150px' }, + { field: 'Phone', width: '200px' }, + { field: 'Fax', width: '200px' } + ]; + + this.hColumns = [ + { field: 'ID', width: '200px' }, + { field: 'ChildLevels', width: '200px' }, + { field: 'ProductName', width: '200px' }, + { field: 'Col1', width: '200px' }, + { field: 'Col2', width: '200px' }, + { field: 'Col3', width: '200px' }, + { field: 'childData', width: '200px' }, + { field: 'childData2', width: '200px' }, + { field: 'hasChild', width: '200px' } + ]; + + this.treeGridHierColumns = [ + { field: 'ID', width: 200, resizable: true, pinned: true }, + { field: 'CompanyName', width: 150, resizable: true }, + { field: 'ContactName', width: 150, resizable: true }, + { field: 'ContactTitle', width: 150, resizable: true }, + { field: 'Address', width: 150, resizable: true }, + { field: 'City', width: 150, resizable: true, summary: true }, + { field: 'Region', width: 150, resizable: true }, + { field: 'PostalCode', width: 150, resizable: true }, + { field: 'Phone', width: 150, resizable: true }, + { field: 'Fax', width: 150, resizable: true } + ]; + this.treeGridHierData = HIERARCHICAL_SAMPLE_DATA.slice(0); + + this.data = [ + /* eslint-disable max-len */ + { ID: 'ALFKI', CompanyName: 'Alfreds Futterkiste', ContactName: 'Maria Anders', ContactTitle: 'Sales Representative', Address: 'Obere Str. 57', City: 'Berlin', Region: null, PostalCode: '12209', Country: 'Germany', Phone: '030-0074321', Fax: '030-0076545' }, + { ID: 'ANATR', CompanyName: 'Ana Trujillo Emparedados y helados', ContactName: 'Ana Trujillo', ContactTitle: 'Owner', Address: 'Avda. de la Constitución 2222', City: 'México D.F.', Region: null, PostalCode: '05021', Country: 'Mexico', Phone: '(5) 555-4729', Fax: '(5) 555-3745' }, + { ID: 'ANTON', CompanyName: 'Antonio Moreno Taquería', ContactName: 'Antonio Moreno', ContactTitle: 'Owner', Address: 'Mataderos 2312', City: 'México D.F.', Region: null, PostalCode: '05023', Country: 'Mexico', Phone: '(5) 555-3932', Fax: null }, + { ID: 'AROUT', CompanyName: 'Around the Horn', ContactName: 'Thomas Hardy', ContactTitle: 'Sales Representative', Address: '120 Hanover Sq.', City: 'London', Region: null, PostalCode: 'WA1 1DP', Country: 'UK', Phone: '(171) 555-7788', Fax: '(171) 555-6750' }, + { ID: 'BERGS', CompanyName: 'Berglunds snabbköp', ContactName: 'Christina Berglund', ContactTitle: 'Order Administrator', Address: 'Berguvsvägen 8', City: 'Luleå', Region: null, PostalCode: 'S-958 22', Country: 'Sweden', Phone: '0921-12 34 65', Fax: '0921-12 34 67' }, + { ID: 'BLAUS', CompanyName: 'Blauer See Delikatessen', ContactName: 'Hanna Moos', ContactTitle: 'Sales Representative', Address: 'Forsterstr. 57', City: 'Mannheim', Region: null, PostalCode: '68306', Country: 'Germany', Phone: '0621-08460', Fax: '0621-08924' }, + { ID: 'BLONP', CompanyName: 'Blondesddsl père et fils', ContactName: 'Frédérique Citeaux', ContactTitle: 'Marketing Manager', Address: '24, place Kléber', City: 'Strasbourg', Region: null, PostalCode: '67000', Country: 'France', Phone: '88.60.15.31', Fax: '88.60.15.32' }, + { ID: 'BOLID', CompanyName: 'Bólido Comidas preparadas', ContactName: 'Martín Sommer', ContactTitle: 'Owner', Address: 'C/ Araquil, 67', City: 'Madrid', Region: null, PostalCode: '28023', Country: 'Spain', Phone: '(91) 555 22 82', Fax: '(91) 555 91 99' }, + { ID: 'BONAP', CompanyName: 'Bon app\'', ContactName: 'Laurence Lebihan', ContactTitle: 'Owner', Address: '12, rue des Bouchers', City: 'Marseille', Region: null, PostalCode: '13008', Country: 'France', Phone: '91.24.45.40', Fax: '91.24.45.41' }, + { ID: 'BOTTM', CompanyName: 'Bottom-Dollar Markets', ContactName: 'Elizabeth Lincoln', ContactTitle: 'Accounting Manager', Address: '23 Tsawassen Blvd.', City: 'Tsawassen', Region: 'BC', PostalCode: 'T2F 8M4', Country: 'Canada', Phone: '(604) 555-4729', Fax: '(604) 555-3745' }, + { ID: 'BSBEV', CompanyName: 'B\'s Beverages', ContactName: 'Victoria Ashworth', ContactTitle: 'Sales Representative', Address: 'Fauntleroy Circus', City: 'London', Region: null, PostalCode: 'EC2 5NT', Country: 'UK', Phone: '(171) 555-1212', Fax: null }, + { ID: 'CACTU', CompanyName: 'Cactus Comidas para llevar', ContactName: 'Patricio Simpson', ContactTitle: 'Sales Agent', Address: 'Cerrito 333', City: 'Buenos Aires', Region: null, PostalCode: '1010', Country: 'Argentina', Phone: '(1) 135-5555', Fax: '(1) 135-4892' }, + { ID: 'CENTC', CompanyName: 'Centro comercial Moctezuma', ContactName: 'Francisco Chang', ContactTitle: 'Marketing Manager', Address: 'Sierras de Granada 9993', City: 'México D.F.', Region: null, PostalCode: '05022', Country: 'Mexico', Phone: '(5) 555-3392', Fax: '(5) 555-7293' }, + { ID: 'CHOPS', CompanyName: 'Chop-suey Chinese', ContactName: 'Yang Wang', ContactTitle: 'Owner', Address: 'Hauptstr. 29', City: 'Bern', Region: null, PostalCode: '3012', Country: 'Switzerland', Phone: '0452-076545', Fax: null }, + { ID: 'COMMI', CompanyName: 'Comércio Mineiro', ContactName: 'Pedro Afonso', ContactTitle: 'Sales Associate', Address: 'Av. dos Lusíadas, 23', City: 'Sao Paulo', Region: 'SP', PostalCode: '05432-043', Country: 'Brazil', Phone: '(11) 555-7647', Fax: null }, + { ID: 'CONSH', CompanyName: 'Consolidated Holdings', ContactName: 'Elizabeth Brown', ContactTitle: 'Sales Representative', Address: 'Berkeley Gardens 12 Brewery', City: 'London', Region: null, PostalCode: 'WX1 6LT', Country: 'UK', Phone: '(171) 555-2282', Fax: '(171) 555-9199' }, + { ID: 'DRACD', CompanyName: 'Drachenblut Delikatessen', ContactName: 'Sven Ottlieb', ContactTitle: 'Order Administrator', Address: 'Walserweg 21', City: 'Aachen', Region: null, PostalCode: '52066', Country: 'Germany', Phone: '0241-039123', Fax: '0241-059428' }, + { ID: 'DUMON', CompanyName: 'Du monde entier', ContactName: 'Janine Labrune', ContactTitle: 'Owner', Address: '67, rue des Cinquante Otages', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.67.88.88', Fax: '40.67.89.89' }, + { ID: 'EASTC', CompanyName: 'Eastern Connection', ContactName: 'Ann Devon', ContactTitle: 'Sales Agent', Address: '35 King George', City: 'London', Region: null, PostalCode: 'WX3 6FW', Country: 'UK', Phone: '(171) 555-0297', Fax: '(171) 555-3373' }, + { ID: 'ERNSH', CompanyName: 'Ernst Handel', ContactName: 'Roland Mendel', ContactTitle: 'Sales Manager', Address: 'Kirchgasse 6', City: 'Graz', Region: null, PostalCode: '8010', Country: 'Austria', Phone: '7675-3425', Fax: '7675-3426' }, + { ID: 'FAMIA', CompanyName: 'Familia Arquibaldo', ContactName: 'Aria Cruz', ContactTitle: 'Marketing Assistant', Address: 'Rua Orós, 92', City: 'Sao Paulo', Region: 'SP', PostalCode: '05442-030', Country: 'Brazil', Phone: '(11) 555-9857', Fax: null }, + { ID: 'FISSA', CompanyName: 'FISSA Fabrica Inter. Salchichas S.A.', ContactName: 'Diego Roel', ContactTitle: 'Accounting Manager', Address: 'C/ Moralzarzal, 86', City: 'Madrid', Region: null, PostalCode: '28034', Country: 'Spain', Phone: '(91) 555 94 44', Fax: '(91) 555 55 93' }, + { ID: 'FOLIG', CompanyName: 'Folies gourmandes', ContactName: 'Martine Rancé', ContactTitle: 'Assistant Sales Agent', Address: '184, chaussée de Tournai', City: 'Lille', Region: null, PostalCode: '59000', Country: 'France', Phone: '20.16.10.16', Fax: '20.16.10.17' }, + { ID: 'FOLKO', CompanyName: 'Folk och fä HB', ContactName: 'Maria Larsson', ContactTitle: 'Owner', Address: 'Åkergatan 24', City: 'Bräcke', Region: null, PostalCode: 'S-844 67', Country: 'Sweden', Phone: '0695-34 67 21', Fax: null }, + { ID: 'FRANK', CompanyName: 'Frankenversand', ContactName: 'Peter Franken', ContactTitle: 'Marketing Manager', Address: 'Berliner Platz 43', City: 'München', Region: null, PostalCode: '80805', Country: 'Germany', Phone: '089-0877310', Fax: '089-0877451' }, + { ID: 'FRANR', CompanyName: 'France restauration', ContactName: 'Carine Schmitt', ContactTitle: 'Marketing Manager', Address: '54, rue Royale', City: 'Nantes', Region: null, PostalCode: '44000', Country: 'France', Phone: '40.32.21.21', Fax: '40.32.21.20' }, + { ID: 'FRANS', CompanyName: 'Franchi S.p.A.', ContactName: 'Paolo Accorti', ContactTitle: 'Sales Representative', Address: 'Via Monte Bianco 34', City: 'Torino', Region: null, PostalCode: '10100', Country: 'Italy', Phone: '011-4988260', Fax: '011-4988261' } + ]; + this.hierarchicalData = this.generateDataUneven(100, 3); + + // treegrid cols and data + this.treeColumns = [ + { field: 'employeeID', label: 'ID', width: 200, resizable: true, dataType: 'number', hasSummary: false }, + { field: 'Salary', label: 'Salary', width: 200, resizable: true, dataType: 'number', hasSummary: true }, + { field: 'firstName', label: 'First Name', width: 300, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'lastName', label: 'Last Name', width: 150, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'Title', label: 'Title', width: 200, resizable: true, dataType: 'string', hasSummary: true } + ]; + this.treeData = [ + { Salary: 2500, employeeID: 0, PID: -1, firstName: 'Andrew', lastName: 'Fuller', Title: 'Vice President, Sales' }, + { Salary: 3500, employeeID: 1, PID: -1, firstName: 'Jonathan', lastName: 'Smith', Title: 'Human resources' }, + { Salary: 1500, employeeID: 2, PID: -1, firstName: 'Nancy', lastName: 'Davolio', Title: 'CFO' }, + { Salary: 2500, employeeID: 3, PID: -1, firstName: 'Steven', lastName: 'Buchanan', Title: 'CTO' }, + // sub of ID 0 + { Salary: 2500, employeeID: 4, PID: 0, firstName: 'Janet', lastName: 'Leverling', Title: 'Sales Manager' }, + { Salary: 3500, employeeID: 5, PID: 0, firstName: 'Laura', lastName: 'Callahan', Title: 'Inside Sales Coordinator' }, + { Salary: 1500, employeeID: 6, PID: 0, firstName: 'Margaret', lastName: 'Peacock', Title: 'Sales Representative' }, + { Salary: 2500, employeeID: 7, PID: 0, firstName: 'Michael', lastName: 'Suyama', Title: 'Sales Representative' }, + // sub of ID 4 + { Salary: 2500, employeeID: 8, PID: 4, firstName: 'Anne', lastName: 'Dodsworth', Title: 'Sales Representative' }, + { Salary: 3500, employeeID: 9, PID: 4, firstName: 'Danielle', lastName: 'Davis', Title: 'Sales Representative' }, + { Salary: 1500, employeeID: 10, PID: 4, firstName: 'Robert', lastName: 'King', Title: 'Sales Representative' }, + // sub of ID 2 + { Salary: 2500, employeeID: 11, PID: 2, firstName: 'Peter', lastName: 'Lewis', Title: 'Chief Accountant' }, + { Salary: 3500, employeeID: 12, PID: 2, firstName: 'Ryder', lastName: 'Zenaida', Title: 'Accountant' }, + { Salary: 1500, employeeID: 13, PID: 2, firstName: 'Wang', lastName: 'Mercedes', Title: 'Accountant' }, + // sub of ID 3 + { Salary: 1500, employeeID: 14, PID: 3, firstName: 'Theodore', lastName: 'Zia', Title: 'Software Architect' }, + { Salary: 4500, employeeID: 15, PID: 3, firstName: 'Lacota', lastName: 'Mufutau', Title: 'Product Manager' }, + // sub of ID 16 + { Salary: 2500, employeeID: 16, PID: 15, firstName: 'Jin', lastName: 'Elliott', Title: 'Product Owner' }, + { Salary: 3500, employeeID: 17, PID: 15, firstName: 'Armand', lastName: 'Ross', Title: 'Product Owner' }, + { Salary: 1500, employeeID: 18, PID: 15, firstName: 'Dane', lastName: 'Rodriquez', Title: 'Team Leader' }, + // sub of ID 19 + { Salary: 2500, employeeID: 19, PID: 18, firstName: 'Declan', lastName: 'Lester', Title: 'Senior Software Developer' }, + { Salary: 3500, employeeID: 20, PID: 18, firstName: 'Bernard', lastName: 'Jarvis', Title: 'Senior Software Developer' }, + { Salary: 1500, employeeID: 21, PID: 18, firstName: 'Jason', lastName: 'Clark', Title: 'QA' }, + { Salary: 1500, employeeID: 22, PID: 18, firstName: 'Mark', lastName: 'Young', Title: 'QA' }, + // sub of ID 20 + { Salary: 1500, employeeID: 23, PID: 20, firstName: 'Jeremy', lastName: 'Donaldson', Title: 'Software Developer' } + ]; + /* eslint-enable max-len */ + } + + public togglePinning(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, + byIndex: boolean, index: number, key: any) { + const row: RowType = byIndex ? grid.getRowByIndex(index) : grid.getRowByKey(key); + if (row.pinned) { + row.unpin(); + } else { + row.pin(); + } + } + + + public deleteRow(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { + const row = grid.getRowByIndex(index); + row.delete(); + } + + public toggle(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { + const row = grid.getRowByIndex(index); + row.expanded = !row.expanded; + } + + public select(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number) { + const row = grid.getRowByIndex(index); + row.selected = !row.selected; + } + + public selectChildren(grid: IgxGridComponent | IgxTreeGridComponent, index: number) { + const row = grid.getRowByIndex(index); + const children = row.children; + children.forEach(ch => { + ch.selected = !ch.selected; + }); + } + + public selectParent(grid: IgxGridComponent | IgxTreeGridComponent, index: number) { + const row = grid.getRowByIndex(index); + const parent = row.parent; + parent.selected = !parent.selected; + if (parent) { + parent.selected = !parent.selected; + } + } + + public generateDataUneven(count: number, level: number, parendID: string = null) { + const prods = []; + const currLevel = level; + let children; + for (let i = 0; i < count; i++) { + const rowID = parendID ? parendID + i : i.toString(); + if (level > 0) { + // Have child grids for row with even id less rows by not multiplying by 2 + children = this.generateDataUneven(((i % 2) + 1) * Math.round(count / 3), currLevel - 1, rowID); + } + prods.push({ + ID: rowID, + ChildLevels: currLevel, + ProductName: 'Product: A' + i, + Col1: i, + Col2: i, + Col3: i, + childData: children, + childData2: children, + hasChild: true + }); + } + return prods; + } + + public clearLog(logger: HTMLElement) { + const elements = logger.querySelectorAll('p'); + + elements.forEach(element => { + this.renderer.removeChild(logger, element); + }); + } + + public logState(grid: IgxGridComponent | IgxTreeGridComponent | IgxHierarchicalGridComponent, index: number, logger: HTMLElement) { + this.clearLog(logger); + const row = grid.getRowByIndex(index); + const state = ` + index: ${row.index}, + viewIndex: ${row.viewIndex}, + -----------------------------, + isSummaryRow: ${row.isSummaryRow}, + summaries: ${row.summaries}, + -----------------------------, + isGroupByRow: ${row.isGroupByRow}, + groupByRow: ${row.groupRow?.value}, + -----------------------------, + parent: ${row.parent}, + expanded: ${row.expanded}, + key: ${row.key}, + pinned: ${row.pinned}, + deleted: ${row.deleted}, + inEditMode: ${row.inEditMode}, + selected: ${row.selected}, + hasChildren: ${row.hasChildren}, + disabled: ${row.disabled}, + --------------------------------, + cells.length: ${row.cells?.length}`; + // firstCell: ${row.cells[0].value}, + // lastCell: ${row.cells[row.cells.length - 1].value}`; + + const states = state.split(','); + const createElem = this.renderer.createElement('p'); + + states.forEach(st => { + const text = this.renderer.createText(st); + this.renderer.appendChild(createElem, text); + this.renderer.appendChild(createElem, this.renderer.createElement('br')); + }); + + this.renderer.insertBefore(logger, createElem, logger.children[0]); + } + + public onRowDragEnd(args) { + args.animation = true; + } + + public onDropAllowed(args) { + let selected = false; + const ids = this.grid.selectedRows; + const selectedRowData = this.grid.data.filter((record) => ids.includes(record.ID)); + selectedRowData.forEach((rowData) => { + selected = true; + this.targetGrid.addRow(rowData); + this.grid.deleteRow(rowData.ID); + }); + if (selected === false) { + this.targetGrid.addRow(args.dragData.data); + // this.grid.deleteRow(args.dragData.key); + } + } + + public onEnter() { + this.dragIcon = 'add'; + } + public onRowDragStart() { + const count = this.grid.selectedRows.length || 1; + this.countIcon = `filter_${count > 9 ? '9_plus' : `${count}`}`; + } + public onLeave() { + this.onRowDragStart(); + this.dragIcon = 'arrow_right_alt'; + } +} diff --git a/src/app/grid-row-edit/grid-row-edit-sample.component.html b/src/app/grid-row-edit/grid-row-edit-sample.component.html index d4f1a3a0dbd..4142d0b4737 100644 --- a/src/app/grid-row-edit/grid-row-edit-sample.component.html +++ b/src/app/grid-row-edit/grid-row-edit-sample.component.html @@ -41,7 +41,7 @@

Grid with rowEditing and with transactions

Row
@@ -102,8 +102,8 @@

Cancel Grid Edit Events

- - + + diff --git a/src/app/grid-updates-test/aminoData.ts b/src/app/grid-updates-test/aminoData.ts index ece38ccb18e..478adcbb7cf 100644 --- a/src/app/grid-updates-test/aminoData.ts +++ b/src/app/grid-updates-test/aminoData.ts @@ -1,552 +1,552 @@ -export const AMINO_DATA = [ - { - id: 1, - name: 'Alanine', - abbreviation: { - short: 'A', - long: 'Ala', - owner: { - name: 'User' - } - }, - weight: { - molecular: 89.1, - residue: 71.08 - }, - formula: { - molecular: 'C3H7NO2', - residue: 'C3H5NO' - }, - p: { - Ka: 2.34, - Kb: 9.69, - Kx: null, - l: 6.0 - } - }, - { - id: 2, - name: 'Arginine', - abbreviation: { - short: 'R', - long: 'Arg', - owner: { - name: 'User' - } - }, - weight: { - molecular: 174.2, - residue: 156.19 - }, - formula: { - molecular: 'C6H14N4O2', - residue: 'C6H12N4O' - }, - p: { - Ka: 2.17, - Kb: 9.04, - Kx: 12.48, - l: 10.76 - } - }, - { - id: 3, - name: 'Asparagine', - abbreviation: { - short: 'N', - long: 'Asn', - owner: { - name: 'User' - } - }, - weight: { - molecular: 132.12, - residue: 114.11 - }, - formula: { - molecular: 'C4H8N2O3', - residue: 'C4H6N2O2' - }, - p: { - Ka: 2.02, - Kb: 8.8, - Kx: null, - l: 5.41 - } - }, - { - id: 4, - name: 'Aspartic acid', - abbreviation: { - short: 'D', - long: 'Asp', - owner: { - name: 'User' - } - }, - weight: { - molecular: 133.11, - residue: 115.09 - }, - formula: { - molecular: 'C4H7NO4', - residue: 'C4H5NO3' - }, - p: { - Ka: 1.88, - Kb: 9.6, - Kx: 3.65, - l: 2.77 - } - }, - { - id: 5, - name: 'Cysteine', - abbreviation: { - short: 'C', - long: 'Cys', - owner: { - name: 'User' - } - }, - weight: { - molecular: 121.16, - residue: 103.15 - }, - formula: { - molecular: 'C3H7NO2S', - residue: 'C3H5NOS' - }, - p: { - Ka: 1.96, - Kb: 10.28, - Kx: 8.18, - l: 5.07 - } - }, - { - id: 6, - name: 'Glutamic acid', - abbreviation: { - short: 'E', - long: 'Glu', - owner: { - name: 'User' - } - }, - weight: { - molecular: 147.13, - residue: 129.12 - }, - formula: { - molecular: 'C5H9NO4', - residue: 'C5H7NO3' - }, - p: { - Ka: 2.19, - Kb: 9.67, - Kx: 4.25, - l: 3.22 - } - }, - { - id: 7, - name: 'Glutamine', - abbreviation: { - short: 'Q', - long: 'Gln', - owner: { - name: 'User' - } - }, - weight: { - molecular: 146.15, - residue: 128.13 - }, - formula: { - molecular: 'C5H10N2O3', - residue: 'C5H8N2O2' - }, - p: { - Ka: 2.17, - Kb: 9.13, - Kx: null, - l: 5.65 - } - }, - { - id: 8, - name: 'Glycine', - abbreviation: { - short: 'G', - long: 'Gly', - owner: { - name: 'User' - } - }, - weight: { - molecular: 75.07, - residue: 57.05 - }, - formula: { - molecular: 'C2H5NO2', - residue: 'C2H3NO' - }, - p: { - Ka: 2.34, - Kb: 9.6, - Kx: null, - l: 5.97 - } - }, - { - id: 9, - name: 'Histidine', - abbreviation: { - short: 'H', - long: 'His', - owner: { - name: 'User' - } - }, - weight: { - molecular: 155.16, - residue: 137.14 - }, - formula: { - molecular: 'C6H9N3O2', - residue: 'C6H7N3O' - }, - p: { - Ka: 1.82, - Kb: 9.17, - Kx: 6.0, - l: 7.59 - } - }, - { - id: 10, - name: 'Hydroxyproline', - abbreviation: { - short: 'O', - long: 'Hyp', - owner: { - name: 'User' - } - }, - weight: { - molecular: 131.13, - residue: 113.11 - }, - formula: { - molecular: 'C5H9NO3', - residue: 'C5H7NO2' - }, - p: { - Ka: 1.82, - Kb: 9.65, - Kx: null, - l: null - } - }, - { - id: 11, - name: 'Isoleucine', - abbreviation: { - short: 'I', - long: 'Ile', - owner: { - name: 'User' - } - }, - weight: { - molecular: 131.18, - residue: 113.16 - }, - formula: { - molecular: 'C6H13NO2', - residue: 'C6H11NO' - }, - p: { - Ka: 2.36, - Kb: 9.6, - Kx: null, - l: 6.02 - } - }, - { - id: 12, - name: 'Leucine', - abbreviation: { - short: 'L', - long: 'Leu', - owner: { - name: 'User' - } - }, - weight: { - molecular: 131.18, - residue: 113.16 - }, - formula: { - molecular: 'C6H13NO2', - residue: 'C6H11NO' - }, - p: { - Ka: 2.36, - Kb: 9.6, - Kx: null, - l: 5.98 - } - }, - { - id: 13, - name: 'Lysine', - abbreviation: { - short: 'K', - long: 'Lys', - owner: { - name: 'User' - } - }, - weight: { - molecular: 146.19, - residue: 128.18 - }, - formula: { - molecular: 'C6H14N2O2', - residue: 'C6H12N2O' - }, - p: { - Ka: 2.18, - Kb: 8.95, - Kx: 10.53, - l: 9.74 - } - }, - { - id: 14, - name: 'Methionine', - abbreviation: { - short: 'M', - long: 'Met', - owner: { - name: 'User' - } - }, - weight: { - molecular: 149.21, - residue: 131.2 - }, - formula: { - molecular: 'C5H11NO2S', - residue: 'C5H9NOS' - }, - p: { - Ka: 2.28, - Kb: 9.21, - Kx: null, - l: 5.74 - } - }, - { - id: 15, - name: 'Phenylalanine', - abbreviation: { - short: 'F', - long: 'Phe', - owner: { - name: 'User' - } - }, - weight: { - molecular: 165.19, - residue: 147.18 - }, - formula: { - molecular: 'C9H11NO2', - residue: 'C9H9NO' - }, - p: { - Ka: 1.83, - Kb: 9.13, - Kx: null, - l: 5.48 - } - }, - { - id: 16, - name: 'Proline', - abbreviation: { - short: 'P', - long: 'Pro', - owner: { - name: 'User' - } - }, - weight: { - molecular: 115.13, - residue: 97.12 - }, - formula: { - molecular: 'C5H9NO2', - residue: 'C5H7NO' - }, - p: { - Ka: 1.99, - Kb: 10.6, - Kx: null, - l: 6.3 - } - }, - { - id: 17, - name: 'Pyroglutamatic', - abbreviation: { - short: 'U', - long: 'Glp', - owner: { - name: 'User' - } - }, - weight: { - molecular: 139.11, - residue: 121.09 - }, - formula: { - molecular: 'C5H7NO3', - residue: 'C5H5NO2' - }, - p: { - Ka: null, - Kb: null, - Kx: null, - l: 5.68 - } - }, - { - id: 18, - name: 'Serine', - abbreviation: { - short: 'S', - long: 'Ser', - owner: { - name: 'User' - } - }, - weight: { - molecular: 105.09, - residue: 87.08 - }, - formula: { - molecular: 'C3H7NO3', - residue: 'C3H5NO2' - }, - p: { - Ka: 2.21, - Kb: 9.15, - Kx: null, - l: 5.68 - } - }, - { - id: 19, - name: 'Threonine', - abbreviation: { - short: 'T', - long: 'Thr', - owner: { - name: 'User' - } - }, - weight: { - molecular: 119.12, - residue: 101.11 - }, - formula: { - molecular: 'C4H9NO3', - residue: 'C4H7NO2' - }, - p: { - Ka: 2.09, - Kb: 9.1, - Kx: null, - l: 5.6 - } - }, - { - id: 20, - name: 'Tryptophan', - abbreviation: { - short: 'W', - long: 'Trp', - owner: { - name: 'User' - } - }, - weight: { - molecular: 204.23, - residue: 186.22 - }, - formula: { - molecular: 'C11H12N2O2', - residue: 'C11H10N2O' - }, - p: { - Ka: 2.83, - Kb: 9.39, - Kx: null, - l: 5.89 - } - }, - { - id: 21, - name: 'Tyrosine', - abbreviation: { - short: 'Y', - long: 'Tyr', - owner: { - name: 'User' - } - }, - weight: { - molecular: 181.19, - residue: 163.18 - }, - formula: { - molecular: 'C9H11NO3', - residue: 'C9H9NO2' - }, - p: { - Ka: 2.2, - Kb: 9.11, - Kx: 10.07, - l: 5.66 - } - }, - { - id: 22, - name: 'Valine', - abbreviation: { - short: 'V', - long: 'Val', - owner: { - name: 'User' - } - }, - weight: { - molecular: 117.15, - residue: 99.13 - }, - formula: { - molecular: 'C5H11NO2', - residue: 'C5H9NO' - }, - p: { - Ka: 2.32, - Kb: 9.62, - Kx: null, - l: 5.96 - } - } - ]; +export const AMINO_DATA = [ + { + id: 1, + name: 'Alanine', + abbreviation: { + short: 'A', + long: 'Ala', + owner: { + name: 'User' + } + }, + weight: { + molecular: 89.1, + residue: 71.08 + }, + formula: { + molecular: 'C3H7NO2', + residue: 'C3H5NO' + }, + p: { + Ka: 2.34, + Kb: 9.69, + Kx: null, + l: 6.0 + } + }, + { + id: 2, + name: 'Arginine', + abbreviation: { + short: 'R', + long: 'Arg', + owner: { + name: 'User' + } + }, + weight: { + molecular: 174.2, + residue: 156.19 + }, + formula: { + molecular: 'C6H14N4O2', + residue: 'C6H12N4O' + }, + p: { + Ka: 2.17, + Kb: 9.04, + Kx: 12.48, + l: 10.76 + } + }, + { + id: 3, + name: 'Asparagine', + abbreviation: { + short: 'N', + long: 'Asn', + owner: { + name: 'User' + } + }, + weight: { + molecular: 132.12, + residue: 114.11 + }, + formula: { + molecular: 'C4H8N2O3', + residue: 'C4H6N2O2' + }, + p: { + Ka: 2.02, + Kb: 8.8, + Kx: null, + l: 5.41 + } + }, + { + id: 4, + name: 'Aspartic acid', + abbreviation: { + short: 'D', + long: 'Asp', + owner: { + name: 'User' + } + }, + weight: { + molecular: 133.11, + residue: 115.09 + }, + formula: { + molecular: 'C4H7NO4', + residue: 'C4H5NO3' + }, + p: { + Ka: 1.88, + Kb: 9.6, + Kx: 3.65, + l: 2.77 + } + }, + { + id: 5, + name: 'Cysteine', + abbreviation: { + short: 'C', + long: 'Cys', + owner: { + name: 'User' + } + }, + weight: { + molecular: 121.16, + residue: 103.15 + }, + formula: { + molecular: 'C3H7NO2S', + residue: 'C3H5NOS' + }, + p: { + Ka: 1.96, + Kb: 10.28, + Kx: 8.18, + l: 5.07 + } + }, + { + id: 6, + name: 'Glutamic acid', + abbreviation: { + short: 'E', + long: 'Glu', + owner: { + name: 'User' + } + }, + weight: { + molecular: 147.13, + residue: 129.12 + }, + formula: { + molecular: 'C5H9NO4', + residue: 'C5H7NO3' + }, + p: { + Ka: 2.19, + Kb: 9.67, + Kx: 4.25, + l: 3.22 + } + }, + { + id: 7, + name: 'Glutamine', + abbreviation: { + short: 'Q', + long: 'Gln', + owner: { + name: 'User' + } + }, + weight: { + molecular: 146.15, + residue: 128.13 + }, + formula: { + molecular: 'C5H10N2O3', + residue: 'C5H8N2O2' + }, + p: { + Ka: 2.17, + Kb: 9.13, + Kx: null, + l: 5.65 + } + }, + { + id: 8, + name: 'Glycine', + abbreviation: { + short: 'G', + long: 'Gly', + owner: { + name: 'User' + } + }, + weight: { + molecular: 75.07, + residue: 57.05 + }, + formula: { + molecular: 'C2H5NO2', + residue: 'C2H3NO' + }, + p: { + Ka: 2.34, + Kb: 9.6, + Kx: null, + l: 5.97 + } + }, + { + id: 9, + name: 'Histidine', + abbreviation: { + short: 'H', + long: 'His', + owner: { + name: 'User' + } + }, + weight: { + molecular: 155.16, + residue: 137.14 + }, + formula: { + molecular: 'C6H9N3O2', + residue: 'C6H7N3O' + }, + p: { + Ka: 1.82, + Kb: 9.17, + Kx: 6.0, + l: 7.59 + } + }, + { + id: 10, + name: 'Hydroxyproline', + abbreviation: { + short: 'O', + long: 'Hyp', + owner: { + name: 'User' + } + }, + weight: { + molecular: 131.13, + residue: 113.11 + }, + formula: { + molecular: 'C5H9NO3', + residue: 'C5H7NO2' + }, + p: { + Ka: 1.82, + Kb: 9.65, + Kx: null, + l: null + } + }, + { + id: 11, + name: 'Isoleucine', + abbreviation: { + short: 'I', + long: 'Ile', + owner: { + name: 'User' + } + }, + weight: { + molecular: 131.18, + residue: 113.16 + }, + formula: { + molecular: 'C6H13NO2', + residue: 'C6H11NO' + }, + p: { + Ka: 2.36, + Kb: 9.6, + Kx: null, + l: 6.02 + } + }, + { + id: 12, + name: 'Leucine', + abbreviation: { + short: 'L', + long: 'Leu', + owner: { + name: 'User' + } + }, + weight: { + molecular: 131.18, + residue: 113.16 + }, + formula: { + molecular: 'C6H13NO2', + residue: 'C6H11NO' + }, + p: { + Ka: 2.36, + Kb: 9.6, + Kx: null, + l: 5.98 + } + }, + { + id: 13, + name: 'Lysine', + abbreviation: { + short: 'K', + long: 'Lys', + owner: { + name: 'User' + } + }, + weight: { + molecular: 146.19, + residue: 128.18 + }, + formula: { + molecular: 'C6H14N2O2', + residue: 'C6H12N2O' + }, + p: { + Ka: 2.18, + Kb: 8.95, + Kx: 10.53, + l: 9.74 + } + }, + { + id: 14, + name: 'Methionine', + abbreviation: { + short: 'M', + long: 'Met', + owner: { + name: 'User' + } + }, + weight: { + molecular: 149.21, + residue: 131.2 + }, + formula: { + molecular: 'C5H11NO2S', + residue: 'C5H9NOS' + }, + p: { + Ka: 2.28, + Kb: 9.21, + Kx: null, + l: 5.74 + } + }, + { + id: 15, + name: 'Phenylalanine', + abbreviation: { + short: 'F', + long: 'Phe', + owner: { + name: 'User' + } + }, + weight: { + molecular: 165.19, + residue: 147.18 + }, + formula: { + molecular: 'C9H11NO2', + residue: 'C9H9NO' + }, + p: { + Ka: 1.83, + Kb: 9.13, + Kx: null, + l: 5.48 + } + }, + { + id: 16, + name: 'Proline', + abbreviation: { + short: 'P', + long: 'Pro', + owner: { + name: 'User' + } + }, + weight: { + molecular: 115.13, + residue: 97.12 + }, + formula: { + molecular: 'C5H9NO2', + residue: 'C5H7NO' + }, + p: { + Ka: 1.99, + Kb: 10.6, + Kx: null, + l: 6.3 + } + }, + { + id: 17, + name: 'Pyroglutamatic', + abbreviation: { + short: 'U', + long: 'Glp', + owner: { + name: 'User' + } + }, + weight: { + molecular: 139.11, + residue: 121.09 + }, + formula: { + molecular: 'C5H7NO3', + residue: 'C5H5NO2' + }, + p: { + Ka: null, + Kb: null, + Kx: null, + l: 5.68 + } + }, + { + id: 18, + name: 'Serine', + abbreviation: { + short: 'S', + long: 'Ser', + owner: { + name: 'User' + } + }, + weight: { + molecular: 105.09, + residue: 87.08 + }, + formula: { + molecular: 'C3H7NO3', + residue: 'C3H5NO2' + }, + p: { + Ka: 2.21, + Kb: 9.15, + Kx: null, + l: 5.68 + } + }, + { + id: 19, + name: 'Threonine', + abbreviation: { + short: 'T', + long: 'Thr', + owner: { + name: 'User' + } + }, + weight: { + molecular: 119.12, + residue: 101.11 + }, + formula: { + molecular: 'C4H9NO3', + residue: 'C4H7NO2' + }, + p: { + Ka: 2.09, + Kb: 9.1, + Kx: null, + l: 5.6 + } + }, + { + id: 20, + name: 'Tryptophan', + abbreviation: { + short: 'W', + long: 'Trp', + owner: { + name: 'User' + } + }, + weight: { + molecular: 204.23, + residue: 186.22 + }, + formula: { + molecular: 'C11H12N2O2', + residue: 'C11H10N2O' + }, + p: { + Ka: 2.83, + Kb: 9.39, + Kx: null, + l: 5.89 + } + }, + { + id: 21, + name: 'Tyrosine', + abbreviation: { + short: 'Y', + long: 'Tyr', + owner: { + name: 'User' + } + }, + weight: { + molecular: 181.19, + residue: 163.18 + }, + formula: { + molecular: 'C9H11NO3', + residue: 'C9H9NO2' + }, + p: { + Ka: 2.2, + Kb: 9.11, + Kx: 10.07, + l: 5.66 + } + }, + { + id: 22, + name: 'Valine', + abbreviation: { + short: 'V', + long: 'Val', + owner: { + name: 'User' + } + }, + weight: { + molecular: 117.15, + residue: 99.13 + }, + formula: { + molecular: 'C5H11NO2', + residue: 'C5H9NO' + }, + p: { + Ka: 2.32, + Kb: 9.62, + Kx: null, + l: 5.96 + } + } + ]; diff --git a/src/app/grid-updates-test/grid-updates.component.html b/src/app/grid-updates-test/grid-updates.component.html index 0ebfed7848a..4e3a9e15c35 100644 --- a/src/app/grid-updates-test/grid-updates.component.html +++ b/src/app/grid-updates-test/grid-updates.component.html @@ -1,24 +1,24 @@ - - - - - - - - -

Selected Name: {{selectedItem?.name}}

-

Selected Abbr. (long): {{selectedItem?.abbreviation.long}}

-

Selected Abbr. (owner name): {{selectedItem?.abbreviation.owner.name}}

+ + + + + + + + +

Selected Name: {{selectedItem?.name}}

+

Selected Abbr. (long): {{selectedItem?.abbreviation.long}}

+

Selected Abbr. (owner name): {{selectedItem?.abbreviation.owner.name}}

diff --git a/src/app/grid-updates-test/grid-updates.component.ts b/src/app/grid-updates-test/grid-updates.component.ts index e144eb665ef..877ac7f15ed 100644 --- a/src/app/grid-updates-test/grid-updates.component.ts +++ b/src/app/grid-updates-test/grid-updates.component.ts @@ -1,86 +1,86 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { IRowSelectionEventArgs } from 'igniteui-angular'; -import { IgxGridComponent } from 'projects/igniteui-angular/src/lib/grids/grid/grid.component'; -import { AMINO_DATA } from './aminoData'; - -@Component({ - selector: 'app-grid-updates', - styleUrls: ['./grid-updates.component.scss'], - templateUrl: './grid-updates.component.html' -}) -export class GridUpdatesComponent implements OnInit { - @ViewChild('grid') public grid: IgxGridComponent; - public data = [...AMINO_DATA]; - - public nestedConfigColumns = [ - { field: 'name', header: 'Name' }, - { field: 'abbreviation.long', header: 'Abbr. (long)' }, - { field: 'abbreviation.owner.name', header: 'Abbr. (owner name)' } - ]; - - public counter = 0; - public selectedItem?: any; - private _update; - private itemUpdater: ItemUpdater; - - public ngOnInit(): void { - this.itemUpdater = new ItemUpdater(this.data[0], item => - this.onItemUpdate(item) - ); - } - - public update(): void { - this.itemUpdater.update(); - } - - public stop(): void { - this.itemUpdater.stop(); - } - - public handleRowSelectionChange(args: IRowSelectionEventArgs): void { - if (!args.newSelection || args.newSelection.length === 0) { - this.selectedItem = undefined; - } else { - const selectedItemId = args.newSelection[0] as number; - this.selectedItem = this.data.find(d => d.id === selectedItemId); - - // Fix: Make a copy - //this.selectedItem = this.originalItems.find(d => d === selectedItemId); - } - } - - private onItemUpdate(item: any) { - // update the data record reference - const itemIndex = this.data.findIndex(d => d.id === item.id); - this.data[itemIndex] = { ...item }; - - // Needed because of OnPush strategy - this.grid.markForCheck(); - } -} - -class ItemUpdater { - public counter = 0; - private _update; - - constructor(private item: any, private cb: (i: any) => void) {} - - public update(): void { - this._update = setInterval(() => this.updateData(this.counter), 100); - } - - public stop(): void { - clearInterval(this._update); - } - - public updateData(counter: number): void { - this.counter++; - - // update - this.item.name = `Alanine ${counter}`; - this.item.abbreviation.long = `Ala ${counter}`; - this.item.abbreviation.owner.name = `User ${counter}`; - - this.cb(this.item); - } -} +import { Component, OnInit, ViewChild } from '@angular/core'; +import { IRowSelectionEventArgs } from 'igniteui-angular'; +import { IgxGridComponent } from 'projects/igniteui-angular/src/lib/grids/grid/grid.component'; +import { AMINO_DATA } from './aminoData'; + +@Component({ + selector: 'app-grid-updates', + styleUrls: ['./grid-updates.component.scss'], + templateUrl: './grid-updates.component.html' +}) +export class GridUpdatesComponent implements OnInit { + @ViewChild('grid') public grid: IgxGridComponent; + public data = [...AMINO_DATA]; + + public nestedConfigColumns = [ + { field: 'name', header: 'Name' }, + { field: 'abbreviation.long', header: 'Abbr. (long)' }, + { field: 'abbreviation.owner.name', header: 'Abbr. (owner name)' } + ]; + + public counter = 0; + public selectedItem?: any; + private _update; + private itemUpdater: ItemUpdater; + + public ngOnInit(): void { + this.itemUpdater = new ItemUpdater(this.data[0], item => + this.onItemUpdate(item) + ); + } + + public update(): void { + this.itemUpdater.update(); + } + + public stop(): void { + this.itemUpdater.stop(); + } + + public handleRowSelectionChange(args: IRowSelectionEventArgs): void { + if (!args.newSelection || args.newSelection.length === 0) { + this.selectedItem = undefined; + } else { + const selectedItemId = args.newSelection[0] as number; + this.selectedItem = this.data.find(d => d.id === selectedItemId); + + // Fix: Make a copy + //this.selectedItem = this.originalItems.find(d => d === selectedItemId); + } + } + + private onItemUpdate(item: any) { + // update the data record reference + const itemIndex = this.data.findIndex(d => d.id === item.id); + this.data[itemIndex] = { ...item }; + + // Needed because of OnPush strategy + this.grid.markForCheck(); + } +} + +class ItemUpdater { + public counter = 0; + private _update; + + constructor(private item: any, private cb: (i: any) => void) {} + + public update(): void { + this._update = setInterval(() => this.updateData(this.counter), 100); + } + + public stop(): void { + clearInterval(this._update); + } + + public updateData(counter: number): void { + this.counter++; + + // update + this.item.name = `Alanine ${counter}`; + this.item.abbreviation.long = `Ala ${counter}`; + this.item.abbreviation.owner.name = `User ${counter}`; + + this.cb(this.item); + } +} diff --git a/src/app/overlay/overlay.sample.html b/src/app/overlay/overlay.sample.html index 8a61cb89f69..f5f79803da8 100644 --- a/src/app/overlay/overlay.sample.html +++ b/src/app/overlay/overlay.sample.html @@ -61,6 +61,10 @@ Open in outlet
+
+ Has animation +
diff --git a/src/app/overlay/overlay.sample.ts b/src/app/overlay/overlay.sample.ts index 92a323848bb..a2e0d1b0474 100644 --- a/src/app/overlay/overlay.sample.ts +++ b/src/app/overlay/overlay.sample.ts @@ -49,6 +49,7 @@ export class OverlaySampleComponent implements OnInit { public closeOnOutsideClick = true; public modal = true; public useOutlet = false; + public hasAnimation = true; public animationLength = 300; // in ms private xAddition = 0; @@ -347,6 +348,10 @@ export class OverlaySampleComponent implements OnInit { = `${this.animationLength}ms`; (this._overlaySettings.positionStrategy.settings.closeAnimation.options.params as IAnimationParams).duration = `${this.animationLength}ms`; + if (!this.hasAnimation) { + this._overlaySettings.positionStrategy.settings.openAnimation = null; + this._overlaySettings.positionStrategy.settings.closeAnimation = null; + } } this.igxDropDown.toggle(this._overlaySettings); } diff --git a/src/app/pageHeading/pageHeading.styles.scss b/src/app/pageHeading/pageHeading.styles.scss index d1905df8f93..4d6e3859a90 100644 --- a/src/app/pageHeading/pageHeading.styles.scss +++ b/src/app/pageHeading/pageHeading.styles.scss @@ -11,6 +11,7 @@ :host ::ng-deep { .hamburger { + color: var(--igc-surface-500-contrast); margin-right: rem(16px); [dir='rtl'] & { diff --git a/src/web.config b/src/web.config index 30edc0f7ba0..5d230644d8a 100644 --- a/src/web.config +++ b/src/web.config @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file