@verisoft/ui-core 18.0.0 → 18.3.0

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.
Files changed (75) hide show
  1. package/package.json +6 -3
  2. package/src/index.ts +3 -1
  3. package/src/lib/common/constants.ts +5 -1
  4. package/src/lib/common/control.models.ts +14 -0
  5. package/src/lib/common/datasource-component.model.ts +6 -4
  6. package/src/lib/common/deactivate-guard.model.ts +5 -0
  7. package/src/lib/common/download-file.ts +20 -0
  8. package/src/lib/common/filter.ts +7 -0
  9. package/src/lib/common/icons.ts +34 -0
  10. package/src/lib/common/index.ts +7 -1
  11. package/src/lib/common/notificable-property.model.ts +5 -0
  12. package/src/lib/common/rxjs.spec.ts +58 -0
  13. package/src/lib/common/rxjs.ts +21 -0
  14. package/src/lib/components/action-button-group/action-button-group.model.ts +9 -10
  15. package/src/lib/components/action-button-group/action-button.model.ts +14 -15
  16. package/src/lib/components/base-form/base-form-input.component.ts +7 -1
  17. package/src/lib/components/base-form/base-form.component.ts +33 -6
  18. package/src/lib/components/base-form/directives/detail-store.directive.ts +104 -31
  19. package/src/lib/components/breadcrumb/breadcrumbcore.component.ts +50 -29
  20. package/src/lib/components/button/button.model.ts +0 -1
  21. package/src/lib/components/calendar/calendar.model.ts +1 -1
  22. package/src/lib/components/checkbox/checkbox.model.ts +1 -2
  23. package/src/lib/components/confirm-dialog/confirm-dialog.model.ts +22 -17
  24. package/src/lib/components/confirm-dialog/index.ts +1 -1
  25. package/src/lib/components/dropdown/dropdown.model.ts +3 -0
  26. package/src/lib/components/dynamic-component/dynamic-component.model.ts +2 -0
  27. package/src/lib/components/dynamic-component/index.ts +1 -0
  28. package/src/lib/components/filter/filter.model.ts +17 -0
  29. package/src/lib/components/filter/index.ts +1 -0
  30. package/src/lib/components/generic-field/generic-field.model.ts +1 -1
  31. package/src/lib/components/generic-form/generic-form.component.ts +33 -0
  32. package/src/lib/components/generic-form/index.ts +1 -0
  33. package/src/lib/components/header/header.model.ts +2 -2
  34. package/src/lib/components/icons/icons.component.ts +17 -0
  35. package/src/lib/components/icons/icons.model.ts +10 -0
  36. package/src/lib/components/icons/index.ts +2 -0
  37. package/src/lib/components/index.ts +4 -0
  38. package/src/lib/components/loader/loader.model.ts +1 -2
  39. package/src/lib/components/page-header/index.ts +3 -1
  40. package/src/lib/components/page-header/page-header.model.ts +1 -7
  41. package/src/lib/components/page-header/page-header.service.ts +9 -0
  42. package/src/lib/components/page-header/page-headercore.component.ts +40 -0
  43. package/src/lib/components/password/password.model.ts +14 -0
  44. package/src/lib/components/side-menu/directives/side-menu-service.directive.ts +31 -0
  45. package/src/lib/components/side-menu/index.ts +2 -1
  46. package/src/lib/components/side-menu/services/side-menu.service.ts +8 -4
  47. package/src/lib/components/side-menu/side-menu.model.ts +14 -11
  48. package/src/lib/components/snackbar/snackbar.model.ts +1 -2
  49. package/src/lib/components/stepper/stepper.model.ts +13 -3
  50. package/src/lib/components/switch/switch.model.ts +1 -2
  51. package/src/lib/components/tab-view/tab-view.model.ts +7 -4
  52. package/src/lib/components/table/column-configuration.ts +38 -0
  53. package/src/lib/components/table/index.ts +3 -1
  54. package/src/lib/components/table/table-builder.ts +93 -0
  55. package/src/lib/components/table/table-column.directive.ts +62 -0
  56. package/src/lib/components/table/table.models.ts +116 -44
  57. package/src/lib/components/textfield/textfield.model.ts +1 -1
  58. package/src/lib/components/tristatecheckbox/tristatecheckbox.model.ts +1 -2
  59. package/src/lib/directives/datasource.directive.ts +10 -10
  60. package/src/lib/directives/index.ts +3 -0
  61. package/src/lib/directives/shortcut.directive.ts +37 -0
  62. package/src/lib/directives/table-datasource.directive.ts +184 -0
  63. package/src/lib/directives/table-filter.directive.ts +69 -0
  64. package/src/lib/format/format.ts +74 -0
  65. package/src/lib/pipes/error/error.codes.ts +6 -1
  66. package/src/lib/pipes/helper/enumToList.pipe.ts +16 -0
  67. package/src/lib/pipes/index.ts +1 -2
  68. package/src/lib/services/confirm-dialog.service.ts +44 -0
  69. package/src/lib/services/index.ts +4 -0
  70. package/src/lib/services/leave-form.service.ts +53 -0
  71. package/src/lib/services/screen-size.service.ts +25 -0
  72. package/src/lib/services/table.service.ts +22 -0
  73. package/src/lib/components/table/template-column.directive.ts +0 -45
  74. package/src/lib/pipes/gov/gov-color.pipe.ts +0 -24
  75. package/src/lib/pipes/gov/gov-size.pipe.ts +0 -16
