@alaarab/ogrid-angular-material 2.0.7 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,8 @@ import { Component, input, viewChild, ChangeDetectionStrategy, } from '@angular/
8
8
  import { BaseDataGridTableComponent, DataGridStateService, MarchingAntsOverlayComponent, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, } from '@alaarab/ogrid-angular';
9
9
  import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
10
10
  import { ColumnHeaderMenuComponent } from '../column-header-menu/column-header-menu.component';
11
+ import { InlineCellEditorComponent } from './inline-cell-editor.component';
12
+ import { PopoverCellEditorComponent } from './popover-cell-editor.component';
11
13
  /**
12
14
  * DataGridTable component using native HTML table with Material Design-inspired styling.
13
15
  * Standalone component — this is the workhorse of the grid.
@@ -34,7 +36,7 @@ DataGridTableComponent = __decorate([
34
36
  Component({
35
37
  selector: 'ogrid-datagrid-table',
36
38
  standalone: true,
37
- imports: [ColumnHeaderFilterComponent, ColumnHeaderMenuComponent, MarchingAntsOverlayComponent],
39
+ imports: [ColumnHeaderFilterComponent, ColumnHeaderMenuComponent, MarchingAntsOverlayComponent, InlineCellEditorComponent, PopoverCellEditorComponent],
38
40
  providers: [DataGridStateService],
39
41
  changeDetection: ChangeDetectionStrategy.OnPush,
40
42
  template: `
@@ -50,6 +52,7 @@ DataGridTableComponent = __decorate([
50
52
  [attr.aria-labelledby]="ariaLabelledBy()"
51
53
  (mousedown)="onWrapperMouseDown($event)"
52
54
  (keydown)="onGridKeyDown($event)"
55
+ (scroll)="onWrapperScroll($event)"
53
56
  (contextmenu)="$event.preventDefault()"
54
57
  [attr.data-overflow-x]="allowOverflowX() ? 'true' : 'false'"
55
58
  >
@@ -152,7 +155,11 @@ DataGridTableComponent = __decorate([
152
155
  </thead>
153
156
  @if (!showEmptyInGrid()) {
154
157
  <tbody>
155
- @for (item of items(); track getRowId()(item); let rowIndex = $index) {
158
+ @if (vsEnabled() && vsTopSpacerHeight() > 0) {
159
+ <tr class="ogrid-datagrid-vs-spacer" [style.height.px]="vsTopSpacerHeight()"></tr>
160
+ }
161
+ @for (item of vsVisibleItems(); track getRowId()(item); let localIdx = $index) {
162
+ @let rowIndex = vsStartIndex() + localIdx;
156
163
  @let rowId = getRowId()(item);
157
164
  @let isSelected = selectedRowIds().has(rowId);
158
165
  <tr
@@ -196,47 +203,26 @@ DataGridTableComponent = __decorate([
196
203
  >
197
204
  @let descriptor = getCellDescriptor(item, colLayout.col, rowIndex, colIdx);
198
205
  @if (descriptor.mode === 'editing-inline') {
199
- <div class="ogrid-datagrid-cell ogrid-datagrid-cell--editing">
200
- @switch (descriptor.editorType) {
201
- @case ('checkbox') {
202
- <input
203
- type="checkbox"
204
- [checked]="!!descriptor.value"
205
- (change)="commitEdit(item, colLayout.col.columnId, descriptor.value, ($event.target as HTMLInputElement).checked, rowIndex, descriptor.globalColIndex)"
206
- (keydown)="$event.key === 'Escape' && cancelEdit()"
207
- />
208
- }
209
- @case ('select') {
210
- <select
211
- class="ogrid-datagrid-editor-select"
212
- [value]="descriptor.value != null ? '' + descriptor.value : ''"
213
- (change)="commitEdit(item, colLayout.col.columnId, descriptor.value, ($event.target as HTMLSelectElement).value, rowIndex, descriptor.globalColIndex)"
214
- (keydown)="$event.key === 'Escape' && cancelEdit()"
215
- >
216
- @for (v of getSelectValues(colLayout.col); track v) {
217
- <option [value]="v">{{ v }}</option>
218
- }
219
- </select>
220
- }
221
- @case ('date') {
222
- <input
223
- type="date"
224
- class="ogrid-datagrid-editor-input"
225
- [value]="formatDateForInput(descriptor.value)"
226
- (change)="commitEdit(item, colLayout.col.columnId, descriptor.value, ($event.target as HTMLInputElement).value, rowIndex, descriptor.globalColIndex)"
227
- (keydown)="onEditorKeydown($event, item, colLayout.col.columnId, descriptor.value, rowIndex, descriptor.globalColIndex)"
228
- />
229
- }
230
- @default {
231
- <input
232
- type="text"
233
- class="ogrid-datagrid-editor-input"
234
- [value]="descriptor.value != null ? '' + descriptor.value : ''"
235
- (keydown)="onEditorKeydown($event, item, colLayout.col.columnId, descriptor.value, rowIndex, descriptor.globalColIndex)"
236
- />
237
- }
238
- }
239
- </div>
206
+ <ogrid-mat-inline-cell-editor
207
+ [value]="descriptor.value"
208
+ [item]="item"
209
+ [column]="colLayout.col"
210
+ [rowIndex]="rowIndex"
211
+ [editorType]="descriptor.editorType ?? 'text'"
212
+ (commit)="commitEdit(item, colLayout.col.columnId, descriptor.value, $event, rowIndex, descriptor.globalColIndex)"
213
+ (cancel)="cancelEdit()"
214
+ ></ogrid-mat-inline-cell-editor>
215
+ } @else if (descriptor.mode === 'editing-popover') {
216
+ @let editorProps = buildPopoverEditorProps(item, colLayout.col, descriptor);
217
+ <ogrid-mat-popover-cell-editor
218
+ [item]="item"
219
+ [column]="colLayout.col"
220
+ [rowIndex]="rowIndex"
221
+ [globalColIndex]="descriptor.globalColIndex"
222
+ [displayValue]="descriptor.displayValue"
223
+ [editorProps]="editorProps"
224
+ [onCancel]="() => cancelEdit()"
225
+ ></ogrid-mat-popover-cell-editor>
240
226
  } @else {
241
227
  @let content = resolveCellContent(colLayout.col, item, descriptor.displayValue);
242
228
  @let cellStyle = resolveCellStyleFn(colLayout.col, item);
@@ -273,6 +259,9 @@ DataGridTableComponent = __decorate([
273
259
  }
274
260
  </tr>
275
261
  }
262
+ @if (vsEnabled() && vsBottomSpacerHeight() > 0) {
263
+ <tr class="ogrid-datagrid-vs-spacer" [style.height.px]="vsBottomSpacerHeight()"></tr>
264
+ }
276
265
  </tbody>
277
266
  }
278
267
  </table>
@@ -0,0 +1,158 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, output, signal, effect, viewChild } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ let InlineCellEditorComponent = class InlineCellEditorComponent {
10
+ constructor() {
11
+ this.value = input.required();
12
+ this.item = input.required();
13
+ this.column = input.required();
14
+ this.rowIndex = input.required();
15
+ this.editorType = input.required();
16
+ this.commit = output();
17
+ this.cancel = output();
18
+ this.inputEl = viewChild('inputEl');
19
+ this.localValue = signal('');
20
+ this.selectOptions = signal([]);
21
+ effect(() => {
22
+ const v = this.value();
23
+ this.localValue.set(v != null ? String(v) : '');
24
+ const col = this.column();
25
+ if (col.cellEditorParams?.values) {
26
+ this.selectOptions.set(col.cellEditorParams.values);
27
+ }
28
+ });
29
+ }
30
+ ngAfterViewInit() {
31
+ setTimeout(() => {
32
+ const el = this.inputEl()?.nativeElement;
33
+ if (el) {
34
+ el.focus();
35
+ if (el instanceof HTMLInputElement && el.type === 'text') {
36
+ el.select();
37
+ }
38
+ }
39
+ });
40
+ }
41
+ commitValue(value) {
42
+ this.commit.emit(value);
43
+ }
44
+ onTextKeyDown(e) {
45
+ if (e.key === 'Enter') {
46
+ e.preventDefault();
47
+ this.commitValue(this.localValue());
48
+ }
49
+ else if (e.key === 'Escape') {
50
+ e.preventDefault();
51
+ this.cancel.emit();
52
+ }
53
+ else if (e.key === 'Tab') {
54
+ e.preventDefault();
55
+ this.commitValue(this.localValue());
56
+ }
57
+ }
58
+ onSelectKeyDown(e) {
59
+ if (e.key === 'Escape') {
60
+ e.preventDefault();
61
+ this.cancel.emit();
62
+ }
63
+ }
64
+ onCheckboxKeyDown(e) {
65
+ if (e.key === 'Escape') {
66
+ e.preventDefault();
67
+ this.cancel.emit();
68
+ }
69
+ }
70
+ onTextBlur() {
71
+ this.commitValue(this.localValue());
72
+ }
73
+ getInputStyle() {
74
+ const baseStyle = 'width:100%;box-sizing:border-box;padding:6px 10px;border:2px solid var(--ogrid-selection, #217346);border-radius:2px;outline:none;font:inherit;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);';
75
+ const col = this.column();
76
+ if (col.type === 'numeric') {
77
+ return baseStyle + 'text-align:right;';
78
+ }
79
+ return baseStyle;
80
+ }
81
+ };
82
+ InlineCellEditorComponent = __decorate([
83
+ Component({
84
+ selector: 'ogrid-mat-inline-cell-editor',
85
+ standalone: true,
86
+ imports: [CommonModule],
87
+ template: `
88
+ @switch (editorType()) {
89
+ @case ('text') {
90
+ <input
91
+ #inputEl
92
+ type="text"
93
+ [value]="localValue()"
94
+ (input)="localValue.set($any($event.target).value)"
95
+ (keydown)="onTextKeyDown($event)"
96
+ (blur)="onTextBlur()"
97
+ [style]="getInputStyle()"
98
+ />
99
+ }
100
+ @case ('select') {
101
+ <div style="width:100%;height:100%;display:flex;align-items:center;padding:6px 10px;box-sizing:border-box;overflow:hidden;min-width:0">
102
+ <select
103
+ #inputEl
104
+ [value]="localValue()"
105
+ (change)="commitValue($any($event.target).value)"
106
+ (keydown)="onSelectKeyDown($event)"
107
+ style="width:100%;border:none;outline:none;background:transparent;font:inherit;cursor:pointer;color:var(--ogrid-fg, #242424)"
108
+ >
109
+ @for (opt of selectOptions(); track opt) {
110
+ <option [value]="opt">{{ opt }}</option>
111
+ }
112
+ </select>
113
+ </div>
114
+ }
115
+ @case ('checkbox') {
116
+ <div style="display:flex;align-items:center;justify-content:center;width:100%;height:100%">
117
+ <input
118
+ type="checkbox"
119
+ [checked]="!!localValue()"
120
+ (change)="commitValue($any($event.target).checked)"
121
+ (keydown)="onCheckboxKeyDown($event)"
122
+ />
123
+ </div>
124
+ }
125
+ @case ('date') {
126
+ <input
127
+ #inputEl
128
+ type="date"
129
+ [value]="localValue()"
130
+ (change)="commitValue($any($event.target).value)"
131
+ (keydown)="onTextKeyDown($event)"
132
+ (blur)="onTextBlur()"
133
+ [style]="getInputStyle()"
134
+ />
135
+ }
136
+ @default {
137
+ <input
138
+ #inputEl
139
+ type="text"
140
+ [value]="localValue()"
141
+ (input)="localValue.set($any($event.target).value)"
142
+ (keydown)="onTextKeyDown($event)"
143
+ (blur)="onTextBlur()"
144
+ [style]="getInputStyle()"
145
+ />
146
+ }
147
+ }
148
+ `,
149
+ styles: [`
150
+ :host {
151
+ display: block;
152
+ width: 100%;
153
+ height: 100%;
154
+ }
155
+ `],
156
+ })
157
+ ], InlineCellEditorComponent);
158
+ export { InlineCellEditorComponent };
@@ -0,0 +1,99 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, ChangeDetectionStrategy, signal, effect, viewChild, Injector, createComponent, EnvironmentInjector, inject, } from '@angular/core';
8
+ /**
9
+ * PopoverCellEditor component for Angular Material.
10
+ * Renders custom popover editor when anchor element is set.
11
+ */
12
+ let PopoverCellEditorComponent = class PopoverCellEditorComponent {
13
+ constructor() {
14
+ this.item = input.required();
15
+ this.column = input.required();
16
+ this.rowIndex = input.required();
17
+ this.globalColIndex = input.required();
18
+ this.displayValue = input.required();
19
+ this.editorProps = input.required();
20
+ this.onCancel = input.required();
21
+ this.anchorRef = viewChild('anchorEl');
22
+ this.editorContainerRef = viewChild('editorContainer');
23
+ this.injector = inject(Injector);
24
+ this.envInjector = inject(EnvironmentInjector);
25
+ this.showEditor = signal(false);
26
+ // Show editor after anchor is rendered
27
+ effect(() => {
28
+ const anchor = this.anchorRef();
29
+ if (anchor) {
30
+ setTimeout(() => this.showEditor.set(true), 0);
31
+ }
32
+ });
33
+ // Render custom editor component when container is available
34
+ effect(() => {
35
+ const container = this.editorContainerRef();
36
+ const props = this.editorProps();
37
+ const col = this.column();
38
+ if (!container || !this.showEditor() || typeof col.cellEditor !== 'function')
39
+ return;
40
+ const EditorComponent = col.cellEditor; // ComponentType
41
+ const componentRef = createComponent(EditorComponent, {
42
+ environmentInjector: this.envInjector,
43
+ elementInjector: this.injector,
44
+ });
45
+ // Pass props to component instance
46
+ Object.assign(componentRef.instance, props);
47
+ componentRef.changeDetectorRef.detectChanges();
48
+ // Append to DOM
49
+ container.nativeElement.appendChild(componentRef.location.nativeElement);
50
+ // Cleanup on destroy
51
+ return () => componentRef.destroy();
52
+ });
53
+ }
54
+ handleOverlayClick() {
55
+ this.onCancel()();
56
+ }
57
+ };
58
+ PopoverCellEditorComponent = __decorate([
59
+ Component({
60
+ selector: 'ogrid-mat-popover-cell-editor',
61
+ standalone: true,
62
+ changeDetection: ChangeDetectionStrategy.OnPush,
63
+ template: `
64
+ <div #anchorEl
65
+ class="ogrid-popover-anchor"
66
+ [attr.data-row-index]="rowIndex()"
67
+ [attr.data-col-index]="globalColIndex()"
68
+ >
69
+ {{ displayValue() }}
70
+ </div>
71
+ @if (showEditor()) {
72
+ <div class="ogrid-popover-editor-overlay" (click)="handleOverlayClick()">
73
+ <div class="ogrid-popover-editor-content" #editorContainer></div>
74
+ </div>
75
+ }
76
+ `,
77
+ styles: [`
78
+ :host { display: contents; }
79
+ .ogrid-popover-anchor {
80
+ width: 100%; height: 100%; display: flex; align-items: center; min-width: 0;
81
+ padding: 6px 10px; box-sizing: border-box; overflow: hidden;
82
+ text-overflow: ellipsis; white-space: nowrap; user-select: none;
83
+ outline: 2px solid var(--ogrid-selection, #217346); outline-offset: -1px;
84
+ z-index: 2; position: relative;
85
+ }
86
+ .ogrid-popover-editor-overlay {
87
+ position: fixed; inset: 0; z-index: 1000;
88
+ background: rgba(0,0,0,0.3);
89
+ display: flex; align-items: center; justify-content: center;
90
+ }
91
+ .ogrid-popover-editor-content {
92
+ background: #fff; border-radius: 4px; padding: 16px;
93
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
94
+ max-width: 90vw; max-height: 90vh; overflow: auto;
95
+ }
96
+ `],
97
+ })
98
+ ], PopoverCellEditorComponent);
99
+ export { PopoverCellEditorComponent };
package/dist/esm/index.js CHANGED
@@ -7,3 +7,5 @@ export { ColumnHeaderFilterComponent } from './column-header-filter/column-heade
7
7
  export { ColumnChooserComponent } from './column-chooser/column-chooser.component';
