diff --git a/apps/docs/src/app/components/stackblitz/stackblitz-writer.ts b/apps/docs/src/app/components/stackblitz/stackblitz-writer.ts index 51d2998d2..2468e43cd 100644 --- a/apps/docs/src/app/components/stackblitz/stackblitz-writer.ts +++ b/apps/docs/src/app/components/stackblitz/stackblitz-writer.ts @@ -33,7 +33,7 @@ const OPTIONAL_PACKAGE_JSON_DEPENDENCIES = { 'highlight.js': '^11.11.1' }, '@koobiq/ag-grid-angular-theme': { - '@koobiq/ag-grid-angular-theme': '^34.3.1', + '@koobiq/ag-grid-angular-theme': '^34', 'ag-grid-angular': '^34', 'ag-grid-community': '^34' } diff --git a/docs/data-grid/ag-grid/ag-grid.en.md b/docs/data-grid/ag-grid/ag-grid.en.md index 35428b73f..514b908cb 100644 --- a/docs/data-grid/ag-grid/ag-grid.en.md +++ b/docs/data-grid/ag-grid/ag-grid.en.md @@ -72,29 +72,66 @@ Directives save and restore grid state across page reloads. `LocalStorageStore` is used by default. Switch to `QueryParamsStore` if needed. -| Directive | Saves | -| ------------------------------ | ----------------------------------------- | -| `kbqAgGridColumnState` | Sort, column order, visibility, and width | -| `kbqAgGridFilterState` | Column filter models | -| `kbqAgGridQuickFilterState` | Quick filter value | -| `kbqAgGridExternalFilterState` | External filter value | - #### Columns +The `kbqAgGridColumnState` directive saves sort order, column order, visibility, and width. Add it with a unique key and connect the store provider via `kbqAgGridColumnStateStoreProvider`. + #### Filters +The `kbqAgGridFilterState` directive saves column filter models. Add it with a unique key and connect the store provider via `kbqAgGridFilterStateStoreProvider`. + #### Quick filter +The `kbqAgGridQuickFilterState` directive saves the quick filter value. Add it with a unique key, connect the store provider via `kbqAgGridQuickFilterStateStoreProvider`, and bind the input value via `[(kbqAgGridQuickFilterStateValue)]`. + #### External filter +The `kbqAgGridExternalFilterState` directive saves the external filter value. Add it with a unique key, connect the store provider via `kbqAgGridExternalFilterStateStoreProvider`, bind the value via `[(kbqAgGridExternalFilterStateValue)]`, and pass the row check function via `kbqAgGridExternalFilterStatePass`. + +### Column menu + +The `kbqAgGridColumnMenu` directive adds a column management button in the top-right corner of the grid. The panel allows toggling column visibility, reordering columns via drag-and-drop, and pinning them to the left or right. + +Russian labels are used by default. To switch the language, provide a labels provider: + +```ts +providers: [kbqAgGridColumnMenuLabelsProvider(KBQ_AG_GRID_COLUMN_MENU_LABELS_EN)]; +``` + +To prevent a specific column from being hidden, set `lockVisible: true` in its `ColDef`. + + + +### Loading overlay + +The `kbqAgGridLoadingOverlay` directive controls the grid loading state: when the value is `true`, a skeleton overlay is shown on top of the rows. The number of skeleton rows and columns is configured via `kbqAgGridLoadingOverlayConfigProvider`. + + + +### Skeleton cell renderer + +`KbqAgGridSkeletonCellRenderer` is used together with the infinite row model (`rowModelType="infinite"`). While a data block is not yet loaded (`params.data === undefined`), cells display skeleton placeholders. + + + +### Infinite selection + +The `kbqAgGridInfiniteSelection` directive implements inverse selection for the infinite row model: the state is stored as `KbqAgGridInfiniteSelectionState` — equivalent to `WHERE id NOT IN (excludedIds)`. This is convenient for passing to the backend without loading all rows. + +Instead of `datasource`, use `kbqAgGridInfiniteSelectionDatasource` — the directive wraps the data source to automatically select rows as new blocks load. Also make sure to specify `getRowId` for stable row identification. + +Ctrl + A selects all rows; pressing it again when all rows are already selected does nothing. + + + ### Custom keyboard shortcuts You can add custom keyboard shortcuts by adding the appropriate directives to your `` component. @@ -106,6 +143,7 @@ You can add custom keyboard shortcuts by adding the appropriate directives to yo | Ctrl + click | Select a row | `kbqAgGridSelectRowsByCtrlClick` | | Ctrl + C | Copy selected rows | `kbqAgGridCopyByCtrlC` | | Shift + click | Select a range of rows | `kbqAgGridSelectRowsByShiftClick` | +| Ctrl + A | Select all rows | `kbqAgGridInfiniteSelection` | More information about keyboard shortcuts can be found in the [ag-grid-angular documentation](https://www.ag-grid.com/angular-data-grid/keyboard-navigation/). diff --git a/docs/data-grid/ag-grid/ag-grid.ru.md b/docs/data-grid/ag-grid/ag-grid.ru.md index 3883a408c..76c8cfcd5 100644 --- a/docs/data-grid/ag-grid/ag-grid.ru.md +++ b/docs/data-grid/ag-grid/ag-grid.ru.md @@ -72,31 +72,66 @@ import { AgGridModule } from 'ag-grid-angular'; По умолчанию используется `LocalStorageStore`. При необходимости можно переключиться на `QueryParamsStore`. -| Директива | Что сохраняет | -| ------------------------------ | --------------------------------------- | -| `kbqAgGridColumnState` | Сортировку, порядок, видимость и ширину | -| `kbqAgGridFilterState` | Модели фильтров колонок | -| `kbqAgGridQuickFilterState` | Значение быстрого фильтра | -| `kbqAgGridExternalFilterState` | Значение внешнего фильтра | - -Ниже примеры для каждого типа состояния. - #### Колонки +Директива `kbqAgGridColumnState` сохраняет сортировку, порядок, видимость и ширину колонок. Добавьте её с уникальным ключом и подключите провайдер хранилища через `kbqAgGridColumnStateStoreProvider`. + #### Фильтры +Директива `kbqAgGridFilterState` сохраняет модели фильтров колонок. Добавьте её с уникальным ключом и подключите провайдер хранилища через `kbqAgGridFilterStateStoreProvider`. + #### Быстрый фильтр +Директива `kbqAgGridQuickFilterState` сохраняет значение быстрого фильтра. Добавьте её с уникальным ключом, подключите провайдер хранилища через `kbqAgGridQuickFilterStateStoreProvider` и привяжите значение поля ввода через `[(kbqAgGridQuickFilterStateValue)]`. + #### Внешний фильтр +Директива `kbqAgGridExternalFilterState` сохраняет значение внешнего фильтра. Добавьте её с уникальным ключом, подключите провайдер хранилища через `kbqAgGridExternalFilterStateStoreProvider`, привяжите значение через `[(kbqAgGridExternalFilterStateValue)]` и передайте функцию проверки строк через `kbqAgGridExternalFilterStatePass`. + +### Меню колонок + +Директива `kbqAgGridColumnMenu` добавляет кнопку управления колонками в правом верхнем углу таблицы. Панель позволяет переключать видимость колонок, изменять их порядок перетаскиванием и закреплять слева или справа. + +По умолчанию используются русские подписи. Для смены языка подключите провайдер: + +```ts +providers: [kbqAgGridColumnMenuLabelsProvider(KBQ_AG_GRID_COLUMN_MENU_LABELS_EN)]; +``` + +Чтобы запретить скрытие конкретной колонки, добавьте `lockVisible: true` в её `ColDef`. + + + +### Оверлей загрузки + +Директива `kbqAgGridLoadingOverlay` управляет состоянием загрузки таблицы: когда значение `true`, поверх строк отображается скелетон-оверлей. Число строк и колонок скелетона задаётся через `kbqAgGridLoadingOverlayConfigProvider`. + + + +### Скелетон в ячейках + +`KbqAgGridSkeletonCellRenderer` используется совместно с моделью бесконечной прокрутки (`rowModelType="infinite"`). Пока блок данных не загружен (`params.data === undefined`), ячейки отображают скелетон-заглушки. + + + +### Бесконечная выборка + +Директива `kbqAgGridInfiniteSelection` реализует инвертированную выборку для модели бесконечной прокрутки: состояние хранится как `KbqAgGridInfiniteSelectionState` — эквивалент `WHERE id NOT IN (excludedIds)`. Это удобно для передачи на backend без загрузки всех строк. + +Вместо `datasource` используйте `kbqAgGridInfiniteSelectionDatasource` — директива оборачивает источник данных, чтобы автоматически выделять строки при загрузке новых блоков. Также обязательно укажите `getRowId` для стабильной идентификации строк. + +Комбинация клавиш Ctrl + A выделяет все строки, повторное нажатие, когда все строки уже выделены, не снимает выделение. + + + ### Пользовательские сочетания клавиш Вы можете добавить пользовательские сочетания клавиш, добавив соответствующие директивы к вашему компоненту ``. @@ -108,6 +143,7 @@ import { AgGridModule } from 'ag-grid-angular'; | Ctrl + click | Выделить строку | `kbqAgGridSelectRowsByCtrlClick` | | Ctrl + C | Копировать выделенные строки | `kbqAgGridCopyByCtrlC` | | Shift + click | Выделить диапазон строк | `kbqAgGridSelectRowsByShiftClick` | +| Ctrl + A | Выделить все строки | `kbqAgGridInfiniteSelection` | Больше информации о сочетаниях клавиш можно найти в [документации ag-grid-angular](https://www.ag-grid.com/angular-data-grid/keyboard-navigation/). diff --git a/package.json b/package.json index 4234c8a36..ae3e44de1 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@docsearch/js": "^3.9.0", "@fontsource/inter": "^5.2.8", "@fontsource/jetbrains-mono": "^5.2.6", - "@koobiq/ag-grid-angular-theme": "^34.3.1", + "@koobiq/ag-grid-angular-theme": "^34.4.0", "@koobiq/date-adapter": "^3.5.1", "@koobiq/date-formatter": "^3.5.1", "@koobiq/icons": "^11.7.1", diff --git a/packages/components-dev/ag-grid/module.ts b/packages/components-dev/ag-grid/module.ts index 3913ecf98..f53088f70 100644 --- a/packages/components-dev/ag-grid/module.ts +++ b/packages/components-dev/ag-grid/module.ts @@ -1,13 +1,17 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; import { + AgGridColumnMenuExample, AgGridColumnStateExample, AgGridCopySelectedExample, AgGridExternalFilterStateExample, AgGridFilterStateExample, + AgGridInfiniteSelectionExample, + AgGridLoadingOverlayExample, AgGridOverviewExample, AgGridQuickFilterStateExample, AgGridRowActionsExample, AgGridRowDraggingExample, + AgGridSkeletonCellRendererExample, AgGridStatusBarExample } from 'packages/docs-examples/components/ag-grid'; import { DevThemeToggle } from '../theme-toggle'; @@ -15,6 +19,7 @@ import { DevThemeToggle } from '../theme-toggle'; @Component({ selector: 'dev-examples', imports: [ + AgGridColumnMenuExample, AgGridOverviewExample, AgGridRowDraggingExample, AgGridCopySelectedExample, @@ -23,9 +28,20 @@ import { DevThemeToggle } from '../theme-toggle'; AgGridColumnStateExample, AgGridFilterStateExample, AgGridQuickFilterStateExample, - AgGridExternalFilterStateExample + AgGridExternalFilterStateExample, + AgGridLoadingOverlayExample, + AgGridSkeletonCellRendererExample, + AgGridInfiniteSelectionExample ], template: ` + +
+ +
+ +
+ +

