eru-grid 0.0.31 → 0.0.32
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.
- package/fesm2022/eru-grid.mjs +72 -18
- package/fesm2022/eru-grid.mjs.map +1 -1
- package/package.json +1 -1
- package/types/eru-grid.d.ts +16 -2
package/fesm2022/eru-grid.mjs
CHANGED
|
@@ -2556,6 +2556,11 @@ class EruGridStore {
|
|
|
2556
2556
|
// Consumers (e.g. eru-studio) listen to persist the design back into their config.
|
|
2557
2557
|
_columnDesignUpdate = signal(null, ...(ngDevMode ? [{ debugName: "_columnDesignUpdate" }] : []));
|
|
2558
2558
|
columnDesignUpdate = this._columnDesignUpdate.asReadonly();
|
|
2559
|
+
// Emits ONLY the changed attribute(s) for a single column on each design edit.
|
|
2560
|
+
// Lets consumers store per-column overrides (deltas) rather than a full snapshot,
|
|
2561
|
+
// so non-overridden attributes keep flowing from their source (e.g. entity fields).
|
|
2562
|
+
_columnMetaPatch = signal(null, ...(ngDevMode ? [{ debugName: "_columnMetaPatch" }] : []));
|
|
2563
|
+
columnMetaPatch = this._columnMetaPatch.asReadonly();
|
|
2559
2564
|
// Computed signals
|
|
2560
2565
|
columns = this._columns.asReadonly();
|
|
2561
2566
|
groups = this._groups.asReadonly();
|
|
@@ -2799,6 +2804,7 @@ class EruGridStore {
|
|
|
2799
2804
|
const columns = this.columns().map(col => col.name === columnName ? { ...col, ...patch } : col);
|
|
2800
2805
|
this.setColumns(columns);
|
|
2801
2806
|
this._columnDesignUpdate.set(this.columns());
|
|
2807
|
+
this._columnMetaPatch.set({ name: columnName, patch });
|
|
2802
2808
|
}
|
|
2803
2809
|
selectDesignColumn(columnName) {
|
|
2804
2810
|
this._selectedDesignColumn.set(columnName);
|
|
@@ -3080,7 +3086,6 @@ class EruGridStore {
|
|
|
3080
3086
|
transformToPivot() {
|
|
3081
3087
|
// Prefer explicit pivot configuration signal, fall back to configuration().pivot
|
|
3082
3088
|
const pivotConfig = this.pivotConfiguration() || this.configuration().pivot;
|
|
3083
|
-
console.log('transformToPivot', pivotConfig, this.configuration().data, this.pivotResult());
|
|
3084
3089
|
// Prefer explicit source data, fall back to all loaded group rows
|
|
3085
3090
|
let sourceData = this.configuration().data || [];
|
|
3086
3091
|
if (sourceData.length === 0) {
|
|
@@ -3290,7 +3295,6 @@ class EruGridStore {
|
|
|
3290
3295
|
this.updateGroupLoadingState(groupId, false);
|
|
3291
3296
|
}
|
|
3292
3297
|
resetGroupRows() {
|
|
3293
|
-
console.log('resetGroupRows');
|
|
3294
3298
|
this._groupRows.set(new Map());
|
|
3295
3299
|
}
|
|
3296
3300
|
/**
|
|
@@ -3644,7 +3648,6 @@ class EruGridService {
|
|
|
3644
3648
|
* Switch grid to table mode or pivot mode
|
|
3645
3649
|
*/
|
|
3646
3650
|
set_grid_mode(mode) {
|
|
3647
|
-
console.log('set_grid_mode called for ', mode);
|
|
3648
3651
|
if (mode === 'pivot') {
|
|
3649
3652
|
this.eruGridStore.setGridMode('pivot');
|
|
3650
3653
|
}
|
|
@@ -6454,6 +6457,17 @@ class ProgressComponent {
|
|
|
6454
6457
|
// Clamp between 0 and 100
|
|
6455
6458
|
return Math.max(0, Math.min(100, numVal));
|
|
6456
6459
|
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
|
|
6460
|
+
// Bar colour from the column's configured value ranges (e.g. 0-20 green).
|
|
6461
|
+
// Applies in both editable and view/disabled modes. Null when no range
|
|
6462
|
+
// matches, so the bar falls back to the default theme colour.
|
|
6463
|
+
barColor = computed(() => {
|
|
6464
|
+
const ranges = this.config()?.color_ranges;
|
|
6465
|
+
if (!Array.isArray(ranges) || ranges.length === 0)
|
|
6466
|
+
return null;
|
|
6467
|
+
const v = this.displayValue();
|
|
6468
|
+
const match = ranges.find((r) => v >= Number(r?.from) && v <= Number(r?.to));
|
|
6469
|
+
return match?.color || null;
|
|
6470
|
+
}, ...(ngDevMode ? [{ debugName: "barColor" }] : []));
|
|
6457
6471
|
constructor() {
|
|
6458
6472
|
// Sync signal-based input with internal state
|
|
6459
6473
|
effect(() => {
|
|
@@ -6504,11 +6518,11 @@ class ProgressComponent {
|
|
|
6504
6518
|
return cfg[property];
|
|
6505
6519
|
}
|
|
6506
6520
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6507
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: ProgressComponent, isStandalone: true, selector: "eru-progress", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null }, isActive: { classPropertyName: "isActive", publicName: "isActive", isSignal: true, isRequired: false, transformFunction: null }, isDrillable: { classPropertyName: "isDrillable", publicName: "isDrillable", isSignal: true, isRequired: false, transformFunction: null }, columnWidth: { classPropertyName: "columnWidth", publicName: "columnWidth", isSignal: true, isRequired: false, transformFunction: null }, fieldSize: { classPropertyName: "fieldSize", publicName: "fieldSize", isSignal: true, isRequired: false, transformFunction: null }, eruGridStore: { classPropertyName: "eruGridStore", publicName: "eruGridStore", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus", drilldownClick: "drilldownClick", editModeChange: "editModeChange" }, ngImport: i0, template: "<div class=\"progress-input-container\" [class.disabled]=\"!isActive()\"
|
|
6521
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: ProgressComponent, isStandalone: true, selector: "eru-progress", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null }, isActive: { classPropertyName: "isActive", publicName: "isActive", isSignal: true, isRequired: false, transformFunction: null }, isDrillable: { classPropertyName: "isDrillable", publicName: "isDrillable", isSignal: true, isRequired: false, transformFunction: null }, columnWidth: { classPropertyName: "columnWidth", publicName: "columnWidth", isSignal: true, isRequired: false, transformFunction: null }, fieldSize: { classPropertyName: "fieldSize", publicName: "fieldSize", isSignal: true, isRequired: false, transformFunction: null }, eruGridStore: { classPropertyName: "eruGridStore", publicName: "eruGridStore", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus", drilldownClick: "drilldownClick", editModeChange: "editModeChange" }, ngImport: i0, template: "<div class=\"progress-input-container\" [class.disabled]=\"!isActive()\"\n [style.--mat-slider-active-track-color]=\"barColor()\"\n [style.--mat-slider-disabled-active-track-color]=\"barColor()\"\n [style.--mat-slider-with-tick-marks-active-container-color]=\"barColor()\"\n [style.--mat-slider-handle-color]=\"barColor()\"\n [style.--mat-slider-focus-handle-color]=\"barColor()\"\n [style.--mat-slider-hover-handle-color]=\"barColor()\"\n [style.--mat-slider-disabled-handle-color]=\"barColor()\"\n [style.--mat-slider-ripple-color]=\"barColor()\"\n (dblclick)=\"onActivate(); $event.stopPropagation()\">\n <span class=\"progress-value\" [style.color]=\"barColor()\">{{displayValue()}}%</span>\n <mat-slider \n class=\"progress-slider\" \n [min]=\"0\" \n [max]=\"100\" \n [step]=\"1\" \n [discrete]=\"true\" \n [showTickMarks]=\"true\"\n [disabled]=\"!isActive()\">\n <input \n matSliderThumb \n [ngModel]=\"displayValue()\" \n (ngModelChange)=\"onValueChange($event)\" \n (blur)=\"onBlur()\"\n [disabled]=\"!isActive()\">\n </mat-slider>\n</div>\n\n", styles: [":root{--mat-slider-with-overlap-handle-outline-color: var(--grid-primary, #6750a4) !important;--mat-slider-with-tick-marks-active-container-color: var(--grid-primary, #6750a4) !important;--mat-slider-handle-height: 12px;--mat-slider-handle-width: 12px;--mat-slider-disabled-active-track-color: var(--grid-primary, #6750a4);--mat-slider-active-track-color: var(--grid-primary, #6750a4)}:host{display:block;height:100%;width:100%}.progress-bar-container{width:100%;height:100%;min-height:30px;position:relative;background:#e0e0e0;border-radius:4px;cursor:pointer}.progress-bar{height:100%;background:var(--grid-primary, #6750a4);transition:width .3s;border-radius:4px}.progress-text{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:var(--grid-font-size-body, 12px);font-weight:500;color:var(--grid-on-surface, #1d1b20);pointer-events:none;z-index:1}.drillable-value{color:inherit;text-decoration:underline;cursor:pointer;pointer-events:auto}.drillable-value:hover{opacity:.8}.progress-input-container{display:flex;flex-direction:row-reverse;align-items:center;padding:4px;width:100%;box-sizing:border-box;overflow:visible;max-height:30px}.progress-input-container.disabled{cursor:pointer}.progress-input-container.disabled .progress-slider{pointer-events:none}.progress-value{font-size:var(--grid-font-size-body, 12px);font-weight:500;color:var(--grid-on-surface, #1d1b20);flex:0 0 auto;min-width:35px;text-align:right;white-space:nowrap;flex-shrink:0}.progress-slider{flex:1 1 0%;min-width:0!important;max-width:100%!important;overflow:visible}.progress-slider .mdc-slider__track{left:0!important}.progress-slider .mdc-slider{padding-left:0!important;margin-left:0!important}.progress-slider .mdc-slider__value-indicator,.progress-slider .mdc-slider__value-indicator-container{display:none!important;visibility:hidden!important}.progress-slider .mdc-slider__thumb:before,.progress-slider .mdc-slider__thumb:after{display:none!important;content:none!important}.progress-slider .mdc-slider__thumb-knob:before,.progress-slider .mdc-slider__thumb-knob:after{display:none!important;content:none!important}.progress-slider [class*=value-indicator],.progress-slider [class*=tooltip]{display:none!important}\n"], 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: "ngmodule", type: MatSliderModule }, { kind: "component", type: i2$3.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i2$3.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6508
6522
|
}
|
|
6509
6523
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ProgressComponent, decorators: [{
|
|
6510
6524
|
type: Component,
|
|
6511
|
-
args: [{ selector: 'eru-progress', standalone: true, imports: [FormsModule, MatSliderModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"progress-input-container\" [class.disabled]=\"!isActive()\"
|
|
6525
|
+
args: [{ selector: 'eru-progress', standalone: true, imports: [FormsModule, MatSliderModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"progress-input-container\" [class.disabled]=\"!isActive()\"\n [style.--mat-slider-active-track-color]=\"barColor()\"\n [style.--mat-slider-disabled-active-track-color]=\"barColor()\"\n [style.--mat-slider-with-tick-marks-active-container-color]=\"barColor()\"\n [style.--mat-slider-handle-color]=\"barColor()\"\n [style.--mat-slider-focus-handle-color]=\"barColor()\"\n [style.--mat-slider-hover-handle-color]=\"barColor()\"\n [style.--mat-slider-disabled-handle-color]=\"barColor()\"\n [style.--mat-slider-ripple-color]=\"barColor()\"\n (dblclick)=\"onActivate(); $event.stopPropagation()\">\n <span class=\"progress-value\" [style.color]=\"barColor()\">{{displayValue()}}%</span>\n <mat-slider \n class=\"progress-slider\" \n [min]=\"0\" \n [max]=\"100\" \n [step]=\"1\" \n [discrete]=\"true\" \n [showTickMarks]=\"true\"\n [disabled]=\"!isActive()\">\n <input \n matSliderThumb \n [ngModel]=\"displayValue()\" \n (ngModelChange)=\"onValueChange($event)\" \n (blur)=\"onBlur()\"\n [disabled]=\"!isActive()\">\n </mat-slider>\n</div>\n\n", styles: [":root{--mat-slider-with-overlap-handle-outline-color: var(--grid-primary, #6750a4) !important;--mat-slider-with-tick-marks-active-container-color: var(--grid-primary, #6750a4) !important;--mat-slider-handle-height: 12px;--mat-slider-handle-width: 12px;--mat-slider-disabled-active-track-color: var(--grid-primary, #6750a4);--mat-slider-active-track-color: var(--grid-primary, #6750a4)}:host{display:block;height:100%;width:100%}.progress-bar-container{width:100%;height:100%;min-height:30px;position:relative;background:#e0e0e0;border-radius:4px;cursor:pointer}.progress-bar{height:100%;background:var(--grid-primary, #6750a4);transition:width .3s;border-radius:4px}.progress-text{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:var(--grid-font-size-body, 12px);font-weight:500;color:var(--grid-on-surface, #1d1b20);pointer-events:none;z-index:1}.drillable-value{color:inherit;text-decoration:underline;cursor:pointer;pointer-events:auto}.drillable-value:hover{opacity:.8}.progress-input-container{display:flex;flex-direction:row-reverse;align-items:center;padding:4px;width:100%;box-sizing:border-box;overflow:visible;max-height:30px}.progress-input-container.disabled{cursor:pointer}.progress-input-container.disabled .progress-slider{pointer-events:none}.progress-value{font-size:var(--grid-font-size-body, 12px);font-weight:500;color:var(--grid-on-surface, #1d1b20);flex:0 0 auto;min-width:35px;text-align:right;white-space:nowrap;flex-shrink:0}.progress-slider{flex:1 1 0%;min-width:0!important;max-width:100%!important;overflow:visible}.progress-slider .mdc-slider__track{left:0!important}.progress-slider .mdc-slider{padding-left:0!important;margin-left:0!important}.progress-slider .mdc-slider__value-indicator,.progress-slider .mdc-slider__value-indicator-container{display:none!important;visibility:hidden!important}.progress-slider .mdc-slider__thumb:before,.progress-slider .mdc-slider__thumb:after{display:none!important;content:none!important}.progress-slider .mdc-slider__thumb-knob:before,.progress-slider .mdc-slider__thumb-knob:after{display:none!important;content:none!important}.progress-slider [class*=value-indicator],.progress-slider [class*=tooltip]{display:none!important}\n"] }]
|
|
6512
6526
|
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], isEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditable", required: false }] }], isActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "isActive", required: false }] }], isDrillable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDrillable", required: false }] }], columnWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnWidth", required: false }] }], fieldSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldSize", required: false }] }], eruGridStore: [{ type: i0.Input, args: [{ isSignal: true, alias: "eruGridStore", required: false }] }], valueChange: [{
|
|
6513
6527
|
type: Output
|
|
6514
6528
|
}], blur: [{
|
|
@@ -7054,8 +7068,6 @@ class StatusComponent {
|
|
|
7054
7068
|
openOptions = filter(openOptions);
|
|
7055
7069
|
closeOptions = filter(closeOptions);
|
|
7056
7070
|
}
|
|
7057
|
-
console.log('openOptions', openOptions);
|
|
7058
|
-
console.log('closeOptions', closeOptions);
|
|
7059
7071
|
return { open: openOptions, close: closeOptions };
|
|
7060
7072
|
}, ...(ngDevMode ? [{ debugName: "groupedOptions" }] : []));
|
|
7061
7073
|
filteredOptions = computed(() => {
|
|
@@ -8310,7 +8322,6 @@ class AttachmentComponent {
|
|
|
8310
8322
|
this.pendingViewRequests.forEach((requestKey, fileName) => {
|
|
8311
8323
|
if (dynamicDataMap.has(requestKey)) {
|
|
8312
8324
|
const data = dynamicDataMap.get(requestKey);
|
|
8313
|
-
console.log('[eru-attachment] view-file response received', { requestKey, data });
|
|
8314
8325
|
if (data && data.file && data.file_type) {
|
|
8315
8326
|
this.openBase64InNewTab(data.file, data.file_type, fileName);
|
|
8316
8327
|
}
|
|
@@ -8485,7 +8496,6 @@ class AttachmentComponent {
|
|
|
8485
8496
|
const requestKey = `ds_view_file_grid:${file}`;
|
|
8486
8497
|
this.pendingViewRequests.set(file, requestKey);
|
|
8487
8498
|
store.setDynamicDataRequest(requestKey);
|
|
8488
|
-
console.log('[eru-attachment] view-file request fired', { requestKey });
|
|
8489
8499
|
}
|
|
8490
8500
|
openBase64InNewTab(base64, fileType, fileName) {
|
|
8491
8501
|
try {
|
|
@@ -9749,7 +9759,6 @@ class DataCellComponent {
|
|
|
9749
9759
|
// })
|
|
9750
9760
|
}
|
|
9751
9761
|
fileInputChange(e) {
|
|
9752
|
-
debugger;
|
|
9753
9762
|
Array.from(e.target.files).forEach((file) => {
|
|
9754
9763
|
this.processFile(file);
|
|
9755
9764
|
});
|
|
@@ -10406,8 +10415,8 @@ const OPTION_FIELDS = [
|
|
|
10406
10415
|
showWhen: f => f.option_type === 'API',
|
|
10407
10416
|
},
|
|
10408
10417
|
];
|
|
10409
|
-
// Priority options are always static and carry a colour — no option source.
|
|
10410
|
-
const
|
|
10418
|
+
// Priority and tag options are always static and carry a colour — no option source.
|
|
10419
|
+
const STATIC_COLOR_OPTION_FIELDS = [
|
|
10411
10420
|
{ key: 'options', label: 'Options', control: 'color_list', defaultColor: '#9CA3AF' },
|
|
10412
10421
|
];
|
|
10413
10422
|
const DATATYPE_FIELDS = {
|
|
@@ -10415,8 +10424,8 @@ const DATATYPE_FIELDS = {
|
|
|
10415
10424
|
currency: NUMBER_FIELDS,
|
|
10416
10425
|
dropdown_single_select: OPTION_FIELDS,
|
|
10417
10426
|
dropdown_multi_select: OPTION_FIELDS,
|
|
10418
|
-
tag:
|
|
10419
|
-
priority:
|
|
10427
|
+
tag: STATIC_COLOR_OPTION_FIELDS,
|
|
10428
|
+
priority: STATIC_COLOR_OPTION_FIELDS,
|
|
10420
10429
|
status: [
|
|
10421
10430
|
{ key: 'open_status', label: 'Open statuses', control: 'color_list', defaultColor: '#22C55E' },
|
|
10422
10431
|
{ key: 'close_status', label: 'Close statuses', control: 'color_list', defaultColor: '#EF4444' },
|
|
@@ -10433,6 +10442,9 @@ const DATATYPE_FIELDS = {
|
|
|
10433
10442
|
],
|
|
10434
10443
|
phone: [{ key: 'default_country', label: 'Default country (ISO)', control: 'text' }],
|
|
10435
10444
|
textbox: [{ key: 'data_length', label: 'Max length', control: 'number' }],
|
|
10445
|
+
progress: [
|
|
10446
|
+
{ key: 'color_ranges', label: 'Colour ranges (by %)', control: 'range_color_list' },
|
|
10447
|
+
],
|
|
10436
10448
|
};
|
|
10437
10449
|
function getMetaFields(field) {
|
|
10438
10450
|
if (!field)
|
|
@@ -10508,11 +10520,40 @@ class ColumnDesignPanelComponent {
|
|
|
10508
10520
|
const items = this.colorListItems(key).map((it, i) => i === index ? { ...it, color } : it);
|
|
10509
10521
|
this.patch(key, items);
|
|
10510
10522
|
}
|
|
10523
|
+
// ── Range-colour-list control (value ranges -> colour, e.g. progress) ──
|
|
10524
|
+
rangeListItems(key) {
|
|
10525
|
+
const arr = this.field()?.[key];
|
|
10526
|
+
if (!Array.isArray(arr))
|
|
10527
|
+
return [];
|
|
10528
|
+
return arr.map(it => ({
|
|
10529
|
+
from: Number(it?.from) || 0,
|
|
10530
|
+
to: Number(it?.to) || 0,
|
|
10531
|
+
color: it?.color ?? '#22C55E',
|
|
10532
|
+
}));
|
|
10533
|
+
}
|
|
10534
|
+
addRangeItem(key) {
|
|
10535
|
+
const items = this.rangeListItems(key);
|
|
10536
|
+
const lastTo = items.length ? items[items.length - 1].to : -1;
|
|
10537
|
+
const from = Math.min(100, lastTo + 1);
|
|
10538
|
+
this.patch(key, [...items, { from, to: 100, color: '#22C55E' }]);
|
|
10539
|
+
}
|
|
10540
|
+
removeRangeItem(key, index) {
|
|
10541
|
+
this.patch(key, this.rangeListItems(key).filter((_, i) => i !== index));
|
|
10542
|
+
}
|
|
10543
|
+
onRangeChange(key, index, prop, value) {
|
|
10544
|
+
const num = value === '' ? 0 : Number(value);
|
|
10545
|
+
const items = this.rangeListItems(key).map((it, i) => i === index ? { ...it, [prop]: num } : it);
|
|
10546
|
+
this.patch(key, items);
|
|
10547
|
+
}
|
|
10548
|
+
onRangeColorChange(key, index, color) {
|
|
10549
|
+
const items = this.rangeListItems(key).map((it, i) => i === index ? { ...it, color } : it);
|
|
10550
|
+
this.patch(key, items);
|
|
10551
|
+
}
|
|
10511
10552
|
close() {
|
|
10512
10553
|
this.gridStore.selectDesignColumn(null);
|
|
10513
10554
|
}
|
|
10514
10555
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ColumnDesignPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10515
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: ColumnDesignPanelComponent, isStandalone: true, selector: "eru-column-design-panel", ngImport: i0, template: "@if (isOpen()) {\n<div class=\"design-panel-backdrop\" (click)=\"close()\"></div>\n}\n<aside class=\"column-design-panel\" [class.open]=\"isOpen()\">\n @if (field(); as col) {\n <header class=\"design-panel-header\">\n <div class=\"design-panel-title\">\n <mat-icon>tune</mat-icon>\n <span>{{ col.label || col.name }}</span>\n </div>\n <button mat-icon-button (click)=\"close()\" title=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"design-panel-body\">\n @for (def of metaFields(); track def.key) {\n <div class=\"design-field\">\n @switch (def.control) {\n\n @case ('text') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onText(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('number') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput type=\"number\" [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onNumber(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('select') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <mat-select [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onSelect(def.key, $event)\">\n @for (opt of def.options; track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @case ('checkbox') {\n <mat-checkbox [checked]=\"!!getValue(def.key)\" (change)=\"onCheckbox(def.key, $event.checked)\">\n {{ def.label }}\n </mat-checkbox>\n }\n\n @case ('list') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <textarea matInput rows=\"4\" [ngModel]=\"listText(def.key)\"\n (ngModelChange)=\"onList(def.key, $event)\"></textarea>\n </mat-form-field>\n }\n\n @case ('color_list') {\n <div class=\"color-list\">\n <label class=\"color-list-label\">{{ def.label }}</label>\n <div class=\"color-list-chips\">\n @for (opt of colorListItems(def.key); track $index) {\n <div class=\"color-list-chip\">\n <input type=\"color\" class=\"color-list-dot\" [value]=\"opt.color || '#9CA3AF'\"\n (input)=\"onColorListColorChange(def.key, $index, $any($event.target).value)\" title=\"Change colour\" />\n <span class=\"color-list-name\">{{ opt.name }}</span>\n <button type=\"button\" class=\"color-list-remove\" (click)=\"removeColorListItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n </div>\n <input class=\"color-list-add\" type=\"text\" placeholder=\"Add option, press Enter\"\n (keydown.enter)=\"addColorListItem(def.key, $any($event.target).value, def.defaultColor || '#9CA3AF'); $any($event.target).value = ''\" />\n </div>\n }\n\n }\n </div>\n }\n </div>\n }\n</aside>\n", styles: [".design-panel-backdrop{position:fixed;inset:0;background:#0000002e;z-index:1000}.column-design-panel{position:fixed;top:0;right:0;bottom:0;width:360px;max-width:90vw;background:var(--grid-surface, #fff);box-shadow:-4px 0 16px #00000029;transform:translate(100%);transition:transform .22s ease;z-index:1001;display:flex;flex-direction:column;font-family:var(--grid-font-family, inherit)}.column-design-panel.open{transform:translate(0)}.design-panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--grid-border-color, #e0e0e0)}.design-panel-header .design-panel-title{display:flex;align-items:center;gap:8px;font-weight:600;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.design-panel-body{padding:16px;overflow-y:auto;flex:1}.design-panel-body .design-field{margin-bottom:4px}.design-panel-body .design-field .full-width{width:100%}.design-panel-body .design-field mat-checkbox{display:block;margin:8px 0 16px}.design-panel-body .color-list{margin:4px 0 16px}.design-panel-body .color-list .color-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.design-panel-body .color-list .color-list-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.design-panel-body .color-list .color-list-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px 3px 4px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:16px;background:var(--grid-surface, #fff);font-size:12px}.design-panel-body .color-list .color-list-dot{width:18px;height:18px;padding:0;border:none;background:none;border-radius:50%;cursor:pointer}.design-panel-body .color-list .color-list-remove{border:none;background:none;cursor:pointer;font-size:14px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.design-panel-body .color-list .color-list-remove:hover{color:var(--grid-error, #b3261e)}.design-panel-body .color-list .color-list-add{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.design-panel-body .color-list .color-list-add:focus{border-color:var(--grid-primary, #6750a4)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { 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: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i2$2.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
10556
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: ColumnDesignPanelComponent, isStandalone: true, selector: "eru-column-design-panel", ngImport: i0, template: "@if (isOpen()) {\n<div class=\"design-panel-backdrop\" (click)=\"close()\"></div>\n}\n<aside class=\"column-design-panel\" [class.open]=\"isOpen()\">\n @if (field(); as col) {\n <header class=\"design-panel-header\">\n <div class=\"design-panel-title\">\n <mat-icon>tune</mat-icon>\n <span>{{ col.label || col.name }}</span>\n </div>\n <button mat-icon-button (click)=\"close()\" title=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"design-panel-body\">\n @for (def of metaFields(); track def.key) {\n <div class=\"design-field\">\n @switch (def.control) {\n\n @case ('text') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onText(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('number') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput type=\"number\" [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onNumber(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('select') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <mat-select [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onSelect(def.key, $event)\">\n @for (opt of def.options; track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @case ('checkbox') {\n <mat-checkbox [checked]=\"!!getValue(def.key)\" (change)=\"onCheckbox(def.key, $event.checked)\">\n {{ def.label }}\n </mat-checkbox>\n }\n\n @case ('list') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <textarea matInput rows=\"4\" [ngModel]=\"listText(def.key)\"\n (ngModelChange)=\"onList(def.key, $event)\"></textarea>\n </mat-form-field>\n }\n\n @case ('color_list') {\n <div class=\"color-list\">\n <label class=\"color-list-label\">{{ def.label }}</label>\n <div class=\"color-list-chips\">\n @for (opt of colorListItems(def.key); track $index) {\n <div class=\"color-list-chip\">\n <input type=\"color\" class=\"color-list-dot\" [value]=\"opt.color || '#9CA3AF'\"\n (input)=\"onColorListColorChange(def.key, $index, $any($event.target).value)\" title=\"Change colour\" />\n <span class=\"color-list-name\">{{ opt.name }}</span>\n <button type=\"button\" class=\"color-list-remove\" (click)=\"removeColorListItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n </div>\n <input class=\"color-list-add\" type=\"text\" placeholder=\"Add option, press Enter\"\n (keydown.enter)=\"addColorListItem(def.key, $any($event.target).value, def.defaultColor || '#9CA3AF'); $any($event.target).value = ''\" />\n </div>\n }\n\n @case ('range_color_list') {\n <div class=\"range-list\">\n <label class=\"range-list-label\">{{ def.label }}</label>\n @for (r of rangeListItems(def.key); track $index) {\n <div class=\"range-list-row\">\n <input type=\"number\" class=\"range-list-num\" min=\"0\" max=\"100\" [value]=\"r.from\"\n (input)=\"onRangeChange(def.key, $index, 'from', $any($event.target).value)\" title=\"From\" />\n <span class=\"range-list-sep\">\u2013</span>\n <input type=\"number\" class=\"range-list-num\" min=\"0\" max=\"100\" [value]=\"r.to\"\n (input)=\"onRangeChange(def.key, $index, 'to', $any($event.target).value)\" title=\"To\" />\n <input type=\"color\" class=\"range-list-color\" [value]=\"r.color || '#22C55E'\"\n (input)=\"onRangeColorChange(def.key, $index, $any($event.target).value)\" title=\"Colour\" />\n <button type=\"button\" class=\"range-list-remove\" (click)=\"removeRangeItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n <button type=\"button\" class=\"range-list-add\" (click)=\"addRangeItem(def.key)\">+ Add range</button>\n </div>\n }\n\n }\n </div>\n }\n </div>\n }\n</aside>\n", styles: [".design-panel-backdrop{position:fixed;inset:0;background:#0000002e;z-index:1000}.column-design-panel{position:fixed;top:0;right:0;bottom:0;width:360px;max-width:90vw;background:var(--grid-surface, #fff);box-shadow:-4px 0 16px #00000029;transform:translate(100%);transition:transform .22s ease;z-index:1001;display:flex;flex-direction:column;font-family:var(--grid-font-family, inherit)}.column-design-panel.open{transform:translate(0)}.design-panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--grid-border-color, #e0e0e0)}.design-panel-header .design-panel-title{display:flex;align-items:center;gap:8px;font-weight:600;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.design-panel-body{padding:16px;overflow-y:auto;flex:1}.design-panel-body .design-field{margin-bottom:4px}.design-panel-body .design-field .full-width{width:100%}.design-panel-body .design-field mat-checkbox{display:block;margin:8px 0 16px}.design-panel-body .color-list{margin:4px 0 16px}.design-panel-body .color-list .color-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.design-panel-body .color-list .color-list-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.design-panel-body .color-list .color-list-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px 3px 4px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:16px;background:var(--grid-surface, #fff);font-size:12px}.design-panel-body .color-list .color-list-dot{width:18px;height:18px;padding:0;border:none;background:none;border-radius:50%;cursor:pointer}.design-panel-body .color-list .color-list-remove{border:none;background:none;cursor:pointer;font-size:14px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.design-panel-body .color-list .color-list-remove:hover{color:var(--grid-error, #b3261e)}.design-panel-body .color-list .color-list-add{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.design-panel-body .color-list .color-list-add:focus{border-color:var(--grid-primary, #6750a4)}.range-list{margin:4px 0 16px}.range-list .range-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.range-list .range-list-row{display:flex;align-items:center;gap:6px;margin-bottom:6px}.range-list .range-list-num{width:56px;padding:6px 8px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.range-list .range-list-num:focus{border-color:var(--grid-primary, #6750a4)}.range-list .range-list-sep{color:var(--grid-on-surface-variant, #49454f)}.range-list .range-list-color{width:28px;height:28px;padding:0;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;background:none;cursor:pointer}.range-list .range-list-remove{margin-left:auto;border:none;background:none;cursor:pointer;font-size:18px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.range-list .range-list-remove:hover{color:var(--grid-error, #b3261e)}.range-list .range-list-add{margin-top:2px;padding:6px 10px;border:1px dashed var(--grid-outline-variant, #cac4d0);border-radius:6px;background:none;font-size:13px;cursor:pointer;color:var(--grid-primary, #6750a4)}.range-list .range-list-add:hover{background:var(--grid-surface-variant, #f3edf7)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { 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: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i2$2.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
10516
10557
|
}
|
|
10517
10558
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ColumnDesignPanelComponent, decorators: [{
|
|
10518
10559
|
type: Component,
|
|
@@ -10525,7 +10566,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
10525
10566
|
MatCheckboxModule,
|
|
10526
10567
|
MatIconModule,
|
|
10527
10568
|
MatButtonModule,
|
|
10528
|
-
], template: "@if (isOpen()) {\n<div class=\"design-panel-backdrop\" (click)=\"close()\"></div>\n}\n<aside class=\"column-design-panel\" [class.open]=\"isOpen()\">\n @if (field(); as col) {\n <header class=\"design-panel-header\">\n <div class=\"design-panel-title\">\n <mat-icon>tune</mat-icon>\n <span>{{ col.label || col.name }}</span>\n </div>\n <button mat-icon-button (click)=\"close()\" title=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"design-panel-body\">\n @for (def of metaFields(); track def.key) {\n <div class=\"design-field\">\n @switch (def.control) {\n\n @case ('text') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onText(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('number') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput type=\"number\" [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onNumber(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('select') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <mat-select [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onSelect(def.key, $event)\">\n @for (opt of def.options; track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @case ('checkbox') {\n <mat-checkbox [checked]=\"!!getValue(def.key)\" (change)=\"onCheckbox(def.key, $event.checked)\">\n {{ def.label }}\n </mat-checkbox>\n }\n\n @case ('list') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <textarea matInput rows=\"4\" [ngModel]=\"listText(def.key)\"\n (ngModelChange)=\"onList(def.key, $event)\"></textarea>\n </mat-form-field>\n }\n\n @case ('color_list') {\n <div class=\"color-list\">\n <label class=\"color-list-label\">{{ def.label }}</label>\n <div class=\"color-list-chips\">\n @for (opt of colorListItems(def.key); track $index) {\n <div class=\"color-list-chip\">\n <input type=\"color\" class=\"color-list-dot\" [value]=\"opt.color || '#9CA3AF'\"\n (input)=\"onColorListColorChange(def.key, $index, $any($event.target).value)\" title=\"Change colour\" />\n <span class=\"color-list-name\">{{ opt.name }}</span>\n <button type=\"button\" class=\"color-list-remove\" (click)=\"removeColorListItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n </div>\n <input class=\"color-list-add\" type=\"text\" placeholder=\"Add option, press Enter\"\n (keydown.enter)=\"addColorListItem(def.key, $any($event.target).value, def.defaultColor || '#9CA3AF'); $any($event.target).value = ''\" />\n </div>\n }\n\n }\n </div>\n }\n </div>\n }\n</aside>\n", styles: [".design-panel-backdrop{position:fixed;inset:0;background:#0000002e;z-index:1000}.column-design-panel{position:fixed;top:0;right:0;bottom:0;width:360px;max-width:90vw;background:var(--grid-surface, #fff);box-shadow:-4px 0 16px #00000029;transform:translate(100%);transition:transform .22s ease;z-index:1001;display:flex;flex-direction:column;font-family:var(--grid-font-family, inherit)}.column-design-panel.open{transform:translate(0)}.design-panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--grid-border-color, #e0e0e0)}.design-panel-header .design-panel-title{display:flex;align-items:center;gap:8px;font-weight:600;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.design-panel-body{padding:16px;overflow-y:auto;flex:1}.design-panel-body .design-field{margin-bottom:4px}.design-panel-body .design-field .full-width{width:100%}.design-panel-body .design-field mat-checkbox{display:block;margin:8px 0 16px}.design-panel-body .color-list{margin:4px 0 16px}.design-panel-body .color-list .color-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.design-panel-body .color-list .color-list-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.design-panel-body .color-list .color-list-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px 3px 4px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:16px;background:var(--grid-surface, #fff);font-size:12px}.design-panel-body .color-list .color-list-dot{width:18px;height:18px;padding:0;border:none;background:none;border-radius:50%;cursor:pointer}.design-panel-body .color-list .color-list-remove{border:none;background:none;cursor:pointer;font-size:14px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.design-panel-body .color-list .color-list-remove:hover{color:var(--grid-error, #b3261e)}.design-panel-body .color-list .color-list-add{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.design-panel-body .color-list .color-list-add:focus{border-color:var(--grid-primary, #6750a4)}\n"] }]
|
|
10569
|
+
], template: "@if (isOpen()) {\n<div class=\"design-panel-backdrop\" (click)=\"close()\"></div>\n}\n<aside class=\"column-design-panel\" [class.open]=\"isOpen()\">\n @if (field(); as col) {\n <header class=\"design-panel-header\">\n <div class=\"design-panel-title\">\n <mat-icon>tune</mat-icon>\n <span>{{ col.label || col.name }}</span>\n </div>\n <button mat-icon-button (click)=\"close()\" title=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"design-panel-body\">\n @for (def of metaFields(); track def.key) {\n <div class=\"design-field\">\n @switch (def.control) {\n\n @case ('text') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onText(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('number') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <input matInput type=\"number\" [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onNumber(def.key, $event)\" />\n </mat-form-field>\n }\n\n @case ('select') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <mat-select [ngModel]=\"getValue(def.key)\" (ngModelChange)=\"onSelect(def.key, $event)\">\n @for (opt of def.options; track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @case ('checkbox') {\n <mat-checkbox [checked]=\"!!getValue(def.key)\" (change)=\"onCheckbox(def.key, $event.checked)\">\n {{ def.label }}\n </mat-checkbox>\n }\n\n @case ('list') {\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>{{ def.label }}</mat-label>\n <textarea matInput rows=\"4\" [ngModel]=\"listText(def.key)\"\n (ngModelChange)=\"onList(def.key, $event)\"></textarea>\n </mat-form-field>\n }\n\n @case ('color_list') {\n <div class=\"color-list\">\n <label class=\"color-list-label\">{{ def.label }}</label>\n <div class=\"color-list-chips\">\n @for (opt of colorListItems(def.key); track $index) {\n <div class=\"color-list-chip\">\n <input type=\"color\" class=\"color-list-dot\" [value]=\"opt.color || '#9CA3AF'\"\n (input)=\"onColorListColorChange(def.key, $index, $any($event.target).value)\" title=\"Change colour\" />\n <span class=\"color-list-name\">{{ opt.name }}</span>\n <button type=\"button\" class=\"color-list-remove\" (click)=\"removeColorListItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n </div>\n <input class=\"color-list-add\" type=\"text\" placeholder=\"Add option, press Enter\"\n (keydown.enter)=\"addColorListItem(def.key, $any($event.target).value, def.defaultColor || '#9CA3AF'); $any($event.target).value = ''\" />\n </div>\n }\n\n @case ('range_color_list') {\n <div class=\"range-list\">\n <label class=\"range-list-label\">{{ def.label }}</label>\n @for (r of rangeListItems(def.key); track $index) {\n <div class=\"range-list-row\">\n <input type=\"number\" class=\"range-list-num\" min=\"0\" max=\"100\" [value]=\"r.from\"\n (input)=\"onRangeChange(def.key, $index, 'from', $any($event.target).value)\" title=\"From\" />\n <span class=\"range-list-sep\">\u2013</span>\n <input type=\"number\" class=\"range-list-num\" min=\"0\" max=\"100\" [value]=\"r.to\"\n (input)=\"onRangeChange(def.key, $index, 'to', $any($event.target).value)\" title=\"To\" />\n <input type=\"color\" class=\"range-list-color\" [value]=\"r.color || '#22C55E'\"\n (input)=\"onRangeColorChange(def.key, $index, $any($event.target).value)\" title=\"Colour\" />\n <button type=\"button\" class=\"range-list-remove\" (click)=\"removeRangeItem(def.key, $index)\"\n title=\"Remove\">×</button>\n </div>\n }\n <button type=\"button\" class=\"range-list-add\" (click)=\"addRangeItem(def.key)\">+ Add range</button>\n </div>\n }\n\n }\n </div>\n }\n </div>\n }\n</aside>\n", styles: [".design-panel-backdrop{position:fixed;inset:0;background:#0000002e;z-index:1000}.column-design-panel{position:fixed;top:0;right:0;bottom:0;width:360px;max-width:90vw;background:var(--grid-surface, #fff);box-shadow:-4px 0 16px #00000029;transform:translate(100%);transition:transform .22s ease;z-index:1001;display:flex;flex-direction:column;font-family:var(--grid-font-family, inherit)}.column-design-panel.open{transform:translate(0)}.design-panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--grid-border-color, #e0e0e0)}.design-panel-header .design-panel-title{display:flex;align-items:center;gap:8px;font-weight:600;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.design-panel-body{padding:16px;overflow-y:auto;flex:1}.design-panel-body .design-field{margin-bottom:4px}.design-panel-body .design-field .full-width{width:100%}.design-panel-body .design-field mat-checkbox{display:block;margin:8px 0 16px}.design-panel-body .color-list{margin:4px 0 16px}.design-panel-body .color-list .color-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.design-panel-body .color-list .color-list-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.design-panel-body .color-list .color-list-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px 3px 4px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:16px;background:var(--grid-surface, #fff);font-size:12px}.design-panel-body .color-list .color-list-dot{width:18px;height:18px;padding:0;border:none;background:none;border-radius:50%;cursor:pointer}.design-panel-body .color-list .color-list-remove{border:none;background:none;cursor:pointer;font-size:14px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.design-panel-body .color-list .color-list-remove:hover{color:var(--grid-error, #b3261e)}.design-panel-body .color-list .color-list-add{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.design-panel-body .color-list .color-list-add:focus{border-color:var(--grid-primary, #6750a4)}.range-list{margin:4px 0 16px}.range-list .range-list-label{display:block;font-size:12px;color:var(--grid-on-surface-variant, #49454f);margin-bottom:6px}.range-list .range-list-row{display:flex;align-items:center;gap:6px;margin-bottom:6px}.range-list .range-list-num{width:56px;padding:6px 8px;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;font-size:13px;outline:none}.range-list .range-list-num:focus{border-color:var(--grid-primary, #6750a4)}.range-list .range-list-sep{color:var(--grid-on-surface-variant, #49454f)}.range-list .range-list-color{width:28px;height:28px;padding:0;border:1px solid var(--grid-outline-variant, #cac4d0);border-radius:6px;background:none;cursor:pointer}.range-list .range-list-remove{margin-left:auto;border:none;background:none;cursor:pointer;font-size:18px;line-height:1;color:var(--grid-on-surface-variant, #49454f)}.range-list .range-list-remove:hover{color:var(--grid-error, #b3261e)}.range-list .range-list-add{margin-top:2px;padding:6px 10px;border:1px dashed var(--grid-outline-variant, #cac4d0);border-radius:6px;background:none;font-size:13px;cursor:pointer;color:var(--grid-primary, #6750a4)}.range-list .range-list-add:hover{background:var(--grid-surface-variant, #f3edf7)}\n"] }]
|
|
10529
10570
|
}] });
|
|
10530
10571
|
|
|
10531
10572
|
/**
|
|
@@ -10657,6 +10698,14 @@ class EruGridComponent {
|
|
|
10657
10698
|
if (rowCount === 0) {
|
|
10658
10699
|
height = Math.min(minHeight / 2, 200);
|
|
10659
10700
|
}
|
|
10701
|
+
else if (groups.length === 1) {
|
|
10702
|
+
// Single (ungrouped) group — the common table case. Fill the grid's
|
|
10703
|
+
// available height so the table occupies the configured gridHeight and
|
|
10704
|
+
// only scrolls once the rows exceed it. Sizing to the estimated content
|
|
10705
|
+
// height instead (rowCount * ROW_HEIGHT) under-counts taller rows
|
|
10706
|
+
// (progress/status/chips), producing a scrollbar even for a few rows.
|
|
10707
|
+
height = availableViewHeight;
|
|
10708
|
+
}
|
|
10660
10709
|
else if (rowCount < 50 && rowsHeight < availableViewHeight) {
|
|
10661
10710
|
// Add scrollbar buffer so the CDK viewport has enough room to render all rows even
|
|
10662
10711
|
// when a horizontal scrollbar is present (scrollbar ~17px reduces usable height).
|
|
@@ -10911,6 +10960,11 @@ class EruGridComponent {
|
|
|
10911
10960
|
} */
|
|
10912
10961
|
ROWS_PER_PAGE = 50;
|
|
10913
10962
|
SCROLL_THRESHOLD = 10;
|
|
10963
|
+
/** Rows per lazy page: grid config `pageSize` if set, else ROWS_PER_PAGE. */
|
|
10964
|
+
get rowsPerPage() {
|
|
10965
|
+
const size = this.gridStore.configuration()?.pageSize;
|
|
10966
|
+
return typeof size === 'number' && size > 0 ? size : this.ROWS_PER_PAGE;
|
|
10967
|
+
}
|
|
10914
10968
|
lastLoadedGroupIds = new Set();
|
|
10915
10969
|
requestedGroupIds = new Set(); // Track which groups have been requested for data
|
|
10916
10970
|
columnsInitialized = signal(false, ...(ngDevMode ? [{ debugName: "columnsInitialized" }] : []));
|
|
@@ -11426,7 +11480,7 @@ class EruGridComponent {
|
|
|
11426
11480
|
groupId: group.id,
|
|
11427
11481
|
groupKey: group.key,
|
|
11428
11482
|
page: group.currentPage || 0,
|
|
11429
|
-
pageSize: group.totalRowCount < this.
|
|
11483
|
+
pageSize: group.totalRowCount < this.rowsPerPage ? group.totalRowCount : this.rowsPerPage,
|
|
11430
11484
|
timestamp: Date.now(),
|
|
11431
11485
|
groupTitle: group.title
|
|
11432
11486
|
};
|
|
@@ -12245,7 +12299,7 @@ class EruGridComponent {
|
|
|
12245
12299
|
groupId: group.id,
|
|
12246
12300
|
groupKey: group.key,
|
|
12247
12301
|
page: group.currentPage || 0,
|
|
12248
|
-
pageSize: this.
|
|
12302
|
+
pageSize: this.rowsPerPage,
|
|
12249
12303
|
timestamp: Date.now(),
|
|
12250
12304
|
groupTitle: group.title,
|
|
12251
12305
|
};
|