8
8
  export { PaginationControlsComponent } from './pagination-controls/pagination-controls.component';
9
9
  export { ColumnHeaderMenuComponent } from './column-header-menu/column-header-menu.component';
10
+ export { InlineCellEditorComponent } from './datagrid-table/inline-cell-editor.component';
11
+ export { PopoverCellEditorComponent } from './datagrid-table/popover-cell-editor.component';
@@ -0,0 +1,22 @@
1
+ import { ElementRef, AfterViewInit } from '@angular/core';
2
+ import type { IColumnDef } from '@alaarab/ogrid-angular';
3
+ export declare class InlineCellEditorComponent<T = unknown> implements AfterViewInit {
4
+ readonly value: import("@angular/core").InputSignal<unknown>;
5
+ readonly item: import("@angular/core").InputSignal<T>;
6
+ readonly column: import("@angular/core").InputSignal<IColumnDef<T>>;
7
+ readonly rowIndex: import("@angular/core").InputSignal<number>;
8
+ readonly editorType: import("@angular/core").InputSignal<"text" | "date" | "select" | "checkbox" | "richSelect">;
9
+ readonly commit: import("@angular/core").OutputEmitterRef<unknown>;
10
+ readonly cancel: import("@angular/core").OutputEmitterRef<void>;
11
+ readonly inputEl: import("@angular/core").Signal<ElementRef<HTMLInputElement | HTMLSelectElement> | undefined>;
12
+ readonly localValue: import("@angular/core").WritableSignal<unknown>;
13
+ readonly selectOptions: import("@angular/core").WritableSignal<unknown[]>;
14
+ constructor();
15
+ ngAfterViewInit(): void;
16
+ commitValue(value: unknown): void;
17
+ onTextKeyDown(e: KeyboardEvent): void;
18
+ onSelectKeyDown(e: KeyboardEvent): void;
19
+ onCheckboxKeyDown(e: KeyboardEvent): void;
20
+ onTextBlur(): void;
21
+ getInputStyle(): string;
22
+ }
@@ -0,0 +1,21 @@
1
+ import type { IColumnDef, ICellEditorProps } from '@alaarab/ogrid-core';
2
+ /**
3
+ * PopoverCellEditor component for Angular Material.
4
+ * Renders custom popover editor when anchor element is set.
5
+ */
6
+ export declare class PopoverCellEditorComponent<T> {
7
+ readonly item: import("@angular/core").InputSignal<T>;
8
+ readonly column: import("@angular/core").InputSignal<IColumnDef<T>>;
9
+ readonly rowIndex: import("@angular/core").InputSignal<number>;
10
+ readonly globalColIndex: import("@angular/core").InputSignal<number>;
11
+ readonly displayValue: import("@angular/core").InputSignal<unknown>;
12
+ readonly editorProps: import("@angular/core").InputSignal<ICellEditorProps<T>>;
13
+ readonly onCancel: import("@angular/core").InputSignal<() => void>;
14
+ private readonly anchorRef;
15
+ private readonly editorContainerRef;
16
+ private readonly injector;
17
+ private readonly envInjector;
18
+ protected readonly showEditor: import("@angular/core").WritableSignal<boolean>;
19
+ constructor();
20
+ protected handleOverlayClick(): void;
21
+ }
@@ -7,3 +7,5 @@ export { ColumnChooserComponent } from './column-chooser/column-chooser.componen
7
7
  export type { IColumnChooserProps } from './column-chooser/column-chooser.component';