@@ -0,0 +1,38 @@
1
+ import { ColumnDefinition } from "./table.models";
2
+
3
+ export class ColumnConfiguration<TEntity> {
4
+ private column: Partial<ColumnDefinition<TEntity>> = {};
5
+
6
+ constructor(private id: string | keyof TEntity) {
7
+ this.column.id = id as string;
8
+ }
9
+
10
+ headerName(headerName: string | ((columnId: string, index?: number) => string)): this {
11
+ this.column.headerName = headerName;
12
+ return this;
13
+ }
14
+
15
+ sortable(sortable: boolean): this {
16
+ this.column.sortable = sortable;
17
+ return this;
18
+ }
19
+
20
+ columnClass(columnClass: string): this {
21
+ this.column.columnClass = columnClass;
22
+ return this;
23
+ }
24
+
25
+ valueFunction(value: (row: TEntity, index?: number) => string): this {
26
+ this.column.value = value;
27
+ return this;
28
+ }
29
+
30
+ type(type: string): this {
31
+ this.column.type = type;
32
+ return this;
33
+ }
34
+
35
+ build(): ColumnDefinition<TEntity> {
36
+ return this.column as ColumnDefinition<TEntity>;
37
+ }
38
+ }
@@ -1,2 +1,4 @@
1
1
  export * from './table.models';
