codexly-ui 0.4.9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4920,6 +4920,9 @@ function resolveFileTypeVisual(name, mimeType) {
4920
4920
  return FILE_TYPE_BY_EXTENSION[ext] ?? FILE_TYPE_DEFAULT;
4921
4921
  }
4922
4922
 
4923
+ const DEFAULT_PRIMARY_ACTION = () => ({
4924
+ icon: 'close', color: 'red', tooltip: 'Eliminar',
4925
+ });
4923
4926
  class ClxUploadComponent {
4924
4927
  _themeSvc = inject(ClxThemeService);
4925
4928
  // ── Inputs ─────────────────────────────────────────────────────────────────
@@ -4936,10 +4939,18 @@ class ClxUploadComponent {
4936
4939
  * preview grid as newly-selected local files. The component never deletes these itself —
4937
4940
  * removing one only emits `existingFileRemove`, letting the caller decide (e.g. call the API). */
4938
4941
  existingFiles = input([], ...(ngDevMode ? [{ debugName: "existingFiles" }] : /* istanbul ignore next */ []));
4942
+ /** Resolves the badge button shown top-right on each thumbnail. Defaults to a red "Eliminar"
4943
+ * button wired to the built-in remove behavior. Return `null` for an item to hide it. */
4944
+ primaryAction = input(DEFAULT_PRIMARY_ACTION, ...(ngDevMode ? [{ debugName: "primaryAction" }] : /* istanbul ignore next */ []));
4945
+ /** Resolves an optional second badge button (bottom-right on each thumbnail). Hidden by
4946
+ * default — the caller owns what clicking it means (e.g. opening a settings modal); this
4947
+ * component only reports which item was clicked via `secondaryActionClick`. */
4948
+ secondaryAction = input(null, ...(ngDevMode ? [{ debugName: "secondaryAction" }] : /* istanbul ignore next */ []));
4939
4949
  // ── Outputs ────────────────────────────────────────────────────────────────
4940
4950
  onUploadError = output();
4941
4951
  onUploadSuccess = output();
4942
4952
  existingFileRemove = output();
4953
+ secondaryActionClick = output();
4943
4954
  // ── Internal state ─────────────────────────────────────────────────────────
4944
4955
  _files = signal([], ...(ngDevMode ? [{ debugName: "_files" }] : /* istanbul ignore next */ []));
4945
4956
  _isDragging = signal(false, ...(ngDevMode ? [{ debugName: "_isDragging" }] : /* istanbul ignore next */ []));
@@ -5104,6 +5115,18 @@ class ClxUploadComponent {
5104
5115
  if (files?.length)
5105
5116
  this._addFiles(files);
5106
5117
  }
5118
+ _resolvePrimaryAction(item) {
5119
+ return this.primaryAction()(item) ?? null;
5120
+ }
5121
+ _resolveSecondaryAction(item) {
5122
+ const resolver = this.secondaryAction();
5123
+ return resolver ? (resolver(item) ?? null) : null;
5124
+ }
5125
+ /** Built-in behavior for the default primary action (remove). Callers who override
5126
+ * `primaryAction` only for its visuals still get this same removal behavior. */
5127
+ _onPrimaryAction(item) {
5128
+ this._removeItem(item);
5129
+ }
5107
5130
  _removeItem(item) {
5108
5131
  if (item.kind === 'existing') {
5109
5132
  this.existingFileRemove.emit(item.existing.id);
@@ -5217,7 +5240,7 @@ class ClxUploadComponent {
5217
5240
  return `${(bytes / 1_048_576).toFixed(1)} MB`;
5218
5241
  }
5219
5242
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5220
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxUploadComponent, isStandalone: true, selector: "clx-upload", inputs: { multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, existingFiles: { classPropertyName: "existingFiles", publicName: "existingFiles", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onUploadError: "onUploadError", onUploadSuccess: "onUploadSuccess", existingFileRemove: "existingFileRemove" }, host: { classAttribute: "block w-full" }, viewQueries: [{ propertyName: "_fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
5243
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxUploadComponent, isStandalone: true, selector: "clx-upload", inputs: { multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, existingFiles: { classPropertyName: "existingFiles", publicName: "existingFiles", isSignal: true, isRequired: false, transformFunction: null }, primaryAction: { classPropertyName: "primaryAction", publicName: "primaryAction", isSignal: true, isRequired: false, transformFunction: null }, secondaryAction: { classPropertyName: "secondaryAction", publicName: "secondaryAction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onUploadError: "onUploadError", onUploadSuccess: "onUploadSuccess", existingFileRemove: "existingFileRemove", secondaryActionClick: "secondaryActionClick" }, host: { classAttribute: "block w-full" }, viewQueries: [{ propertyName: "_fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
5221
5244
  <!-- Hidden file input -->
5222
5245
  <input
5223
5246
  #fileInput
@@ -5286,19 +5309,41 @@ class ClxUploadComponent {
5286
5309
  }
5287
5310
  </div>
5288
5311
 
5289
- <button
5290
- clx-button
5291
- type="button"
5292
- variant="solid"
5293
- color="red"
5294
- shape="circle"
5295
- size="xxs"
5296
- icon="close"
5297
- [iconOnly]="true"
5298
- class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
5299
- [disabled]="_disabled()"
5300
- (click)="_removeItem(item); $event.stopPropagation()">
5301
- </button>
5312
+ @let primaryAction = _resolvePrimaryAction(item);
5313
+ @if (primaryAction) {
5314
+ <button
5315
+ clx-button
5316
+ type="button"
5317
+ variant="solid"
5318
+ [color]="primaryAction.color ?? 'red'"
5319
+ shape="circle"
5320
+ size="xxs"
5321
+ [icon]="primaryAction.icon"
5322
+ [iconOnly]="true"
5323
+ [clxTooltip]="primaryAction.tooltip ?? ''"
5324
+ class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
5325
+ [disabled]="_disabled()"
5326
+ (click)="_onPrimaryAction(item); $event.stopPropagation()">
5327
+ </button>
5328
+ }
5329
+
5330
+ @let secondaryAction = _resolveSecondaryAction(item);
5331
+ @if (secondaryAction) {
5332
+ <button
5333
+ clx-button
5334
+ type="button"
5335
+ variant="solid"
5336
+ [color]="secondaryAction.color ?? 'slate'"
5337
+ shape="circle"
5338
+ size="xxs"
5339
+ [icon]="secondaryAction.icon"
5340
+ [iconOnly]="true"
5341
+ [clxTooltip]="secondaryAction.tooltip ?? ''"
5342
+ class="absolute bottom-0 right-0 translate-y-1/2 translate-x-1/2 shadow-md z-10"
5343
+ [disabled]="_disabled()"
5344
+ (click)="secondaryActionClick.emit(item); $event.stopPropagation()">
5345
+ </button>
5346
+ }
5302
5347
 
5303
5348
  <div class="px-1.5 py-1 w-full min-w-0">
5304
5349
  <p class="text-[11px] leading-tight text-clx-text-label font-medium truncate" [title]="item.name">{{ item.name }}</p>
@@ -5318,14 +5363,14 @@ class ClxUploadComponent {
5318
5363
  {{ _errorMessage() }}
5319
5364
  </p>
5320
5365
  }
5321
- `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5366
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5322
5367
  }
5323
5368
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxUploadComponent, decorators: [{
5324
5369
  type: Component,
5325
5370
  args: [{
5326
5371
  selector: 'clx-upload',
5327
5372
  standalone: true,
5328
- imports: [ClxIconComponent, ClxButtonComponent],
5373
+ imports: [ClxIconComponent, ClxButtonComponent, ClxTooltipDirective],
5329
5374
  template: `
5330
5375
  <!-- Hidden file input -->
5331
5376
  <input
@@ -5395,19 +5440,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5395
5440
  }
5396
5441
  </div>
5397
5442
 
5398
- <button
5399
- clx-button
5400
- type="button"
5401
- variant="solid"
5402
- color="red"
5403
- shape="circle"
5404
- size="xxs"
5405
- icon="close"
5406
- [iconOnly]="true"
5407
- class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
5408
- [disabled]="_disabled()"
5409
- (click)="_removeItem(item); $event.stopPropagation()">
5410
- </button>
5443
+ @let primaryAction = _resolvePrimaryAction(item);
5444
+ @if (primaryAction) {
5445
+ <button
5446
+ clx-button
5447
+ type="button"
5448
+ variant="solid"
5449
+ [color]="primaryAction.color ?? 'red'"
5450
+ shape="circle"
5451
+ size="xxs"
5452
+ [icon]="primaryAction.icon"
5453
+ [iconOnly]="true"
5454
+ [clxTooltip]="primaryAction.tooltip ?? ''"
5455
+ class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
5456
+ [disabled]="_disabled()"
5457
+ (click)="_onPrimaryAction(item); $event.stopPropagation()">
5458
+ </button>
5459
+ }
5460
+
5461
+ @let secondaryAction = _resolveSecondaryAction(item);
5462
+ @if (secondaryAction) {
5463
+ <button
5464
+ clx-button
5465
+ type="button"
5466
+ variant="solid"
5467
+ [color]="secondaryAction.color ?? 'slate'"
5468
+ shape="circle"
5469
+ size="xxs"
5470
+ [icon]="secondaryAction.icon"
5471
+ [iconOnly]="true"
5472
+ [clxTooltip]="secondaryAction.tooltip ?? ''"
5473
+ class="absolute bottom-0 right-0 translate-y-1/2 translate-x-1/2 shadow-md z-10"
5474
+ [disabled]="_disabled()"
5475
+ (click)="secondaryActionClick.emit(item); $event.stopPropagation()">
5476
+ </button>
5477
+ }
5411
5478
 
5412
5479
  <div class="px-1.5 py-1 w-full min-w-0">
5413
5480
  <p class="text-[11px] leading-tight text-clx-text-label font-medium truncate" [title]="item.name">{{ item.name }}</p>
@@ -5434,7 +5501,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5434
5501
  class: 'block w-full',
5435
5502
  },
5436
5503
  }]
5437
- }], ctorParameters: () => [], propDecorators: { multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], existingFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFiles", required: false }] }], onUploadError: [{ type: i0.Output, args: ["onUploadError"] }], onUploadSuccess: [{ type: i0.Output, args: ["onUploadSuccess"] }], existingFileRemove: [{ type: i0.Output, args: ["existingFileRemove"] }], _fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
5504
+ }], ctorParameters: () => [], propDecorators: { multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], existingFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFiles", required: false }] }], primaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryAction", required: false }] }], secondaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryAction", required: false }] }], onUploadError: [{ type: i0.Output, args: ["onUploadError"] }], onUploadSuccess: [{ type: i0.Output, args: ["onUploadSuccess"] }], existingFileRemove: [{ type: i0.Output, args: ["existingFileRemove"] }], secondaryActionClick: [{ type: i0.Output, args: ["secondaryActionClick"] }], _fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
5438
5505
 
5439
5506
  const COLOR_PICKER_SIZE_MAP = {
5440
5507
  sm: { minH: 'min-h-8', text: 'text-sm', label: 'text-xs font-medium', hint: 'text-xs', px: 'px-2.5', swatchSz: 'w-4 h-4', btnSize: 'xs' },