@fuentis/phoenix-ui 0.0.9-alpha.566 → 0.0.9-alpha.570

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.
@@ -3243,8 +3243,9 @@ function buildFileName(baseKey, ext = 'xlsx') {
3243
3243
  */
3244
3244
  const DEFAULT_MORE_ACTIONS = {
3245
3245
  context: tableButtonContext.MORE,
3246
- type: 'menu',
3246
+ type: tableActionType.MENU,
3247
3247
  buttonType: 'multiselect',
3248
+ label: '',
3248
3249
  items: [
3249
3250
  { key: 'EXPORT_PDF', label: 'ACTION.EXPORT_PDF', icon: 'pi pi-file-pdf' },
3250
3251
  { key: 'EXPORT_EXCEL', label: 'ACTION.EXPORT_EXCEL', icon: 'pi pi-file-excel' },
@@ -3336,13 +3337,116 @@ class TableComponent {
3336
3337
  skeletonRowCount = 8;
3337
3338
  /** Array used by Angular @for (must be iterable) */
3338
3339
  skeletonRows = Array.from({ length: this.skeletonRowCount });
3340
+ createSortWorkerInline() {
3341
+ const code = `
3342
+ const compareStringsNatural = (a,b) => a.localeCompare(b, undefined, { numeric:true, sensitivity:'base' });
3343
+
3344
+ const getNestedValue = (obj, path) => {
3345
+ try {
3346
+ if (!obj || !path) return undefined;
3347
+ if (Object.prototype.hasOwnProperty.call(obj, path)) {
3348
+ const v = obj[path];
3349
+ return (v && v.key !== undefined) ? v.key : v;
3350
+ }
3351
+ return path.split('.').reduce((acc, k) => (acc == null ? undefined : acc[k]), obj);
3352
+ } catch { return undefined; }
3353
+ };
3354
+
3355
+ const getComparableValue = (val, field, columnTypeMap, dateTypeKey='DATE') => {
3356
+ if (val === null || val === undefined) return '';
3357
+ const columnType = columnTypeMap?.[field];
3358
+
3359
+ if ((columnType || '').toString().toUpperCase() === 'BOOLEAN') {
3360
+ if (typeof val === 'boolean') return val ? 1 : 0;
3361
+ if (typeof val === 'number') return val ? 1 : 0;
3362
+ if (typeof val === 'string') {
3363
+ const s = val.trim().toLowerCase();
3364
+ if (['true','1','yes'].includes(s)) return 1;
3365
+ if (['false','0','no'].includes(s)) return 0;
3366
+ return '';
3367
+ }
3368
+ if (typeof val === 'object') {
3369
+ const v = val.value ?? val.key ?? val.enabled ?? val.checked;
3370
+ return getComparableValue(v, field, columnTypeMap, dateTypeKey);
3371
+ }
3372
+ return '';
3373
+ }
3374
+
3375
+ if (typeof val === 'number') return val;
3376
+
3377
+ if (typeof val === 'string') {
3378
+ if (columnType === dateTypeKey) {
3379
+ const t = Date.parse(val);
3380
+ return Number.isNaN(t) ? val.toLowerCase() : t;
3381
+ }
3382
+ return val.toLowerCase();
3383
+ }
3384
+
3385
+ if (val instanceof Date) return val.getTime();
3386
+
3387
+ if (typeof val === 'object') {
3388
+ if (val.from && val.to) {
3389
+ const from = Date.parse(val.from);
3390
+ const to = Date.parse(val.to);
3391
+ return (Number.isNaN(from) || Number.isNaN(to)) ? '' : (from + to);
3392
+ }
3393
+ if (Array.isArray(val) && val.length) return getComparableValue(val[0], field, columnTypeMap, dateTypeKey);
3394
+
3395
+ for (const k of ['name','value','label','key','date']) {
3396
+ if (val[k] !== undefined && val[k] !== null) return getComparableValue(val[k], field, columnTypeMap, dateTypeKey);
3397
+ }
3398
+ for (const v of Object.values(val)) {
3399
+ if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
3400
+ return getComparableValue(v, field, columnTypeMap, dateTypeKey);
3401
+ }
3402
+ }
3403
+ }
3404
+ return '';
3405
+ };
3406
+
3407
+ const sortData = (data, meta, columnTypeMap, dateTypeKey='DATE') => {
3408
+ const sorted = (data || []).slice();
3409
+ if (!meta?.length) return sorted;
3410
+
3411
+ sorted.sort((a,b) => {
3412
+ for (const s of meta) {
3413
+ const field = s.field;
3414
+ const order = s.order ?? 1;
3415
+
3416
+ const A = getComparableValue(getNestedValue(a, field), field, columnTypeMap, dateTypeKey);
3417
+ const B = getComparableValue(getNestedValue(b, field), field, columnTypeMap, dateTypeKey);
3418
+
3419
+ if (typeof A === 'string' && typeof B === 'string') {
3420
+ const c = compareStringsNatural(A,B);
3421
+ if (c !== 0) return c * order;
3422
+ } else {
3423
+ if (A < B) return -1 * order;
3424
+ if (A > B) return 1 * order;
3425
+ }
3426
+ }
3427
+ return 0;
3428
+ });
3429
+
3430
+ return sorted;
3431
+ };
3432
+
3433
+ self.addEventListener('message', (ev) => {
3434
+ const data = ev.data || {};
3435
+ const sorted = sortData(data.data, data.multiSortMeta, data.columnTypeMap, data.dateTypeKey || 'DATE');
3436
+ self.postMessage({ reqId: data.reqId, sorted });
3437
+ });
3438
+ `;
3439
+ const blob = new Blob([code], { type: 'text/javascript' });
3440
+ const url = URL.createObjectURL(blob);
3441
+ return new Worker(url); // classic worker, nema module MIME problema
3442
+ }
3339
3443
  constructor() {
3340
3444
  /**
3341
3445
  * Create sorting worker if supported.
3342
3446
  * Fallback is synchronous sort (still works, but can freeze on huge arrays).
3343
3447
  */
3344
3448
  if (typeof Worker !== 'undefined') {
3345
- this.sortWorker = new Worker(new URL('./table-sort.worker', import.meta.url), { type: 'module' });
3449
+ this.sortWorker = this.createSortWorkerInline();
3346
3450
  this.sortWorker.onmessage = (e) => {
3347
3451
  const { reqId, sorted } = e.data ?? {};
3348
3452
  if (reqId !== this.latestWorkerReqId)
@@ -3495,6 +3599,22 @@ class TableComponent {
3495
3599
  });
3496
3600
  return sorted;
3497
3601
  }
3602
+ normalizeInitialSort(cfg) {
3603
+ const s = cfg?.initialSort;
3604
+ // NEW style
3605
+ if (Array.isArray(s))
3606
+ return s.filter(x => !!x?.field);
3607
+ if (s && typeof s === 'object' && s.field)
3608
+ return [s];
3609
+ // LEGACY fallback
3610
+ if (cfg?.initialSortField) {
3611
+ return [{
3612
+ field: cfg.initialSortField,
3613
+ order: (cfg.initialSortOrder ?? 1),
3614
+ }];
3615
+ }
3616
+ return [];
3617
+ }
3498
3618
  /**
3499
3619
  * Applies initial sort once:
3500
3620
  * - Uses tableConfiguration.initialSortField if provided
@@ -3509,7 +3629,6 @@ class TableComponent {
3509
3629
  applyInitialSortIfNeeded() {
3510
3630
  if (this.initialSortApplied)
3511
3631
  return;
3512
- // if no data, just render empty fast and stop loading
3513
3632
  if (!this.allData?.length) {
3514
3633
  this.tableData = [];
3515
3634
  this.totalRecords = 0;
@@ -3519,33 +3638,32 @@ class TableComponent {
3519
3638
  }
3520
3639
  const cols = (this.selectedColumns?.length ? this.selectedColumns : this.columns) ?? [];
3521
3640
  if (!cols.length)
3522
- return; // columns not ready yet (ngOnChanges will call again)
3523
- // pick configured or fallback
3524
- const configuredField = this.tableConfiguration?.initialSortField;
3641
+ return;
3642
+ // 1) normalize config
3643
+ const cfgMeta = this.normalizeInitialSort(this.tableConfiguration);
3644
+ // 2) if nothing configured -> fallback first column ASC
3525
3645
  const fallbackField = cols[0]?.field;
3526
- const field = configuredField || fallbackField;
3527
- const exists = cols.some((c) => c?.field === field);
3528
- const finalField = exists ? field : fallbackField;
3529
- // if still no field, just render base
3530
- if (!finalField) {
3646
+ const wantedMeta = (cfgMeta.length ? cfgMeta : [{ field: fallbackField, order: 1 }])
3647
+ .filter(m => !!m?.field);
3648
+ // 3) keep only fields that exist in columns
3649
+ const validMeta = wantedMeta.filter(m => cols.some((c) => c?.field === m.field));
3650
+ const finalMeta = validMeta.length ? validMeta : [{ field: fallbackField, order: 1 }];
3651
+ if (!finalMeta[0]?.field) {
3531
3652
  this.tableData = this.allData;
3532
3653
  this.totalRecords = this.tableData.length;
3533
3654
  this.initialSortLoading = false;
3534
3655
  this.initialSortApplied = true;
3535
- this.cdr.markForCheck();
3536
3656
  return;
3537
3657
  }
3538
- const meta = [{ field: finalField, order: 1 }];
3539
3658
  try {
3540
3659
  if (this.dt)
3541
- this.dt.multiSortMeta = meta;
3660
+ this.dt.multiSortMeta = finalMeta;
3542
3661
  }
3543
3662
  catch { }
3544
- this.lastSortKey = JSON.stringify(meta);
3663
+ this.lastSortKey = JSON.stringify(finalMeta);
3545
3664
  this.initialSortApplied = true;
3546
- this.initialSortFieldApplied = finalField;
3547
- // keep loading true until worker returns
3548
- this.runWorkerSort(meta);
3665
+ this.initialSortFieldApplied = finalMeta[0].field;
3666
+ this.runWorkerSort(finalMeta);
3549
3667
  }
3550
3668
  // ============================================================
3551
3669
  // SEARCH / FILTER
@@ -3756,8 +3874,9 @@ class TableComponent {
3756
3874
  * Some tables allow click on first N columns to open details.
3757
3875
  */
3758
3876
  isColumnClickable(columnIndex) {
3759
- return (columnIndex < (this.tableConfiguration?.clickableColumnCount ?? 1) &&
3760
- this.tableConfiguration?.hasCellClick);
3877
+ const max = this.tableConfiguration?.clickableColumnCount ?? 1;
3878
+ const enabled = this.tableConfiguration?.hasCellClick ?? false;
3879
+ return columnIndex < max && enabled;
3761
3880
  }
3762
3881
  /**
3763
3882
  * Utility for copy icons inside cells (prevents row-click).
@@ -4272,6 +4391,9 @@ class ObjectItemDialogComponent {
4272
4391
  key: 'asd',
4273
4392
  rows: 20,
4274
4393
  selectionType: tableSelectionType.RADIO_BTN,
4394
+ actions: [],
4395
+ exportTable: false,
4396
+ filterConfiguration: []
4275
4397
  };
4276
4398
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ObjectItemDialogComponent, deps: [{ token: i1$2.DynamicDialogRef }, { token: i1$2.DynamicDialogConfig }], target: i0.ɵɵFactoryTarget.Component });
4277
4399
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ObjectItemDialogComponent, isStandalone: true, selector: "phoenix-object-item-dialog", viewQueries: [{ propertyName: "table", first: true, predicate: ["table"], descendants: true }], ngImport: i0, template: "<div style=\"overflow: hidden; padding-bottom: 50px\">\n <phoenix-table\n #table\n [data]=\"tableData\"\n [columns]=\"columns\"\n [tableConfiguration]=\"tableConfiguration\"\n (checkBoxSelection)=\"onSelectionChanged($event)\"\n >\n </phoenix-table>\n</div>\n<div class=\"absolute bottom-0 right-0 p-4\">\n <div class=\"flex gap-2\">\n <ng-container *ngFor=\"let action of actions\">\n <p-button\n [label]=\"action.label | translate\"\n [icon]=\"action.icon || ''\"\n [severity]=\"action.severity || 'primary'\"\n [disabled]=\"action.id === 'assign' && !hasSelection\"\n [id]=\"action.id\"\n (onClick)=\"actionClick(action.id)\"\n size=\"small\"\n ></p-button>\n </ng-container>\n</div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: TableComponent, selector: "phoenix-table", inputs: ["columns", "selectedColumnsInput", "tableConfiguration", "filters", "data"], outputs: ["actionClick", "rowSelection", "checkBoxSelection", "saveColumns"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: i3$2.TranslatePipe, name: "translate" }] });