2
- export * from './template-column.directive';
2
+ export * from './table-column.directive';
3
+ export * from './table-builder';
4
+ export * from './column-configuration';
@@ -0,0 +1,93 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { ColumnConfiguration } from './column-configuration';
3
+ import { ColumnDefinition } from './table.models';
4
+
5
+ @Injectable({
6
+ providedIn: 'root',
7
+ })
8
+ export class TableBuilder<TEntity> {
9
+ private columns: ColumnDefinition<TEntity>[] = [];
10
+
11
+ addColumn(
12
+ id: string | keyof TEntity,
13
+ config?: (v: ColumnConfiguration<TEntity>) => void
14
+ ): TableBuilder<TEntity> {
15
+ const columnConfig = new ColumnConfiguration<TEntity>(id);
16
+ config?.(columnConfig);
17
+ this.columns.push(columnConfig.build());
18
+ return this;
19
+ }
20
+
21
+ addTextColumn(
22
+ id: string | keyof TEntity,
23
+ config?: (v: ColumnConfiguration<TEntity>) => void
24
+ ): TableBuilder<TEntity> {
25
+ return this.addColumn(id, (x) => {
26
+ config?.(x);
27
+ x.type('text');
28
+ });
29
+ }
30
+
31
+ addNumberColumn(
32
+ id: string | keyof TEntity,
33
+ config?: (v: ColumnConfiguration<TEntity>) => void
34
+ ): TableBuilder<TEntity> {
35
+ return this.addColumn(id, (x) => {
36
+ config?.(x);
37
+ x.type('number').columnClass('text-end');
38
+ });
39
+ }
40
+
41
+ addDateColumn(
42
+ id: string | keyof TEntity,
43
+ config?: (v: ColumnConfiguration<TEntity>) => void
44
+ ): TableBuilder<TEntity> {
45
+ return this.addColumn(id, (x) => {
46
+ config?.(x);
47
+ x.type('date').valueFunction((row) => covertFromDateToUserLocale(row, id as string));
48
+ });
49
+ }
50
+
51
+ addBooleanColumn(
52
+ id: string,
53
+ config?: (v: ColumnConfiguration<TEntity>) => void
54
+ ): TableBuilder<TEntity> {
55
+ return this.addColumn(id, (x) => {
56
+ config?.(x);
57
+ x.type('boolean');
58
+ });
59
+ }
60
+
61
+ addEnumColumn(
62
+ id: string | keyof TEntity,
63
+ config?: (v: ColumnConfiguration<TEntity>) => void
64
+ ): TableBuilder<TEntity> {
65
+ return this.addColumn(id, (x) => {
66
+ config?.(x);
67
+ x.type('enum');
68
+ });
69
+ }
70
+
71
+ build(): ColumnDefinition<TEntity>[] {
72
+ return this.columns;
73
+ }
74
+ }
75
+
76
+ function covertFromDateToUserLocale<TEntity>(row: TEntity, id: string) {
77
+ const value = (row as { [key: string]: undefined | string | Date })[id];
78
+ if (value === undefined) {
79
+ return '';
80
+ }
81
+
82
+ const locale = navigator.language;
83
+
84
+ if (value instanceof Date) {
85
+ return value.toLocaleDateString(locale);
86
+ }
87
+
88
+ if (typeof value === 'string' && !isNaN(Date.parse(value))) {
89
+ return new Date(value).toLocaleDateString(locale);
90
+ }
91
+
92
+ return value;
93
+ }
@@ -0,0 +1,62 @@
1
+ import { ContentChild, Directive, Input, TemplateRef } from '@angular/core';
2
+ import { Params } from '@angular/router';
3
+ import { FieldAlign, FieldAlignType } from '../../common';
4
+ import { ColumnDefinition, ColumnProvider, ColumnVisibility, ColumnVisibilityType, TABLE_COLUMN_PROVIDER } from './table.models';
5
+
6
+ @Directive({
7
+ // eslint-disable-next-line @angular-eslint/directive-selector
8
+ selector: 'v-table-column',
9
+ standalone: true,
10
+ providers: [
11
+ {
12
+ provide: TABLE_COLUMN_PROVIDER,
13
+ useExisting: TableColumnDirective,
14
+ multi: true,
15
+ },
16
+ ],
17
+ })
18
+ export class TableColumnDirective<T> implements ColumnProvider<T> {
19
+ @ContentChild(TemplateRef) template!:
20
+ TemplateRef<{ $implicit: T }>;
21
+
22
+ @Input() index = 0;
23
+
24
+ @Input() id!: string;
25
+
26
+ @Input() columnClass!: string;
27
+
28
+ @Input() sortable!: boolean;
29
+
30
+ @Input() routerLink!: (row: T) => string;
31
+
32
+ @Input() queryParams!: Params;
33
+
34
+ @Input() headerName!: ((column: string, index?: number) => string) | string;
35
+
36
+ @Input() width: string | number | undefined;
37
+
38
+ @Input() textAlign: FieldAlignType = FieldAlign.left;
39
+
40
+ @Input() format!: (row: T) => string
41
+
42
+ @Input() forceVisibility: ColumnVisibilityType = ColumnVisibility.default;
43
+
44
+ @Input() visible = true;
45
+
46
+ getDefinition(): ColumnDefinition<T> {
47
+ return {
48
+ id: this.id,
49
+ columnClass: this.columnClass,
50
+ template: this.template,
51
+ headerName: this.headerName,
52
+ routerLink: this.routerLink,
53
+ queryParams: this.queryParams,
54
+ sortable: this.sortable,
55
+ width: this.width,
56
+ format: this.format,
57
+ textAlign: this.textAlign,
58
+ forceVisibility: this.forceVisibility,
59
+ visible: this.visible,
60
+ };
61
+ }
62
+ }
@@ -1,7 +1,8 @@
1
- import { InjectionToken, TemplateRef } from '@angular/core';
1
+ import { EventEmitter, InjectionToken, TemplateRef } from '@angular/core';
2
2
  import { Params } from '@angular/router';
