@tinya-code/gas-admin-ui 0.0.0-stage → 0.1.0-alpha.2
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/README.md +125 -2
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs +281 -0
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs +1022 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs +101 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs +89 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs +45 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs +408 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs +4514 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs +416 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs +439 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs +637 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs +251 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs +309 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs +546 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs +131 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs +80 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs +292 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs +60 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs +4403 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
- package/package.json +31 -3
- package/types/tinya-code-gas-admin-ui.d.ts +3099 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import { t as Button } from "./tinya-code-gas-admin-ui-button-BQjNW8KB.mjs";
|
|
2
|
+
import { t as NotificationService } from "./tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs";
|
|
3
|
+
import { i as listTree, r as itemTree } from "./tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs";
|
|
4
|
+
import { a as colSpanClass, i as seedFields } from "./tinya-code-gas-admin-ui-seed-CNbQtREA.mjs";
|
|
5
|
+
import { n as appendPosition, r as computeMove, t as GAP } from "./tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs";
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import { Component, computed, inject, input, signal } from "@angular/core";
|
|
8
|
+
import { LucideArrowDown, LucideArrowUp, LucideGripVertical, LucideTrash2 } from "@lucide/angular";
|
|
9
|
+
import { NgTemplateOutlet } from "@angular/common";
|
|
10
|
+
import { CdkDrag, CdkDragHandle, CdkDropList, moveItemInArray } from "@angular/cdk/drag-drop";
|
|
11
|
+
var FieldList = class FieldList {
|
|
12
|
+
field = input.required(...ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []);
|
|
13
|
+
tree = input.required(...ngDevMode ? [{ debugName: "tree" }] : /* istanbul ignore next */ []);
|
|
14
|
+
serverErrors = input({}, ...ngDevMode ? [{ debugName: "serverErrors" }] : /* istanbul ignore next */ []);
|
|
15
|
+
pathPrefix = input("", ...ngDevMode ? [{ debugName: "pathPrefix" }] : /* istanbul ignore next */ []);
|
|
16
|
+
exists = input(false, ...ngDevMode ? [{ debugName: "exists" }] : /* istanbul ignore next */ []);
|
|
17
|
+
childTemplate = input.required(...ngDevMode ? [{ debugName: "childTemplate" }] : /* istanbul ignore next */ []);
|
|
18
|
+
notifications = inject(NotificationService);
|
|
19
|
+
intent = signal({}, ...ngDevMode ? [{ debugName: "intent" }] : /* istanbul ignore next */ []);
|
|
20
|
+
itemsTree = computed(() => listTree(this.tree(), this.field().key), ...ngDevMode ? [{ debugName: "itemsTree" }] : /* istanbul ignore next */ []);
|
|
21
|
+
state = computed(() => this.itemsTree()(), ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
|
|
22
|
+
items = computed(() => this.state().value() ?? [], ...ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []);
|
|
23
|
+
sortable = computed(() => this.field().sortable ?? true, ...ngDevMode ? [{ debugName: "sortable" }] : /* istanbul ignore next */ []);
|
|
24
|
+
canAdd() {
|
|
25
|
+
const max = this.field().max;
|
|
26
|
+
return max === void 0 || this.items().length < max;
|
|
27
|
+
}
|
|
28
|
+
canRemove() {
|
|
29
|
+
return this.items().length > (this.field().min ?? 0);
|
|
30
|
+
}
|
|
31
|
+
isAccordion() {
|
|
32
|
+
return this.field().itemDisplay === "accordion";
|
|
33
|
+
}
|
|
34
|
+
isRow() {
|
|
35
|
+
return this.field().itemDisplay === "row";
|
|
36
|
+
}
|
|
37
|
+
isPrimary(index) {
|
|
38
|
+
return (this.field().primaryFirst ?? false) && index === 0;
|
|
39
|
+
}
|
|
40
|
+
titleOf(index) {
|
|
41
|
+
const item = this.items()[index];
|
|
42
|
+
const key = this.field().itemTitle;
|
|
43
|
+
const value = key !== void 0 && item !== void 0 ? item[key] : void 0;
|
|
44
|
+
return typeof value === "string" && value !== "" ? value : `#${index + 1}`;
|
|
45
|
+
}
|
|
46
|
+
openOf(index) {
|
|
47
|
+
const forced = this.intent()[index];
|
|
48
|
+
if (forced !== void 0) return forced;
|
|
49
|
+
return this.hasProblem(index);
|
|
50
|
+
}
|
|
51
|
+
toggle(index) {
|
|
52
|
+
const willOpen = !this.openOf(index);
|
|
53
|
+
this.intent.update((map) => ({
|
|
54
|
+
...map,
|
|
55
|
+
[index]: willOpen
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
hasProblem(index) {
|
|
59
|
+
const state = itemTree(this.itemsTree(), index)();
|
|
60
|
+
if (state.invalid() && state.touched()) return true;
|
|
61
|
+
const prefix = `${this.pathPrefix()}${this.field().key}[${index}].`;
|
|
62
|
+
return Object.keys(this.serverErrors()).some((path) => path.startsWith(prefix));
|
|
63
|
+
}
|
|
64
|
+
add() {
|
|
65
|
+
if (!this.canAdd()) return;
|
|
66
|
+
const item = seedFields(this.field().itemFields);
|
|
67
|
+
const positionKey = this.field().positionField;
|
|
68
|
+
if (positionKey !== void 0) item[positionKey] = appendPosition(this.positioned());
|
|
69
|
+
const newIndex = this.items().length;
|
|
70
|
+
this.updateItems((next) => {
|
|
71
|
+
next.push(item);
|
|
72
|
+
});
|
|
73
|
+
this.intent.update((map) => ({
|
|
74
|
+
...map,
|
|
75
|
+
[newIndex]: true
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
move(index, direction) {
|
|
79
|
+
const target = index + direction;
|
|
80
|
+
if (target < 0 || target >= this.items().length) return;
|
|
81
|
+
this.applyMove(index, target);
|
|
82
|
+
}
|
|
83
|
+
onDrop(event) {
|
|
84
|
+
if (event.previousIndex === event.currentIndex) return;
|
|
85
|
+
this.applyMove(event.previousIndex, event.currentIndex);
|
|
86
|
+
}
|
|
87
|
+
async remove(index) {
|
|
88
|
+
if (!this.canRemove()) return;
|
|
89
|
+
if (this.hasContent(index)) {
|
|
90
|
+
if (!await this.notifications.confirm({
|
|
91
|
+
title: `Eliminar elemento ${index + 1}`,
|
|
92
|
+
message: "Esta acción no se puede deshacer.",
|
|
93
|
+
confirmLabel: "Eliminar",
|
|
94
|
+
cancelLabel: "Cancelar"
|
|
95
|
+
})) return;
|
|
96
|
+
}
|
|
97
|
+
this.updateItems((next) => {
|
|
98
|
+
next.splice(index, 1);
|
|
99
|
+
});
|
|
100
|
+
this.intent.set({});
|
|
101
|
+
}
|
|
102
|
+
childContext(index) {
|
|
103
|
+
const context = {
|
|
104
|
+
fields: this.field().itemFields,
|
|
105
|
+
tree: itemTree(this.itemsTree(), index),
|
|
106
|
+
prefix: `${this.pathPrefix()}${this.field().key}[${index}].`,
|
|
107
|
+
errors: this.serverErrors(),
|
|
108
|
+
exists: this.exists(),
|
|
109
|
+
row: this.isRow(),
|
|
110
|
+
colSpan: colSpanClass
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
...context,
|
|
114
|
+
$implicit: context
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
hasContent(index) {
|
|
118
|
+
const item = this.items()[index];
|
|
119
|
+
if (item === void 0) return false;
|
|
120
|
+
return JSON.stringify(item) !== JSON.stringify(seedFields(this.field().itemFields));
|
|
121
|
+
}
|
|
122
|
+
applyMove(from, to) {
|
|
123
|
+
const positionKey = this.field().positionField;
|
|
124
|
+
this.updateItems((next) => {
|
|
125
|
+
if (positionKey !== void 0) {
|
|
126
|
+
const result = computeMove(this.positioned(), String(from), to);
|
|
127
|
+
for (const change of result.changes) {
|
|
128
|
+
const item = next[Number(change.key)];
|
|
129
|
+
if (item !== void 0) item[positionKey] = change.position;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
moveItemInArray(next, from, to);
|
|
133
|
+
});
|
|
134
|
+
this.intent.set({});
|
|
135
|
+
}
|
|
136
|
+
positioned() {
|
|
137
|
+
const positionKey = this.field().positionField;
|
|
138
|
+
return this.items().map((item, index) => {
|
|
139
|
+
const raw = positionKey !== void 0 ? item[positionKey] : void 0;
|
|
140
|
+
return {
|
|
141
|
+
key: String(index),
|
|
142
|
+
position: typeof raw === "number" ? raw : (index + 1) * GAP
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
updateItems(mutator) {
|
|
147
|
+
this.state().controlValue.update((current) => {
|
|
148
|
+
const next = [...current];
|
|
149
|
+
mutator(next);
|
|
150
|
+
return next;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
154
|
+
minVersion: "12.0.0",
|
|
155
|
+
version: "22.2.3",
|
|
156
|
+
ngImport: i0,
|
|
157
|
+
type: FieldList,
|
|
158
|
+
deps: [],
|
|
159
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
160
|
+
});
|
|
161
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
162
|
+
minVersion: "17.0.0",
|
|
163
|
+
version: "22.2.3",
|
|
164
|
+
type: FieldList,
|
|
165
|
+
isStandalone: true,
|
|
166
|
+
selector: "app-field-list",
|
|
167
|
+
inputs: {
|
|
168
|
+
field: {
|
|
169
|
+
classPropertyName: "field",
|
|
170
|
+
publicName: "field",
|
|
171
|
+
isSignal: true,
|
|
172
|
+
isRequired: true,
|
|
173
|
+
transformFunction: null
|
|
174
|
+
},
|
|
175
|
+
tree: {
|
|
176
|
+
classPropertyName: "tree",
|
|
177
|
+
publicName: "tree",
|
|
178
|
+
isSignal: true,
|
|
179
|
+
isRequired: true,
|
|
180
|
+
transformFunction: null
|
|
181
|
+
},
|
|
182
|
+
serverErrors: {
|
|
183
|
+
classPropertyName: "serverErrors",
|
|
184
|
+
publicName: "serverErrors",
|
|
185
|
+
isSignal: true,
|
|
186
|
+
isRequired: false,
|
|
187
|
+
transformFunction: null
|
|
188
|
+
},
|
|
189
|
+
pathPrefix: {
|
|
190
|
+
classPropertyName: "pathPrefix",
|
|
191
|
+
publicName: "pathPrefix",
|
|
192
|
+
isSignal: true,
|
|
193
|
+
isRequired: false,
|
|
194
|
+
transformFunction: null
|
|
195
|
+
},
|
|
196
|
+
exists: {
|
|
197
|
+
classPropertyName: "exists",
|
|
198
|
+
publicName: "exists",
|
|
199
|
+
isSignal: true,
|
|
200
|
+
isRequired: false,
|
|
201
|
+
transformFunction: null
|
|
202
|
+
},
|
|
203
|
+
childTemplate: {
|
|
204
|
+
classPropertyName: "childTemplate",
|
|
205
|
+
publicName: "childTemplate",
|
|
206
|
+
isSignal: true,
|
|
207
|
+
isRequired: true,
|
|
208
|
+
transformFunction: null
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
ngImport: i0,
|
|
212
|
+
template: `
|
|
213
|
+
@if (items().length === 0) {
|
|
214
|
+
<p
|
|
215
|
+
class="rounded-lg border border-dashed border-neutral/30 px-4 py-6 text-center text-sm text-neutral"
|
|
216
|
+
>
|
|
217
|
+
{{ field().emptyText ?? 'Sin elementos.' }}
|
|
218
|
+
</p>
|
|
219
|
+
} @else {
|
|
220
|
+
<div
|
|
221
|
+
cdkDropList
|
|
222
|
+
class="space-y-3"
|
|
223
|
+
[cdkDropListData]="items()"
|
|
224
|
+
[cdkDropListDisabled]="!sortable()"
|
|
225
|
+
(cdkDropListDropped)="onDrop($event)"
|
|
226
|
+
>
|
|
227
|
+
@for (item of items(); track $index; let i = $index) {
|
|
228
|
+
<div
|
|
229
|
+
class="rounded-xl border border-neutral/20 bg-surface p-3"
|
|
230
|
+
cdkDrag
|
|
231
|
+
[cdkDragDisabled]="!sortable()"
|
|
232
|
+
>
|
|
233
|
+
<div class="mb-2 flex items-center gap-1.5">
|
|
234
|
+
@if (sortable()) {
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
app-button
|
|
238
|
+
variant="ghost"
|
|
239
|
+
size="icon"
|
|
240
|
+
class="cursor-grab"
|
|
241
|
+
cdkDragHandle
|
|
242
|
+
[attr.aria-label]="'Reordenar elemento ' + (i + 1)"
|
|
243
|
+
title="Arrastrar para reordenar"
|
|
244
|
+
>
|
|
245
|
+
<svg lucideGripVertical size="16" />
|
|
246
|
+
</button>
|
|
247
|
+
}
|
|
248
|
+
@if (isAccordion()) {
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
class="flex min-w-0 items-center gap-1 text-sm font-medium"
|
|
252
|
+
[attr.aria-expanded]="openOf(i)"
|
|
253
|
+
(click)="toggle(i)"
|
|
254
|
+
>
|
|
255
|
+
<span aria-hidden="true">{{ openOf(i) ? '▾' : '▸' }}</span>
|
|
256
|
+
<span class="truncate">{{ titleOf(i) }}</span>
|
|
257
|
+
</button>
|
|
258
|
+
}
|
|
259
|
+
@if (isPrimary(i)) {
|
|
260
|
+
<span
|
|
261
|
+
class="rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary"
|
|
262
|
+
>
|
|
263
|
+
Principal
|
|
264
|
+
</span>
|
|
265
|
+
}
|
|
266
|
+
@if (hasProblem(i)) {
|
|
267
|
+
<span class="text-xs font-medium text-danger">Revisar</span>
|
|
268
|
+
}
|
|
269
|
+
<span class="ml-auto flex shrink-0 items-center gap-2 lg:gap-1">
|
|
270
|
+
<button
|
|
271
|
+
type="button"
|
|
272
|
+
app-button
|
|
273
|
+
variant="ghost"
|
|
274
|
+
size="icon"
|
|
275
|
+
[disabled]="i === 0"
|
|
276
|
+
[attr.aria-label]="'Subir elemento ' + (i + 1)"
|
|
277
|
+
(click)="move(i, -1)"
|
|
278
|
+
>
|
|
279
|
+
<svg lucideArrowUp size="16" />
|
|
280
|
+
</button>
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
app-button
|
|
284
|
+
variant="ghost"
|
|
285
|
+
size="icon"
|
|
286
|
+
[disabled]="i === items().length - 1"
|
|
287
|
+
[attr.aria-label]="'Bajar elemento ' + (i + 1)"
|
|
288
|
+
(click)="move(i, 1)"
|
|
289
|
+
>
|
|
290
|
+
<svg lucideArrowDown size="16" />
|
|
291
|
+
</button>
|
|
292
|
+
<button
|
|
293
|
+
type="button"
|
|
294
|
+
app-button
|
|
295
|
+
variant="ghost"
|
|
296
|
+
size="icon"
|
|
297
|
+
[disabled]="!canRemove()"
|
|
298
|
+
[attr.aria-label]="'Eliminar elemento ' + (i + 1)"
|
|
299
|
+
(click)="remove(i)"
|
|
300
|
+
>
|
|
301
|
+
<svg lucideTrash2 size="16" />
|
|
302
|
+
</button>
|
|
303
|
+
</span>
|
|
304
|
+
</div>
|
|
305
|
+
@if (!isAccordion() || openOf(i)) {
|
|
306
|
+
@if (isRow()) {
|
|
307
|
+
<div class="flex flex-wrap items-start gap-3">
|
|
308
|
+
<ng-container
|
|
309
|
+
[ngTemplateOutlet]="childTemplate()"
|
|
310
|
+
[ngTemplateOutletContext]="childContext(i)"
|
|
311
|
+
/>
|
|
312
|
+
</div>
|
|
313
|
+
} @else {
|
|
314
|
+
<div class="grid grid-cols-1 gap-x-4 sm:grid-cols-12">
|
|
315
|
+
<ng-container
|
|
316
|
+
[ngTemplateOutlet]="childTemplate()"
|
|
317
|
+
[ngTemplateOutletContext]="childContext(i)"
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
</div>
|
|
323
|
+
}
|
|
324
|
+
</div>
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
<div class="mt-3">
|
|
328
|
+
<button app-button variant="outline" type="button" [disabled]="!canAdd()" (click)="add()">
|
|
329
|
+
+ {{ field().addLabel ?? 'Agregar elemento' }}
|
|
330
|
+
</button>
|
|
331
|
+
</div>
|
|
332
|
+
`,
|
|
333
|
+
isInline: true,
|
|
334
|
+
dependencies: [
|
|
335
|
+
{
|
|
336
|
+
kind: "component",
|
|
337
|
+
type: Button,
|
|
338
|
+
selector: "button[app-button]",
|
|
339
|
+
inputs: [
|
|
340
|
+
"variant",
|
|
341
|
+
"size",
|
|
342
|
+
"type",
|
|
343
|
+
"disabled",
|
|
344
|
+
"loading"
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
kind: "directive",
|
|
349
|
+
type: CdkDrag,
|
|
350
|
+
selector: "[cdkDrag]",
|
|
351
|
+
inputs: [
|
|
352
|
+
"cdkDragData",
|
|
353
|
+
"cdkDragLockAxis",
|
|
354
|
+
"cdkDragRootElement",
|
|
355
|
+
"cdkDragBoundary",
|
|
356
|
+
"cdkDragStartDelay",
|
|
357
|
+
"cdkDragFreeDragPosition",
|
|
358
|
+
"cdkDragDisabled",
|
|
359
|
+
"cdkDragConstrainPosition",
|
|
360
|
+
"cdkDragPreviewClass",
|
|
361
|
+
"cdkDragPreviewContainer",
|
|
362
|
+
"cdkDragScale"
|
|
363
|
+
],
|
|
364
|
+
outputs: [
|
|
365
|
+
"cdkDragStarted",
|
|
366
|
+
"cdkDragReleased",
|
|
367
|
+
"cdkDragEnded",
|
|
368
|
+
"cdkDragEntered",
|
|
369
|
+
"cdkDragExited",
|
|
370
|
+
"cdkDragDropped",
|
|
371
|
+
"cdkDragMoved"
|
|
372
|
+
],
|
|
373
|
+
exportAs: ["cdkDrag"]
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
kind: "directive",
|
|
377
|
+
type: CdkDragHandle,
|
|
378
|
+
selector: "[cdkDragHandle]",
|
|
379
|
+
inputs: ["cdkDragHandleDisabled"]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
kind: "directive",
|
|
383
|
+
type: CdkDropList,
|
|
384
|
+
selector: "[cdkDropList], cdk-drop-list",
|
|
385
|
+
inputs: [
|
|
386
|
+
"cdkDropListConnectedTo",
|
|
387
|
+
"cdkDropListData",
|
|
388
|
+
"cdkDropListOrientation",
|
|
389
|
+
"id",
|
|
390
|
+
"cdkDropListLockAxis",
|
|
391
|
+
"cdkDropListDisabled",
|
|
392
|
+
"cdkDropListSortingDisabled",
|
|
393
|
+
"cdkDropListEnterPredicate",
|
|
394
|
+
"cdkDropListSortPredicate",
|
|
395
|
+
"cdkDropListAutoScrollDisabled",
|
|
396
|
+
"cdkDropListAutoScrollStep",
|
|
397
|
+
"cdkDropListElementContainer",
|
|
398
|
+
"cdkDropListHasAnchor"
|
|
399
|
+
],
|
|
400
|
+
outputs: [
|
|
401
|
+
"cdkDropListDropped",
|
|
402
|
+
"cdkDropListEntered",
|
|
403
|
+
"cdkDropListExited",
|
|
404
|
+
"cdkDropListSorted"
|
|
405
|
+
],
|
|
406
|
+
exportAs: ["cdkDropList"]
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
kind: "component",
|
|
410
|
+
type: LucideArrowDown,
|
|
411
|
+
selector: "svg[lucideArrowDown]"
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
kind: "component",
|
|
415
|
+
type: LucideArrowUp,
|
|
416
|
+
selector: "svg[lucideArrowUp]"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
kind: "component",
|
|
420
|
+
type: LucideGripVertical,
|
|
421
|
+
selector: "svg[lucideGripVertical]"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
kind: "component",
|
|
425
|
+
type: LucideTrash2,
|
|
426
|
+
selector: "svg[lucideTrash], svg[lucideTrash2]"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
kind: "directive",
|
|
430
|
+
type: NgTemplateOutlet,
|
|
431
|
+
selector: "[ngTemplateOutlet]",
|
|
432
|
+
inputs: [
|
|
433
|
+
"ngTemplateOutletContext",
|
|
434
|
+
"ngTemplateOutlet",
|
|
435
|
+
"ngTemplateOutletInjector"
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
]
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
442
|
+
minVersion: "12.0.0",
|
|
443
|
+
version: "22.2.3",
|
|
444
|
+
ngImport: i0,
|
|
445
|
+
type: FieldList,
|
|
446
|
+
decorators: [{
|
|
447
|
+
type: Component,
|
|
448
|
+
args: [{
|
|
449
|
+
selector: "app-field-list",
|
|
450
|
+
imports: [
|
|
451
|
+
Button,
|
|
452
|
+
CdkDrag,
|
|
453
|
+
CdkDragHandle,
|
|
454
|
+
CdkDropList,
|
|
455
|
+
LucideArrowDown,
|
|
456
|
+
LucideArrowUp,
|
|
457
|
+
LucideGripVertical,
|
|
458
|
+
LucideTrash2,
|
|
459
|
+
NgTemplateOutlet
|
|
460
|
+
],
|
|
461
|
+
template: `
|
|
462
|
+
@if (items().length === 0) {
|
|
463
|
+
<p
|
|
464
|
+
class="rounded-lg border border-dashed border-neutral/30 px-4 py-6 text-center text-sm text-neutral"
|
|
465
|
+
>
|
|
466
|
+
{{ field().emptyText ?? 'Sin elementos.' }}
|
|
467
|
+
</p>
|
|
468
|
+
} @else {
|
|
469
|
+
<div
|
|
470
|
+
cdkDropList
|
|
471
|
+
class="space-y-3"
|
|
472
|
+
[cdkDropListData]="items()"
|
|
473
|
+
[cdkDropListDisabled]="!sortable()"
|
|
474
|
+
(cdkDropListDropped)="onDrop($event)"
|
|
475
|
+
>
|
|
476
|
+
@for (item of items(); track $index; let i = $index) {
|
|
477
|
+
<div
|
|
478
|
+
class="rounded-xl border border-neutral/20 bg-surface p-3"
|
|
479
|
+
cdkDrag
|
|
480
|
+
[cdkDragDisabled]="!sortable()"
|
|
481
|
+
>
|
|
482
|
+
<div class="mb-2 flex items-center gap-1.5">
|
|
483
|
+
@if (sortable()) {
|
|
484
|
+
<button
|
|
485
|
+
type="button"
|
|
486
|
+
app-button
|
|
487
|
+
variant="ghost"
|
|
488
|
+
size="icon"
|
|
489
|
+
class="cursor-grab"
|
|
490
|
+
cdkDragHandle
|
|
491
|
+
[attr.aria-label]="'Reordenar elemento ' + (i + 1)"
|
|
492
|
+
title="Arrastrar para reordenar"
|
|
493
|
+
>
|
|
494
|
+
<svg lucideGripVertical size="16" />
|
|
495
|
+
</button>
|
|
496
|
+
}
|
|
497
|
+
@if (isAccordion()) {
|
|
498
|
+
<button
|
|
499
|
+
type="button"
|
|
500
|
+
class="flex min-w-0 items-center gap-1 text-sm font-medium"
|
|
501
|
+
[attr.aria-expanded]="openOf(i)"
|
|
502
|
+
(click)="toggle(i)"
|
|
503
|
+
>
|
|
504
|
+
<span aria-hidden="true">{{ openOf(i) ? '▾' : '▸' }}</span>
|
|
505
|
+
<span class="truncate">{{ titleOf(i) }}</span>
|
|
506
|
+
</button>
|
|
507
|
+
}
|
|
508
|
+
@if (isPrimary(i)) {
|
|
509
|
+
<span
|
|
510
|
+
class="rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary"
|
|
511
|
+
>
|
|
512
|
+
Principal
|
|
513
|
+
</span>
|
|
514
|
+
}
|
|
515
|
+
@if (hasProblem(i)) {
|
|
516
|
+
<span class="text-xs font-medium text-danger">Revisar</span>
|
|
517
|
+
}
|
|
518
|
+
<span class="ml-auto flex shrink-0 items-center gap-2 lg:gap-1">
|
|
519
|
+
<button
|
|
520
|
+
type="button"
|
|
521
|
+
app-button
|
|
522
|
+
variant="ghost"
|
|
523
|
+
size="icon"
|
|
524
|
+
[disabled]="i === 0"
|
|
525
|
+
[attr.aria-label]="'Subir elemento ' + (i + 1)"
|
|
526
|
+
(click)="move(i, -1)"
|
|
527
|
+
>
|
|
528
|
+
<svg lucideArrowUp size="16" />
|
|
529
|
+
</button>
|
|
530
|
+
<button
|
|
531
|
+
type="button"
|
|
532
|
+
app-button
|
|
533
|
+
variant="ghost"
|
|
534
|
+
size="icon"
|
|
535
|
+
[disabled]="i === items().length - 1"
|
|
536
|
+
[attr.aria-label]="'Bajar elemento ' + (i + 1)"
|
|
537
|
+
(click)="move(i, 1)"
|
|
538
|
+
>
|
|
539
|
+
<svg lucideArrowDown size="16" />
|
|
540
|
+
</button>
|
|
541
|
+
<button
|
|
542
|
+
type="button"
|
|
543
|
+
app-button
|
|
544
|
+
variant="ghost"
|
|
545
|
+
size="icon"
|
|
546
|
+
[disabled]="!canRemove()"
|
|
547
|
+
[attr.aria-label]="'Eliminar elemento ' + (i + 1)"
|
|
548
|
+
(click)="remove(i)"
|
|
549
|
+
>
|
|
550
|
+
<svg lucideTrash2 size="16" />
|
|
551
|
+
</button>
|
|
552
|
+
</span>
|
|
553
|
+
</div>
|
|
554
|
+
@if (!isAccordion() || openOf(i)) {
|
|
555
|
+
@if (isRow()) {
|
|
556
|
+
<div class="flex flex-wrap items-start gap-3">
|
|
557
|
+
<ng-container
|
|
558
|
+
[ngTemplateOutlet]="childTemplate()"
|
|
559
|
+
[ngTemplateOutletContext]="childContext(i)"
|
|
560
|
+
/>
|
|
561
|
+
</div>
|
|
562
|
+
} @else {
|
|
563
|
+
<div class="grid grid-cols-1 gap-x-4 sm:grid-cols-12">
|
|
564
|
+
<ng-container
|
|
565
|
+
[ngTemplateOutlet]="childTemplate()"
|
|
566
|
+
[ngTemplateOutletContext]="childContext(i)"
|
|
567
|
+
/>
|
|
568
|
+
</div>
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
</div>
|
|
572
|
+
}
|
|
573
|
+
</div>
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
<div class="mt-3">
|
|
577
|
+
<button app-button variant="outline" type="button" [disabled]="!canAdd()" (click)="add()">
|
|
578
|
+
+ {{ field().addLabel ?? 'Agregar elemento' }}
|
|
579
|
+
</button>
|
|
580
|
+
</div>
|
|
581
|
+
`
|
|
582
|
+
}]
|
|
583
|
+
}],
|
|
584
|
+
propDecorators: {
|
|
585
|
+
field: [{
|
|
586
|
+
type: i0.Input,
|
|
587
|
+
args: [{
|
|
588
|
+
isSignal: true,
|
|
589
|
+
alias: "field",
|
|
590
|
+
required: true
|
|
591
|
+
}]
|
|
592
|
+
}],
|
|
593
|
+
tree: [{
|
|
594
|
+
type: i0.Input,
|
|
595
|
+
args: [{
|
|
596
|
+
isSignal: true,
|
|
597
|
+
alias: "tree",
|
|
598
|
+
required: true
|
|
599
|
+
}]
|
|
600
|
+
}],
|
|
601
|
+
serverErrors: [{
|
|
602
|
+
type: i0.Input,
|
|
603
|
+
args: [{
|
|
604
|
+
isSignal: true,
|
|
605
|
+
alias: "serverErrors",
|
|
606
|
+
required: false
|
|
607
|
+
}]
|
|
608
|
+
}],
|
|
609
|
+
pathPrefix: [{
|
|
610
|
+
type: i0.Input,
|
|
611
|
+
args: [{
|
|
612
|
+
isSignal: true,
|
|
613
|
+
alias: "pathPrefix",
|
|
614
|
+
required: false
|
|
615
|
+
}]
|
|
616
|
+
}],
|
|
617
|
+
exists: [{
|
|
618
|
+
type: i0.Input,
|
|
619
|
+
args: [{
|
|
620
|
+
isSignal: true,
|
|
621
|
+
alias: "exists",
|
|
622
|
+
required: false
|
|
623
|
+
}]
|
|
624
|
+
}],
|
|
625
|
+
childTemplate: [{
|
|
626
|
+
type: i0.Input,
|
|
627
|
+
args: [{
|
|
628
|
+
isSignal: true,
|
|
629
|
+
alias: "childTemplate",
|
|
630
|
+
required: true
|
|
631
|
+
}]
|
|
632
|
+
}]
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
export { FieldList };
|
|
636
|
+
|
|
637
|
+
//# sourceMappingURL=tinya-code-gas-admin-ui-list-CMzl9MkX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tinya-code-gas-admin-ui-list-CMzl9MkX.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/fields/list/list.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { Component, computed, inject, input, signal, type TemplateRef } from '@angular/core';\nimport {\n CdkDrag,\n CdkDragDrop,\n CdkDragHandle,\n CdkDropList,\n moveItemInArray,\n} from '@angular/cdk/drag-drop';\n\nimport { LucideArrowDown, LucideArrowUp, LucideGripVertical, LucideTrash2 } from '@lucide/angular';\nimport type { ListField } from '../../core/models/schema.model';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { Button } from '../../shared/components/button/button';\nimport { colSpanClass } from '../../shared/utils/col-span';\nimport {\n appendPosition,\n computeMove,\n GAP,\n type PositionedItem,\n} from '../../shared/utils/gap-sorting';\nimport { seedFields } from '../../shared/utils/seed';\nimport { itemTree, listTree, type ListChildContext, type RootTree } from '../field-node';\n\n/**\n * Lista de ítems (base.md §6): asa de arrastre, subir/bajar, eliminar con\n * confirmación (sólo si el ítem tiene contenido) y agregar respetando\n * `min`/`max`.\n *\n * **Los hijos los renderiza el despachador**: este componente recibe el\n * `TemplateRef` (#childTpl, declarado en el `@case` de `field-host`) y lo\n * instancia una vez por ítem con `ngTemplateOutlet`. No importa FieldHost —\n * un import mutuo revienta en runtime (ciclo-check del repo).\n *\n * El orden de visualización ES el orden del arreglo modelo; `position`\n * (gap-sorting, base.md §8) se calcula junto con cada mutación y viaja en el\n * payload. Un hueco que deja un `remove` se autocura en el siguiente `move`.\n */\n@Component({\n selector: 'app-field-list',\n imports: [\n Button,\n CdkDrag,\n CdkDragHandle,\n CdkDropList,\n LucideArrowDown,\n LucideArrowUp,\n LucideGripVertical,\n LucideTrash2,\n NgTemplateOutlet,\n ],\n template: `\n @if (items().length === 0) {\n <p\n class=\"rounded-lg border border-dashed border-neutral/30 px-4 py-6 text-center text-sm text-neutral\"\n >\n {{ field().emptyText ?? 'Sin elementos.' }}\n </p>\n } @else {\n <div\n cdkDropList\n class=\"space-y-3\"\n [cdkDropListData]=\"items()\"\n [cdkDropListDisabled]=\"!sortable()\"\n (cdkDropListDropped)=\"onDrop($event)\"\n >\n @for (item of items(); track $index; let i = $index) {\n <div\n class=\"rounded-xl border border-neutral/20 bg-surface p-3\"\n cdkDrag\n [cdkDragDisabled]=\"!sortable()\"\n >\n <div class=\"mb-2 flex items-center gap-1.5\">\n @if (sortable()) {\n <button\n type=\"button\"\n app-button\n variant=\"ghost\"\n size=\"icon\"\n class=\"cursor-grab\"\n cdkDragHandle\n [attr.aria-label]=\"'Reordenar elemento ' + (i + 1)\"\n title=\"Arrastrar para reordenar\"\n >\n <svg lucideGripVertical size=\"16\" />\n </button>\n }\n @if (isAccordion()) {\n <button\n type=\"button\"\n class=\"flex min-w-0 items-center gap-1 text-sm font-medium\"\n [attr.aria-expanded]=\"openOf(i)\"\n (click)=\"toggle(i)\"\n >\n <span aria-hidden=\"true\">{{ openOf(i) ? '▾' : '▸' }}</span>\n <span class=\"truncate\">{{ titleOf(i) }}</span>\n </button>\n }\n @if (isPrimary(i)) {\n <span\n class=\"rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n Principal\n </span>\n }\n @if (hasProblem(i)) {\n <span class=\"text-xs font-medium text-danger\">Revisar</span>\n }\n <span class=\"ml-auto flex shrink-0 items-center gap-2 lg:gap-1\">\n <button\n type=\"button\"\n app-button\n variant=\"ghost\"\n size=\"icon\"\n [disabled]=\"i === 0\"\n [attr.aria-label]=\"'Subir elemento ' + (i + 1)\"\n (click)=\"move(i, -1)\"\n >\n <svg lucideArrowUp size=\"16\" />\n </button>\n <button\n type=\"button\"\n app-button\n variant=\"ghost\"\n size=\"icon\"\n [disabled]=\"i === items().length - 1\"\n [attr.aria-label]=\"'Bajar elemento ' + (i + 1)\"\n (click)=\"move(i, 1)\"\n >\n <svg lucideArrowDown size=\"16\" />\n </button>\n <button\n type=\"button\"\n app-button\n variant=\"ghost\"\n size=\"icon\"\n [disabled]=\"!canRemove()\"\n [attr.aria-label]=\"'Eliminar elemento ' + (i + 1)\"\n (click)=\"remove(i)\"\n >\n <svg lucideTrash2 size=\"16\" />\n </button>\n </span>\n </div>\n @if (!isAccordion() || openOf(i)) {\n @if (isRow()) {\n <div class=\"flex flex-wrap items-start gap-3\">\n <ng-container\n [ngTemplateOutlet]=\"childTemplate()\"\n [ngTemplateOutletContext]=\"childContext(i)\"\n />\n </div>\n } @else {\n <div class=\"grid grid-cols-1 gap-x-4 sm:grid-cols-12\">\n <ng-container\n [ngTemplateOutlet]=\"childTemplate()\"\n [ngTemplateOutletContext]=\"childContext(i)\"\n />\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <div class=\"mt-3\">\n <button app-button variant=\"outline\" type=\"button\" [disabled]=\"!canAdd()\" (click)=\"add()\">\n + {{ field().addLabel ?? 'Agregar elemento' }}\n </button>\n </div>\n `,\n})\nexport class FieldList {\n readonly field = input.required<ListField>();\n readonly tree = input.required<RootTree>();\n readonly serverErrors = input<Record<string, string>>({});\n readonly pathPrefix = input('');\n readonly exists = input(false);\n /** Template de los hijos, declarado en el `@case` del despachador. */\n readonly childTemplate = input.required<TemplateRef<ListChildContext>>();\n\n private readonly notifications = inject(NotificationService);\n\n /** Intento de apertura por ítem (`undefined` → sigue el auto-open). */\n private readonly intent = signal<Record<number, boolean>>({});\n\n private readonly itemsTree = computed(() =>\n listTree<Record<string, unknown>>(this.tree(), this.field().key),\n );\n private readonly state = computed(() => this.itemsTree()());\n protected readonly items = computed(() => this.state().value() ?? []);\n\n protected readonly sortable = computed(() => this.field().sortable ?? true);\n /** Clase fija de la barra de ícono (literal: Tailwind v4 es JIT). */\n\n protected canAdd(): boolean {\n const max = this.field().max;\n return max === undefined || this.items().length < max;\n }\n\n protected canRemove(): boolean {\n return this.items().length > (this.field().min ?? 0);\n }\n\n protected isAccordion(): boolean {\n return this.field().itemDisplay === 'accordion';\n }\n\n protected isRow(): boolean {\n return this.field().itemDisplay === 'row';\n }\n\n protected isPrimary(index: number): boolean {\n return (this.field().primaryFirst ?? false) && index === 0;\n }\n\n protected titleOf(index: number): string {\n const item = this.items()[index];\n const key = this.field().itemTitle;\n const value = key !== undefined && item !== undefined ? item[key] : undefined;\n return typeof value === 'string' && value !== '' ? value : `#${index + 1}`;\n }\n\n // ── Ítem abierto (accordion) ──\n\n protected openOf(index: number): boolean {\n const forced = this.intent()[index];\n if (forced !== undefined) {\n return forced;\n }\n return this.hasProblem(index);\n }\n\n protected toggle(index: number): void {\n const willOpen = !this.openOf(index);\n this.intent.update((map) => ({ ...map, [index]: willOpen }));\n }\n\n /** Inválido+tocado (agrega hijos) o error del backend bajo `key[i].`. */\n hasProblem(index: number): boolean {\n const state = itemTree(this.itemsTree(), index)();\n if (state.invalid() && state.touched()) {\n return true;\n }\n const prefix = `${this.pathPrefix()}${this.field().key}[${index}].`;\n return Object.keys(this.serverErrors()).some((path) => path.startsWith(prefix));\n }\n\n // ── Mutaciones (todas sobre `controlValue` → marcan `dirty` solas) ──\n\n protected add(): void {\n if (!this.canAdd()) {\n return;\n }\n const item = seedFields(this.field().itemFields);\n const positionKey = this.field().positionField;\n if (positionKey !== undefined) {\n item[positionKey] = appendPosition(this.positioned());\n }\n const newIndex = this.items().length;\n this.updateItems((next) => {\n next.push(item);\n });\n // El ítem nuevo arranca visible.\n this.intent.update((map) => ({ ...map, [newIndex]: true }));\n }\n\n protected move(index: number, direction: -1 | 1): void {\n const target = index + direction;\n if (target < 0 || target >= this.items().length) {\n return;\n }\n this.applyMove(index, target);\n }\n\n protected onDrop(event: CdkDragDrop<Record<string, unknown>[]>): void {\n if (event.previousIndex === event.currentIndex) {\n return;\n }\n this.applyMove(event.previousIndex, event.currentIndex);\n }\n\n protected async remove(index: number): Promise<void> {\n if (!this.canRemove()) {\n return;\n }\n if (this.hasContent(index)) {\n const accepted = await this.notifications.confirm({\n title: `Eliminar elemento ${index + 1}`,\n message: 'Esta acción no se puede deshacer.',\n confirmLabel: 'Eliminar',\n cancelLabel: 'Cancelar',\n });\n if (!accepted) {\n return;\n }\n }\n this.updateItems((next) => {\n next.splice(index, 1);\n });\n this.intent.set({});\n }\n\n /**\n * Contexto fresco por ítem: los nodos se atan a la identidad (NG01904).\n *\n * `$implicit` lleva el objeto completo: en el `ng-template` del host,\n * `let-child` (sin alias) lee `$implicit` — `NgTemplateOutlet` reparte el\n * contexto como propiedades top-level, no existe un alias mágico.\n */\n protected childContext(index: number): ListChildContext & { $implicit: ListChildContext } {\n const context: ListChildContext = {\n fields: this.field().itemFields,\n tree: itemTree(this.itemsTree(), index),\n prefix: `${this.pathPrefix()}${this.field().key}[${index}].`,\n errors: this.serverErrors(),\n exists: this.exists(),\n row: this.isRow(),\n colSpan: colSpanClass,\n };\n return { ...context, $implicit: context };\n }\n\n /** Ítem con algo distinto de sus `default` → pide confirmación. */\n private hasContent(index: number): boolean {\n const item = this.items()[index];\n if (item === undefined) {\n return false;\n }\n return JSON.stringify(item) !== JSON.stringify(seedFields(this.field().itemFields));\n }\n\n /**\n * Mueve el ítem y recalcula su `position` (base.md §8): los cambios de\n * posición se aplican ANTES del splice porque las claves referencian los\n * índices originales.\n */\n private applyMove(from: number, to: number): void {\n const positionKey = this.field().positionField;\n this.updateItems((next) => {\n if (positionKey !== undefined) {\n const result = computeMove(this.positioned(), String(from), to);\n for (const change of result.changes) {\n const item = next[Number(change.key)];\n if (item !== undefined) {\n item[positionKey] = change.position;\n }\n }\n }\n moveItemInArray(next, from, to);\n });\n this.intent.set({});\n }\n\n /** Posiciones actuales; sin `positionField` no se calculan. */\n private positioned(): PositionedItem[] {\n const positionKey = this.field().positionField;\n return this.items().map((item, index) => {\n const raw = positionKey !== undefined ? item[positionKey] : undefined;\n // Fallback: un registro viejo sin `position` se numera como rebalanceo.\n return {\n key: String(index),\n position: typeof raw === 'number' ? raw : (index + 1) * GAP,\n };\n });\n }\n\n private updateItems(mutator: (next: Record<string, unknown>[]) => void): void {\n this.state().controlValue.update((current) => {\n const next = [...current];\n mutator(next);\n return next;\n });\n }\n}\n"],"mappings":"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"}
|