ui-core-abv 0.5.3 → 0.5.4

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.
@@ -5173,12 +5173,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
5173
5173
 
5174
5174
  class UicTreeAdminComponent {
5175
5175
  loading = false;
5176
- createSubcategoryFn;
5176
+ emptyMessage = 'Sin subcategorias';
5177
+ duplicateNameMessage = 'La subcategoria ya existe';
5177
5178
  edit = new EventEmitter();
5178
5179
  remove = new EventEmitter();
5179
5180
  deactivate = new EventEmitter();
5180
5181
  nameChange = new EventEmitter();
5182
+ create = new EventEmitter();
5181
5183
  tree = [
5184
+ {
5185
+ id: 0,
5186
+ name: 'Sin hijos',
5187
+ enabled: true
5188
+ },
5182
5189
  {
5183
5190
  id: 1,
5184
5191
  name: 'Equipo A',
@@ -5231,14 +5238,17 @@ class UicTreeAdminComponent {
5231
5238
  ];
5232
5239
  childNameInputs;
5233
5240
  parentNameInputs;
5241
+ pushAler = inject(UicPushAlertService);
5234
5242
  collapsedIds = new Set();
5235
5243
  editingParentId = null;
5236
5244
  editingChildId = null;
5237
5245
  pendingFocusParentId = null;
5238
5246
  pendingFocusChildId = null;
5239
- creatingChildIds = new Set();
5247
+ pendingCreateIds = new Set();
5248
+ originalNames = new Map();
5240
5249
  tempId = 0;
5241
5250
  ngAfterViewChecked() {
5251
+ this.cleanupPendingCreateIds();
5242
5252
  if (this.pendingFocusParentId !== null) {
5243
5253
  const targetId = String(this.pendingFocusParentId);
5244
5254
  const target = this.parentNameInputs?.find((input) => input.nativeElement.dataset['id'] === targetId);
@@ -5273,6 +5283,9 @@ class UicTreeAdminComponent {
5273
5283
  event?.stopPropagation();
5274
5284
  this.editingParentId = item.id;
5275
5285
  this.pendingFocusParentId = item.id;
5286
+ if (!this.originalNames.has(item.id)) {
5287
+ this.originalNames.set(item.id, item.name);
5288
+ }
5276
5289
  }
5277
5290
  stopEditParent() {
5278
5291
  this.editingParentId = null;
@@ -5282,8 +5295,14 @@ class UicTreeAdminComponent {
5282
5295
  }
5283
5296
  startEditChild(subitem, event) {
5284
5297
  event?.stopPropagation();
5298
+ if (this.pendingCreateIds.has(subitem.id)) {
5299
+ return;
5300
+ }
5285
5301
  this.editingChildId = subitem.id;
5286
5302
  this.pendingFocusChildId = subitem.id;
5303
+ if (!this.originalNames.has(subitem.id)) {
5304
+ this.originalNames.set(subitem.id, subitem.name);
5305
+ }
5287
5306
  }
5288
5307
  stopEditChild() {
5289
5308
  this.editingChildId = null;
@@ -5293,33 +5312,69 @@ class UicTreeAdminComponent {
5293
5312
  }
5294
5313
  updateParentName(item, event) {
5295
5314
  item.name = event.target.value;
5296
- this.nameChange.emit({ kind: 'parent', id: item.id, name: item.name });
5297
5315
  }
5298
5316
  updateChildName(parent, subitem, event) {
5299
5317
  subitem.name = event.target.value;
5300
- this.nameChange.emit({ kind: 'child', parentId: parent.id, id: subitem.id, name: subitem.name });
5301
5318
  }
5302
- async finalizeChildEdit(parent, subitem) {
5319
+ finalizeParentEdit(item) {
5320
+ const original = this.originalNames.get(item.id);
5321
+ if (original === undefined || original !== item.name) {
5322
+ this.nameChange.emit({ kind: 'parent', id: item.id, name: item.name });
5323
+ }
5324
+ this.originalNames.delete(item.id);
5325
+ this.stopEditParent();
5326
+ }
5327
+ finalizeChildEdit(parent, subitem) {
5303
5328
  const trimmed = subitem.name.trim();
5304
5329
  if (!trimmed) {
5305
5330
  this.removeChild(parent, subitem);
5331
+ this.originalNames.delete(subitem.id);
5306
5332
  return;
5307
5333
  }
5308
5334
  subitem.name = trimmed;
5309
- this.nameChange.emit({ kind: 'child', parentId: parent.id, id: subitem.id, name: subitem.name });
5310
- if (this.isTempId(subitem.id) && this.createSubcategoryFn) {
5311
- const tempId = subitem.id;
5312
- this.creatingChildIds.add(tempId);
5313
- try {
5314
- const result = await Promise.resolve(this.createSubcategoryFn({ parentId: Number(parent.id), name: trimmed }));
5315
- if (result?.id !== undefined) {
5316
- subitem.id = result.id;
5317
- }
5335
+ const original = this.originalNames.get(subitem.id);
5336
+ const isNew = this.isNewChild(subitem.id);
5337
+ if (isNew) {
5338
+ if (original !== undefined && original === subitem.name) {
5339
+ this.originalNames.delete(subitem.id);
5340
+ this.stopEditChild();
5341
+ return;
5342
+ }
5343
+ if (this.hasDuplicateChildName(parent, subitem)) {
5344
+ this.pushAler.warning(this.duplicateNameMessage);
5345
+ this.removeChild(parent, subitem);
5346
+ this.originalNames.delete(subitem.id);
5347
+ return;
5318
5348
  }
5319
- finally {
5320
- this.creatingChildIds.delete(tempId);
5349
+ this.pendingCreateIds.add(subitem.id);
5350
+ this.create.emit({ parentId: parent.id, name: subitem.name, tempId: subitem.id });
5351
+ }
5352
+ else {
5353
+ if (original === undefined || original !== subitem.name) {
5354
+ this.nameChange.emit({ kind: 'child', parentId: parent.id, id: subitem.id, name: subitem.name });
5321
5355
  }
5322
5356
  }
5357
+ this.originalNames.delete(subitem.id);
5358
+ this.stopEditChild();
5359
+ }
5360
+ cancelParentEdit(item) {
5361
+ const original = this.originalNames.get(item.id);
5362
+ if (original !== undefined) {
5363
+ item.name = original;
5364
+ }
5365
+ this.originalNames.delete(item.id);
5366
+ this.stopEditParent();
5367
+ }
5368
+ cancelChildEdit(parent, subitem) {
5369
+ const original = this.originalNames.get(subitem.id);
5370
+ if (original !== undefined) {
5371
+ subitem.name = original;
5372
+ }
5373
+ this.originalNames.delete(subitem.id);
5374
+ if (this.isNewChild(subitem.id) && !subitem.name.trim()) {
5375
+ this.removeChild(parent, subitem);
5376
+ return;
5377
+ }
5323
5378
  this.stopEditChild();
5324
5379
  }
5325
5380
  addSubItem(item, event) {
@@ -5342,7 +5397,6 @@ class UicTreeAdminComponent {
5342
5397
  emitEditChild(parent, subitem, event) {
5343
5398
  event?.stopPropagation();
5344
5399
  this.edit.emit({ kind: 'child', parentId: parent.id, id: subitem.id });
5345
- this.startEditChild(subitem);
5346
5400
  }
5347
5401
  emitDeleteParent(item, event) {
5348
5402
  event?.stopPropagation();
@@ -5353,13 +5407,15 @@ class UicTreeAdminComponent {
5353
5407
  this.remove.emit({ kind: 'child', parentId: parent.id, id: subitem.id });
5354
5408
  }
5355
5409
  emitDeactivateParent(item, checked) {
5410
+ item.enabled = checked;
5356
5411
  this.deactivate.emit({ kind: 'parent', id: item.id, checked });
5357
5412
  }
5358
5413
  emitDeactivateChild(parent, subitem, checked) {
5414
+ subitem.enabled = checked;
5359
5415
  this.deactivate.emit({ kind: 'child', parentId: parent.id, id: subitem.id, checked });
5360
5416
  }
5361
- isCreatingChild(subitem) {
5362
- return this.creatingChildIds.has(subitem.id);
5417
+ isPendingCreate(subitem) {
5418
+ return this.pendingCreateIds.has(subitem.id);
5363
5419
  }
5364
5420
  removeChild(parent, subitem) {
5365
5421
  if (!parent.children) {
@@ -5369,12 +5425,44 @@ class UicTreeAdminComponent {
5369
5425
  if (this.editingChildId === subitem.id) {
5370
5426
  this.editingChildId = null;
5371
5427
  }
5428
+ this.pendingCreateIds.delete(subitem.id);
5429
+ }
5430
+ isNewChild(id) {
5431
+ if (id === null || id === undefined) {
5432
+ return true;
5433
+ }
5434
+ if (typeof id === 'string') {
5435
+ return id.trim() === '' || id.startsWith('new-');
5436
+ }
5437
+ return id <= 0;
5438
+ }
5439
+ hasDuplicateChildName(parent, subitem) {
5440
+ const trimmed = subitem.name.trim().toLowerCase();
5441
+ if (!trimmed || !parent.children) {
5442
+ return false;
5443
+ }
5444
+ return parent.children.some((child) => child.id !== subitem.id && child.name.trim().toLowerCase() === trimmed);
5372
5445
  }
5373
- isTempId(id) {
5374
- return typeof id === 'string' && id.startsWith('new-');
5446
+ cleanupPendingCreateIds() {
5447
+ if (!this.pendingCreateIds.size) {
5448
+ return;
5449
+ }
5450
+ const idsInTree = new Set();
5451
+ for (const parent of this.tree) {
5452
+ if (parent.children?.length) {
5453
+ for (const child of parent.children) {
5454
+ idsInTree.add(child.id);
5455
+ }
5456
+ }
5457
+ }
5458
+ for (const pendingId of Array.from(this.pendingCreateIds)) {
5459
+ if (!idsInTree.has(pendingId)) {
5460
+ this.pendingCreateIds.delete(pendingId);
5461
+ }
5462
+ }
5375
5463
  }
5376
5464
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicTreeAdminComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5377
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicTreeAdminComponent, isStandalone: true, selector: "ui-tree-admin", inputs: { loading: "loading", createSubcategoryFn: "createSubcategoryFn", tree: "tree" }, outputs: { edit: "edit", remove: "remove", deactivate: "deactivate", nameChange: "nameChange" }, viewQueries: [{ propertyName: "childNameInputs", predicate: ["childNameInput"], descendants: true }, { propertyName: "parentNameInputs", predicate: ["parentNameInput"], descendants: true }], ngImport: i0, template: "@if (loading) {\n <div class=\"tree-skeleton\">\n <ui-skeleton-loader [inputs]=\"4\" [cols]=\"1\"></ui-skeleton-loader>\n </div>\n} @else {\n @for (item of tree; track item.id) {\n <div class=\"tree-item\" [class.is-collapsed]=\"!isExpanded(item)\">\n <div class=\"tree-header\" (click)=\"toggleItem(item)\">\n <i class=\"ri-arrow-down-s-line tree-arrow\" [class.is-collapsed]=\"!isExpanded(item)\"></i>\n <div class=\"tree-header-name\">\n @if (isEditingParent(item)) {\n <input\n #parentNameInput\n class=\"tree-name-input\"\n [attr.data-id]=\"item.id\"\n [value]=\"item.name\"\n (input)=\"updateParentName(item, $event)\"\n (blur)=\"stopEditParent()\"\n (keydown.enter)=\"stopEditParent()\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <div class=\"tree-name-text\" (click)=\"startEditParent(item, $event)\">\n {{item.name}}\n </div>\n }\n <span>{{item.children?.length || 0}}</span>\n </div>\n <div>\n <ui-switch\n [checked]=\"item.enabled\"\n (click)=\"$event.stopPropagation()\"\n (checkedChange)=\"emitDeactivateParent(item, $event)\"\n ></ui-switch>\n </div>\n <ui-button (click)=\"addSubItem(item, $event)\" icon=\"ri-add-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitEditParent(item, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitDeleteParent(item, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n </div>\n @if (item.children) {\n <div class=\"tree-body\">\n @for (subitem of item.children; track subitem.id) {\n <div class=\"tree-subitem\" [class.is-loading]=\"isCreatingChild(subitem)\">\n <div class=\"tree-subitem-name\">\n @if (isEditingChild(subitem)) {\n <input\n #childNameInput\n class=\"tree-name-input tree-name-input--sub\"\n [attr.data-id]=\"subitem.id\"\n [value]=\"subitem.name\"\n (input)=\"updateChildName(item, subitem, $event)\"\n (blur)=\"finalizeChildEdit(item, subitem)\"\n (keydown.enter)=\"finalizeChildEdit(item, subitem)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span class=\"tree-name-text\" (click)=\"startEditChild(subitem, $event)\">{{subitem.name}}</span>\n }\n </div>\n <div class=\"tree-subitem-actions\">\n @if (isCreatingChild(subitem)) {\n <div class=\"tree-subitem-loader\"></div>\n } @else {\n <ui-switch\n [checked]=\"subitem.enabled\"\n (checkedChange)=\"emitDeactivateChild(item, subitem, $event)\"\n ></ui-switch>\n <ui-button (click)=\"emitEditChild(item, subitem, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitDeleteChild(item, subitem, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n}\n", styles: [":host{width:100%}.tree-item{border:solid 1px var(--grey-400);border-radius:10px;overflow:hidden;margin-bottom:4px;background:linear-gradient(180deg,var(--grey-100) 0%,var(--grey-50) 100%);transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.tree-header{width:100%;display:flex;align-items:center;gap:5px;padding:4px 10px;border-bottom:solid 1px var(--grey-400);background-color:var(--grey-100);cursor:pointer;transition:background-color .16s ease}.tree-header:hover{background-color:var(--grey-200)}.tree-header-name{flex:1 1;display:flex;align-items:center;gap:10px}.tree-header-name span{border-radius:10px;padding:2px 10px;font-size:10px;color:var(--blue-800);background-color:var(--blue-100)}.tree-arrow{transition:transform .18s ease}.tree-arrow.is-collapsed{transform:rotate(-90deg)}.tree-body{background-color:var(--white);padding:2px 16px;display:flex;flex-direction:column;gap:1px;max-height:600px;opacity:1;transition:max-height .2s ease,opacity .2s ease,padding .2s ease}.tree-subitem{color:var(--grey-700);border-radius:5px;padding:3px 15px;gap:5px;font-size:13px;display:flex;align-items:center;justify-content:space-between}.tree-subitem-name{flex:1 1}.tree-subitem-actions{display:inline-flex;align-items:center;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease,transform .16s ease}.tree-subitem-loader{width:46px;height:8px;border-radius:999px;background:linear-gradient(90deg,var(--grey-200) 0%,var(--grey-100) 40%,var(--grey-200) 80%);background-size:200% 100%;animation:shimmer 1.2s linear infinite}.tree-subitem:hover{background-color:var(--blue-100)}.tree-subitem:hover .tree-subitem-actions{opacity:1;pointer-events:auto;transform:translateY(0)}.tree-subitem.is-loading{opacity:.8}.tree-subitem.is-loading .tree-subitem-actions{opacity:1;pointer-events:none;transform:translateY(0)}.tree-name-text{color:var(--grey-900);cursor:text}.tree-name-input{flex:1 1;min-width:0;width:300px;border:solid 1px var(--grey-300);border-radius:6px;padding:2px 6px;font-size:13px;color:var(--grey-900);background-color:var(--white);outline:none;transition:border-color .16s ease,box-shadow .16s ease}.tree-name-input:focus{border-color:var(--blue-500);box-shadow:0 0 0 2px #3b82f626}.tree-name-input--sub{font-size:12px;padding:1px 6px}.tree-item.is-collapsed .tree-body{max-height:0;opacity:0;padding-top:0;padding-bottom:0;overflow:hidden}.tree-skeleton{padding:8px;border:solid 1px var(--grey-400);border-radius:10px;background-color:var(--grey-50)}@keyframes shimmer{0%{background-position:0% 0%}to{background-position:200% 0%}}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicSkeletonLoaderComponent, selector: "ui-skeleton-loader", inputs: ["inputs", "cols"] }, { kind: "component", type: UicSwichComponent, selector: "ui-switch", inputs: ["checked", "disabled", "placeholder", "label"], outputs: ["checkedChange"] }] });
5465
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicTreeAdminComponent, isStandalone: true, selector: "ui-tree-admin", inputs: { loading: "loading", emptyMessage: "emptyMessage", duplicateNameMessage: "duplicateNameMessage", tree: "tree" }, outputs: { edit: "edit", remove: "remove", deactivate: "deactivate", nameChange: "nameChange", create: "create" }, viewQueries: [{ propertyName: "childNameInputs", predicate: ["childNameInput"], descendants: true }, { propertyName: "parentNameInputs", predicate: ["parentNameInput"], descendants: true }], ngImport: i0, template: "@if (loading) {\n <div class=\"tree-skeleton\">\n <ui-skeleton-loader [inputs]=\"4\" [cols]=\"1\"></ui-skeleton-loader>\n </div>\n} @else {\n @for (item of tree; track item.id) {\n <div class=\"tree-item\" [class.is-collapsed]=\"!isExpanded(item)\">\n <div class=\"tree-header\" (click)=\"toggleItem(item)\">\n <i class=\"ri-arrow-down-s-line tree-arrow\" [class.is-collapsed]=\"!isExpanded(item)\"></i>\n <div class=\"tree-header-name\">\n @if (isEditingParent(item)) {\n <input\n #parentNameInput\n class=\"tree-name-input\"\n [attr.data-id]=\"item.id\"\n [value]=\"item.name\"\n (input)=\"updateParentName(item, $event)\"\n (blur)=\"finalizeParentEdit(item)\"\n (keydown.enter)=\"finalizeParentEdit(item)\"\n (keydown.escape)=\"cancelParentEdit(item)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <div class=\"tree-name-text\" (click)=\"startEditParent(item, $event)\">\n {{item.name}}\n </div>\n }\n <span>{{item.children?.length || 0}}</span>\n </div>\n <div>\n <ui-switch\n [checked]=\"item.enabled\"\n (click)=\"$event.stopPropagation()\"\n (checkedChange)=\"emitDeactivateParent(item, $event)\"\n ></ui-switch>\n </div>\n <ui-button (click)=\"addSubItem(item, $event)\" icon=\"ri-add-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitEditParent(item, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n @if (!item.children?.length) {\n <ui-button (click)=\"emitDeleteParent(item, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n }\n </div>\n <div class=\"tree-body\">\n @if (item.children?.length) {\n @for (subitem of item.children; track subitem.id) {\n <div\n class=\"tree-subitem\"\n [class.is-disabled]=\"!subitem.enabled\"\n [class.is-pending]=\"isPendingCreate(subitem)\"\n [class.is-parent-disabled]=\"!item.enabled\"\n >\n <div class=\"tree-subitem-name\">\n <i\n class=\"ri-circle-fill tree-subitem-status\"\n [class.is-enabled]=\"subitem.enabled\"\n [class.is-disabled]=\"!subitem.enabled\"\n ></i>\n @if (isEditingChild(subitem)) {\n <input\n #childNameInput\n class=\"tree-name-input tree-name-input--sub\"\n [attr.data-id]=\"subitem.id\"\n [value]=\"subitem.name\"\n (input)=\"updateChildName(item, subitem, $event)\"\n (blur)=\"finalizeChildEdit(item, subitem)\"\n (keydown.enter)=\"finalizeChildEdit(item, subitem)\"\n (keydown.escape)=\"cancelChildEdit(item, subitem)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span\n class=\"tree-name-text\"\n (click)=\"!isPendingCreate(subitem) && startEditChild(subitem, $event)\"\n >{{subitem.name}}</span>\n }\n </div>\n <div class=\"tree-subitem-actions\">\n <ui-switch\n [checked]=\"subitem.enabled\"\n [disabled]=\"isPendingCreate(subitem)\"\n (checkedChange)=\"emitDeactivateChild(item, subitem, $event)\"\n ></ui-switch>\n <ui-button\n (click)=\"emitEditChild(item, subitem, $event)\"\n [disabled]=\"isPendingCreate(subitem)\"\n icon=\"ri-edit-line\"\n type=\"ghost\"\n [iconOnly]=\"true\"\n size=\"s\"\n ></ui-button>\n <ui-button \n (click)=\"emitDeleteChild(item, subitem, $event)\" \n icon=\"ri-delete-bin-line\" \n [disabled]=\"isPendingCreate(subitem)\"\n type=\"ghost\" \n [iconOnly]=\"true\" size=\"s\"></ui-button>\n </div>\n </div>\n }\n } @else {\n <div class=\"tree-empty\">\n {{emptyMessage}}\n </div>\n }\n </div>\n </div>\n }\n}\n", styles: [":host{width:100%}.tree-item{border:solid 1px var(--grey-400);border-radius:10px;overflow:hidden;margin-bottom:4px;background:linear-gradient(180deg,var(--grey-100) 0%,var(--grey-50) 100%);transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.tree-header{width:100%;display:flex;align-items:center;gap:5px;padding:4px 10px;border-bottom:solid 1px var(--grey-400);background-color:var(--grey-100);cursor:pointer;transition:background-color .16s ease}.tree-header:hover{background-color:var(--grey-200)}.tree-header-name{flex:1 1;display:flex;align-items:center;gap:10px}.tree-header-name span{border-radius:10px;padding:2px 10px;font-size:10px;color:var(--blue-800);background-color:var(--blue-100)}.tree-arrow{transition:transform .18s ease}.tree-arrow.is-collapsed{transform:rotate(-90deg)}.tree-body{background-color:var(--white);padding:2px 16px;display:flex;flex-direction:column;gap:1px;max-height:600px;opacity:1;transition:max-height .2s ease,opacity .2s ease,padding .2s ease}.tree-empty{padding:8px 10px;font-size:12px;color:var(--grey-500);background-color:var(--grey-50);border-radius:8px}.tree-subitem{color:var(--grey-700);border-radius:5px;padding:3px 15px;gap:5px;font-size:13px;display:flex;align-items:center;justify-content:space-between}.tree-subitem-name{flex:1 1;display:inline-flex;align-items:center;gap:6px}.tree-subitem-status{font-size:10px;color:var(--grey-400)}.tree-subitem-status.is-enabled{color:var(--blue-500)}.tree-subitem-status.is-disabled{color:var(--grey-400)}.tree-subitem-actions{display:inline-flex;align-items:center;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease,transform .16s ease}.tree-subitem:hover{background-color:var(--blue-100)}.tree-subitem:hover .tree-subitem-actions{opacity:1;pointer-events:auto;transform:translateY(0)}.tree-subitem.is-disabled .tree-name-text,.tree-subitem.is-disabled .tree-name-input,.tree-subitem.is-parent-disabled .tree-name-text,.tree-subitem.is-parent-disabled .tree-name-input{color:var(--grey-500)}.tree-subitem.is-parent-disabled .tree-subitem-status{color:var(--grey-400)}.tree-subitem.is-pending .tree-name-text{cursor:default;color:var(--grey-500)}.tree-name-text{width:50%;color:var(--grey-900);cursor:text}.tree-name-input{flex:1 1;min-width:0;border:solid 1px var(--grey-300);border-radius:6px;padding:7px;font-size:13px;color:var(--grey-900);background-color:var(--white);outline:none;transition:border-color .16s ease,box-shadow .16s ease;width:300px}.tree-name-input:focus{border-color:var(--blue-500);box-shadow:0 0 0 2px #3b82f626}.tree-name-input--sub{font-size:12px;padding:5px}.tree-item.is-collapsed .tree-body{max-height:0;opacity:0;padding-top:0;padding-bottom:0;overflow:hidden}.tree-skeleton{padding:8px;border:solid 1px var(--grey-400);border-radius:10px;background-color:var(--grey-50)}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicSkeletonLoaderComponent, selector: "ui-skeleton-loader", inputs: ["inputs", "cols"] }, { kind: "component", type: UicSwichComponent, selector: "ui-switch", inputs: ["checked", "disabled", "placeholder", "label"], outputs: ["checkedChange"] }] });
5378
5466
  }
5379
5467
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicTreeAdminComponent, decorators: [{
5380
5468
  type: Component,
@@ -5382,10 +5470,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
5382
5470
  UicButtonComponent,
5383
5471
  UicSkeletonLoaderComponent,
5384
5472
  UicSwichComponent
5385
- ], template: "@if (loading) {\n <div class=\"tree-skeleton\">\n <ui-skeleton-loader [inputs]=\"4\" [cols]=\"1\"></ui-skeleton-loader>\n </div>\n} @else {\n @for (item of tree; track item.id) {\n <div class=\"tree-item\" [class.is-collapsed]=\"!isExpanded(item)\">\n <div class=\"tree-header\" (click)=\"toggleItem(item)\">\n <i class=\"ri-arrow-down-s-line tree-arrow\" [class.is-collapsed]=\"!isExpanded(item)\"></i>\n <div class=\"tree-header-name\">\n @if (isEditingParent(item)) {\n <input\n #parentNameInput\n class=\"tree-name-input\"\n [attr.data-id]=\"item.id\"\n [value]=\"item.name\"\n (input)=\"updateParentName(item, $event)\"\n (blur)=\"stopEditParent()\"\n (keydown.enter)=\"stopEditParent()\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <div class=\"tree-name-text\" (click)=\"startEditParent(item, $event)\">\n {{item.name}}\n </div>\n }\n <span>{{item.children?.length || 0}}</span>\n </div>\n <div>\n <ui-switch\n [checked]=\"item.enabled\"\n (click)=\"$event.stopPropagation()\"\n (checkedChange)=\"emitDeactivateParent(item, $event)\"\n ></ui-switch>\n </div>\n <ui-button (click)=\"addSubItem(item, $event)\" icon=\"ri-add-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitEditParent(item, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitDeleteParent(item, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n </div>\n @if (item.children) {\n <div class=\"tree-body\">\n @for (subitem of item.children; track subitem.id) {\n <div class=\"tree-subitem\" [class.is-loading]=\"isCreatingChild(subitem)\">\n <div class=\"tree-subitem-name\">\n @if (isEditingChild(subitem)) {\n <input\n #childNameInput\n class=\"tree-name-input tree-name-input--sub\"\n [attr.data-id]=\"subitem.id\"\n [value]=\"subitem.name\"\n (input)=\"updateChildName(item, subitem, $event)\"\n (blur)=\"finalizeChildEdit(item, subitem)\"\n (keydown.enter)=\"finalizeChildEdit(item, subitem)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span class=\"tree-name-text\" (click)=\"startEditChild(subitem, $event)\">{{subitem.name}}</span>\n }\n </div>\n <div class=\"tree-subitem-actions\">\n @if (isCreatingChild(subitem)) {\n <div class=\"tree-subitem-loader\"></div>\n } @else {\n <ui-switch\n [checked]=\"subitem.enabled\"\n (checkedChange)=\"emitDeactivateChild(item, subitem, $event)\"\n ></ui-switch>\n <ui-button (click)=\"emitEditChild(item, subitem, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitDeleteChild(item, subitem, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n}\n", styles: [":host{width:100%}.tree-item{border:solid 1px var(--grey-400);border-radius:10px;overflow:hidden;margin-bottom:4px;background:linear-gradient(180deg,var(--grey-100) 0%,var(--grey-50) 100%);transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.tree-header{width:100%;display:flex;align-items:center;gap:5px;padding:4px 10px;border-bottom:solid 1px var(--grey-400);background-color:var(--grey-100);cursor:pointer;transition:background-color .16s ease}.tree-header:hover{background-color:var(--grey-200)}.tree-header-name{flex:1 1;display:flex;align-items:center;gap:10px}.tree-header-name span{border-radius:10px;padding:2px 10px;font-size:10px;color:var(--blue-800);background-color:var(--blue-100)}.tree-arrow{transition:transform .18s ease}.tree-arrow.is-collapsed{transform:rotate(-90deg)}.tree-body{background-color:var(--white);padding:2px 16px;display:flex;flex-direction:column;gap:1px;max-height:600px;opacity:1;transition:max-height .2s ease,opacity .2s ease,padding .2s ease}.tree-subitem{color:var(--grey-700);border-radius:5px;padding:3px 15px;gap:5px;font-size:13px;display:flex;align-items:center;justify-content:space-between}.tree-subitem-name{flex:1 1}.tree-subitem-actions{display:inline-flex;align-items:center;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease,transform .16s ease}.tree-subitem-loader{width:46px;height:8px;border-radius:999px;background:linear-gradient(90deg,var(--grey-200) 0%,var(--grey-100) 40%,var(--grey-200) 80%);background-size:200% 100%;animation:shimmer 1.2s linear infinite}.tree-subitem:hover{background-color:var(--blue-100)}.tree-subitem:hover .tree-subitem-actions{opacity:1;pointer-events:auto;transform:translateY(0)}.tree-subitem.is-loading{opacity:.8}.tree-subitem.is-loading .tree-subitem-actions{opacity:1;pointer-events:none;transform:translateY(0)}.tree-name-text{color:var(--grey-900);cursor:text}.tree-name-input{flex:1 1;min-width:0;width:300px;border:solid 1px var(--grey-300);border-radius:6px;padding:2px 6px;font-size:13px;color:var(--grey-900);background-color:var(--white);outline:none;transition:border-color .16s ease,box-shadow .16s ease}.tree-name-input:focus{border-color:var(--blue-500);box-shadow:0 0 0 2px #3b82f626}.tree-name-input--sub{font-size:12px;padding:1px 6px}.tree-item.is-collapsed .tree-body{max-height:0;opacity:0;padding-top:0;padding-bottom:0;overflow:hidden}.tree-skeleton{padding:8px;border:solid 1px var(--grey-400);border-radius:10px;background-color:var(--grey-50)}@keyframes shimmer{0%{background-position:0% 0%}to{background-position:200% 0%}}\n"] }]
5473
+ ], template: "@if (loading) {\n <div class=\"tree-skeleton\">\n <ui-skeleton-loader [inputs]=\"4\" [cols]=\"1\"></ui-skeleton-loader>\n </div>\n} @else {\n @for (item of tree; track item.id) {\n <div class=\"tree-item\" [class.is-collapsed]=\"!isExpanded(item)\">\n <div class=\"tree-header\" (click)=\"toggleItem(item)\">\n <i class=\"ri-arrow-down-s-line tree-arrow\" [class.is-collapsed]=\"!isExpanded(item)\"></i>\n <div class=\"tree-header-name\">\n @if (isEditingParent(item)) {\n <input\n #parentNameInput\n class=\"tree-name-input\"\n [attr.data-id]=\"item.id\"\n [value]=\"item.name\"\n (input)=\"updateParentName(item, $event)\"\n (blur)=\"finalizeParentEdit(item)\"\n (keydown.enter)=\"finalizeParentEdit(item)\"\n (keydown.escape)=\"cancelParentEdit(item)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <div class=\"tree-name-text\" (click)=\"startEditParent(item, $event)\">\n {{item.name}}\n </div>\n }\n <span>{{item.children?.length || 0}}</span>\n </div>\n <div>\n <ui-switch\n [checked]=\"item.enabled\"\n (click)=\"$event.stopPropagation()\"\n (checkedChange)=\"emitDeactivateParent(item, $event)\"\n ></ui-switch>\n </div>\n <ui-button (click)=\"addSubItem(item, $event)\" icon=\"ri-add-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n <ui-button (click)=\"emitEditParent(item, $event)\" icon=\"ri-edit-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n @if (!item.children?.length) {\n <ui-button (click)=\"emitDeleteParent(item, $event)\" icon=\"ri-delete-bin-line\" type=\"ghost\" [iconOnly]=\"true\" size=\"s\"></ui-button>\n }\n </div>\n <div class=\"tree-body\">\n @if (item.children?.length) {\n @for (subitem of item.children; track subitem.id) {\n <div\n class=\"tree-subitem\"\n [class.is-disabled]=\"!subitem.enabled\"\n [class.is-pending]=\"isPendingCreate(subitem)\"\n [class.is-parent-disabled]=\"!item.enabled\"\n >\n <div class=\"tree-subitem-name\">\n <i\n class=\"ri-circle-fill tree-subitem-status\"\n [class.is-enabled]=\"subitem.enabled\"\n [class.is-disabled]=\"!subitem.enabled\"\n ></i>\n @if (isEditingChild(subitem)) {\n <input\n #childNameInput\n class=\"tree-name-input tree-name-input--sub\"\n [attr.data-id]=\"subitem.id\"\n [value]=\"subitem.name\"\n (input)=\"updateChildName(item, subitem, $event)\"\n (blur)=\"finalizeChildEdit(item, subitem)\"\n (keydown.enter)=\"finalizeChildEdit(item, subitem)\"\n (keydown.escape)=\"cancelChildEdit(item, subitem)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span\n class=\"tree-name-text\"\n (click)=\"!isPendingCreate(subitem) && startEditChild(subitem, $event)\"\n >{{subitem.name}}</span>\n }\n </div>\n <div class=\"tree-subitem-actions\">\n <ui-switch\n [checked]=\"subitem.enabled\"\n [disabled]=\"isPendingCreate(subitem)\"\n (checkedChange)=\"emitDeactivateChild(item, subitem, $event)\"\n ></ui-switch>\n <ui-button\n (click)=\"emitEditChild(item, subitem, $event)\"\n [disabled]=\"isPendingCreate(subitem)\"\n icon=\"ri-edit-line\"\n type=\"ghost\"\n [iconOnly]=\"true\"\n size=\"s\"\n ></ui-button>\n <ui-button \n (click)=\"emitDeleteChild(item, subitem, $event)\" \n icon=\"ri-delete-bin-line\" \n [disabled]=\"isPendingCreate(subitem)\"\n type=\"ghost\" \n [iconOnly]=\"true\" size=\"s\"></ui-button>\n </div>\n </div>\n }\n } @else {\n <div class=\"tree-empty\">\n {{emptyMessage}}\n </div>\n }\n </div>\n </div>\n }\n}\n", styles: [":host{width:100%}.tree-item{border:solid 1px var(--grey-400);border-radius:10px;overflow:hidden;margin-bottom:4px;background:linear-gradient(180deg,var(--grey-100) 0%,var(--grey-50) 100%);transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.tree-header{width:100%;display:flex;align-items:center;gap:5px;padding:4px 10px;border-bottom:solid 1px var(--grey-400);background-color:var(--grey-100);cursor:pointer;transition:background-color .16s ease}.tree-header:hover{background-color:var(--grey-200)}.tree-header-name{flex:1 1;display:flex;align-items:center;gap:10px}.tree-header-name span{border-radius:10px;padding:2px 10px;font-size:10px;color:var(--blue-800);background-color:var(--blue-100)}.tree-arrow{transition:transform .18s ease}.tree-arrow.is-collapsed{transform:rotate(-90deg)}.tree-body{background-color:var(--white);padding:2px 16px;display:flex;flex-direction:column;gap:1px;max-height:600px;opacity:1;transition:max-height .2s ease,opacity .2s ease,padding .2s ease}.tree-empty{padding:8px 10px;font-size:12px;color:var(--grey-500);background-color:var(--grey-50);border-radius:8px}.tree-subitem{color:var(--grey-700);border-radius:5px;padding:3px 15px;gap:5px;font-size:13px;display:flex;align-items:center;justify-content:space-between}.tree-subitem-name{flex:1 1;display:inline-flex;align-items:center;gap:6px}.tree-subitem-status{font-size:10px;color:var(--grey-400)}.tree-subitem-status.is-enabled{color:var(--blue-500)}.tree-subitem-status.is-disabled{color:var(--grey-400)}.tree-subitem-actions{display:inline-flex;align-items:center;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease,transform .16s ease}.tree-subitem:hover{background-color:var(--blue-100)}.tree-subitem:hover .tree-subitem-actions{opacity:1;pointer-events:auto;transform:translateY(0)}.tree-subitem.is-disabled .tree-name-text,.tree-subitem.is-disabled .tree-name-input,.tree-subitem.is-parent-disabled .tree-name-text,.tree-subitem.is-parent-disabled .tree-name-input{color:var(--grey-500)}.tree-subitem.is-parent-disabled .tree-subitem-status{color:var(--grey-400)}.tree-subitem.is-pending .tree-name-text{cursor:default;color:var(--grey-500)}.tree-name-text{width:50%;color:var(--grey-900);cursor:text}.tree-name-input{flex:1 1;min-width:0;border:solid 1px var(--grey-300);border-radius:6px;padding:7px;font-size:13px;color:var(--grey-900);background-color:var(--white);outline:none;transition:border-color .16s ease,box-shadow .16s ease;width:300px}.tree-name-input:focus{border-color:var(--blue-500);box-shadow:0 0 0 2px #3b82f626}.tree-name-input--sub{font-size:12px;padding:5px}.tree-item.is-collapsed .tree-body{max-height:0;opacity:0;padding-top:0;padding-bottom:0;overflow:hidden}.tree-skeleton{padding:8px;border:solid 1px var(--grey-400);border-radius:10px;background-color:var(--grey-50)}\n"] }]
5386
5474
  }], propDecorators: { loading: [{
5387
5475
  type: Input
5388
- }], createSubcategoryFn: [{
5476
+ }], emptyMessage: [{
5477
+ type: Input
5478
+ }], duplicateNameMessage: [{
5389
5479
  type: Input
5390
5480
  }], edit: [{
5391
5481
  type: Output
@@ -5395,6 +5485,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
5395
5485
  type: Output
5396
5486
  }], nameChange: [{
5397
5487
  type: Output
5488
+ }], create: [{
5489
+ type: Output
5398
5490
  }], tree: [{
5399
5491
  type: Input
5400
5492
  }], childNameInputs: [{