8
8
  export { PaginationControlsComponent } from './pagination-controls/pagination-controls.component';
9
9
  export { ColumnHeaderMenuComponent } from './column-header-menu/column-header-menu.component';
10
+ export { InlineCellEditorComponent } from './datagrid-table/inline-cell-editor.component';
11
+ export { PopoverCellEditorComponent } from './datagrid-table/popover-cell-editor.component';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular-material",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "OGrid Angular Material – MatTable-based data grid with sorting, filtering, pagination, column chooser, and CSV export.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -16,33 +16,48 @@
16
16
  "build": "rimraf dist && tsc -p tsconfig.build.json",
17
17
  "test": "jest --passWithNoTests"
18
18
  },
19
- "keywords": ["ogrid", "angular", "material", "datatable", "typescript", "grid"],
19
+ "keywords": [
20
+ "ogrid",
21
+ "angular",
22
+ "material",
23
+ "datatable",
24
+ "typescript",
25
+ "grid"
26
+ ],
20
27
  "author": "Ala Arab",
21
28
  "license": "MIT",
22
- "files": ["dist", "README.md", "LICENSE"],
23
- "engines": { "node": ">=18" },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
24
37
  "dependencies": {
25
38
  "@alaarab/ogrid-angular": "2.0.7"
26
39
  },