3
- import { Page, Sort } from '@verisoft/core';
4
- import { Observable } from 'rxjs';
3
+ import { LazyLoadEvent, Sort, SortDirectionType } from '@verisoft/core';
4
+ import { v4 } from 'uuid';
5
+ import { FieldAlignType } from '../../common';
5
6
 
6
7
  export const TABLE_COMPONENT_TOKEN = new InjectionToken<TableCore<any>>(
7
8
  'TabVIewComponentToken'
@@ -11,26 +12,58 @@ export const TABLE_COLUMN_PROVIDER = new InjectionToken(
11
12
  'TABLE_COLUMN_PROVIDER'
12
13
  );
13
14
 
15
+ export enum TableSelectionMode {
16
+ single = 'single',
17
+ multiple = 'multiple',
18
+ }
19
+
20
+ export enum TableButtonSeverity {
21
+ success = 'success',
22
+ info = 'info',
23
+ warning = 'warning',
24
+ danger = 'danger',
25
+ help = 'help',
26
+ primary = 'primary',
27
+ secondary = 'secondary',
28
+ contrast = 'contrast',
29
+ }
30
+
31
+ export enum ColumnVisibility {
32
+ visible = 'visible',
33
+ hidden = 'hidden',
34
+ default = 'default',
35
+ }
36
+
37
+ export type TableSelectionModeType = keyof typeof TableSelectionMode;
38
+ export type TableButtonSeverityType = keyof typeof TableButtonSeverity;
39
+ export type ColumnVisibilityType = keyof typeof ColumnVisibility;
40
+
14
41
  export interface TableCore<T> {
15
42
  sorters: Sort[];
43
+ columns: ColumnDefinition<T>[];
16
44
  data: T[];
17
45
  total: number;
46
+ filter: Partial<T> | undefined;
18
47
  loading: boolean;
19
- scrollHeight: string;
20
48
  scrollable: boolean;
21
49
  pageSize: number;
22
50
  currentPage: number;
23
51
  showPaginator: boolean;
24
52
  sortMultiple: boolean;
25
53
  lazy: boolean;
26
- selectionMode: 'single' | 'multiple' | null | undefined;
27
- multipleSelect: boolean;
28
- autoIndex: boolean;
54
+ selection: T[];
55
+ selectionMode: TableSelectionModeType | undefined;
29
56
  showPageSizePicker: boolean;
30
- page: Page<T> | undefined;
31
- entityId: string;
32
- searchIndex$: Observable<number>;
33
- filterComponent: any;
57
+ entityKey: string | undefined;
58
+ selectionChange: EventEmitter<T[]>;
59
+ lazyLoad: EventEmitter<LazyLoadEvent>;
60
+ maximumColumnLength: number;
61
+ disableCustomClicks: boolean;
62
+ }
63
+
64
+ export interface TableSignal {
65
+ name: string,
66
+ symbol: symbol
34
67
  }
35
68
 
36
69
  export interface ActionColumnsDefinition<T> {
@@ -54,29 +87,23 @@ export interface ColumnProvider<T> {
54
87
  index: number;
55
88
  }
56
89
 
57
- export enum TableButtonSeverity {
58
- SUCCESS = 'success',
59
- INFO = 'info',
60
- WARNING = 'warning',
61
- DANGER = 'danger',
62
- HELP = 'help',
63
- PRIMARY = 'primary',
64
- SECONDARY = 'secondary',
65
- CONTRAST = 'contrast',
66
- }
67
-
68
90
  export interface ColumnDefinition<T, _KEY = keyof T> {
69
91
  id: string;
70
92
  value?: (row: T, index?: number) => string;
71
- headerName?: (column: string, index?: number) => string;
93
+ headerName?: ((columnId: string, index?: number) => string) | string;
72
94
  icon?: string | ((row?: T | undefined) => string);
73
95
  type?: string;
74
96
  sortable?: boolean;
97
+ format?: (row: T) => string
75
98
  routerLink?: (row: T) => string | string;
76
99
  queryParams?: Params;
77
100
  columnClass?: string;
78
- template?: TemplateRef<T>;
101
+ template?: TemplateRef<{ $implicit: T }>;
79
102
  actions?: ActionColumnsDefinition<T>[];
103
+ textAlign?: FieldAlignType;
104
+ width?: string | number;
105
+ forceVisibility?: ColumnVisibilityType;
106
+ visible?: boolean;
80
107
  }
81
108
 
82
109
  export function LinkRenderer<T>(text: string, href: string) {
@@ -98,30 +125,40 @@ export function Renderer<T>(fnc: (row: T) => string) {
98
125
  }
99
126
 
100
127
  export class ColumnModel<T> {
101
- sortDirection: SortDirection | undefined = undefined;
102
- sortColumn: Sort | undefined = undefined;
128
+ sortDirection: SortDirectionType | undefined = undefined;
103
129
  columnClass?: string;
104
130
  queryParams?: Params;
105
131
  routerLink?: (row: T) => string | string;
106
132
  valueGetter!: (row: T, index: number) => string;
107
- headerGetter!: (column: string, index: number) => string | string;
108
- template?: TemplateRef<T>;
133
+ headerGetter!: (columnId: string, index: number) => string | string;
134
+ template?: TemplateRef<{ $implicit: T }>;
109
135
  actions?: ActionColumnsDefinition<T>[];
110
136
  sortable?: boolean;
111
-
112
- get headerName(): string {
113
- return this.configuration.headerName?.toString() ?? 'undefined';
114
- }
137
+ id: string;
138
+ // eslint-disable-next-line @typescript-eslint/ban-types
139
+ format!: Function;
140
+ textAlign?: FieldAlignType;
141
+ width: string | undefined;
142
+ forceVisibility: ColumnVisibilityType = ColumnVisibility.default;
143
+ visible = true;
115
144
 
116
145
  constructor(readonly configuration: ColumnDefinition<T>) {
146
+ this.id = this.configuration.id;
147
+
148
+ if (this.configuration.format) {
149
+ this.format = this.configuration.format;
150
+ }
151
+
117
152
  if (this.configuration.value) {
118
- this.valueGetter = (row, index) =>
119
- this.configuration.value?.(row, index) ?? '-';
153
+ this.valueGetter = (row, index) => {
154
+ const value = this.configuration.value?.(row, index) ?? '-';
155
+ return this.format ? this.format(value, row) : value;
156
+ };
120
157
  } else {
121
- this.valueGetter = (row) =>
122
- ((row as { [key: string]: string })?.[
123
- this.configuration.id
124
- ] as string) ?? '';
158
+ this.valueGetter = (row) => {
159
+ const value = ((row as { [key: string]: string })?.[this.configuration.id]) ?? '';
160
+ return this.format ? this.format(value, row) : value;
161
+ };
125
162
  }
126
163
 
127
164
  if (this.configuration.actions) {
@@ -141,10 +178,16 @@ export class ColumnModel<T> {
141
178
  }
142
179
 
143
180
  if (this.configuration.headerName) {
144
- this.headerGetter = (column, index) =>
145
- this.configuration.headerName?.(column, index) ?? '';
181
+ this.headerGetter = (
182
+ typeof this.configuration.headerName === 'string'
183
+ ? () => this.configuration.headerName ?? ''
184
+ : (columnId: string, index: number) =>
185
+ (<(columnId: string, index?: number) => string>(
186
+ this.configuration.headerName
187
+ ))?.(columnId, index) ?? this.id
188
+ ) as (columnId: string, index: number) => string;
146
189
  } else {
147
- this.headerGetter = (column, index) => column[index];
190
+ this.headerGetter = () => "";
148
191
  }
149
192
 
150
193
  if (this.configuration.queryParams) {
@@ -160,30 +203,59 @@ export class ColumnModel<T> {
160
203
  } else {
161
204
  this.sortable = true;
162
205
  }
206
+
207
+ if (this.configuration.width){
208
+ this.width = typeof this.configuration.width === "number" ? this.configuration.width + 'px' : this.configuration.width;
209
+ }
210
+
211
+ if (this.configuration.textAlign !== undefined) {
212
+ this.textAlign = this.configuration.textAlign;
213
+ }
214
+
215
+ if (this.configuration.forceVisibility) {
216
+ this.forceVisibility = this.configuration.forceVisibility;
217
+ }
218
+
219
+ if (this.configuration.visible !== undefined) {
220
+ this.visible = this.configuration.visible;
221
+ }
163
222
  }
164
223
  }
165
224
 
166
- export declare type SortDirection = 'asc' | 'desc' | undefined;
167
-
168
225
  export class RowModel<T> {
169
226
  row: T;
170
227
  index?: number;
228
+ id: number | string;
171
229
  selected: boolean;
172
230
  marked?: boolean;
173
231
  focused?: boolean;
232
+ expanded: boolean;
174
233
  fnc?: void;
234
+ customRoute: string | undefined;
175
235
 
176
236
  constructor(
177
237
  row: T,
178
238
  selected: boolean,
239
+ expanded: boolean,
179
240
  marked?: boolean,
180
241
  index?: number,
181
- fnc?: void
242
+ fnc?: void,
243
+ customRoute?: string,
244
+ entityKey?: string
182
245
  ) {
183
246
  this.row = row;
247
+ this.id = (row as { [key: string]: string })['id'] ?? v4();
184
248
  this.index = index;
185
249
  this.selected = selected;
250
+ this.expanded = expanded;
186
251
  this.marked = marked;
187
252
  this.fnc = fnc;
253
+ this.customRoute = createCustomRoute<T>(row, entityKey, customRoute);
188
254
  }
189
255
  }
256
+
257
+ function createCustomRoute<T>(row: T, entityKey?: string, customRoute?: string) {
258
+ return customRoute && entityKey
259
+ ? `${customRoute}/` + (row as { [key: string]: string })[entityKey]
260
+ : undefined;
261
+ }
@@ -7,7 +7,7 @@ export const TEXTFIELD_COMPONENT_TOKEN = new InjectionToken<TextfieldCore>(
7
7
  );
8
8
 
9
9
  export interface TextfieldCore extends BaseFormCore {
10
- size?: FieldSizeType;
10
+ size: FieldSizeType | undefined;
11
11
  type: FieldTypeType;
12
12
  floatLabel: boolean;
13
13
  }
@@ -5,5 +5,4 @@ export const TRISTATE_CHECKBOX_COMPONENT_TOKEN = new InjectionToken<TristateChec
5
5
  'TristateCheckboxComponentToken'
6
6
  );
7
7
 
8
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
9
- export interface TristateCheckboxCore extends BaseFormCore {}
8
+ export type TristateCheckboxCore = BaseFormCore
@@ -1,4 +1,3 @@
1
- /* eslint-disable @angular-eslint/directive-selector */
2
1
  import { HttpClient } from '@angular/common/http';
3
2
  import {
4
3
  ChangeDetectorRef,
@@ -35,6 +34,7 @@ import {
35
34
  import {
36
35
  DataSourceComponentModel,
37
36
  DEFAULT_DEBOUNCE_TIME,
37
+ ExtendedRequestType,
38
38
  setComponentProperties,
39
39
  setDataToArray,
40
40
  } from '../common';
@@ -48,10 +48,9 @@ import {
48
48
  UnsubscribeComponent,
49
49
  } from '../components';
50
50
 
51
- type ExtendedRequestType<T> = RequestParams<T> & { useNewData: boolean}
52
-
53
51
  @Directive({
54
52
  selector:
53
+ // eslint-disable-next-line @angular-eslint/directive-selector
55
54
  'v-dropdown[useDatasource], v-multiselect[useDatasource], v-generic-field[useDatasource]',
56
55
  standalone: true,
57
56
  })
@@ -69,6 +68,8 @@ export class DatasourceDirective<T>
69
68
 
70
69
  @Input() transformFn?: (data: T) => unknown;
71
70
 
71
+ @Input() extraFilter!: any;
72
+
72
73
  get activeComponent(): DataSourceComponentModel<T> {
73
74
  return (this.dropdownComponent ??
74
75
  this.multiSelectComponent ??
@@ -136,6 +137,7 @@ export class DatasourceDirective<T>
136
137
  offset: 0,
137
138
  filter: {
138
139
  [property]: value.filter ? value.filter : undefined,
140
+ ...(this.extraFilter ?? {})
139
141
  } as Partial<T>,
140
142
  useNewData: true,
141
143
  });
@@ -147,7 +149,7 @@ export class DatasourceDirective<T>
147
149
  takeUntil(this.destroyed$),
148
150
  filter(request => !this.isDataForRequestLoaded(request)),
149
151
  map((request) => {
150
- const extendedParams = normalizeRequest({ ...this.lastParameter, ...request }) as ExtendedRequestType<T>;
152
+ const extendedParams = normalizeRequest({ ...this.lastParameter, ...request }, DEFAULT_SEARCH_LIMIT) as ExtendedRequestType<T>;
151
153
  extendedParams.useNewData = request.useNewData ?? false;
152
154
  return extendedParams;
153
155
  }),
@@ -203,8 +205,6 @@ export class DatasourceDirective<T>
203
205
  this.httpClient
204
206
  );
205
207
 
206
- this.activeComponent.loading = true;
207
- this.changeDetectorRef.detectChanges();
208
208
  if (this.autoBind) {
209
209
  this.parameters$.next({ offset: 0 });
210
210
  }
@@ -212,22 +212,22 @@ export class DatasourceDirective<T>
212
212
  }
213
213
 
214
214
  private isDataForRequestLoaded(request: Partial<ExtendedRequestType<T>>) {
215
- if (request.useNewData){
215
+ if (request.useNewData) {
216
216
  return false;
217
217
  }
218
218
 
219
219
  const offset = request.offset ?? 0;
220
220
  const limit = request.limit ?? DEFAULT_SEARCH_LIMIT;
221
221
  const options = this.activeComponent.options;
222
- if (!options){
222
+ if (!options) {
223
223
  return false;
224
224
  }
225
225
 
226
226
  if (options.length < offset + limit) {
227
227
  return false;
228
228
  }
229
-
230
- const allItemsFilled = options.slice(offset, offset + limit).every(item => item !== undefined && item != this.loadingPlaceholderItem);
229
+
230
+ const allItemsFilled = options.slice(offset, offset + limit).every(item => item !== undefined && item != this.loadingPlaceholderItem);
231
231
  return allItemsFilled;
232
232
  }
233
233
 
@@ -1 +1,4 @@
1
1
  export * from './datasource.directive';
2
+ export * from './table-datasource.directive';
3
+ export * from './shortcut.directive';
4
+ export * from './table-filter.directive';
@@ -0,0 +1,37 @@
1
+ import { Directive, HostListener, Input } from "@angular/core";
2
+
3
+ @Directive({
4
+ // eslint-disable-next-line @angular-eslint/directive-selector
5
+ selector: 'v-button[useShortCut]',
6
+ exportAs: 'useShortCut',
7
+ standalone: true,
8
+ })
9
+ export class ButtonShortCutDirective
10
+ {
11
+ @Input() shortCutFn?: () => void;
12
+ @Input() shortCutKey!: string;
13
+
14
+ private keyMap: { [key: string]: boolean } = {};
15
+
16
+ @HostListener('document:keydown', ['$event'])
17
+ onKeyDown(event: KeyboardEvent) {
18
+ this.keyMap[event.key.toLowerCase()] = true;
19
+ this.checkShortcut();
20
+ }
21
+
22
+ @HostListener('document:keyup', ['$event'])
23
+ onKeyUp(event: KeyboardEvent) {
24
+ this.keyMap[event.key.toLowerCase()] = false;
25
+ }
26
+
27
+ private checkShortcut() {
28
+ if (this.shortCutKey && this.shortCutFn) {
29
+ const keys = this.shortCutKey.toLowerCase().split('+');
30
+ const isShortcutPressed = keys.every((key) => this.keyMap[key]);
31
+ if (isShortcutPressed) {
32
+ this.shortCutFn?.();
33
+ keys.forEach((key) => (this.keyMap[key] = false));
34
+ }
35
+ }
36
+ }
37
+ }