@xsmusices/ui 1.44.4 → 1.44.7

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.
@@ -5223,36 +5223,82 @@ class XSBodyOverflowService {
5223
5223
  platformId;
5224
5224
  documentService;
5225
5225
  count = 0;
5226
+ locked = [];
5226
5227
  constructor(platformId, documentService) {
5227
5228
  this.platformId = platformId;
5228
5229
  this.documentService = documentService;
5229
5230
  }
5230
- lock() {
5231
+ /**
5232
+ * Bloquea el scroll. Sin selector bloquea body (histórico).
5233
+ * Con selector CSS bloquea ese elemento ADEMÁS de body, restaurando
5234
+ * el valor previo exacto al desbloquear. Si el selector no existe,
5235
+ * solo se bloquea body. Contador para aperturas repetidas.
5236
+ */
5237
+ lock(selector) {
5231
5238
  this.count++;
5232
- if (isPlatformBrowser(this.platformId) && this.count === 1) {
5233
- const doc = this.documentService.document;
5234
- if (doc) {
5235
- doc.body.style.overflow = 'hidden';
5239
+ if (!isPlatformBrowser(this.platformId))
5240
+ return;
5241
+ if (this.count !== 1)
5242
+ return;
5243
+ const doc = this.documentService.document;
5244
+ if (!doc)
5245
+ return;
5246
+ this.lockEl(doc.body);
5247
+ if (selector) {
5248
+ try {
5249
+ const target = doc.querySelector(selector);
5250
+ if (target instanceof HTMLElement &&
5251
+ target !== doc.body) {
5252
+ this.lockEl(target);
5253
+ }
5254
+ }
5255
+ catch {
5256
+ // Selector inválido: se mantiene solo el bloqueo de body.
5236
5257
  }
5237
5258
  }
5238
5259
  }
5260
+ lockEl(el) {
5261
+ this.locked.push({ el, prevOverflow: el.style.overflow });
5262
+ el.style.overflow = 'hidden';
5263
+ }
5239
5264
  unlock() {
5240
5265
  this.count = Math.max(0, this.count - 1);
5241
- if (isPlatformBrowser(this.platformId) && this.count === 0) {
5242
- const doc = this.documentService.document;
5243
- if (doc) {
5244
- doc.body.style.overflow = '';
5266
+ if (!isPlatformBrowser(this.platformId))
5267
+ return;
5268
+ if (this.count !== 0)
5269
+ return;
5270
+ const doc = this.documentService.document;
5271
+ if (!doc) {
5272
+ this.locked = [];
5273
+ return;
5274
+ }
5275
+ for (const { el, prevOverflow } of this.locked) {
5276
+ if (el.isConnected) {
5277
+ el.style.overflow = prevOverflow;
5245
5278
  }
5246
5279
  }
5280
+ this.locked = [];
5247
5281
  }
5248
5282
  reset() {
5249
5283
  this.count = 0;
5250
- if (isPlatformBrowser(this.platformId)) {
5251
- const doc = this.documentService.document;
5252
- if (doc) {
5284
+ if (!isPlatformBrowser(this.platformId)) {
5285
+ this.locked = [];
5286
+ return;
5287
+ }
5288
+ const doc = this.documentService.document;
5289
+ if (doc) {
5290
+ for (const { el, prevOverflow } of this.locked) {
5291
+ if (el.isConnected) {
5292
+ el.style.overflow = prevOverflow;
5293
+ }
5294
+ }
5295
+ // Compatibilidad: por si algún consumidor antiguo mutó body
5296
+ // directamente fuera del registro.
5297
+ if (doc.body.style.overflow === 'hidden') {
5253
5298
  doc.body.style.overflow = '';
5254
5299
  }
5255
5300
  }
5301
+ this.locked = [];
5256
5302
  }
5257
5303
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: XSBodyOverflowService, deps: [{ token: PLATFORM_ID }, { token: XSDocumentService }], target: i0.ɵɵFactoryTarget.Injectable });
5258
5304
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: XSBodyOverflowService, providedIn: 'root' });
@@ -7884,6 +7930,7 @@ class XSModalComponent {
7884
7930
  injector = inject(Injector);
7885
7931
  dynamicContentPlaceholder = viewChild.required('dynamicContentPlaceholder', { read: ViewContainerRef });
7886
7932
  destroyRef = inject(DestroyRef);
7933
+ hostRef = inject(ElementRef);
7887
7934
  translateService = inject(XSTranslateService);
7888
7935
  langVersion = signal(0, ...(ngDevMode ? [{ debugName: "langVersion" }] : []));
7889
7936
  platformId = inject(PLATFORM_ID);
@@ -7901,6 +7948,10 @@ class XSModalComponent {
7901
7948
  buttons = signal([], ...(ngDevMode ? [{ debugName: "buttons" }] : []));
7902
7949
  hasOverflow = true;
7903
7950
  closeOnBackdropClick = signal(true, ...(ngDevMode ? [{ debugName: "closeOnBackdropClick" }] : []));
7951
+ showCloseButton = signal(false, ...(ngDevMode ? [{ debugName: "showCloseButton" }] : []));
7952
+ closeLabel = signal('', ...(ngDevMode ? [{ debugName: "closeLabel" }] : []));
7953
+ contentPadding = signal('default', ...(ngDevMode ? [{ debugName: "contentPadding" }] : []));
7954
+ panelSize = signal('xl', ...(ngDevMode ? [{ debugName: "panelSize" }] : []));
7904
7955
  // unknown es necesario porque la acción que emite el modal es genérica y la define el componente inyectado
7905
7956
  onAction = new Subject();
7906
7957
  constructor() {
@@ -7932,7 +7983,7 @@ class XSModalComponent {
7932
7983
  // unknown es necesario porque el modal acepta cualquier tipo de componente a inyectar
7933
7984
  componentType,
7934
7985
  // unknown es necesario porque el dato es genérico y lo define el componente inyectado
7935
- data, type, title, subTitle, buttons, _hasOverflow, _closeOnBackdropClick) {
7986
+ data, type, title, subTitle, buttons, _hasOverflow, _closeOnBackdropClick, _showCloseButton, _closeLabel, _contentPadding, _panelSize) {
7936
7987
  const placeholder = this.dynamicContentPlaceholder();
7937
7988
  placeholder?.clear();
7938
7989
  this.injectedComponentRef = placeholder.createComponent(componentType, {
@@ -7954,8 +8005,13 @@ class XSModalComponent {
7954
8005
  }
7955
8006
  this.closeRequested = false;
7956
8007
  this.isVisible.set(true);
7957
- this.previousActiveElement =
7958
- document.activeElement;
8008
+ if (isPlatformBrowser(this.platformId)) {
8009
+ this.previousActiveElement =
8010
+ document.activeElement;
8011
+ }
8012
+ else {
8013
+ this.previousActiveElement = null;
8014
+ }
7959
8015
  if (title !== undefined)
7960
8016
  this.modalTitle.set(title);
7961
8017
  if (subTitle !== undefined)
@@ -7966,6 +8022,17 @@ class XSModalComponent {
7966
8022
  this.hasOverflow = _hasOverflow;
7967
8023
  if (_closeOnBackdropClick !== undefined)
7968
8024
  this.closeOnBackdropClick.set(_closeOnBackdropClick);
8025
+ if (_showCloseButton !== undefined)
8026
+ this.showCloseButton.set(_showCloseButton);
8027
+ if (_closeLabel !== undefined)
8028
+ this.closeLabel.set(_closeLabel);
8029
+ if (_contentPadding !== undefined)
8030
+ this.contentPadding.set(_contentPadding);
8031
+ if (_panelSize !== undefined)
8032
+ this.panelSize.set(_panelSize);
8033
+ if (isPlatformBrowser(this.platformId)) {
8034
+ setTimeout(() => this.focusInitial(), 0);
8035
+ }
7969
8036
  const fbInstance = this.injectedComponentRef?.instance?.footerButtons;
7970
8037
  if (fbInstance) {
7971
8038
  this.footerSyncEffect?.destroy();
@@ -8000,6 +8067,11 @@ class XSModalComponent {
8000
8067
  onTabKey(event) {
8001
8068
  if (!this.isVisible())
8002
8069
  return;
8070
+ if (!isPlatformBrowser(this.platformId))
8071
+ return;
8072
+ const active = document.activeElement;
8073
+ if (!active || !this.hostRef.nativeElement.contains(active))
8074
+ return;
8003
8075
  const focusable = this.getFocusableElements();
8004
8076
  if (focusable.length === 0) {
8005
8077
  event.preventDefault();
@@ -8018,19 +8090,37 @@ class XSModalComponent {
8018
8090
  }
8019
8091
  }
8020
8092
  getFocusableElements() {
8021
- const hostEl = document.querySelector('[xs-modal-host]');
8093
+ const hostEl = this.hostRef.nativeElement;
8022
8094
  if (!hostEl)
8023
8095
  return [];
8024
8096
  return Array.from(hostEl.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'));
8025
8097
  }
8098
+ /** Lleva el foco al panel o a su primer elemento enfocable. */
8099
+ focusInitial() {
8100
+ if (!this.isVisible())
8101
+ return;
8102
+ const focusable = this.getFocusableElements();
8103
+ if (focusable.length > 0) {
8104
+ focusable[0].focus();
8105
+ return;
8106
+ }
8107
+ const panel = this.hostRef.nativeElement.querySelector('[data-xs-modal-panel]');
8108
+ panel?.focus();
8109
+ }
8026
8110
  close(result) {
8027
8111
  if (this.closeRequested)
8028
8112
  return;
8029
8113
  this.modalResult.next(result !== undefined ? result : null);
8030
8114
  this.closeRequested = true;
8031
8115
  this.isVisible.set(false);
8032
- this.previousActiveElement?.focus();
8116
+ const prev = this.previousActiveElement;
8033
8117
  this.previousActiveElement = null;
8118
+ if (isPlatformBrowser(this.platformId) &&
8119
+ prev &&
8120
+ prev.isConnected &&
8121
+ typeof prev.focus === 'function') {
8122
+ prev.focus();
8123
+ }
8034
8124
  }
8035
8125
  onBackdropMouseDown(event) {
8036
8126
  if (event.target === event.currentTarget &&
@@ -8069,14 +8159,14 @@ class XSModalComponent {
8069
8159
  return this.translateService.translate(key);
8070
8160
  }
8071
8161
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: XSModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8072
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: XSModalComponent, isStandalone: true, selector: "xs-modal", host: { attributes: { "xs-modal-host": "" }, listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, viewQueries: [{ propertyName: "dynamicContentPlaceholder", first: true, predicate: ["dynamicContentPlaceholder"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<div\n class=\"modal-overlay\"\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"modalTitle() || translate('MODAL.DIALOG_LABEL')\"\n [ngClass]=\"{\n 'bottom-sheet': currentModalType() === 'bottom-sheet',\n centered: currentModalType() === 'centered',\n 'side-panel': currentModalType() === 'side-panel',\n }\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n>\n <div\n class=\"relative p-4 bg-black shadow-lg max-h-[80vh] flex flex-col\"\n [ngClass]=\"{\n 'modal-bottom-sheet': currentModalType() === 'bottom-sheet',\n 'modal-centered': currentModalType() === 'centered',\n 'modal-side-panel': currentModalType() === 'side-panel',\n 'animate-slide-up':\n isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-slide-down':\n !isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-zoom-in':\n isVisible() && currentModalType() === 'centered',\n 'animate-slide-in-right':\n isVisible() && currentModalType() === 'side-panel',\n 'animate-slide-out-right':\n !isVisible() && currentModalType() === 'side-panel',\n }\"\n role=\"document\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n (animationend)=\"onAnimationEnd()\"\n >\n @if (currentModalType() === 'bottom-sheet') {\n <div\n class=\"flex justify-center p-2\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <div class=\"w-10 h-1 bg-gray-300 rounded-full\"></div>\n </div>\n }\n @if (modalTitle()) {\n <div\n class=\"px-4 pb-2 text-center\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <h2 class=\"text-xl font-bold text-white\">\n {{ modalTitle() }}\n </h2>\n @if (modalSubTitle()) {\n <p class=\"text-sm text-gray-300\">\n {{ modalSubTitle() }}\n </p>\n }\n </div>\n }\n\n <div class=\"flex-1 px-2 overflow-y-auto overflow-x-hidden\">\n <ng-template #dynamicContentPlaceholder></ng-template>\n </div>\n\n @if (buttons() && buttons().length > 0) {\n <div\n class=\"modal-footer grid grid-cols-12 gap-2 pt-4 mt-2 bg-black border-t border-zinc-800/30\"\n >\n @for (button of buttons(); track button.id ?? $index) {\n <xs-button\n [id]=\"\n button.id\n ? button.id\n : buttons().indexOf(button).toString()\n \"\n (click)=\"handleButtonAction(button.action!)\"\n [variant]=\"\n button.variant ? button.variant : 'primary'\n \"\n [layout]=\"button.layout ? button.layout : 'between'\"\n [isFull]=\"true\"\n [disabled]=\"button.disabled ?? false\"\n [loading]=\"button.loading ?? false\"\n [icon]=\"button.icon\"\n [label]=\"button.label\"\n [ngClass]=\"getButtonClass(button)\"\n >\n </xs-button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".modal-overlay{position:fixed;inset:0;z-index:10000;display:flex;justify-content:center;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .5}.modal-bottom-sheet{z-index:10001;width:100%;max-width:28rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.modal-overlay.bottom-sheet{align-items:flex-end}.modal-centered{z-index:10001;margin:1rem;width:100%;max-width:32rem;border-radius:.75rem}.modal-overlay.centered{align-items:center}.modal-footer{padding-bottom:max(1rem,env(safe-area-inset-bottom,0px))}.modal-side-panel{z-index:10001;margin:0;height:100vh;max-height:100vh;width:100%;border-radius:0;border-left-width:1px;--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}@media(min-width:640px){.modal-side-panel{max-width:36rem}}@media(min-width:1024px){.modal-side-panel{max-width:42rem}}.modal-side-panel{height:100dvh;max-height:100dvh}.modal-overlay.side-panel{align-items:stretch;justify-content:flex-end;padding:0}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideDown{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes zoomIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}@keyframes slideInRight{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes slideOutRight{0%{transform:translate(0)}to{transform:translate(100%)}}.animate-slide-in-right{animation:slideInRight .3s ease-out forwards}.animate-slide-out-right{animation:slideOutRight .3s ease-in forwards}.animate-zoom-in{animation:zoomIn .3s ease-out forwards}.animate-slide-up{animation:slideUp .3s ease-out forwards}.animate-slide-down{animation:slideDown .3s ease-in forwards}:host h2{text-transform:uppercase}:host-context(xs-button) button{text-transform:uppercase}.close-button{position:absolute;top:.75rem;right:.75rem;cursor:pointer;font-size:1.5rem;line-height:2rem;font-weight:700;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.close-button:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.modal-open{overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: XSButtonComponent, selector: "xs-button", inputs: ["id", "label", "type", "icon", "action", "customClass", "variant", "layout", "badgeCount", "isFull", "disabled", "loading", "title", "ariaLabel", "borderRadius", "isGrouped", "groupPosition"], outputs: ["isGroupedChange", "groupPositionChange", "click", "onClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8162
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: XSModalComponent, isStandalone: true, selector: "xs-modal", host: { attributes: { "xs-modal-host": "" }, listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, viewQueries: [{ propertyName: "dynamicContentPlaceholder", first: true, predicate: ["dynamicContentPlaceholder"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<div\n class=\"modal-overlay\"\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"modalTitle() || translate('MODAL.DIALOG_LABEL')\"\n [ngClass]=\"{\n 'bottom-sheet': currentModalType() === 'bottom-sheet',\n centered: currentModalType() === 'centered',\n 'side-panel': currentModalType() === 'side-panel',\n }\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n>\n <div\n data-xs-modal-panel\n tabindex=\"-1\"\n class=\"relative bg-black shadow-lg max-h-[80vh] flex flex-col\"\n [class.p-4]=\"contentPadding() === 'default'\"\n [class.p-0]=\"contentPadding() === 'none'\"\n [ngClass]=\"{\n 'modal-bottom-sheet': currentModalType() === 'bottom-sheet',\n 'modal-centered': currentModalType() === 'centered',\n 'modal-side-panel': currentModalType() === 'side-panel',\n 'modal-side-panel-sm': currentModalType() === 'side-panel' && panelSize() === 'sm',\n 'modal-side-panel-md': currentModalType() === 'side-panel' && panelSize() === 'md',\n 'modal-side-panel-lg': currentModalType() === 'side-panel' && panelSize() === 'lg',\n 'animate-slide-up':\n isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-slide-down':\n !isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-zoom-in':\n isVisible() && currentModalType() === 'centered',\n 'animate-slide-in-right':\n isVisible() && currentModalType() === 'side-panel',\n 'animate-slide-out-right':\n !isVisible() && currentModalType() === 'side-panel',\n }\"\n role=\"document\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n (animationend)=\"onAnimationEnd()\"\n >\n @if (currentModalType() === 'bottom-sheet') {\n <div\n class=\"flex justify-center p-2\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <div class=\"w-10 h-1 bg-gray-300 rounded-full\"></div>\n </div>\n }\n @if (modalTitle()) {\n <div\n class=\"modal-header px-4 pb-2 text-center\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <h2 class=\"text-xl font-bold text-white\">\n {{ modalTitle() }}\n </h2>\n @if (modalSubTitle()) {\n <p class=\"text-sm text-gray-300\">\n {{ modalSubTitle() }}\n </p>\n }\n </div>\n }\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"modal-close-btn\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeLabel() || translate('MODAL.CLOSE')\"\n >\n <span aria-hidden=\"true\">\u00D7</span>\n </button>\n }\n\n <div class=\"flex-1 px-2 overflow-y-auto overflow-x-hidden\">\n <ng-template #dynamicContentPlaceholder></ng-template>\n </div>\n\n @if (buttons() && buttons().length > 0) {\n <div\n class=\"modal-footer grid grid-cols-12 gap-2 pt-4 mt-2 bg-black border-t border-zinc-800/30\"\n >\n @for (button of buttons(); track button.id ?? $index) {\n <xs-button\n [id]=\"\n button.id\n ? button.id\n : buttons().indexOf(button).toString()\n \"\n (click)=\"handleButtonAction(button.action!)\"\n [variant]=\"\n button.variant ? button.variant : 'primary'\n \"\n [layout]=\"button.layout ? button.layout : 'between'\"\n [isFull]=\"true\"\n [disabled]=\"button.disabled ?? false\"\n [loading]=\"button.loading ?? false\"\n [icon]=\"button.icon\"\n [label]=\"button.label\"\n [ngClass]=\"getButtonClass(button)\"\n >\n </xs-button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".modal-overlay{position:fixed;inset:0;z-index:10000;display:flex;justify-content:center;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .5}.modal-bottom-sheet{z-index:10001;width:100%;max-width:28rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.modal-overlay.bottom-sheet{align-items:flex-end}.modal-centered{z-index:10001;margin:1rem;width:100%;max-width:32rem;border-radius:.75rem}.modal-overlay.centered{align-items:center}.modal-footer{padding-bottom:max(1rem,env(safe-area-inset-bottom,0px));padding-left:max(1rem,env(safe-area-inset-left,0px));padding-right:max(1rem,env(safe-area-inset-right,0px))}.modal-overlay.side-panel .modal-header{padding-top:max(.5rem,env(safe-area-inset-top,0px))}.modal-close-btn{position:absolute;top:.75rem;right:.75rem;z-index:10;display:flex;height:2.25rem;width:2.25rem;align-items:center;justify-content:center;border-radius:9999px;font-size:1.5rem;line-height:2rem;line-height:1;--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.modal-close-btn:hover{background-color:#27272a99;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.modal-close-btn:focus-visible{outline-width:2px;outline-color:#fff}.modal-overlay.side-panel .modal-close-btn{top:max(.75rem,env(safe-area-inset-top,0px));right:max(.75rem,env(safe-area-inset-right,0px))}.modal-side-panel{z-index:10001;margin:0;height:100vh;max-height:100vh;width:100%;border-radius:0;border-left-width:1px;--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1));height:100dvh;max-height:100dvh}.modal-side-panel,.modal-side-panel-sm,.modal-side-panel-md,.modal-side-panel-lg{width:100%}@media(min-width:640px){.modal-side-panel-sm{max-width:28rem}.modal-side-panel-md{max-width:32rem}.modal-side-panel-lg,.modal-side-panel{max-width:36rem}}@media(min-width:1024px){.modal-side-panel{max-width:42rem}}.modal-overlay.side-panel{align-items:stretch;justify-content:flex-end;padding:0}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideDown{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes zoomIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}@keyframes slideInRight{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes slideOutRight{0%{transform:translate(0)}to{transform:translate(100%)}}.animate-slide-in-right{animation:slideInRight .3s ease-out forwards}.animate-slide-out-right{animation:slideOutRight .3s ease-in forwards}.animate-zoom-in{animation:zoomIn .3s ease-out forwards}.animate-slide-up{animation:slideUp .3s ease-out forwards}.animate-slide-down{animation:slideDown .3s ease-in forwards}:host h2{text-transform:uppercase}:host-context(xs-button) button{text-transform:uppercase}.close-button{position:absolute;top:.75rem;right:.75rem;cursor:pointer;font-size:1.5rem;line-height:2rem;font-weight:700;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.close-button:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.modal-open{overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: XSButtonComponent, selector: "xs-button", inputs: ["id", "label", "type", "icon", "action", "customClass", "variant", "layout", "badgeCount", "isFull", "disabled", "loading", "title", "ariaLabel", "borderRadius", "isGrouped", "groupPosition"], outputs: ["isGroupedChange", "groupPositionChange", "click", "onClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8073
8163
  }
8074
8164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: XSModalComponent, decorators: [{
8075
8165
  type: Component,
8076
8166
  args: [{ selector: 'xs-modal', imports: [CommonModule, XSButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
8077
8167
  'xs-modal-host': '',
8078
8168
  '(document:keydown.escape)': 'onEscapeKey($event)',
8079
- }, template: "<div\n class=\"modal-overlay\"\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"modalTitle() || translate('MODAL.DIALOG_LABEL')\"\n [ngClass]=\"{\n 'bottom-sheet': currentModalType() === 'bottom-sheet',\n centered: currentModalType() === 'centered',\n 'side-panel': currentModalType() === 'side-panel',\n }\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n>\n <div\n class=\"relative p-4 bg-black shadow-lg max-h-[80vh] flex flex-col\"\n [ngClass]=\"{\n 'modal-bottom-sheet': currentModalType() === 'bottom-sheet',\n 'modal-centered': currentModalType() === 'centered',\n 'modal-side-panel': currentModalType() === 'side-panel',\n 'animate-slide-up':\n isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-slide-down':\n !isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-zoom-in':\n isVisible() && currentModalType() === 'centered',\n 'animate-slide-in-right':\n isVisible() && currentModalType() === 'side-panel',\n 'animate-slide-out-right':\n !isVisible() && currentModalType() === 'side-panel',\n }\"\n role=\"document\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n (animationend)=\"onAnimationEnd()\"\n >\n @if (currentModalType() === 'bottom-sheet') {\n <div\n class=\"flex justify-center p-2\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <div class=\"w-10 h-1 bg-gray-300 rounded-full\"></div>\n </div>\n }\n @if (modalTitle()) {\n <div\n class=\"px-4 pb-2 text-center\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <h2 class=\"text-xl font-bold text-white\">\n {{ modalTitle() }}\n </h2>\n @if (modalSubTitle()) {\n <p class=\"text-sm text-gray-300\">\n {{ modalSubTitle() }}\n </p>\n }\n </div>\n }\n\n <div class=\"flex-1 px-2 overflow-y-auto overflow-x-hidden\">\n <ng-template #dynamicContentPlaceholder></ng-template>\n </div>\n\n @if (buttons() && buttons().length > 0) {\n <div\n class=\"modal-footer grid grid-cols-12 gap-2 pt-4 mt-2 bg-black border-t border-zinc-800/30\"\n >\n @for (button of buttons(); track button.id ?? $index) {\n <xs-button\n [id]=\"\n button.id\n ? button.id\n : buttons().indexOf(button).toString()\n \"\n (click)=\"handleButtonAction(button.action!)\"\n [variant]=\"\n button.variant ? button.variant : 'primary'\n \"\n [layout]=\"button.layout ? button.layout : 'between'\"\n [isFull]=\"true\"\n [disabled]=\"button.disabled ?? false\"\n [loading]=\"button.loading ?? false\"\n [icon]=\"button.icon\"\n [label]=\"button.label\"\n [ngClass]=\"getButtonClass(button)\"\n >\n </xs-button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".modal-overlay{position:fixed;inset:0;z-index:10000;display:flex;justify-content:center;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .5}.modal-bottom-sheet{z-index:10001;width:100%;max-width:28rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.modal-overlay.bottom-sheet{align-items:flex-end}.modal-centered{z-index:10001;margin:1rem;width:100%;max-width:32rem;border-radius:.75rem}.modal-overlay.centered{align-items:center}.modal-footer{padding-bottom:max(1rem,env(safe-area-inset-bottom,0px))}.modal-side-panel{z-index:10001;margin:0;height:100vh;max-height:100vh;width:100%;border-radius:0;border-left-width:1px;--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}@media(min-width:640px){.modal-side-panel{max-width:36rem}}@media(min-width:1024px){.modal-side-panel{max-width:42rem}}.modal-side-panel{height:100dvh;max-height:100dvh}.modal-overlay.side-panel{align-items:stretch;justify-content:flex-end;padding:0}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideDown{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes zoomIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}@keyframes slideInRight{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes slideOutRight{0%{transform:translate(0)}to{transform:translate(100%)}}.animate-slide-in-right{animation:slideInRight .3s ease-out forwards}.animate-slide-out-right{animation:slideOutRight .3s ease-in forwards}.animate-zoom-in{animation:zoomIn .3s ease-out forwards}.animate-slide-up{animation:slideUp .3s ease-out forwards}.animate-slide-down{animation:slideDown .3s ease-in forwards}:host h2{text-transform:uppercase}:host-context(xs-button) button{text-transform:uppercase}.close-button{position:absolute;top:.75rem;right:.75rem;cursor:pointer;font-size:1.5rem;line-height:2rem;font-weight:700;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.close-button:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.modal-open{overflow:hidden}\n"] }]
8169
+ }, template: "<div\n class=\"modal-overlay\"\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"modalTitle() || translate('MODAL.DIALOG_LABEL')\"\n [ngClass]=\"{\n 'bottom-sheet': currentModalType() === 'bottom-sheet',\n centered: currentModalType() === 'centered',\n 'side-panel': currentModalType() === 'side-panel',\n }\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n>\n <div\n data-xs-modal-panel\n tabindex=\"-1\"\n class=\"relative bg-black shadow-lg max-h-[80vh] flex flex-col\"\n [class.p-4]=\"contentPadding() === 'default'\"\n [class.p-0]=\"contentPadding() === 'none'\"\n [ngClass]=\"{\n 'modal-bottom-sheet': currentModalType() === 'bottom-sheet',\n 'modal-centered': currentModalType() === 'centered',\n 'modal-side-panel': currentModalType() === 'side-panel',\n 'modal-side-panel-sm': currentModalType() === 'side-panel' && panelSize() === 'sm',\n 'modal-side-panel-md': currentModalType() === 'side-panel' && panelSize() === 'md',\n 'modal-side-panel-lg': currentModalType() === 'side-panel' && panelSize() === 'lg',\n 'animate-slide-up':\n isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-slide-down':\n !isVisible() && currentModalType() === 'bottom-sheet',\n 'animate-zoom-in':\n isVisible() && currentModalType() === 'centered',\n 'animate-slide-in-right':\n isVisible() && currentModalType() === 'side-panel',\n 'animate-slide-out-right':\n !isVisible() && currentModalType() === 'side-panel',\n }\"\n role=\"document\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n (animationend)=\"onAnimationEnd()\"\n >\n @if (currentModalType() === 'bottom-sheet') {\n <div\n class=\"flex justify-center p-2\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <div class=\"w-10 h-1 bg-gray-300 rounded-full\"></div>\n </div>\n }\n @if (modalTitle()) {\n <div\n class=\"modal-header px-4 pb-2 text-center\"\n (mousedown)=\"$event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n <h2 class=\"text-xl font-bold text-white\">\n {{ modalTitle() }}\n </h2>\n @if (modalSubTitle()) {\n <p class=\"text-sm text-gray-300\">\n {{ modalSubTitle() }}\n </p>\n }\n </div>\n }\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"modal-close-btn\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeLabel() || translate('MODAL.CLOSE')\"\n >\n <span aria-hidden=\"true\">\u00D7</span>\n </button>\n }\n\n <div class=\"flex-1 px-2 overflow-y-auto overflow-x-hidden\">\n <ng-template #dynamicContentPlaceholder></ng-template>\n </div>\n\n @if (buttons() && buttons().length > 0) {\n <div\n class=\"modal-footer grid grid-cols-12 gap-2 pt-4 mt-2 bg-black border-t border-zinc-800/30\"\n >\n @for (button of buttons(); track button.id ?? $index) {\n <xs-button\n [id]=\"\n button.id\n ? button.id\n : buttons().indexOf(button).toString()\n \"\n (click)=\"handleButtonAction(button.action!)\"\n [variant]=\"\n button.variant ? button.variant : 'primary'\n \"\n [layout]=\"button.layout ? button.layout : 'between'\"\n [isFull]=\"true\"\n [disabled]=\"button.disabled ?? false\"\n [loading]=\"button.loading ?? false\"\n [icon]=\"button.icon\"\n [label]=\"button.label\"\n [ngClass]=\"getButtonClass(button)\"\n >\n </xs-button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".modal-overlay{position:fixed;inset:0;z-index:10000;display:flex;justify-content:center;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .5}.modal-bottom-sheet{z-index:10001;width:100%;max-width:28rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.modal-overlay.bottom-sheet{align-items:flex-end}.modal-centered{z-index:10001;margin:1rem;width:100%;max-width:32rem;border-radius:.75rem}.modal-overlay.centered{align-items:center}.modal-footer{padding-bottom:max(1rem,env(safe-area-inset-bottom,0px));padding-left:max(1rem,env(safe-area-inset-left,0px));padding-right:max(1rem,env(safe-area-inset-right,0px))}.modal-overlay.side-panel .modal-header{padding-top:max(.5rem,env(safe-area-inset-top,0px))}.modal-close-btn{position:absolute;top:.75rem;right:.75rem;z-index:10;display:flex;height:2.25rem;width:2.25rem;align-items:center;justify-content:center;border-radius:9999px;font-size:1.5rem;line-height:2rem;line-height:1;--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.modal-close-btn:hover{background-color:#27272a99;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.modal-close-btn:focus-visible{outline-width:2px;outline-color:#fff}.modal-overlay.side-panel .modal-close-btn{top:max(.75rem,env(safe-area-inset-top,0px));right:max(.75rem,env(safe-area-inset-right,0px))}.modal-side-panel{z-index:10001;margin:0;height:100vh;max-height:100vh;width:100%;border-radius:0;border-left-width:1px;--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1));height:100dvh;max-height:100dvh}.modal-side-panel,.modal-side-panel-sm,.modal-side-panel-md,.modal-side-panel-lg{width:100%}@media(min-width:640px){.modal-side-panel-sm{max-width:28rem}.modal-side-panel-md{max-width:32rem}.modal-side-panel-lg,.modal-side-panel{max-width:36rem}}@media(min-width:1024px){.modal-side-panel{max-width:42rem}}.modal-overlay.side-panel{align-items:stretch;justify-content:flex-end;padding:0}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideDown{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes zoomIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}@keyframes slideInRight{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes slideOutRight{0%{transform:translate(0)}to{transform:translate(100%)}}.animate-slide-in-right{animation:slideInRight .3s ease-out forwards}.animate-slide-out-right{animation:slideOutRight .3s ease-in forwards}.animate-zoom-in{animation:zoomIn .3s ease-out forwards}.animate-slide-up{animation:slideUp .3s ease-out forwards}.animate-slide-down{animation:slideDown .3s ease-in forwards}:host h2{text-transform:uppercase}:host-context(xs-button) button{text-transform:uppercase}.close-button{position:absolute;top:.75rem;right:.75rem;cursor:pointer;font-size:1.5rem;line-height:2rem;font-weight:700;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.close-button:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.modal-open{overflow:hidden}\n"] }]
8080
8170
  }], ctorParameters: () => [], propDecorators: { dynamicContentPlaceholder: [{ type: i0.ViewChild, args: ['dynamicContentPlaceholder', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
8081
8171
 
8082
8172
  class XSModalConfirmComponent {
@@ -9125,7 +9215,7 @@ class XSModalService {
9125
9215
  if (this.modalComponentRef) {
9126
9216
  this.doCloseModal();
9127
9217
  }
9128
- this.bodyOverflow.lock();
9218
+ this.bodyOverflow.lock(options.scrollLockSelector);
9129
9219
  this.modalComponentRef = createComponent(XSModalComponent, {
9130
9220
  environmentInjector: this.appRef.injector,
9131
9221
  elementInjector: this.injector,
@@ -9139,7 +9229,7 @@ class XSModalService {
9139
9229
  this.currentModalResult$ = modalResult$;
9140
9230
  const result$ = modalResult$.asObservable();
9141
9231
  if (this.modalComponentRef) {
9142
- this.modalComponentRef.instance.injectComponent(componentType, options.data, options.type, options.title, options.subTitle, options.buttons, options.hasOverflow, options.closeOnBackdropClick);
9232
+ this.modalComponentRef.instance.injectComponent(componentType, options.data, options.type, options.title, options.subTitle, options.buttons, options.hasOverflow, options.closeOnBackdropClick, options.showCloseButton, options.closeLabel, options.contentPadding, options.panelSize);
9143
9233
  result$.onAction =
9144
9234
  this.modalComponentRef.instance.onAction.asObservable();
9145
9235
  this.modalComponentRef.instance.onClose
@@ -9412,17 +9502,23 @@ class XSFormlyEntityPickerComponent extends XSFormlyBaseComponent {
9412
9502
  ngOnDestroy() {
9413
9503
  super.ngOnDestroy();
9414
9504
  this.valueChangesSub.unsubscribe();
9505
+ this.searchSubject.complete();
9415
9506
  }
9416
9507
  syncSelectedItems() {
9417
9508
  const value = this.control.value;
9418
9509
  const items = Array.isArray(value)
9419
- ? [...value].sort((a, b) => a['position'] -
9420
- b['position'])
9510
+ ? [...value].sort((a, b) => (typeof a['position'] === 'number'
9511
+ ? a['position']
9512
+ : 0) -
9513
+ (typeof b['position'] === 'number'
9514
+ ? b['position']
9515
+ : 0))
9421
9516
  : [];
9422
9517
  this.selectedItems.set(items);
9423
9518
  }
9424
9519
  updateControl(items) {
9425
- const sorted = [...items].sort((a, b) => a['position'] - b['position']);
9520
+ const sorted = [...items].sort((a, b) => (typeof a['position'] === 'number' ? a['position'] : 0) -
9521
+ (typeof b['position'] === 'number' ? b['position'] : 0));
9426
9522
  this.selectedItems.set(sorted);
9427
9523
  this.control.setValue(sorted);
9428
9524
  this.control.markAsDirty();
@@ -9489,7 +9585,13 @@ class XSFormlyEntityPickerComponent extends XSFormlyBaseComponent {
9489
9585
  return;
9490
9586
  const onConfirm = this.pickerProps.onConfirmRemove;
9491
9587
  if (onConfirm) {
9492
- const allow = await onConfirm(item);
9588
+ let allow = false;
9589
+ try {
9590
+ allow = await onConfirm(item);
9591
+ }
9592
+ catch {
9593
+ allow = false;
9594
+ }
9493
9595
  if (!allow)
9494
9596
  return;
9495
9597
  }