@maro-tech/table 0.0.1
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.
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
import * as i2 from '@angular/common';
|
|
2
|
+
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, TemplateRef, Input, Directive, EventEmitter, Output, Component, signal, ContentChildren } from '@angular/core';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { FormsModule } from '@angular/forms';
|
|
7
|
+
import * as i4 from '@angular/router';
|
|
8
|
+
import { Router, RouterModule } from '@angular/router';
|
|
9
|
+
import * as i1$1 from 'lucide-angular';
|
|
10
|
+
import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, LucideAngularModule } from 'lucide-angular';
|
|
11
|
+
import { UiSelectComponent } from '@maro-tech/form';
|
|
12
|
+
import { UiButtonComponent, UiBadgeComponent } from '@maro-tech/core';
|
|
13
|
+
|
|
14
|
+
class UiTableColumnDirective {
|
|
15
|
+
columnName;
|
|
16
|
+
template = inject((TemplateRef));
|
|
17
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
18
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.20", type: UiTableColumnDirective, isStandalone: true, selector: "[appUiTableColumn]", inputs: { columnName: ["appUiTableColumn", "columnName"] }, ngImport: i0 });
|
|
19
|
+
}
|
|
20
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableColumnDirective, decorators: [{
|
|
21
|
+
type: Directive,
|
|
22
|
+
args: [{
|
|
23
|
+
selector: '[appUiTableColumn]',
|
|
24
|
+
standalone: true,
|
|
25
|
+
}]
|
|
26
|
+
}], propDecorators: { columnName: [{
|
|
27
|
+
type: Input,
|
|
28
|
+
args: ['appUiTableColumn']
|
|
29
|
+
}] } });
|
|
30
|
+
|
|
31
|
+
class UiTableActionsCellComponent {
|
|
32
|
+
actions = [];
|
|
33
|
+
row;
|
|
34
|
+
actionClick = new EventEmitter();
|
|
35
|
+
onActionClick(event, action) {
|
|
36
|
+
event.stopPropagation();
|
|
37
|
+
if (action.disabled)
|
|
38
|
+
return;
|
|
39
|
+
this.actionClick.emit({ action, row: this.row });
|
|
40
|
+
}
|
|
41
|
+
isIconOnlyAction(action) {
|
|
42
|
+
return !String(action.label || '').trim();
|
|
43
|
+
}
|
|
44
|
+
resolveAriaLabel(action) {
|
|
45
|
+
return String(action.label?.trim() || (action.id === 'edit' ? 'Редагувати' : action.id === 'delete' ? 'Видалити' : action.id));
|
|
46
|
+
}
|
|
47
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableActionsCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
48
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableActionsCellComponent, isStandalone: true, selector: "app-ui-table-actions-cell", inputs: { actions: "actions", row: "row" }, outputs: { actionClick: "actionClick" }, ngImport: i0, template: `
|
|
49
|
+
<div class="flex flex-wrap gap-2" (click)="$event.stopPropagation()">
|
|
50
|
+
@for (action of actions; track action.id) {
|
|
51
|
+
<ui-button
|
|
52
|
+
[variant]="action.variant || 'ghost'"
|
|
53
|
+
[icon]="action.icon || ''"
|
|
54
|
+
[iconOnly]="isIconOnlyAction(action)"
|
|
55
|
+
[ariaLabel]="resolveAriaLabel(action)"
|
|
56
|
+
size="sm"
|
|
57
|
+
type="button"
|
|
58
|
+
[disabled]="action.disabled || false"
|
|
59
|
+
(click)="onActionClick($event, action)"
|
|
60
|
+
>
|
|
61
|
+
@if (action.label?.trim()) {
|
|
62
|
+
{{ action.label }}
|
|
63
|
+
}
|
|
64
|
+
</ui-button>
|
|
65
|
+
}
|
|
66
|
+
</div>
|
|
67
|
+
`, isInline: true, dependencies: [{ kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }] });
|
|
68
|
+
}
|
|
69
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableActionsCellComponent, decorators: [{
|
|
70
|
+
type: Component,
|
|
71
|
+
args: [{
|
|
72
|
+
selector: 'app-ui-table-actions-cell',
|
|
73
|
+
standalone: true,
|
|
74
|
+
imports: [UiButtonComponent],
|
|
75
|
+
template: `
|
|
76
|
+
<div class="flex flex-wrap gap-2" (click)="$event.stopPropagation()">
|
|
77
|
+
@for (action of actions; track action.id) {
|
|
78
|
+
<ui-button
|
|
79
|
+
[variant]="action.variant || 'ghost'"
|
|
80
|
+
[icon]="action.icon || ''"
|
|
81
|
+
[iconOnly]="isIconOnlyAction(action)"
|
|
82
|
+
[ariaLabel]="resolveAriaLabel(action)"
|
|
83
|
+
size="sm"
|
|
84
|
+
type="button"
|
|
85
|
+
[disabled]="action.disabled || false"
|
|
86
|
+
(click)="onActionClick($event, action)"
|
|
87
|
+
>
|
|
88
|
+
@if (action.label?.trim()) {
|
|
89
|
+
{{ action.label }}
|
|
90
|
+
}
|
|
91
|
+
</ui-button>
|
|
92
|
+
}
|
|
93
|
+
</div>
|
|
94
|
+
`,
|
|
95
|
+
}]
|
|
96
|
+
}], propDecorators: { actions: [{
|
|
97
|
+
type: Input
|
|
98
|
+
}], row: [{
|
|
99
|
+
type: Input,
|
|
100
|
+
args: [{ required: true }]
|
|
101
|
+
}], actionClick: [{
|
|
102
|
+
type: Output
|
|
103
|
+
}] } });
|
|
104
|
+
|
|
105
|
+
class UiTableBadgeCellComponent {
|
|
106
|
+
value = '';
|
|
107
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableBadgeCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
108
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableBadgeCellComponent, isStandalone: true, selector: "app-ui-table-badge-cell", inputs: { value: "value" }, ngImport: i0, template: `
|
|
109
|
+
@if (value === null || value === undefined || value === '') {
|
|
110
|
+
—
|
|
111
|
+
} @else {
|
|
112
|
+
<ui-badge variant="neutral">
|
|
113
|
+
{{ value }}
|
|
114
|
+
</ui-badge>
|
|
115
|
+
}
|
|
116
|
+
`, isInline: true, dependencies: [{ kind: "component", type: UiBadgeComponent, selector: "ui-badge", inputs: ["variant", "color"] }] });
|
|
117
|
+
}
|
|
118
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableBadgeCellComponent, decorators: [{
|
|
119
|
+
type: Component,
|
|
120
|
+
args: [{
|
|
121
|
+
selector: 'app-ui-table-badge-cell',
|
|
122
|
+
standalone: true,
|
|
123
|
+
imports: [UiBadgeComponent],
|
|
124
|
+
template: `
|
|
125
|
+
@if (value === null || value === undefined || value === '') {
|
|
126
|
+
—
|
|
127
|
+
} @else {
|
|
128
|
+
<ui-badge variant="neutral">
|
|
129
|
+
{{ value }}
|
|
130
|
+
</ui-badge>
|
|
131
|
+
}
|
|
132
|
+
`,
|
|
133
|
+
}]
|
|
134
|
+
}], propDecorators: { value: [{
|
|
135
|
+
type: Input
|
|
136
|
+
}] } });
|
|
137
|
+
|
|
138
|
+
class UiTableEditableCellComponent {
|
|
139
|
+
column;
|
|
140
|
+
value = null;
|
|
141
|
+
inputType = 'text';
|
|
142
|
+
valueChange = new EventEmitter();
|
|
143
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableEditableCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
144
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableEditableCellComponent, isStandalone: true, selector: "app-ui-table-editable-cell", inputs: { column: "column", value: "value", inputType: "inputType" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
145
|
+
<div class="min-w-0" (click)="$event.stopPropagation()">
|
|
146
|
+
@if (column.editor === 'select') {
|
|
147
|
+
<forms-select
|
|
148
|
+
[options]="column.options || []"
|
|
149
|
+
[includeBlankOption]="column.includeBlankOption ?? true"
|
|
150
|
+
[blankOptionLabel]="column.blankOptionLabel || 'Оберіть значення'"
|
|
151
|
+
[searchable]="true"
|
|
152
|
+
[fullWidth]="true"
|
|
153
|
+
[ngModel]="value"
|
|
154
|
+
[ngModelOptions]="{ standalone: true }"
|
|
155
|
+
(valueChange)="valueChange.emit($event)"
|
|
156
|
+
></forms-select>
|
|
157
|
+
} @else {
|
|
158
|
+
<input
|
|
159
|
+
class="w-full rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-800 outline-none transition focus:border-slate-400"
|
|
160
|
+
[type]="inputType"
|
|
161
|
+
[placeholder]="column.placeholder || ''"
|
|
162
|
+
[ngModel]="value"
|
|
163
|
+
[ngModelOptions]="{ standalone: true }"
|
|
164
|
+
(ngModelChange)="valueChange.emit($event)"
|
|
165
|
+
/>
|
|
166
|
+
}
|
|
167
|
+
</div>
|
|
168
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiSelectComponent, selector: "forms-select", inputs: ["label", "placeholder", "name", "data", "required", "fullWidth", "disabled", "multiple", "includeBlankOption", "blankOptionLabel", "emptyStateLabel", "searchable", "remoteSearch", "searchPlaceholder", "options", "error", "hint", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }] });
|
|
169
|
+
}
|
|
170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableEditableCellComponent, decorators: [{
|
|
171
|
+
type: Component,
|
|
172
|
+
args: [{
|
|
173
|
+
selector: 'app-ui-table-editable-cell',
|
|
174
|
+
standalone: true,
|
|
175
|
+
imports: [FormsModule, UiSelectComponent],
|
|
176
|
+
template: `
|
|
177
|
+
<div class="min-w-0" (click)="$event.stopPropagation()">
|
|
178
|
+
@if (column.editor === 'select') {
|
|
179
|
+
<forms-select
|
|
180
|
+
[options]="column.options || []"
|
|
181
|
+
[includeBlankOption]="column.includeBlankOption ?? true"
|
|
182
|
+
[blankOptionLabel]="column.blankOptionLabel || 'Оберіть значення'"
|
|
183
|
+
[searchable]="true"
|
|
184
|
+
[fullWidth]="true"
|
|
185
|
+
[ngModel]="value"
|
|
186
|
+
[ngModelOptions]="{ standalone: true }"
|
|
187
|
+
(valueChange)="valueChange.emit($event)"
|
|
188
|
+
></forms-select>
|
|
189
|
+
} @else {
|
|
190
|
+
<input
|
|
191
|
+
class="w-full rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-800 outline-none transition focus:border-slate-400"
|
|
192
|
+
[type]="inputType"
|
|
193
|
+
[placeholder]="column.placeholder || ''"
|
|
194
|
+
[ngModel]="value"
|
|
195
|
+
[ngModelOptions]="{ standalone: true }"
|
|
196
|
+
(ngModelChange)="valueChange.emit($event)"
|
|
197
|
+
/>
|
|
198
|
+
}
|
|
199
|
+
</div>
|
|
200
|
+
`,
|
|
201
|
+
}]
|
|
202
|
+
}], propDecorators: { column: [{
|
|
203
|
+
type: Input,
|
|
204
|
+
args: [{ required: true }]
|
|
205
|
+
}], value: [{
|
|
206
|
+
type: Input
|
|
207
|
+
}], inputType: [{
|
|
208
|
+
type: Input
|
|
209
|
+
}], valueChange: [{
|
|
210
|
+
type: Output
|
|
211
|
+
}] } });
|
|
212
|
+
|
|
213
|
+
class UiTableImageCellComponent {
|
|
214
|
+
src = '';
|
|
215
|
+
alt = '';
|
|
216
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableImageCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
217
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableImageCellComponent, isStandalone: true, selector: "app-ui-table-image-cell", inputs: { src: "src", alt: "alt" }, ngImport: i0, template: `
|
|
218
|
+
@if (src) {
|
|
219
|
+
<span class="block">
|
|
220
|
+
<img [src]="src" [alt]="alt" class="h-10 w-10 rounded-lg border border-slate-200 object-cover transition hover:opacity-80" />
|
|
221
|
+
</span>
|
|
222
|
+
} @else {
|
|
223
|
+
<span class="text-slate-400">—</span>
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
`, isInline: true });
|
|
227
|
+
}
|
|
228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableImageCellComponent, decorators: [{
|
|
229
|
+
type: Component,
|
|
230
|
+
args: [{
|
|
231
|
+
selector: 'app-ui-table-image-cell',
|
|
232
|
+
standalone: true,
|
|
233
|
+
imports: [],
|
|
234
|
+
template: `
|
|
235
|
+
@if (src) {
|
|
236
|
+
<span class="block">
|
|
237
|
+
<img [src]="src" [alt]="alt" class="h-10 w-10 rounded-lg border border-slate-200 object-cover transition hover:opacity-80" />
|
|
238
|
+
</span>
|
|
239
|
+
} @else {
|
|
240
|
+
<span class="text-slate-400">—</span>
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
`,
|
|
244
|
+
}]
|
|
245
|
+
}], propDecorators: { src: [{
|
|
246
|
+
type: Input
|
|
247
|
+
}], alt: [{
|
|
248
|
+
type: Input
|
|
249
|
+
}] } });
|
|
250
|
+
|
|
251
|
+
class UiTableTextCellComponent {
|
|
252
|
+
value = '';
|
|
253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableTextCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
254
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableTextCellComponent, isStandalone: true, selector: "app-ui-table-text-cell", inputs: { value: "value" }, ngImport: i0, template: `
|
|
255
|
+
<span class="block whitespace-normal break-words">
|
|
256
|
+
@if (value === null || value === undefined || value === '') {
|
|
257
|
+
—
|
|
258
|
+
} @else {
|
|
259
|
+
{{ value }}
|
|
260
|
+
}
|
|
261
|
+
</span>
|
|
262
|
+
`, isInline: true });
|
|
263
|
+
}
|
|
264
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableTextCellComponent, decorators: [{
|
|
265
|
+
type: Component,
|
|
266
|
+
args: [{
|
|
267
|
+
selector: 'app-ui-table-text-cell',
|
|
268
|
+
standalone: true,
|
|
269
|
+
template: `
|
|
270
|
+
<span class="block whitespace-normal break-words">
|
|
271
|
+
@if (value === null || value === undefined || value === '') {
|
|
272
|
+
—
|
|
273
|
+
} @else {
|
|
274
|
+
{{ value }}
|
|
275
|
+
}
|
|
276
|
+
</span>
|
|
277
|
+
`,
|
|
278
|
+
}]
|
|
279
|
+
}], propDecorators: { value: [{
|
|
280
|
+
type: Input
|
|
281
|
+
}] } });
|
|
282
|
+
|
|
283
|
+
const getConditionPathValue = (source, pathRaw) => {
|
|
284
|
+
const path = String(pathRaw || '').trim();
|
|
285
|
+
if (!path)
|
|
286
|
+
return undefined;
|
|
287
|
+
const parts = path.split('.');
|
|
288
|
+
let current = source;
|
|
289
|
+
for (const part of parts) {
|
|
290
|
+
if (current == null || typeof current !== 'object')
|
|
291
|
+
return undefined;
|
|
292
|
+
current = current[part];
|
|
293
|
+
}
|
|
294
|
+
return current;
|
|
295
|
+
};
|
|
296
|
+
const evaluateCondition = (conditionRaw, context) => {
|
|
297
|
+
const condition = String(conditionRaw || '').trim();
|
|
298
|
+
if (!condition)
|
|
299
|
+
return true;
|
|
300
|
+
const tokens = [];
|
|
301
|
+
let index = 0;
|
|
302
|
+
const pushOperator = (value) => {
|
|
303
|
+
tokens.push({ type: 'operator', value });
|
|
304
|
+
};
|
|
305
|
+
while (index < condition.length) {
|
|
306
|
+
const char = condition[index];
|
|
307
|
+
if (/\s/.test(char)) {
|
|
308
|
+
index += 1;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const nextThree = condition.slice(index, index + 3);
|
|
312
|
+
const nextTwo = condition.slice(index, index + 2);
|
|
313
|
+
if (nextThree === '===') {
|
|
314
|
+
pushOperator('===');
|
|
315
|
+
index += 3;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (nextThree === '!==') {
|
|
319
|
+
pushOperator('!==');
|
|
320
|
+
index += 3;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (nextTwo === '==') {
|
|
324
|
+
pushOperator('==');
|
|
325
|
+
index += 2;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (nextTwo === '!=') {
|
|
329
|
+
pushOperator('!=');
|
|
330
|
+
index += 2;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (nextTwo === '&&') {
|
|
334
|
+
pushOperator('&&');
|
|
335
|
+
index += 2;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (nextTwo === '||') {
|
|
339
|
+
pushOperator('||');
|
|
340
|
+
index += 2;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (char === '!' || char === '(' || char === ')') {
|
|
344
|
+
pushOperator(char);
|
|
345
|
+
index += 1;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (char === '"' || char === "'") {
|
|
349
|
+
const quote = char;
|
|
350
|
+
index += 1;
|
|
351
|
+
let value = '';
|
|
352
|
+
let escaped = false;
|
|
353
|
+
let closed = false;
|
|
354
|
+
while (index < condition.length) {
|
|
355
|
+
const current = condition[index];
|
|
356
|
+
index += 1;
|
|
357
|
+
if (escaped) {
|
|
358
|
+
value += current;
|
|
359
|
+
escaped = false;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (current === '\\') {
|
|
363
|
+
escaped = true;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (current === quote) {
|
|
367
|
+
closed = true;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
value += current;
|
|
371
|
+
}
|
|
372
|
+
if (!closed)
|
|
373
|
+
return true;
|
|
374
|
+
tokens.push({ type: 'string', value });
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (/[0-9.-]/.test(char)) {
|
|
378
|
+
const match = condition.slice(index).match(/^-?\d+(?:\.\d+)?/);
|
|
379
|
+
if (match) {
|
|
380
|
+
tokens.push({ type: 'number', value: Number(match[0]) });
|
|
381
|
+
index += match[0].length;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (/[A-Za-z_$]/.test(char)) {
|
|
386
|
+
const match = condition.slice(index).match(/^[A-Za-z_$][A-Za-z0-9_.$-]*/);
|
|
387
|
+
if (match) {
|
|
388
|
+
const raw = match[0];
|
|
389
|
+
const normalized = raw.toLowerCase();
|
|
390
|
+
if (normalized === 'true' || normalized === 'false') {
|
|
391
|
+
tokens.push({ type: 'boolean', value: normalized === 'true' });
|
|
392
|
+
}
|
|
393
|
+
else if (normalized === 'null') {
|
|
394
|
+
tokens.push({ type: 'null', value: null });
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
tokens.push({ type: 'identifier', value: raw });
|
|
398
|
+
}
|
|
399
|
+
index += raw.length;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
let tokenIndex = 0;
|
|
406
|
+
const peek = () => tokens[tokenIndex];
|
|
407
|
+
const consume = () => {
|
|
408
|
+
const token = tokens[tokenIndex];
|
|
409
|
+
tokenIndex += 1;
|
|
410
|
+
return token;
|
|
411
|
+
};
|
|
412
|
+
const matchOperator = (...values) => {
|
|
413
|
+
const token = peek();
|
|
414
|
+
if (token?.type !== 'operator')
|
|
415
|
+
return false;
|
|
416
|
+
if (!values.includes(token.value))
|
|
417
|
+
return false;
|
|
418
|
+
tokenIndex += 1;
|
|
419
|
+
return true;
|
|
420
|
+
};
|
|
421
|
+
const toBoolean = (value) => {
|
|
422
|
+
if (typeof value === 'string')
|
|
423
|
+
return value.trim().length > 0;
|
|
424
|
+
return Boolean(value);
|
|
425
|
+
};
|
|
426
|
+
const getValue = (identifierRaw) => {
|
|
427
|
+
const identifier = String(identifierRaw || '').trim();
|
|
428
|
+
if (!identifier)
|
|
429
|
+
return undefined;
|
|
430
|
+
if (identifier === 'data')
|
|
431
|
+
return context;
|
|
432
|
+
if (identifier.startsWith('data.'))
|
|
433
|
+
return getConditionPathValue(context, identifier.slice(5));
|
|
434
|
+
return getConditionPathValue(context, identifier);
|
|
435
|
+
};
|
|
436
|
+
const parseExpression = () => parseOr();
|
|
437
|
+
const parseOr = () => {
|
|
438
|
+
let left = parseAnd();
|
|
439
|
+
while (matchOperator('||')) {
|
|
440
|
+
const right = parseAnd();
|
|
441
|
+
left = toBoolean(left) || toBoolean(right);
|
|
442
|
+
}
|
|
443
|
+
return left;
|
|
444
|
+
};
|
|
445
|
+
const parseAnd = () => {
|
|
446
|
+
let left = parseEquality();
|
|
447
|
+
while (matchOperator('&&')) {
|
|
448
|
+
const right = parseEquality();
|
|
449
|
+
left = toBoolean(left) && toBoolean(right);
|
|
450
|
+
}
|
|
451
|
+
return left;
|
|
452
|
+
};
|
|
453
|
+
const parseEquality = () => {
|
|
454
|
+
let left = parseUnary();
|
|
455
|
+
while (true) {
|
|
456
|
+
if (matchOperator('==')) {
|
|
457
|
+
left = left == parseUnary();
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (matchOperator('!=')) {
|
|
461
|
+
left = left != parseUnary();
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (matchOperator('===')) {
|
|
465
|
+
left = left === parseUnary();
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (matchOperator('!==')) {
|
|
469
|
+
left = left !== parseUnary();
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
return left;
|
|
475
|
+
};
|
|
476
|
+
const parseUnary = () => {
|
|
477
|
+
if (matchOperator('!')) {
|
|
478
|
+
return !toBoolean(parseUnary());
|
|
479
|
+
}
|
|
480
|
+
return parsePrimary();
|
|
481
|
+
};
|
|
482
|
+
const parsePrimary = () => {
|
|
483
|
+
const token = consume();
|
|
484
|
+
if (!token)
|
|
485
|
+
return undefined;
|
|
486
|
+
if (token.type === 'operator' && token.value === '(') {
|
|
487
|
+
const value = parseExpression();
|
|
488
|
+
matchOperator(')');
|
|
489
|
+
return value;
|
|
490
|
+
}
|
|
491
|
+
if (token.type === 'identifier')
|
|
492
|
+
return getValue(token.value);
|
|
493
|
+
return token.value;
|
|
494
|
+
};
|
|
495
|
+
return toBoolean(parseExpression());
|
|
496
|
+
};
|
|
497
|
+
class UiTableComponent {
|
|
498
|
+
arrowDownIcon = ArrowDown;
|
|
499
|
+
arrowUpIcon = ArrowUp;
|
|
500
|
+
arrowUpDownIcon = ArrowUpDown;
|
|
501
|
+
chevronLeftIcon = ChevronLeft;
|
|
502
|
+
chevronRightIcon = ChevronRight;
|
|
503
|
+
router = inject(Router);
|
|
504
|
+
columnsSignal = signal([], ...(ngDevMode ? [{ debugName: "columnsSignal" }] : /* istanbul ignore next */ []));
|
|
505
|
+
isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingSignal" }] : /* istanbul ignore next */ []));
|
|
506
|
+
_data = {
|
|
507
|
+
items: [],
|
|
508
|
+
pagination: { page: 0, pageSize: 10, total: 0 },
|
|
509
|
+
};
|
|
510
|
+
dataSignal = signal(this._data, ...(ngDevMode ? [{ debugName: "dataSignal" }] : /* istanbul ignore next */ []));
|
|
511
|
+
disablePagingSignal = signal(false, ...(ngDevMode ? [{ debugName: "disablePagingSignal" }] : /* istanbul ignore next */ []));
|
|
512
|
+
rowClickableSignal = signal(true, ...(ngDevMode ? [{ debugName: "rowClickableSignal" }] : /* istanbul ignore next */ []));
|
|
513
|
+
currentPageIndex = 0;
|
|
514
|
+
currentPageSize = this._data.pagination.pageSize;
|
|
515
|
+
currentSortField = '';
|
|
516
|
+
currentSortDirection = '';
|
|
517
|
+
get currentSortIcon() {
|
|
518
|
+
return this.currentSortDirection === 'asc' ? this.arrowUpIcon : this.arrowDownIcon;
|
|
519
|
+
}
|
|
520
|
+
pageSizeOptions = [5, 10, 20];
|
|
521
|
+
pageSizeSelectOptions = this.pageSizeOptions.map((size) => ({
|
|
522
|
+
label: String(size),
|
|
523
|
+
value: String(size),
|
|
524
|
+
}));
|
|
525
|
+
columnTemplates;
|
|
526
|
+
set columns(value) {
|
|
527
|
+
this.columnsSignal.set(value ?? []);
|
|
528
|
+
}
|
|
529
|
+
set isLoading(value) {
|
|
530
|
+
this.isLoadingSignal.set(!!value);
|
|
531
|
+
}
|
|
532
|
+
set data(value) {
|
|
533
|
+
const normalized = value ?? {
|
|
534
|
+
items: [],
|
|
535
|
+
pagination: { page: 0, pageSize: this.currentPageSize, total: 0 },
|
|
536
|
+
};
|
|
537
|
+
this._data = normalized;
|
|
538
|
+
this.currentPageIndex = normalized.pagination.page ?? this.currentPageIndex;
|
|
539
|
+
this.currentPageSize = normalized.pagination.pageSize ?? this.currentPageSize;
|
|
540
|
+
this.dataSignal.set(normalized);
|
|
541
|
+
this.ensurePageIndexInRange();
|
|
542
|
+
}
|
|
543
|
+
get data() {
|
|
544
|
+
return this._data;
|
|
545
|
+
}
|
|
546
|
+
set disablePaging(value) {
|
|
547
|
+
this.disablePagingSignal.set(!!value);
|
|
548
|
+
}
|
|
549
|
+
get disablePaging() {
|
|
550
|
+
return this.disablePagingSignal();
|
|
551
|
+
}
|
|
552
|
+
set rowClickable(value) {
|
|
553
|
+
this.rowClickableSignal.set(!!value);
|
|
554
|
+
}
|
|
555
|
+
get rowClickable() {
|
|
556
|
+
return this.rowClickableSignal();
|
|
557
|
+
}
|
|
558
|
+
imageResolver = (value) => {
|
|
559
|
+
const raw = String(value ?? '').trim();
|
|
560
|
+
if (!raw)
|
|
561
|
+
return '';
|
|
562
|
+
if (raw.startsWith('http://') || raw.startsWith('https://') || raw.startsWith('/'))
|
|
563
|
+
return raw;
|
|
564
|
+
return `/api/files/${encodeURIComponent(raw)}/preview?scale=25`;
|
|
565
|
+
};
|
|
566
|
+
pageChange = new EventEmitter();
|
|
567
|
+
rowClick = new EventEmitter();
|
|
568
|
+
actionClick = new EventEmitter();
|
|
569
|
+
cellChange = new EventEmitter();
|
|
570
|
+
get totalPages() {
|
|
571
|
+
const total = this.dataSignal().pagination.total ?? this.dataSignal().items.length ?? 0;
|
|
572
|
+
const perPage = this.currentPageSize || 1;
|
|
573
|
+
return Math.max(1, Math.ceil(total / perPage));
|
|
574
|
+
}
|
|
575
|
+
get totalCount() {
|
|
576
|
+
return this.dataSignal().pagination.total ?? this.dataSignal().items.length ?? 0;
|
|
577
|
+
}
|
|
578
|
+
get visibleColumnCount() {
|
|
579
|
+
return Math.max(1, this.columnsSignal().length + (this.hasAnyRowActions() ? 1 : 0));
|
|
580
|
+
}
|
|
581
|
+
get startItemIndex() {
|
|
582
|
+
if (this.totalCount === 0) {
|
|
583
|
+
return 0;
|
|
584
|
+
}
|
|
585
|
+
return this.currentPageIndex * this.currentPageSize + 1;
|
|
586
|
+
}
|
|
587
|
+
get endItemIndex() {
|
|
588
|
+
if (this.totalCount === 0) {
|
|
589
|
+
return 0;
|
|
590
|
+
}
|
|
591
|
+
return Math.min((this.currentPageIndex + 1) * this.currentPageSize, this.totalCount);
|
|
592
|
+
}
|
|
593
|
+
get visiblePageNumbers() {
|
|
594
|
+
const pages = [];
|
|
595
|
+
const maxButtons = 5;
|
|
596
|
+
const total = this.totalPages;
|
|
597
|
+
if (total <= maxButtons) {
|
|
598
|
+
for (let i = 0; i < total; i += 1) {
|
|
599
|
+
pages.push(i);
|
|
600
|
+
}
|
|
601
|
+
return pages;
|
|
602
|
+
}
|
|
603
|
+
let start = Math.max(0, this.currentPageIndex - Math.floor(maxButtons / 2));
|
|
604
|
+
const maxStart = total - maxButtons;
|
|
605
|
+
if (start > maxStart) {
|
|
606
|
+
start = maxStart;
|
|
607
|
+
}
|
|
608
|
+
for (let i = 0; i < maxButtons; i += 1) {
|
|
609
|
+
pages.push(start + i);
|
|
610
|
+
}
|
|
611
|
+
return pages;
|
|
612
|
+
}
|
|
613
|
+
toggleSort(column) {
|
|
614
|
+
if (!column?.key || column.sortable === false) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (this.currentSortField === column.key) {
|
|
618
|
+
if (this.currentSortDirection === 'asc') {
|
|
619
|
+
this.currentSortDirection = 'desc';
|
|
620
|
+
}
|
|
621
|
+
else if (this.currentSortDirection === 'desc') {
|
|
622
|
+
this.currentSortDirection = '';
|
|
623
|
+
this.currentSortField = '';
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
this.currentSortDirection = 'asc';
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
this.currentSortField = column.key;
|
|
631
|
+
this.currentSortDirection = 'asc';
|
|
632
|
+
}
|
|
633
|
+
this.currentPageIndex = 0;
|
|
634
|
+
this.emitPageChange();
|
|
635
|
+
}
|
|
636
|
+
changePage(index) {
|
|
637
|
+
const target = Math.max(0, Math.min(this.totalPages - 1, index));
|
|
638
|
+
if (target === this.currentPageIndex) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
this.currentPageIndex = target;
|
|
642
|
+
this.emitPageChange();
|
|
643
|
+
}
|
|
644
|
+
onPageSizeChange(pageSize) {
|
|
645
|
+
const numericSize = Number(pageSize);
|
|
646
|
+
if (!numericSize || numericSize === this.currentPageSize) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
this.currentPageSize = numericSize;
|
|
650
|
+
this.currentPageIndex = 0;
|
|
651
|
+
this.ensurePageIndexInRange();
|
|
652
|
+
this.emitPageChange();
|
|
653
|
+
}
|
|
654
|
+
processRowClick(rowData) {
|
|
655
|
+
this.rowClick.emit(rowData);
|
|
656
|
+
}
|
|
657
|
+
processActionClick(action, rowData) {
|
|
658
|
+
if (action.disabled)
|
|
659
|
+
return;
|
|
660
|
+
this.actionClick.emit({ action, row: rowData });
|
|
661
|
+
}
|
|
662
|
+
processCellChange(column, rowData, value) {
|
|
663
|
+
const currentValue = this.getCellValue(rowData, column.bindKey || column.key);
|
|
664
|
+
if (this.normalizeCellValue(currentValue) === this.normalizeCellValue(value)) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
this.cellChange.emit({ column, row: rowData, value });
|
|
668
|
+
}
|
|
669
|
+
hasAnyRowActions() {
|
|
670
|
+
return this.dataSignal().items.some((row) => this.getRowActions(row).length > 0);
|
|
671
|
+
}
|
|
672
|
+
getRowActions(row) {
|
|
673
|
+
const raw = row['actions'];
|
|
674
|
+
if (!Array.isArray(raw)) {
|
|
675
|
+
return [];
|
|
676
|
+
}
|
|
677
|
+
return raw
|
|
678
|
+
.filter((item) => !!item && typeof item === 'object' && typeof item.id === 'string')
|
|
679
|
+
.filter((item) => evaluateCondition(item.condition, row))
|
|
680
|
+
.map((item) => ({
|
|
681
|
+
id: item.id,
|
|
682
|
+
label: typeof item.label === 'string' ? item.label : item.id,
|
|
683
|
+
variant: item.variant,
|
|
684
|
+
icon: typeof item.icon === 'string' ? item.icon : undefined,
|
|
685
|
+
type: item.type,
|
|
686
|
+
component: item.component,
|
|
687
|
+
template: typeof item.template === 'string' ? item.template : undefined,
|
|
688
|
+
disabled: !!item.disabled,
|
|
689
|
+
api: typeof item.api === 'string' ? item.api : undefined,
|
|
690
|
+
formId: typeof item.formId === 'string' ? item.formId : undefined,
|
|
691
|
+
title: typeof item.title === 'string' ? item.title : undefined,
|
|
692
|
+
text: typeof item.text === 'string' ? item.text : undefined,
|
|
693
|
+
submit: typeof item.submit === 'string' || typeof item.submit === 'boolean' ? item.submit : undefined,
|
|
694
|
+
danger: typeof item.danger === 'boolean' ? item.danger : undefined,
|
|
695
|
+
method: item.method,
|
|
696
|
+
payload: item.payload && typeof item.payload === 'object' && !Array.isArray(item.payload) ? item.payload : undefined,
|
|
697
|
+
}));
|
|
698
|
+
}
|
|
699
|
+
getTemplateForColumn(column) {
|
|
700
|
+
return this.columnTemplates?.find((template) => template.columnName === column)?.template;
|
|
701
|
+
}
|
|
702
|
+
trackTableRow(index) {
|
|
703
|
+
return index;
|
|
704
|
+
}
|
|
705
|
+
trackPageNumber(_, pageIndex) {
|
|
706
|
+
return pageIndex;
|
|
707
|
+
}
|
|
708
|
+
getCellValue(row, column) {
|
|
709
|
+
return row[column];
|
|
710
|
+
}
|
|
711
|
+
isEditableColumn(row, column) {
|
|
712
|
+
const context = row;
|
|
713
|
+
return evaluateCondition(column.editableCondition, context);
|
|
714
|
+
}
|
|
715
|
+
resolveDisplayValue(row, column) {
|
|
716
|
+
const rawValue = this.getCellValue(row, column.bindKey || column.key);
|
|
717
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
718
|
+
return '—';
|
|
719
|
+
}
|
|
720
|
+
if (column.editor === 'select') {
|
|
721
|
+
const value = String(rawValue).trim();
|
|
722
|
+
const option = (column.options || []).find((item) => String(item.value ?? '').trim() === value);
|
|
723
|
+
return String(option?.label || rawValue).trim() || '—';
|
|
724
|
+
}
|
|
725
|
+
return String(rawValue);
|
|
726
|
+
}
|
|
727
|
+
resolveLink(row, column) {
|
|
728
|
+
const source = row;
|
|
729
|
+
const linkConfig = column.link;
|
|
730
|
+
const template = typeof linkConfig === 'string'
|
|
731
|
+
? linkConfig.trim()
|
|
732
|
+
: String(linkConfig?.values[this.resolveLinkValue(source, linkConfig.by)] || '').trim();
|
|
733
|
+
if (!template)
|
|
734
|
+
return '';
|
|
735
|
+
let hasMissingValue = false;
|
|
736
|
+
const link = template.replace(/\{([a-zA-Z0-9_.]+)\}/g, (_match, key) => {
|
|
737
|
+
const value = key.split('.').reduce((current, part) => {
|
|
738
|
+
if (current == null || typeof current !== 'object')
|
|
739
|
+
return undefined;
|
|
740
|
+
return current[part];
|
|
741
|
+
}, source);
|
|
742
|
+
if (value == null || value === '') {
|
|
743
|
+
hasMissingValue = true;
|
|
744
|
+
return '';
|
|
745
|
+
}
|
|
746
|
+
return encodeURIComponent(String(value));
|
|
747
|
+
});
|
|
748
|
+
return hasMissingValue ? '' : link;
|
|
749
|
+
}
|
|
750
|
+
resolveLinkTree(row, column) {
|
|
751
|
+
const link = this.resolveLink(row, column);
|
|
752
|
+
return link ? this.router.parseUrl(link) : null;
|
|
753
|
+
}
|
|
754
|
+
resolveLinkValue(source, path) {
|
|
755
|
+
const value = path.split('.').reduce((current, part) => {
|
|
756
|
+
if (current == null || typeof current !== 'object')
|
|
757
|
+
return undefined;
|
|
758
|
+
return current[part];
|
|
759
|
+
}, source);
|
|
760
|
+
return value == null ? '' : String(value).trim();
|
|
761
|
+
}
|
|
762
|
+
resolveEditableValue(row, column) {
|
|
763
|
+
const value = this.getCellValue(row, column.bindKey || column.key);
|
|
764
|
+
if (value === null || value === undefined || value === '') {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
if (column.editor === 'number' || column.inputType === 'number') {
|
|
768
|
+
const numeric = Number(value);
|
|
769
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
770
|
+
}
|
|
771
|
+
return String(value);
|
|
772
|
+
}
|
|
773
|
+
resolveEditableInputType(column) {
|
|
774
|
+
return column.editor === 'number' || column.inputType === 'number' ? 'number' : 'text';
|
|
775
|
+
}
|
|
776
|
+
normalizeCellValue(value) {
|
|
777
|
+
if (value === null || value === undefined) {
|
|
778
|
+
return '';
|
|
779
|
+
}
|
|
780
|
+
return String(value).trim();
|
|
781
|
+
}
|
|
782
|
+
resolveColumnWidth(column) {
|
|
783
|
+
if (column.width == null || column.width === '') {
|
|
784
|
+
return '500px';
|
|
785
|
+
}
|
|
786
|
+
if (typeof column.width === 'number') {
|
|
787
|
+
return `${Math.max(1, Math.round(column.width))}px`;
|
|
788
|
+
}
|
|
789
|
+
return String(column.width).trim() || '500px';
|
|
790
|
+
}
|
|
791
|
+
resolveImageSrc(value, row) {
|
|
792
|
+
return this.imageResolver(value, row);
|
|
793
|
+
}
|
|
794
|
+
emitPageChange() {
|
|
795
|
+
this.pageChange.emit({
|
|
796
|
+
pageIndex: this.currentPageIndex,
|
|
797
|
+
pageSize: this.currentPageSize,
|
|
798
|
+
active: this.currentSortField,
|
|
799
|
+
direction: this.currentSortDirection,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
ensurePageIndexInRange() {
|
|
803
|
+
if (this.currentPageIndex >= this.totalPages) {
|
|
804
|
+
this.currentPageIndex = Math.max(0, this.totalPages - 1);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
808
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiTableComponent, isStandalone: true, selector: "app-ui-table", inputs: { columns: "columns", isLoading: "isLoading", data: "data", disablePaging: "disablePaging", rowClickable: "rowClickable", imageResolver: "imageResolver" }, outputs: { pageChange: "pageChange", rowClick: "rowClick", actionClick: "actionClick", cellChange: "cellChange" }, queries: [{ propertyName: "columnTemplates", predicate: UiTableColumnDirective }], ngImport: i0, template: "<div class=\"rounded-2xl border border-slate-200 bg-white shadow-sm\">\n <div class=\"w-full overflow-x-auto rounded-2xl\">\n <div class=\"inline-block min-w-full align-middle\">\n <table class=\"min-w-full table-fixed border-separate border-spacing-0 text-sm\">\n <thead class=\"bg-slate-50 text-xs uppercase tracking-wider text-slate-500\">\n <tr>\n @for (column of columnsSignal(); track column.key) {\n <th\n class=\"sticky top-0 z-10 whitespace-nowrap border-b border-slate-200 bg-slate-50 px-5 py-3 text-left font-semibold\"\n [style.width]=\"resolveColumnWidth(column)\"\n >\n <button type=\"button\" class=\"group flex items-center gap-2\" (click)=\"toggleSort(column)\">\n <span>\n {{ column.labelKey ? column.labelKey : column.key }}\n </span>\n\n @if (column.sortable !== false) {\n @if (currentSortField === column.key) {\n <lucide-icon [img]=\"currentSortIcon\" class=\"text-[0.65rem]\"></lucide-icon>\n } @else {\n <lucide-icon [img]=\"arrowUpDownIcon\" class=\"text-[0.65rem] text-slate-300 group-hover:text-slate-500\"></lucide-icon>\n }\n }\n </button>\n </th>\n }\n @if (hasAnyRowActions()) {\n <th class=\"sticky top-0 z-10 whitespace-nowrap border-b border-slate-200 bg-slate-50 px-5 py-3 text-left font-semibold\">\n \u0414\u0456\u0457\n </th>\n }\n </tr>\n </thead>\n\n <tbody class=\"divide-y divide-slate-100\">\n @if (isLoadingSignal()) {\n <tr>\n <td [attr.colspan]=\"visibleColumnCount\" class=\"py-10 text-center\">\n <div class=\"inline-block\" role=\"status\">\n <svg aria-hidden=\"true\" class=\"h-8 w-8 animate-spin fill-slate-800 text-slate-200\" viewBox=\"0 0 100 101\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z\" fill=\"currentColor\"></path>\n <path d=\"M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z\" fill=\"currentFill\"></path>\n </svg>\n </div>\n </td>\n </tr>\n }\n\n @if (!isLoadingSignal() && dataSignal().items.length === 0) {\n <tr>\n <td [attr.colspan]=\"visibleColumnCount\" class=\"py-10 text-center text-sm text-slate-500\">\n \u041D\u0435\u043C\u0430\u0454 \u0434\u0430\u043D\u0438\u0445\n </td>\n </tr>\n }\n\n @for (row of dataSignal().items; track trackTableRow($index)) {\n <tr\n class=\"transition-colors duration-150 hover:bg-slate-50\"\n [class.cursor-pointer]=\"rowClickableSignal()\"\n (click)=\"processRowClick(row)\"\n >\n @for (column of columnsSignal(); track column.key) {\n <td\n class=\"max-w-0 whitespace-normal break-words px-5 py-4 align-middle text-slate-800\"\n [style.width]=\"resolveColumnWidth(column)\"\n >\n @let template = getTemplateForColumn(column.key);\n @let displayValue = resolveDisplayValue(row, column);\n @let editableValue = resolveEditableValue(row, column);\n\n @if (template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: row, element: row }\" />\n } @else {\n @if (column.editable && isEditableColumn(row, column)) {\n <app-ui-table-editable-cell\n [column]=\"column\"\n [value]=\"editableValue\"\n [inputType]=\"resolveEditableInputType(column)\"\n (valueChange)=\"processCellChange(column, row, $event)\"\n />\n } @else {\n @if (column.link && resolveLink(row, column); as link) {\n <a\n [routerLink]=\"resolveLinkTree(row, column)\"\n class=\"text-blue-700 hover:underline\"\n (click)=\"$event.stopPropagation()\"\n >\n {{ displayValue }}\n </a>\n } @else {\n @switch (column.type) {\n @case ('image') {\n <app-ui-table-image-cell\n [src]=\"resolveImageSrc(getCellValue(row, column.key))\"\n [alt]=\"column.labelKey ? column.labelKey : column.key\"\n />\n }\n @case ('badge') {\n <app-ui-table-badge-cell [value]=\"displayValue\" />\n }\n @default {\n <app-ui-table-text-cell [value]=\"displayValue\" />\n }\n }\n }\n }\n }\n </td>\n }\n @if (hasAnyRowActions()) {\n <td class=\"whitespace-nowrap px-5 py-4 align-middle\">\n <app-ui-table-actions-cell\n [actions]=\"getRowActions(row)\"\n [row]=\"row\"\n (actionClick)=\"processActionClick($event.action, $event.row)\"\n />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n @if (!disablePagingSignal()) {\n <div class=\"flex flex-col gap-4 border-t border-slate-200 px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div class=\"flex items-center gap-3 text-xs uppercase tracking-wider text-slate-500\">\n <span>\u0420\u044F\u0434\u043A\u0456\u0432 \u043D\u0430 \u0441\u0442\u043E\u0440\u0456\u043D\u0446\u0456</span>\n <forms-select\n [options]=\"pageSizeSelectOptions\"\n [ngModel]=\"'' + currentPageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n [searchable]=\"false\"\n [fullWidth]=\"false\"\n ></forms-select>\n\n <span class=\"text-[0.65rem] tracking-[0.2em] text-slate-400\">\n {{ startItemIndex }}-{{ endItemIndex }} / {{ totalCount }}\n </span>\n </div>\n\n <div class=\"flex items-center gap-4 text-sm font-medium\">\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-1.5 transition hover:border-slate-400 disabled:cursor-not-allowed disabled:opacity-40\"\n (click)=\"changePage(currentPageIndex - 1)\"\n [disabled]=\"currentPageIndex === 0\"\n >\n <lucide-icon [img]=\"chevronLeftIcon\" class=\"text-xs\"></lucide-icon>\n </button>\n\n <span class=\"text-slate-600\">{{ currentPageIndex + 1 }} / {{ totalPages }}</span>\n\n <div class=\"hidden items-center gap-2 md:flex\">\n @for (pageIndex of visiblePageNumbers; track trackPageNumber($index, pageIndex)) {\n <button\n type=\"button\"\n class=\"inline-flex h-8 min-w-8 items-center justify-center rounded-lg border px-2 text-xs font-semibold transition\"\n [class.border-slate-900]=\"pageIndex === currentPageIndex\"\n [class.bg-slate-900]=\"pageIndex === currentPageIndex\"\n [class.text-white]=\"pageIndex === currentPageIndex\"\n [class.border-slate-200]=\"pageIndex !== currentPageIndex\"\n [class.text-slate-700]=\"pageIndex !== currentPageIndex\"\n [class.hover:border-slate-400]=\"pageIndex !== currentPageIndex\"\n (click)=\"changePage(pageIndex)\"\n >\n {{ pageIndex + 1 }}\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-1.5 transition hover:border-slate-400 disabled:cursor-not-allowed disabled:opacity-40\"\n (click)=\"changePage(currentPageIndex + 1)\"\n [disabled]=\"currentPageIndex >= totalPages - 1\"\n >\n <lucide-icon [img]=\"chevronRightIcon\" class=\"text-xs\"></lucide-icon>\n </button>\n </div>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: UiSelectComponent, selector: "forms-select", inputs: ["label", "placeholder", "name", "data", "required", "fullWidth", "disabled", "multiple", "includeBlankOption", "blankOptionLabel", "emptyStateLabel", "searchable", "remoteSearch", "searchPlaceholder", "options", "error", "hint", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: UiTableActionsCellComponent, selector: "app-ui-table-actions-cell", inputs: ["actions", "row"], outputs: ["actionClick"] }, { kind: "component", type: UiTableBadgeCellComponent, selector: "app-ui-table-badge-cell", inputs: ["value"] }, { kind: "component", type: UiTableEditableCellComponent, selector: "app-ui-table-editable-cell", inputs: ["column", "value", "inputType"], outputs: ["valueChange"] }, { kind: "component", type: UiTableImageCellComponent, selector: "app-ui-table-image-cell", inputs: ["src", "alt"] }, { kind: "component", type: UiTableTextCellComponent, selector: "app-ui-table-text-cell", inputs: ["value"] }] });
|
|
809
|
+
}
|
|
810
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiTableComponent, decorators: [{
|
|
811
|
+
type: Component,
|
|
812
|
+
args: [{ selector: 'app-ui-table', standalone: true, imports: [
|
|
813
|
+
LucideAngularModule,
|
|
814
|
+
CommonModule,
|
|
815
|
+
NgTemplateOutlet,
|
|
816
|
+
UiSelectComponent,
|
|
817
|
+
FormsModule,
|
|
818
|
+
RouterModule,
|
|
819
|
+
UiTableActionsCellComponent,
|
|
820
|
+
UiTableBadgeCellComponent,
|
|
821
|
+
UiTableEditableCellComponent,
|
|
822
|
+
UiTableImageCellComponent,
|
|
823
|
+
UiTableTextCellComponent,
|
|
824
|
+
], template: "<div class=\"rounded-2xl border border-slate-200 bg-white shadow-sm\">\n <div class=\"w-full overflow-x-auto rounded-2xl\">\n <div class=\"inline-block min-w-full align-middle\">\n <table class=\"min-w-full table-fixed border-separate border-spacing-0 text-sm\">\n <thead class=\"bg-slate-50 text-xs uppercase tracking-wider text-slate-500\">\n <tr>\n @for (column of columnsSignal(); track column.key) {\n <th\n class=\"sticky top-0 z-10 whitespace-nowrap border-b border-slate-200 bg-slate-50 px-5 py-3 text-left font-semibold\"\n [style.width]=\"resolveColumnWidth(column)\"\n >\n <button type=\"button\" class=\"group flex items-center gap-2\" (click)=\"toggleSort(column)\">\n <span>\n {{ column.labelKey ? column.labelKey : column.key }}\n </span>\n\n @if (column.sortable !== false) {\n @if (currentSortField === column.key) {\n <lucide-icon [img]=\"currentSortIcon\" class=\"text-[0.65rem]\"></lucide-icon>\n } @else {\n <lucide-icon [img]=\"arrowUpDownIcon\" class=\"text-[0.65rem] text-slate-300 group-hover:text-slate-500\"></lucide-icon>\n }\n }\n </button>\n </th>\n }\n @if (hasAnyRowActions()) {\n <th class=\"sticky top-0 z-10 whitespace-nowrap border-b border-slate-200 bg-slate-50 px-5 py-3 text-left font-semibold\">\n \u0414\u0456\u0457\n </th>\n }\n </tr>\n </thead>\n\n <tbody class=\"divide-y divide-slate-100\">\n @if (isLoadingSignal()) {\n <tr>\n <td [attr.colspan]=\"visibleColumnCount\" class=\"py-10 text-center\">\n <div class=\"inline-block\" role=\"status\">\n <svg aria-hidden=\"true\" class=\"h-8 w-8 animate-spin fill-slate-800 text-slate-200\" viewBox=\"0 0 100 101\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z\" fill=\"currentColor\"></path>\n <path d=\"M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z\" fill=\"currentFill\"></path>\n </svg>\n </div>\n </td>\n </tr>\n }\n\n @if (!isLoadingSignal() && dataSignal().items.length === 0) {\n <tr>\n <td [attr.colspan]=\"visibleColumnCount\" class=\"py-10 text-center text-sm text-slate-500\">\n \u041D\u0435\u043C\u0430\u0454 \u0434\u0430\u043D\u0438\u0445\n </td>\n </tr>\n }\n\n @for (row of dataSignal().items; track trackTableRow($index)) {\n <tr\n class=\"transition-colors duration-150 hover:bg-slate-50\"\n [class.cursor-pointer]=\"rowClickableSignal()\"\n (click)=\"processRowClick(row)\"\n >\n @for (column of columnsSignal(); track column.key) {\n <td\n class=\"max-w-0 whitespace-normal break-words px-5 py-4 align-middle text-slate-800\"\n [style.width]=\"resolveColumnWidth(column)\"\n >\n @let template = getTemplateForColumn(column.key);\n @let displayValue = resolveDisplayValue(row, column);\n @let editableValue = resolveEditableValue(row, column);\n\n @if (template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: row, element: row }\" />\n } @else {\n @if (column.editable && isEditableColumn(row, column)) {\n <app-ui-table-editable-cell\n [column]=\"column\"\n [value]=\"editableValue\"\n [inputType]=\"resolveEditableInputType(column)\"\n (valueChange)=\"processCellChange(column, row, $event)\"\n />\n } @else {\n @if (column.link && resolveLink(row, column); as link) {\n <a\n [routerLink]=\"resolveLinkTree(row, column)\"\n class=\"text-blue-700 hover:underline\"\n (click)=\"$event.stopPropagation()\"\n >\n {{ displayValue }}\n </a>\n } @else {\n @switch (column.type) {\n @case ('image') {\n <app-ui-table-image-cell\n [src]=\"resolveImageSrc(getCellValue(row, column.key))\"\n [alt]=\"column.labelKey ? column.labelKey : column.key\"\n />\n }\n @case ('badge') {\n <app-ui-table-badge-cell [value]=\"displayValue\" />\n }\n @default {\n <app-ui-table-text-cell [value]=\"displayValue\" />\n }\n }\n }\n }\n }\n </td>\n }\n @if (hasAnyRowActions()) {\n <td class=\"whitespace-nowrap px-5 py-4 align-middle\">\n <app-ui-table-actions-cell\n [actions]=\"getRowActions(row)\"\n [row]=\"row\"\n (actionClick)=\"processActionClick($event.action, $event.row)\"\n />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n @if (!disablePagingSignal()) {\n <div class=\"flex flex-col gap-4 border-t border-slate-200 px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div class=\"flex items-center gap-3 text-xs uppercase tracking-wider text-slate-500\">\n <span>\u0420\u044F\u0434\u043A\u0456\u0432 \u043D\u0430 \u0441\u0442\u043E\u0440\u0456\u043D\u0446\u0456</span>\n <forms-select\n [options]=\"pageSizeSelectOptions\"\n [ngModel]=\"'' + currentPageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n [searchable]=\"false\"\n [fullWidth]=\"false\"\n ></forms-select>\n\n <span class=\"text-[0.65rem] tracking-[0.2em] text-slate-400\">\n {{ startItemIndex }}-{{ endItemIndex }} / {{ totalCount }}\n </span>\n </div>\n\n <div class=\"flex items-center gap-4 text-sm font-medium\">\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-1.5 transition hover:border-slate-400 disabled:cursor-not-allowed disabled:opacity-40\"\n (click)=\"changePage(currentPageIndex - 1)\"\n [disabled]=\"currentPageIndex === 0\"\n >\n <lucide-icon [img]=\"chevronLeftIcon\" class=\"text-xs\"></lucide-icon>\n </button>\n\n <span class=\"text-slate-600\">{{ currentPageIndex + 1 }} / {{ totalPages }}</span>\n\n <div class=\"hidden items-center gap-2 md:flex\">\n @for (pageIndex of visiblePageNumbers; track trackPageNumber($index, pageIndex)) {\n <button\n type=\"button\"\n class=\"inline-flex h-8 min-w-8 items-center justify-center rounded-lg border px-2 text-xs font-semibold transition\"\n [class.border-slate-900]=\"pageIndex === currentPageIndex\"\n [class.bg-slate-900]=\"pageIndex === currentPageIndex\"\n [class.text-white]=\"pageIndex === currentPageIndex\"\n [class.border-slate-200]=\"pageIndex !== currentPageIndex\"\n [class.text-slate-700]=\"pageIndex !== currentPageIndex\"\n [class.hover:border-slate-400]=\"pageIndex !== currentPageIndex\"\n (click)=\"changePage(pageIndex)\"\n >\n {{ pageIndex + 1 }}\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-1.5 transition hover:border-slate-400 disabled:cursor-not-allowed disabled:opacity-40\"\n (click)=\"changePage(currentPageIndex + 1)\"\n [disabled]=\"currentPageIndex >= totalPages - 1\"\n >\n <lucide-icon [img]=\"chevronRightIcon\" class=\"text-xs\"></lucide-icon>\n </button>\n </div>\n </div>\n }\n</div>\n" }]
|
|
825
|
+
}], propDecorators: { columnTemplates: [{
|
|
826
|
+
type: ContentChildren,
|
|
827
|
+
args: [UiTableColumnDirective]
|
|
828
|
+
}], columns: [{
|
|
829
|
+
type: Input
|
|
830
|
+
}], isLoading: [{
|
|
831
|
+
type: Input
|
|
832
|
+
}], data: [{
|
|
833
|
+
type: Input
|
|
834
|
+
}], disablePaging: [{
|
|
835
|
+
type: Input
|
|
836
|
+
}], rowClickable: [{
|
|
837
|
+
type: Input
|
|
838
|
+
}], imageResolver: [{
|
|
839
|
+
type: Input
|
|
840
|
+
}], pageChange: [{
|
|
841
|
+
type: Output
|
|
842
|
+
}], rowClick: [{
|
|
843
|
+
type: Output
|
|
844
|
+
}], actionClick: [{
|
|
845
|
+
type: Output
|
|
846
|
+
}], cellChange: [{
|
|
847
|
+
type: Output
|
|
848
|
+
}] } });
|
|
849
|
+
|
|
850
|
+
class UiPaginatorComponent {
|
|
851
|
+
chevronLeftIcon = ChevronLeft;
|
|
852
|
+
chevronRightIcon = ChevronRight;
|
|
853
|
+
pageIndex = 0;
|
|
854
|
+
pageSize = 10;
|
|
855
|
+
total = 0;
|
|
856
|
+
pageSizeOptions = [5, 10, 20];
|
|
857
|
+
pageChange = new EventEmitter();
|
|
858
|
+
pageSizeChange = new EventEmitter();
|
|
859
|
+
get totalPages() {
|
|
860
|
+
const perPage = this.pageSize || 1;
|
|
861
|
+
return Math.max(1, Math.ceil((this.total || 0) / perPage));
|
|
862
|
+
}
|
|
863
|
+
get startItemIndex() {
|
|
864
|
+
if (!this.total)
|
|
865
|
+
return 0;
|
|
866
|
+
return this.pageIndex * this.pageSize + 1;
|
|
867
|
+
}
|
|
868
|
+
get endItemIndex() {
|
|
869
|
+
if (!this.total)
|
|
870
|
+
return 0;
|
|
871
|
+
return Math.min((this.pageIndex + 1) * this.pageSize, this.total);
|
|
872
|
+
}
|
|
873
|
+
get selectOptions() {
|
|
874
|
+
return this.pageSizeOptions.map((size) => ({ label: String(size), value: String(size) }));
|
|
875
|
+
}
|
|
876
|
+
get paginationItems() {
|
|
877
|
+
const total = this.totalPages;
|
|
878
|
+
if (total <= 7) {
|
|
879
|
+
return Array.from({ length: total }, (_value, index) => index);
|
|
880
|
+
}
|
|
881
|
+
const items = [0];
|
|
882
|
+
const start = Math.max(1, this.pageIndex - 1);
|
|
883
|
+
const end = Math.min(total - 2, this.pageIndex + 1);
|
|
884
|
+
if (start > 1) {
|
|
885
|
+
items.push('ellipsis');
|
|
886
|
+
}
|
|
887
|
+
for (let page = start; page <= end; page++) {
|
|
888
|
+
items.push(page);
|
|
889
|
+
}
|
|
890
|
+
if (end < total - 2) {
|
|
891
|
+
items.push('ellipsis');
|
|
892
|
+
}
|
|
893
|
+
items.push(total - 1);
|
|
894
|
+
return items;
|
|
895
|
+
}
|
|
896
|
+
onPageSizeModelChange(value) {
|
|
897
|
+
const numeric = Number(value);
|
|
898
|
+
if (!numeric || numeric === this.pageSize)
|
|
899
|
+
return;
|
|
900
|
+
this.pageSizeChange.emit(numeric);
|
|
901
|
+
}
|
|
902
|
+
changePage(nextIndex) {
|
|
903
|
+
const clamped = Math.max(0, Math.min(this.totalPages - 1, nextIndex));
|
|
904
|
+
if (clamped === this.pageIndex)
|
|
905
|
+
return;
|
|
906
|
+
this.pageChange.emit(clamped);
|
|
907
|
+
}
|
|
908
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiPaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
909
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiPaginatorComponent, isStandalone: true, selector: "app-ui-paginator", inputs: { pageIndex: "pageIndex", pageSize: "pageSize", total: "total", pageSizeOptions: "pageSizeOptions" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-white px-5 py-4\">\n <div class=\"flex shrink-0 items-center gap-3 text-xs uppercase tracking-wider text-slate-500\">\n <forms-select\n [options]=\"selectOptions\"\n [ngModel]=\"'' + pageSize\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n [searchable]=\"false\"\n [fullWidth]=\"false\"\n [triggerClass]=\"'h-9 rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm hover:border-slate-300 focus:ring-slate-200'\"\n [panelClass]=\"'rounded-xl border-slate-200 shadow-lg'\"\n ></forms-select>\n </div>\n\n <nav class=\"flex min-w-0 flex-1 items-center justify-end gap-x-1 overflow-x-auto\" aria-label=\"Pagination\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 items-center justify-center gap-x-2 rounded-md border border-transparent text-sm text-slate-900 transition hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"changePage(pageIndex - 1)\"\n [disabled]=\"pageIndex === 0\"\n aria-label=\"\u041D\u0430\u0437\u0430\u0434\"\n >\n <lucide-icon [img]=\"chevronLeftIcon\" class=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\"></lucide-icon>\n <span class=\"sr-only\">\u041D\u0430\u0437\u0430\u0434</span>\n </button>\n\n <div class=\"flex min-w-max items-center gap-x-1\">\n @for (item of paginationItems; track $index) {\n @if (item === 'ellipsis') {\n <span class=\"inline-flex h-9 w-9 items-center justify-center rounded-md text-xs text-slate-500\" aria-hidden=\"true\">\u2022\u2022\u2022</span>\n } @else {\n <button\n type=\"button\"\n class=\"inline-flex h-9 min-w-9 items-center justify-center rounded-md border px-3 text-sm transition focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n [class.border-slate-200]=\"item !== pageIndex\"\n [class.border-slate-900]=\"item === pageIndex\"\n [class.text-slate-900]=\"item !== pageIndex\"\n [class.text-white]=\"item === pageIndex\"\n [class.bg-slate-900]=\"item === pageIndex\"\n [class.hover:bg-slate-100]=\"item !== pageIndex\"\n (click)=\"changePage(item)\"\n [attr.aria-current]=\"item === pageIndex ? 'page' : null\"\n >\n {{ item + 1 }}\n </button>\n }\n }\n </div>\n\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 items-center justify-center gap-x-2 rounded-md border border-transparent text-sm text-slate-900 transition hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"changePage(pageIndex + 1)\"\n [disabled]=\"pageIndex >= totalPages - 1\"\n aria-label=\"\u0414\u0430\u043B\u0456\"\n >\n <span class=\"sr-only\">\u0414\u0430\u043B\u0456</span>\n <lucide-icon [img]=\"chevronRightIcon\" class=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\"></lucide-icon>\n </button>\n </nav>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiSelectComponent, selector: "forms-select", inputs: ["label", "placeholder", "name", "data", "required", "fullWidth", "disabled", "multiple", "includeBlankOption", "blankOptionLabel", "emptyStateLabel", "searchable", "remoteSearch", "searchPlaceholder", "options", "error", "hint", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1$1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
|
|
910
|
+
}
|
|
911
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiPaginatorComponent, decorators: [{
|
|
912
|
+
type: Component,
|
|
913
|
+
args: [{ selector: 'app-ui-paginator', standalone: true, imports: [CommonModule, FormsModule, UiSelectComponent, LucideAngularModule], template: "<div class=\"flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-white px-5 py-4\">\n <div class=\"flex shrink-0 items-center gap-3 text-xs uppercase tracking-wider text-slate-500\">\n <forms-select\n [options]=\"selectOptions\"\n [ngModel]=\"'' + pageSize\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n [searchable]=\"false\"\n [fullWidth]=\"false\"\n [triggerClass]=\"'h-9 rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm hover:border-slate-300 focus:ring-slate-200'\"\n [panelClass]=\"'rounded-xl border-slate-200 shadow-lg'\"\n ></forms-select>\n </div>\n\n <nav class=\"flex min-w-0 flex-1 items-center justify-end gap-x-1 overflow-x-auto\" aria-label=\"Pagination\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 items-center justify-center gap-x-2 rounded-md border border-transparent text-sm text-slate-900 transition hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"changePage(pageIndex - 1)\"\n [disabled]=\"pageIndex === 0\"\n aria-label=\"\u041D\u0430\u0437\u0430\u0434\"\n >\n <lucide-icon [img]=\"chevronLeftIcon\" class=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\"></lucide-icon>\n <span class=\"sr-only\">\u041D\u0430\u0437\u0430\u0434</span>\n </button>\n\n <div class=\"flex min-w-max items-center gap-x-1\">\n @for (item of paginationItems; track $index) {\n @if (item === 'ellipsis') {\n <span class=\"inline-flex h-9 w-9 items-center justify-center rounded-md text-xs text-slate-500\" aria-hidden=\"true\">\u2022\u2022\u2022</span>\n } @else {\n <button\n type=\"button\"\n class=\"inline-flex h-9 min-w-9 items-center justify-center rounded-md border px-3 text-sm transition focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n [class.border-slate-200]=\"item !== pageIndex\"\n [class.border-slate-900]=\"item === pageIndex\"\n [class.text-slate-900]=\"item !== pageIndex\"\n [class.text-white]=\"item === pageIndex\"\n [class.bg-slate-900]=\"item === pageIndex\"\n [class.hover:bg-slate-100]=\"item !== pageIndex\"\n (click)=\"changePage(item)\"\n [attr.aria-current]=\"item === pageIndex ? 'page' : null\"\n >\n {{ item + 1 }}\n </button>\n }\n }\n </div>\n\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 items-center justify-center gap-x-2 rounded-md border border-transparent text-sm text-slate-900 transition hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"changePage(pageIndex + 1)\"\n [disabled]=\"pageIndex >= totalPages - 1\"\n aria-label=\"\u0414\u0430\u043B\u0456\"\n >\n <span class=\"sr-only\">\u0414\u0430\u043B\u0456</span>\n <lucide-icon [img]=\"chevronRightIcon\" class=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\"></lucide-icon>\n </button>\n </nav>\n</div>\n" }]
|
|
914
|
+
}], propDecorators: { pageIndex: [{
|
|
915
|
+
type: Input
|
|
916
|
+
}], pageSize: [{
|
|
917
|
+
type: Input
|
|
918
|
+
}], total: [{
|
|
919
|
+
type: Input
|
|
920
|
+
}], pageSizeOptions: [{
|
|
921
|
+
type: Input
|
|
922
|
+
}], pageChange: [{
|
|
923
|
+
type: Output
|
|
924
|
+
}], pageSizeChange: [{
|
|
925
|
+
type: Output
|
|
926
|
+
}] } });
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Generated bundle index. Do not edit.
|
|
930
|
+
*/
|
|
931
|
+
|
|
932
|
+
export { UiPaginatorComponent, UiTableActionsCellComponent, UiTableBadgeCellComponent, UiTableColumnDirective, UiTableComponent, UiTableEditableCellComponent, UiTableImageCellComponent, UiTableTextCellComponent };
|
|
933
|
+
//# sourceMappingURL=maro-tech-table.mjs.map
|