27
40
  "peerDependencies": {
28
- "@angular/core": "^21.0.0",
41
+ "@angular/cdk": "^21.0.0",
29
42
  "@angular/common": "^21.0.0",
30
- "@angular/material": "^21.0.0",
31
- "@angular/cdk": "^21.0.0"
43
+ "@angular/core": "^21.0.0",
44
+ "@angular/material": "^21.0.0"
32
45
  },
33
46
  "devDependencies": {
34
- "@angular/core": "^21.1.4",
47
+ "@angular/animations": "^21.1.4",
48
+ "@angular/cdk": "^21.1.4",
35
49
  "@angular/common": "^21.1.4",
36
50
  "@angular/compiler": "^21.1.4",
51
+ "@angular/core": "^21.1.4",
52
+ "@angular/material": "^21.1.4",
37
53
  "@angular/platform-browser": "^21.1.4",
38
54
  "@angular/platform-browser-dynamic": "^21.1.4",
39
- "@angular/material": "^21.1.4",
40
- "@angular/cdk": "^21.1.4",
41
- "@angular/animations": "^21.1.4",
42
55
  "rxjs": "^7.8.2",
43
- "zone.js": "^0.15.0",
44
- "typescript": "^5.9.3"
56
+ "typescript": "^5.9.3",
57
+ "zone.js": "^0.15.0"
45
58
  },
46
59
  "sideEffects": false,
47
- "publishConfig": { "access": "public" }
60
+ "publishConfig": {
61
+ "access": "public"
62
+ }
48
63
  }