codexly-ui 0.4.8 → 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.
- package/fesm2022/codexly-ui.mjs +84 -17
- package/fesm2022/codexly-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/codexly-ui.d.ts +29 -3
package/fesm2022/codexly-ui.mjs
CHANGED
|
@@ -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 */ []));
|
|
@@ -4962,7 +4973,7 @@ class ClxUploadComponent {
|
|
|
4962
4973
|
_disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
|
|
4963
4974
|
_sizeConfig = computed(() => UPLOAD_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
|
|
4964
4975
|
_isError = computed(() => this._errorKey() !== null, ...(ngDevMode ? [{ debugName: "_isError" }] : /* istanbul ignore next */ []));
|
|
4965
|
-
_thumbClass = computed(() => `relative group flex flex-col ${resolveRadius(this._themeSvc.config().borderRadius)}
|
|
4976
|
+
_thumbClass = computed(() => `relative group flex flex-col ${resolveRadius(this._themeSvc.config().borderRadius)} border border-clx-border bg-clx-surface-2`, ...(ngDevMode ? [{ debugName: "_thumbClass" }] : /* istanbul ignore next */ []));
|
|
4966
4977
|
_previewItems = computed(() => {
|
|
4967
4978
|
const existing = this.existingFiles().map(f => ({
|
|
4968
4979
|
kind: 'existing',
|
|
@@ -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
|
|
@@ -5266,7 +5289,7 @@ class ClxUploadComponent {
|
|
|
5266
5289
|
<div class="mt-3 grid grid-cols-4 sm:grid-cols-6 gap-2">
|
|
5267
5290
|
@for (item of _previewItems(); track item.key) {
|
|
5268
5291
|
<div [class]="_thumbClass()">
|
|
5269
|
-
<div class="relative aspect-square w-full overflow-hidden">
|
|
5292
|
+
<div class="relative aspect-square w-full overflow-hidden rounded-[inherit]">
|
|
5270
5293
|
@if (item.isImage) {
|
|
5271
5294
|
<img [src]="item.thumbUrl" [alt]="item.name" loading="lazy" class="w-full h-full object-cover" />
|
|
5272
5295
|
} @else {
|
|
@@ -5284,21 +5307,43 @@ class ClxUploadComponent {
|
|
|
5284
5307
|
<span clx-icon name="error" size="md" class="text-white"></span>
|
|
5285
5308
|
</div>
|
|
5286
5309
|
}
|
|
5310
|
+
</div>
|
|
5287
5311
|
|
|
5312
|
+
@let primaryAction = _resolvePrimaryAction(item);
|
|
5313
|
+
@if (primaryAction) {
|
|
5288
5314
|
<button
|
|
5289
5315
|
clx-button
|
|
5290
5316
|
type="button"
|
|
5291
5317
|
variant="solid"
|
|
5292
|
-
color="red"
|
|
5318
|
+
[color]="primaryAction.color ?? 'red'"
|
|
5293
5319
|
shape="circle"
|
|
5294
5320
|
size="xxs"
|
|
5295
|
-
icon="
|
|
5321
|
+
[icon]="primaryAction.icon"
|
|
5296
5322
|
[iconOnly]="true"
|
|
5297
|
-
|
|
5323
|
+
[clxTooltip]="primaryAction.tooltip ?? ''"
|
|
5324
|
+
class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
|
|
5298
5325
|
[disabled]="_disabled()"
|
|
5299
|
-
(click)="
|
|
5326
|
+
(click)="_onPrimaryAction(item); $event.stopPropagation()">
|
|
5300
5327
|
</button>
|
|
5301
|
-
|
|
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
|
|
@@ -5375,7 +5420,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
5375
5420
|
<div class="mt-3 grid grid-cols-4 sm:grid-cols-6 gap-2">
|
|
5376
5421
|
@for (item of _previewItems(); track item.key) {
|
|
5377
5422
|
<div [class]="_thumbClass()">
|
|
5378
|
-
<div class="relative aspect-square w-full overflow-hidden">
|
|
5423
|
+
<div class="relative aspect-square w-full overflow-hidden rounded-[inherit]">
|
|
5379
5424
|
@if (item.isImage) {
|
|
5380
5425
|
<img [src]="item.thumbUrl" [alt]="item.name" loading="lazy" class="w-full h-full object-cover" />
|
|
5381
5426
|
} @else {
|
|
@@ -5393,21 +5438,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
5393
5438
|
<span clx-icon name="error" size="md" class="text-white"></span>
|
|
5394
5439
|
</div>
|
|
5395
5440
|
}
|
|
5441
|
+
</div>
|
|
5396
5442
|
|
|
5443
|
+
@let primaryAction = _resolvePrimaryAction(item);
|
|
5444
|
+
@if (primaryAction) {
|
|
5397
5445
|
<button
|
|
5398
5446
|
clx-button
|
|
5399
5447
|
type="button"
|
|
5400
5448
|
variant="solid"
|
|
5401
|
-
color="red"
|
|
5449
|
+
[color]="primaryAction.color ?? 'red'"
|
|
5402
5450
|
shape="circle"
|
|
5403
5451
|
size="xxs"
|
|
5404
|
-
icon="
|
|
5452
|
+
[icon]="primaryAction.icon"
|
|
5405
5453
|
[iconOnly]="true"
|
|
5406
|
-
|
|
5454
|
+
[clxTooltip]="primaryAction.tooltip ?? ''"
|
|
5455
|
+
class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 shadow-md z-10"
|
|
5407
5456
|
[disabled]="_disabled()"
|
|
5408
|
-
(click)="
|
|
5457
|
+
(click)="_onPrimaryAction(item); $event.stopPropagation()">
|
|
5409
5458
|
</button>
|
|
5410
|
-
|
|
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' },
|