diff --git a/packages/docs-examples/components/actions-panel/actions-panel-overview/actions-panel-overview-example.ts b/packages/docs-examples/components/actions-panel/actions-panel-overview/actions-panel-overview-example.ts index edeb777c7..61a15a6cf 100644 --- a/packages/docs-examples/components/actions-panel/actions-panel-overview/actions-panel-overview-example.ts +++ b/packages/docs-examples/components/actions-panel/actions-panel-overview/actions-panel-overview-example.ts @@ -51,6 +51,7 @@ type ExampleTableItem = unknown; kbqAgGridToNextRowByTab kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick + [alwaysMultiSort]="true" [rowSelection]="rowSelection" [selectionColumnDef]="selectionColumnDef" [columnDefs]="columnDefs" @@ -133,7 +134,6 @@ export class ExampleGrid { } protected onFirstDataRendered({ api }: FirstDataRenderedEvent): void { - api.setFocusedCell(0, 'column0'); api.forEachNode((node) => { if (node.rowIndex === 3 || node.rowIndex === 4) { node.setSelected(true); diff --git a/packages/docs-examples/components/ag-grid/ag-grid-column-menu/ag-grid-column-menu-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-column-menu/ag-grid-column-menu-example.ts new file mode 100644 index 000000000..f1b662113 --- /dev/null +++ b/packages/docs-examples/components/ag-grid/ag-grid-column-menu/ag-grid-column-menu-example.ts @@ -0,0 +1,56 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { + KBQ_AG_GRID_COLUMN_MENU_LABELS_EN, + kbqAgGridColumnMenuLabelsProvider, + KbqAgGridThemeModule +} from '@koobiq/ag-grid-angular-theme'; +import { AgGridModule } from 'ag-grid-angular'; +import { AllCommunityModule, ColDef, ModuleRegistry } from 'ag-grid-community'; + +ModuleRegistry.registerModules([AllCommunityModule]); + +/** + * @title AG Grid with column menu + */ +@Component({ + selector: 'ag-grid-column-menu-example', + imports: [AgGridModule, KbqAgGridThemeModule], + template: ` + + `, + styles: ` + ag-grid-angular { + height: 400px; + max-width: 100%; + } + `, + providers: [kbqAgGridColumnMenuLabelsProvider(KBQ_AG_GRID_COLUMN_MENU_LABELS_EN)], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AgGridColumnMenuExample { + readonly columnDefs: ColDef[] = [ + { field: 'column0', headerName: 'Column 0', lockVisible: true, pinned: 'left', width: 150 }, + { field: 'column1', headerName: 'Column 1', lockVisible: true, width: 150 }, + { field: 'column2', headerName: 'Column 2', width: 150 }, + { field: 'column3', headerName: 'Column 3', pinned: 'right', width: 150 }, + { field: 'column4', headerName: 'Column 4', width: 150 } + ]; + readonly rowData = Array.from({ length: 30 }, (_, index) => ({ + column0: 'Text ' + index, + column1: 'Text ' + index, + column2: 'Text ' + index, + column3: 'Text ' + index, + column4: 'Text ' + index + })); +} diff --git a/packages/docs-examples/components/ag-grid/ag-grid-column-state/ag-grid-column-state-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-column-state/ag-grid-column-state-example.ts index a7b41d0f0..d2d165105 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-column-state/ag-grid-column-state-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-column-state/ag-grid-column-state-example.ts @@ -23,6 +23,7 @@ ModuleRegistry.registerModules([AllCommunityModule]); kbqAgGridTheme kbqAgGridThemeDisableCellFocusStyles kbqAgGridColumnState="dev-ag-grid-column-state" + [alwaysMultiSort]="true" [rowData]="rowData" [columnDefs]="columnDefs" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-copy-selected/ag-grid-copy-selected-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-copy-selected/ag-grid-copy-selected-example.ts index 5806faa0e..fd31b7126 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-copy-selected/ag-grid-copy-selected-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-copy-selected/ag-grid-copy-selected-example.ts @@ -51,6 +51,7 @@ const customCopyFormatterWithHeaderRow: KbqAgGridCopyFormatter = (api) => { kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick kbqAgGridCopyByCtrlC + [alwaysMultiSort]="true" [kbqAgGridCopyFormatter]="copyFormatter()" [rowSelection]="rowSelection" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-external-filter-state/ag-grid-external-filter-state-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-external-filter-state/ag-grid-external-filter-state-example.ts index 657a9235c..d09c703e7 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-external-filter-state/ag-grid-external-filter-state-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-external-filter-state/ag-grid-external-filter-state-example.ts @@ -32,6 +32,7 @@ ModuleRegistry.registerModules([AllCommunityModule]); #externalFilterState="kbqAgGridExternalFilterState" kbqAgGridTheme kbqAgGridExternalFilterState="dev-ag-grid-external-filter-state" + [alwaysMultiSort]="true" [rowData]="rowData" [columnDefs]="columnDefs" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-filter-state/ag-grid-filter-state-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-filter-state/ag-grid-filter-state-example.ts index 33ce32fd4..ca1f26f5a 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-filter-state/ag-grid-filter-state-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-filter-state/ag-grid-filter-state-example.ts @@ -24,6 +24,7 @@ ModuleRegistry.registerModules([AllCommunityModule]); kbqAgGridTheme kbqAgGridThemeDisableCellFocusStyles kbqAgGridFilterState="dev-ag-grid-filter-state" + [alwaysMultiSort]="true" [rowData]="rowData" [columnDefs]="columnDefs" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-infinite-selection/ag-grid-infinite-selection-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-infinite-selection/ag-grid-infinite-selection-example.ts new file mode 100644 index 000000000..9a78c1567 --- /dev/null +++ b/packages/docs-examples/components/ag-grid/ag-grid-infinite-selection/ag-grid-infinite-selection-example.ts @@ -0,0 +1,109 @@ +import { JsonPipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { + KbqAgGridInfiniteSelectionState, + KbqAgGridSkeletonCellRenderer, + KbqAgGridThemeModule +} from '@koobiq/ag-grid-angular-theme'; +import { AgGridModule } from 'ag-grid-angular'; +import { + AllCommunityModule, + ColDef, + GetRowIdFunc, + ICellRendererParams, + IDatasource, + IGetRowsParams, + ModuleRegistry, + RowSelectionOptions +} from 'ag-grid-community'; + +ModuleRegistry.registerModules([AllCommunityModule]); + +const ROW_DATA = Array.from({ length: 1000 }, (_, index) => ({ + id: 'ProjectName' + index, + column0: 'ProjectName ' + index, + column1: 'Text ' + index, + column2: 'Text ' + index, + column3: 'Text ' + index, + column4: 'Text ' + index, + column5: 'Text ' + index +})); + +/** + * @title AG Grid with infinite selection + */ +@Component({ + selector: 'ag-grid-infinite-selection-example', + imports: [AgGridModule, KbqAgGridThemeModule, JsonPipe], + template: ` + +
{{ selection.state() | json }}
+ `, + styles: ` + :host { + display: flex; + gap: var(--kbq-size-xl); + } + + ag-grid-angular { + flex-grow: 1; + height: 300px; + } + + pre { + margin: 0; + width: 180px; + max-height: 300px; + overflow: scroll; + font-size: var(--kbq-typography-text-compact-font-size); + color: var(--kbq-foreground-contrast-secondary); + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AgGridInfiniteSelectionExample { + protected readonly columnDefs: ColDef[] = [ + { field: 'column0', headerName: 'Project name' }, + { field: 'column1', headerName: 'Text' }, + { field: 'column2', headerName: 'Text' }, + { field: 'column3', headerName: 'Text' }, + { field: 'column4', headerName: 'Text' }, + { field: 'column5', headerName: 'Text' } + ]; + protected readonly datasource: IDatasource = { + rowCount: ROW_DATA.length, + getRows: ({ startRow, endRow, successCallback }: IGetRowsParams) => { + setTimeout(() => successCallback(ROW_DATA.slice(startRow, endRow), ROW_DATA.length), 500); + } + }; + protected readonly getRowId: GetRowIdFunc = ({ data }) => data.id; + protected readonly defaultColDef: ColDef = { + cellRendererSelector: (params: ICellRendererParams) => + params.data === undefined ? { component: KbqAgGridSkeletonCellRenderer } : undefined + }; + protected readonly rowSelection: RowSelectionOptions = { + mode: 'multiRow', + isRowSelectable: ({ data }) => data !== undefined + }; + + protected onSelectAllChange(state: KbqAgGridInfiniteSelectionState): void { + console.debug('onSelectAllChange: ', state); + } +} diff --git a/packages/docs-examples/components/ag-grid/ag-grid-loading-overlay/ag-grid-loading-overlay-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-loading-overlay/ag-grid-loading-overlay-example.ts new file mode 100644 index 000000000..e46c527bf --- /dev/null +++ b/packages/docs-examples/components/ag-grid/ag-grid-loading-overlay/ag-grid-loading-overlay-example.ts @@ -0,0 +1,72 @@ +import { ChangeDetectionStrategy, Component, effect, model } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { kbqAgGridLoadingOverlayConfigProvider, KbqAgGridThemeModule } from '@koobiq/ag-grid-angular-theme'; +import { KbqCheckboxModule } from '@koobiq/components/checkbox'; +import { AgGridModule } from 'ag-grid-angular'; +import { AllCommunityModule, ColDef, ModuleRegistry } from 'ag-grid-community'; + +ModuleRegistry.registerModules([AllCommunityModule]); + +/** + * @title AG Grid with loading overlay + */ +@Component({ + selector: 'ag-grid-loading-overlay-example', + imports: [AgGridModule, KbqAgGridThemeModule, FormsModule, KbqCheckboxModule], + template: ` + Show loading overlay + + + `, + styles: ` + :host { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--kbq-size-xl); + } + + ag-grid-angular { + height: 300px; + width: 100%; + } + `, + providers: [kbqAgGridLoadingOverlayConfigProvider({ rows: 6, cols: 4 })], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AgGridLoadingOverlayExample { + readonly loading = model(true); + readonly columnDefs: ColDef[] = [ + { field: 'column0', headerName: 'Text' }, + { field: 'column1', headerName: 'Text' }, + { field: 'column2', headerName: 'Text' }, + { field: 'column3', headerName: 'Text' }, + { field: 'column4', headerName: 'Text' }, + { field: 'column5', headerName: 'Text' } + ]; + readonly rowData = Array.from({ length: 50 }, (_, index) => ({ + column0: 'Text ' + index, + column1: 'Text ' + index, + column2: 'Text ' + index, + column3: 'Text ' + index, + column4: 'Text ' + index, + column5: 'Text ' + index + })); + + constructor() { + effect(() => { + console.log('this.loading', this.loading()); + }); + } +} diff --git a/packages/docs-examples/components/ag-grid/ag-grid-overview/ag-grid-overview-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-overview/ag-grid-overview-example.ts index 7a6a9beeb..10884b6e2 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-overview/ag-grid-overview-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-overview/ag-grid-overview-example.ts @@ -54,6 +54,7 @@ export class ExampleLinkCellRenderer implements ICellRendererAngularComp { kbqAgGridToNextRowByTab kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick + [alwaysMultiSort]="true" [rowSelection]="rowSelection" [selectionColumnDef]="selectionColumnDef" [style.height.px]="300" @@ -142,8 +143,6 @@ export class AgGridOverviewExample { })); protected onFirstDataRendered({ api }: FirstDataRenderedEvent): void { - api.setFocusedCell(0, 'column0'); - api.forEachNode((node) => { if (node.rowIndex === 3 || node.rowIndex === 4) { node.setSelected(true); diff --git a/packages/docs-examples/components/ag-grid/ag-grid-quick-filter-state/ag-grid-quick-filter-state-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-quick-filter-state/ag-grid-quick-filter-state-example.ts index ab43e857d..6ec425637 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-quick-filter-state/ag-grid-quick-filter-state-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-quick-filter-state/ag-grid-quick-filter-state-example.ts @@ -29,6 +29,7 @@ ModuleRegistry.registerModules([AllCommunityModule]); kbqAgGridTheme kbqAgGridThemeDisableCellFocusStyles kbqAgGridQuickFilterState="dev-ag-grid-quick-filter-state" + [alwaysMultiSort]="true" [rowData]="rowData" [columnDefs]="columnDefs" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-row-actions/ag-grid-row-actions-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-row-actions/ag-grid-row-actions-example.ts index c7c468d6e..1f12bd8f8 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-row-actions/ag-grid-row-actions-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-row-actions/ag-grid-row-actions-example.ts @@ -64,6 +64,7 @@ export class ExampleRowActionsComponent { kbqAgGridToNextRowByTab kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick + [alwaysMultiSort]="true" [kbqAgGridRowActions]="rowActionsComponent" [rowSelection]="rowSelection" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-row-dragging/ag-grid-row-dragging-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-row-dragging/ag-grid-row-dragging-example.ts index bfe7067ae..668b2f9e4 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-row-dragging/ag-grid-row-dragging-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-row-dragging/ag-grid-row-dragging-example.ts @@ -25,6 +25,7 @@ ModuleRegistry.registerModules([AllCommunityModule]); kbqAgGridToNextRowByTab kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick + [alwaysMultiSort]="true" [rowSelection]="rowSelection" [style.height.px]="300" [columnDefs]="columnDefs" diff --git a/packages/docs-examples/components/ag-grid/ag-grid-skeleton-cell-renderer/ag-grid-skeleton-cell-renderer-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-skeleton-cell-renderer/ag-grid-skeleton-cell-renderer-example.ts new file mode 100644 index 000000000..0d45f1b2f --- /dev/null +++ b/packages/docs-examples/components/ag-grid/ag-grid-skeleton-cell-renderer/ag-grid-skeleton-cell-renderer-example.ts @@ -0,0 +1,83 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqAgGridSkeletonCellRenderer, KbqAgGridThemeModule } from '@koobiq/ag-grid-angular-theme'; +import { AgGridModule } from 'ag-grid-angular'; +import { + AllCommunityModule, + ColDef, + ICellRendererParams, + IDatasource, + IGetRowsParams, + ModuleRegistry +} from 'ag-grid-community'; + +ModuleRegistry.registerModules([AllCommunityModule]); + +const getRandomInt = (min: number, max: number): number => { + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + +const ROW_DATA = Array.from({ length: 1000 }, (_, index) => ({ + column0: 'Text ' + index, + column1: 'Text ' + index, + column2: 'Text ' + index, + column3: 'Text ' + index, + column4: 'Text ' + index, + column5: 'Text ' + index +})); + +/** + * @title AG Grid with skeleton cell renderer + */ +@Component({ + selector: 'ag-grid-skeleton-cell-renderer-example', + imports: [AgGridModule, KbqAgGridThemeModule], + template: ` + + `, + styles: ` + ag-grid-angular { + height: 300px; + max-width: 100%; + } + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AgGridSkeletonCellRendererExample { + readonly columnDefs: ColDef[] = [ + { field: 'column0', headerName: 'Text' }, + { field: 'column1', headerName: 'Text' }, + { field: 'column2', headerName: 'Text' }, + { field: 'column3', headerName: 'Text' }, + { field: 'column4', headerName: 'Text' }, + { field: 'column5', headerName: 'Text' } + ]; + readonly datasource: IDatasource = { + rowCount: ROW_DATA.length, + getRows: ({ startRow, endRow, successCallback }: IGetRowsParams) => { + setTimeout( + () => successCallback(ROW_DATA.slice(startRow, endRow), ROW_DATA.length), + getRandomInt(500, 2000) + ); + } + }; + readonly defaultColDef: ColDef = { + cellRendererSelector: (params: ICellRendererParams) => + params.data === undefined ? { component: KbqAgGridSkeletonCellRenderer } : undefined + }; +} diff --git a/packages/docs-examples/components/ag-grid/ag-grid-status-bar/ag-grid-status-bar-example.ts b/packages/docs-examples/components/ag-grid/ag-grid-status-bar/ag-grid-status-bar-example.ts index ea47459a8..1afc9ae75 100644 --- a/packages/docs-examples/components/ag-grid/ag-grid-status-bar/ag-grid-status-bar-example.ts +++ b/packages/docs-examples/components/ag-grid/ag-grid-status-bar/ag-grid-status-bar-example.ts @@ -70,6 +70,7 @@ export class ExampleAgGridStatusBarComponent { kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick pagination + [alwaysMultiSort]="true" [kbqAgGridStatusBar]="statusBarComponent" [rowSelection]="rowSelection" [style.height.px]="300" diff --git a/packages/docs-examples/components/ag-grid/index.ts b/packages/docs-examples/components/ag-grid/index.ts index 7053f2f3f..87b6f3706 100644 --- a/packages/docs-examples/components/ag-grid/index.ts +++ b/packages/docs-examples/components/ag-grid/index.ts @@ -1,27 +1,36 @@ import { NgModule } from '@angular/core'; +import { AgGridColumnMenuExample } from './ag-grid-column-menu/ag-grid-column-menu-example'; import { AgGridColumnStateExample } from './ag-grid-column-state/ag-grid-column-state-example'; import { AgGridCopySelectedExample } from './ag-grid-copy-selected/ag-grid-copy-selected-example'; import { AgGridExternalFilterStateExample } from './ag-grid-external-filter-state/ag-grid-external-filter-state-example'; import { AgGridFilterStateExample } from './ag-grid-filter-state/ag-grid-filter-state-example'; +import { AgGridInfiniteSelectionExample } from './ag-grid-infinite-selection/ag-grid-infinite-selection-example'; +import { AgGridLoadingOverlayExample } from './ag-grid-loading-overlay/ag-grid-loading-overlay-example'; import { AgGridOverviewExample } from './ag-grid-overview/ag-grid-overview-example'; import { AgGridQuickFilterStateExample } from './ag-grid-quick-filter-state/ag-grid-quick-filter-state-example'; import { AgGridRowActionsExample } from './ag-grid-row-actions/ag-grid-row-actions-example'; import { AgGridRowDraggingExample } from './ag-grid-row-dragging/ag-grid-row-dragging-example'; +import { AgGridSkeletonCellRendererExample } from './ag-grid-skeleton-cell-renderer/ag-grid-skeleton-cell-renderer-example'; import { AgGridStatusBarExample } from './ag-grid-status-bar/ag-grid-status-bar-example'; export { + AgGridColumnMenuExample, AgGridColumnStateExample, AgGridCopySelectedExample, AgGridExternalFilterStateExample, AgGridFilterStateExample, + AgGridInfiniteSelectionExample, + AgGridLoadingOverlayExample, AgGridOverviewExample, AgGridQuickFilterStateExample, AgGridRowActionsExample, AgGridRowDraggingExample, + AgGridSkeletonCellRendererExample, AgGridStatusBarExample }; const EXAMPLES = [ + AgGridColumnMenuExample, AgGridOverviewExample, AgGridRowDraggingExample, AgGridCopySelectedExample, @@ -30,7 +39,10 @@ const EXAMPLES = [ AgGridColumnStateExample, AgGridFilterStateExample, AgGridQuickFilterStateExample, - AgGridExternalFilterStateExample + AgGridExternalFilterStateExample, + AgGridLoadingOverlayExample, + AgGridSkeletonCellRendererExample, + AgGridInfiniteSelectionExample ]; @NgModule({ diff --git a/packages/docs-examples/components/content-panel/content-panel-with-grid/content-panel-with-grid-example.ts b/packages/docs-examples/components/content-panel/content-panel-with-grid/content-panel-with-grid-example.ts index f130a9bb2..3470c438d 100644 --- a/packages/docs-examples/components/content-panel/content-panel-with-grid/content-panel-with-grid-example.ts +++ b/packages/docs-examples/components/content-panel/content-panel-with-grid/content-panel-with-grid-example.ts @@ -36,6 +36,7 @@ type ExampleRowData = Record; kbqAgGridToNextRowByTab kbqAgGridSelectRowsByShiftArrow kbqAgGridSelectRowsByCtrlClick + [alwaysMultiSort]="true" [rowSelection]="rowSelection" [style.height]="'100%'" [columnDefs]="columnDefs" diff --git a/packages/docs-examples/example-module.ts b/packages/docs-examples/example-module.ts index 77308dd0e..e89eb3fc7 100644 --- a/packages/docs-examples/example-module.ts +++ b/packages/docs-examples/example-module.ts @@ -243,6 +243,30 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "ag-grid-filter-state-example.ts", "importPath": "components/ag-grid" }, + "ag-grid-infinite-selection": { + "packagePath": "components/ag-grid/ag-grid-infinite-selection", + "title": "AG Grid with infinite selection", + "componentName": "AgGridInfiniteSelectionExample", + "files": [ + "ag-grid-infinite-selection-example.ts" + ], + "selector": "ag-grid-infinite-selection-example", + "additionalComponents": [], + "primaryFile": "ag-grid-infinite-selection-example.ts", + "importPath": "components/ag-grid" + }, + "ag-grid-loading-overlay": { + "packagePath": "components/ag-grid/ag-grid-loading-overlay", + "title": "AG Grid with loading overlay", + "componentName": "AgGridLoadingOverlayExample", + "files": [ + "ag-grid-loading-overlay-example.ts" + ], + "selector": "ag-grid-loading-overlay-example", + "additionalComponents": [], + "primaryFile": "ag-grid-loading-overlay-example.ts", + "importPath": "components/ag-grid" + }, "ag-grid-overview": { "packagePath": "components/ag-grid/ag-grid-overview", "title": "AG Grid overview", @@ -295,6 +319,18 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "ag-grid-row-dragging-example.ts", "importPath": "components/ag-grid" }, + "ag-grid-skeleton-cell-renderer": { + "packagePath": "components/ag-grid/ag-grid-skeleton-cell-renderer", + "title": "AG Grid with skeleton cell renderer", + "componentName": "AgGridSkeletonCellRendererExample", + "files": [ + "ag-grid-skeleton-cell-renderer-example.ts" + ], + "selector": "ag-grid-skeleton-cell-renderer-example", + "additionalComponents": [], + "primaryFile": "ag-grid-skeleton-cell-renderer-example.ts", + "importPath": "components/ag-grid" + }, "ag-grid-status-bar": { "packagePath": "components/ag-grid/ag-grid-status-bar", "title": "AG Grid with `KbqAgGridStatusBar` directive", @@ -3327,6 +3363,18 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "markdown-table-example.ts", "importPath": "components/markdown" }, + "modal-caption": { + "packagePath": "components/modal/modal-caption", + "title": "Modal with caption", + "componentName": "ModalCaptionExample", + "files": [ + "modal-caption-example.ts" + ], + "selector": "modal-caption-example", + "additionalComponents": [], + "primaryFile": "modal-caption-example.ts", + "importPath": "components/modal" + }, "modal-component-with-injector": { "packagePath": "components/modal/modal-component-with-injector", "title": "Modal component With Injector", @@ -4845,6 +4893,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "tag-autocomplete-editable-example.ts", "importPath": "components/tags" }, + "tag-autocomplete-onpaste-off": { + "packagePath": "components/tags/tag-autocomplete-onpaste-off", + "title": "Tag autocomplete onpaste off", + "componentName": "TagAutocompleteOnpasteOffExample", + "files": [ + "tag-autocomplete-onpaste-off-example.ts", + "tag-autocomplete-onpaste-off-example.html" + ], + "selector": "tag-autocomplete-onpaste-off-example", + "additionalComponents": [], + "primaryFile": "tag-autocomplete-onpaste-off-example.ts", + "importPath": "components/tags" + }, "tag-autocomplete-option-operations": { "packagePath": "components/tags/tag-autocomplete-option-operations", "title": "Tag autocomplete option operations", @@ -4953,6 +5014,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "tag-input-editable-example.ts", "importPath": "components/tags" }, + "tag-input-onpaste-off": { + "packagePath": "components/tags/tag-input-onpaste-off", + "title": "Tag input onpaste off", + "componentName": "TagInputOnpasteOffExample", + "files": [ + "tag-input-onpaste-off-example.ts", + "tag-input-onpaste-off-example.html" + ], + "selector": "tag-input-onpaste-off-example", + "additionalComponents": [], + "primaryFile": "tag-input-onpaste-off-example.ts", + "importPath": "components/tags" + }, "tag-input-overview": { "packagePath": "components/tags/tag-input-overview", "title": "Tag input overview", @@ -5097,32 +5171,6 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "tag-with-icon-example.ts", "importPath": "components/tags" }, - "tags-autocomplete-onpaste-off": { - "packagePath": "components/tags/tags-autocomplete-onpaste-off", - "title": "Tags autocomplete onpaste off", - "componentName": "TagsAutocompleteOnpasteOffExample", - "files": [ - "tags-autocomplete-onpaste-off-example.ts", - "tags-autocomplete-onpaste-off-example.html" - ], - "selector": "tags-autocomplete-onpaste-off-example", - "additionalComponents": [], - "primaryFile": "tags-autocomplete-onpaste-off-example.ts", - "importPath": "components/tags" - }, - "tags-input-onpaste-off": { - "packagePath": "components/tags/tags-input-onpaste-off", - "title": "Tags input onpaste off", - "componentName": "TagsInputOnpasteOffExample", - "files": [ - "tags-input-onpaste-off-example.ts", - "tags-input-onpaste-off-example.html" - ], - "selector": "tags-input-onpaste-off-example", - "additionalComponents": [], - "primaryFile": "tags-input-onpaste-off-example.ts", - "importPath": "components/tags" - }, "textarea-can-grow": { "packagePath": "components/textarea/textarea-can-grow", "title": "Textarea with canGrow attribute", @@ -6438,6 +6486,10 @@ return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-external-filter-state': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-filter-state': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-infinite-selection': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-loading-overlay': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-overview': return import('@koobiq/docs-examples/components/ag-grid'); @@ -6446,6 +6498,8 @@ return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-row-actions': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-row-dragging': +return import('@koobiq/docs-examples/components/ag-grid'); + case 'ag-grid-skeleton-cell-renderer': return import('@koobiq/docs-examples/components/ag-grid'); case 'ag-grid-status-bar': return import('@koobiq/docs-examples/components/ag-grid'); @@ -6939,6 +6993,8 @@ return import('@koobiq/docs-examples/components/markdown'); return import('@koobiq/docs-examples/components/markdown'); case 'markdown-table': return import('@koobiq/docs-examples/components/markdown'); + case 'modal-caption': +return import('@koobiq/docs-examples/components/modal'); case 'modal-component-with-injector': return import('@koobiq/docs-examples/components/modal'); case 'modal-component': @@ -7184,6 +7240,8 @@ return import('@koobiq/docs-examples/components/tabs'); case 'tag-autocomplete-draggable': return import('@koobiq/docs-examples/components/tags'); case 'tag-autocomplete-editable': +return import('@koobiq/docs-examples/components/tags'); + case 'tag-autocomplete-onpaste-off': return import('@koobiq/docs-examples/components/tags'); case 'tag-autocomplete-option-operations': return import('@koobiq/docs-examples/components/tags'); @@ -7202,6 +7260,8 @@ return import('@koobiq/docs-examples/components/tags'); case 'tag-input-draggable': return import('@koobiq/docs-examples/components/tags'); case 'tag-input-editable': +return import('@koobiq/docs-examples/components/tags'); + case 'tag-input-onpaste-off': return import('@koobiq/docs-examples/components/tags'); case 'tag-input-overview': return import('@koobiq/docs-examples/components/tags'); @@ -7226,10 +7286,6 @@ return import('@koobiq/docs-examples/components/tags'); case 'tag-selectable': return import('@koobiq/docs-examples/components/tags'); case 'tag-with-icon': -return import('@koobiq/docs-examples/components/tags'); - case 'tags-autocomplete-onpaste-off': -return import('@koobiq/docs-examples/components/tags'); - case 'tags-input-onpaste-off': return import('@koobiq/docs-examples/components/tags'); case 'textarea-can-grow': return import('@koobiq/docs-examples/components/textarea'); diff --git a/yarn.lock b/yarn.lock index 207f96655..bde0041ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6103,9 +6103,9 @@ __metadata: languageName: node linkType: hard -"@koobiq/ag-grid-angular-theme@npm:^34.3.1": - version: 34.3.1 - resolution: "@koobiq/ag-grid-angular-theme@npm:34.3.1" +"@koobiq/ag-grid-angular-theme@npm:^34.4.0": + version: 34.4.0 + resolution: "@koobiq/ag-grid-angular-theme@npm:34.4.0" dependencies: tslib: "npm:^2.3.0" peerDependencies: @@ -6115,7 +6115,7 @@ __metadata: "@koobiq/icons": ">=9.0.0" ag-grid-angular: ^34.0.0 ag-grid-community: ^34.0.0 - checksum: 10c0/c2e3dcb8103239d826b25c073cef102d5ba949b5d2d5ddddc501a9a8e722529f040eb2dc70e9ced0e04226d5dd8cd73684e9f278b9ee7dbb69efa33b4b03c086 + checksum: 10c0/788e3f9505345e759163d5643666d389d68e6c5715cea272cad8b1d7cda348906b8fbf6e32f600bd7d512fa7afe663e02f7a0b87894acc748ecb798c23c0a390 languageName: node linkType: hard @@ -17977,7 +17977,7 @@ __metadata: "@eslint/js": "npm:^9.0.0" "@fontsource/inter": "npm:^5.2.8" "@fontsource/jetbrains-mono": "npm:^5.2.6" - "@koobiq/ag-grid-angular-theme": "npm:^34.3.1" + "@koobiq/ag-grid-angular-theme": "npm:^34.4.0" "@koobiq/date-adapter": "npm:^3.5.1" "@koobiq/date-formatter": "npm:^3.5.1" "@koobiq/design-tokens": "npm:^3.17.2"