@alexochihua/exos-library-components 2.25.25 → 2.25.26

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.
@@ -54105,8 +54105,22 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_2 = ["id", "value"];
54105
54105
  let digitIndex = 0;
54106
54106
  const hiddenChar = props.hiddenChar || '*';
54107
54107
 
54108
- // Seguir el patrón exactamente, insertando separadores según corresponda
54109
- for (let i = 0; i < pattern.length; i++) {
54108
+ // Índice del primer placeholder (# o hiddenChar) en el patrón
54109
+ let firstPlaceholderIndex = -1;
54110
+ for (let k = 0; k < pattern.length; k++) {
54111
+ if (pattern[k] === '#' || pattern[k] === hiddenChar) {
54112
+ firstPlaceholderIndex = k;
54113
+ break;
54114
+ }
54115
+ }
54116
+
54117
+ // Prefijo literal completo antes del primer placeholder (ej. "PROD-" en "PROD-##-#####")
54118
+ if (cleanValue.length > 0 && firstPlaceholderIndex > 0) {
54119
+ displayValue += pattern.substring(0, firstPlaceholderIndex);
54120
+ }
54121
+
54122
+ // Seguir el patrón exactamente desde el primer placeholder, insertando separadores según corresponda
54123
+ for (let i = firstPlaceholderIndex >= 0 ? firstPlaceholderIndex : 0; i < pattern.length; i++) {
54110
54124
  const patternChar = pattern[i];
54111
54125
  if (patternChar === '#') {
54112
54126
  if (digitIndex < cleanValue.length) {
@@ -54121,8 +54135,7 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_2 = ["id", "value"];
54121
54135
  digitIndex++;
54122
54136
  } else {
54123
54137
  // Es un separador - agregarlo solo si hay dígitos para mostrar
54124
- // o si es el separador inicial y hay al menos un dígito
54125
- if (digitIndex > 0 || i === 0 && cleanValue.length > 0) {
54138
+ if (digitIndex > 0) {
54126
54139
  displayValue += patternChar;
54127
54140
  }
54128
54141
  }
@@ -55289,6 +55302,17 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
55289
55302
  };
55290
55303
 
55291
55304
 
55305
+ const DROPDOWN_GAP_PX = 8;
55306
+ /** Gap entre el dropdown y el trigger cuando abre hacia arriba, para que el label quede siempre visible. */
55307
+ const DROPDOWN_LABEL_GAP_PX = 8;
55308
+ const DROPDOWN_MAX_HEIGHT_VH = 0.4;
55309
+
55310
+ /**
55311
+ * Encuentra el primer ancestro con overflow-y auto/scroll (scroll-parent).
55312
+ * En modo Teleport el dropdown se posiciona respecto al viewport; sin Teleport,
55313
+ * usar el scroll-parent evita que el menú empuje el scroll del contenedor.
55314
+ */
55315
+
55292
55316
  /* harmony default export */ var ESelectvue_type_script_setup_true_lang_js = ({
55293
55317
  __name: 'ESelect',
55294
55318
  props: {
@@ -55576,7 +55600,9 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
55576
55600
  */
55577
55601
  dropdownAppendTo: {
55578
55602
  type: String,
55579
- default: null
55603
+ default: () => {
55604
+ return (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.inject)('$exosSelectConfig')?.dropdownAppendTo ?? null;
55605
+ }
55580
55606
  },
55581
55607
  /**
55582
55608
  * Dirección de apertura del dropdown de opciones.
@@ -56019,42 +56045,89 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56019
56045
  }
56020
56046
  });
56021
56047
  };
56048
+ const getScrollParent = element => {
56049
+ if (!element) return null;
56050
+ let parent = element.parentElement;
56051
+ while (parent) {
56052
+ const style = window.getComputedStyle(parent);
56053
+ const overflowY = style.overflowY;
56054
+ if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {
56055
+ return parent;
56056
+ }
56057
+ parent = parent.parentElement;
56058
+ }
56059
+ return null;
56060
+ };
56061
+
56062
+ /**
56063
+ * Rect del viewport (para medir espacio arriba/abajo cuando se usa Teleport).
56064
+ */
56065
+ const getViewportRect = () => ({
56066
+ top: 0,
56067
+ bottom: window.innerHeight
56068
+ });
56069
+
56070
+ /**
56071
+ * Decide si el dropdown debe abrir hacia arriba según openDirection y espacio disponible.
56072
+ * En auto: compara topSpace y bottomSpace con la altura del dropdown (no umbral fijo).
56073
+ * @param {{ referenceEl: HTMLElement, dropdownHeight: number, useViewport: boolean }} opts
56074
+ * @returns {boolean} true = abrir arriba, false = abrir abajo
56075
+ */
56076
+ const shouldOpenAbove = ({
56077
+ referenceEl,
56078
+ dropdownHeight,
56079
+ useViewport
56080
+ }) => {
56081
+ if (props.openDirection === 'up') return true;
56082
+ if (props.openDirection === 'down') return false;
56083
+ const rect = referenceEl.getBoundingClientRect();
56084
+ const containerRect = useViewport ? getViewportRect() : (() => {
56085
+ const scrollParent = getScrollParent(mainSelectComponent.value);
56086
+ return scrollParent ? scrollParent.getBoundingClientRect() : getViewportRect();
56087
+ })();
56088
+ const topSpace = rect.top - containerRect.top;
56089
+ const bottomSpace = containerRect.bottom - rect.bottom;
56090
+ const needHeight = dropdownHeight + DROPDOWN_GAP_PX;
56091
+ if (bottomSpace >= needHeight) return false;
56092
+ if (topSpace >= needHeight) return true;
56093
+ return topSpace >= bottomSpace;
56094
+ };
56022
56095
  const calculateDropPositionY = () => {
56023
56096
  if (isSelectOpened.value) {
56024
56097
  const refComp = instance.refs[`mainSelectComp-${instance.uid}`];
56025
- const openAbove = props.openDirection === 'up' || props.openDirection === 'auto' && calculateAvailableSpaceY(refComp);
56098
+ const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * DROPDOWN_MAX_HEIGHT_VH;
56099
+ const openAbove = refComp ? shouldOpenAbove({
56100
+ referenceEl: refComp,
56101
+ dropdownHeight,
56102
+ useViewport: useTeleport.value
56103
+ }) : false;
56026
56104
  if (openAbove) {
56027
56105
  showPosition.value = false;
56028
- // Gap solo cuando hay valor seleccionado para que el label flotante no quede tapado
56029
56106
  return {
56030
56107
  top: 'auto',
56031
56108
  left: '0',
56032
- bottom: hasInfo.value ? 'calc(100% + 0.8rem)' : '100%'
56033
- };
56034
- } else {
56035
- showPosition.value = true;
56036
- return {
56037
- left: '0',
56038
- top: selectHeight.value + 'px' // Despliega hacia abajo
56109
+ bottom: `calc(100% + ${DROPDOWN_LABEL_GAP_PX}px)`
56039
56110
  };
56040
56111
  }
56112
+ showPosition.value = true;
56113
+ return {
56114
+ left: '0',
56115
+ top: selectHeight.value + 'px'
56116
+ };
56041
56117
  }
56042
-
56043
- // Otro manejo si es necesario cuando el dropdown no está abierto
56044
56118
  return {};
56045
56119
  };
56046
- const calculateAvailableSpaceY = referenceComp => {
56047
- const rect = referenceComp.getBoundingClientRect();
56048
- const bottomSpace = window.innerHeight - rect.bottom;
56049
- return bottomSpace < 200 ? true : false; // arriba : abajo
56050
- };
56051
56120
  const getTriggerEl = () => mainSelectComponent.value?.querySelector('[data-testid="select-default"], [data-testid="select-suggest"]');
56052
56121
  const updateDropdownPositionFixed = () => {
56053
56122
  const triggerEl = getTriggerEl();
56054
56123
  if (!triggerEl) return;
56055
56124
  const rect = triggerEl.getBoundingClientRect();
56056
- const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * 0.4;
56057
- const openAbove = props.openDirection === 'up' || props.openDirection === 'auto' && calculateAvailableSpaceY(triggerEl);
56125
+ const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * DROPDOWN_MAX_HEIGHT_VH;
56126
+ const openAbove = shouldOpenAbove({
56127
+ referenceEl: triggerEl,
56128
+ dropdownHeight,
56129
+ useViewport: true
56130
+ });
56058
56131
  const style = {
56059
56132
  position: 'fixed',
56060
56133
  left: rect.left + 'px',
@@ -56064,8 +56137,7 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56064
56137
  zIndex: 9999
56065
56138
  };
56066
56139
  if (openAbove) {
56067
- const gapLabel = hasInfo.value ? 8 : 0;
56068
- style.top = Math.max(0, rect.top - dropdownHeight - gapLabel) + 'px';
56140
+ style.top = Math.max(0, rect.top - dropdownHeight - DROPDOWN_LABEL_GAP_PX) + 'px';
56069
56141
  } else {
56070
56142
  style.top = rect.bottom + 'px';
56071
56143
  }
@@ -56421,7 +56493,7 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56421
56493
 
56422
56494
  // Watch effects
56423
56495
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.watch)(() => props.modelValue, newVal => {
56424
- if (newVal !== null && newVal !== undefined) {
56496
+ if (newVal !== null && newVal !== undefined && newVal !== '') {
56425
56497
  validate();
56426
56498
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.nextTick)(() => {
56427
56499
  calculateWidthLabelSelected();
@@ -62553,7 +62625,15 @@ const ExosLibraryComponents = {
62553
62625
  }
62554
62626
  }
62555
62627
  }
62628
+ let defaultSelectConfig = {
62629
+ dropdownAppendTo: null
62630
+ };
62631
+ let selectConfig = defaultSelectConfig;
62632
+ if (options.select?.dropdownAppendTo) {
62633
+ selectConfig.dropdownAppendTo = options.select.dropdownAppendTo;
62634
+ }
62556
62635
  app.provide('$exosDialogConfig', dialogConfig);
62636
+ app.provide('$exosSelectConfig', selectConfig);
62557
62637
  app.provide('$loading', loading);
62558
62638
  app.provide('$notify', notify);
62559
62639
  app.provide('$showMessage', message);
@@ -54123,8 +54123,22 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_2 = ["id", "value"];
54123
54123
  let digitIndex = 0;
54124
54124
  const hiddenChar = props.hiddenChar || '*';
54125
54125
 
54126
- // Seguir el patrón exactamente, insertando separadores según corresponda
54127
- for (let i = 0; i < pattern.length; i++) {
54126
+ // Índice del primer placeholder (# o hiddenChar) en el patrón
54127
+ let firstPlaceholderIndex = -1;
54128
+ for (let k = 0; k < pattern.length; k++) {
54129
+ if (pattern[k] === '#' || pattern[k] === hiddenChar) {
54130
+ firstPlaceholderIndex = k;
54131
+ break;
54132
+ }
54133
+ }
54134
+
54135
+ // Prefijo literal completo antes del primer placeholder (ej. "PROD-" en "PROD-##-#####")
54136
+ if (cleanValue.length > 0 && firstPlaceholderIndex > 0) {
54137
+ displayValue += pattern.substring(0, firstPlaceholderIndex);
54138
+ }
54139
+
54140
+ // Seguir el patrón exactamente desde el primer placeholder, insertando separadores según corresponda
54141
+ for (let i = firstPlaceholderIndex >= 0 ? firstPlaceholderIndex : 0; i < pattern.length; i++) {
54128
54142
  const patternChar = pattern[i];
54129
54143
  if (patternChar === '#') {
54130
54144
  if (digitIndex < cleanValue.length) {
@@ -54139,8 +54153,7 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_2 = ["id", "value"];
54139
54153
  digitIndex++;
54140
54154
  } else {
54141
54155
  // Es un separador - agregarlo solo si hay dígitos para mostrar
54142
- // o si es el separador inicial y hay al menos un dígito
54143
- if (digitIndex > 0 || i === 0 && cleanValue.length > 0) {
54156
+ if (digitIndex > 0) {
54144
54157
  displayValue += patternChar;
54145
54158
  }
54146
54159
  }
@@ -55307,6 +55320,17 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
55307
55320
  };
55308
55321
 
55309
55322
 
55323
+ const DROPDOWN_GAP_PX = 8;
55324
+ /** Gap entre el dropdown y el trigger cuando abre hacia arriba, para que el label quede siempre visible. */
55325
+ const DROPDOWN_LABEL_GAP_PX = 8;
55326
+ const DROPDOWN_MAX_HEIGHT_VH = 0.4;
55327
+
55328
+ /**
55329
+ * Encuentra el primer ancestro con overflow-y auto/scroll (scroll-parent).
55330
+ * En modo Teleport el dropdown se posiciona respecto al viewport; sin Teleport,
55331
+ * usar el scroll-parent evita que el menú empuje el scroll del contenedor.
55332
+ */
55333
+
55310
55334
  /* harmony default export */ var ESelectvue_type_script_setup_true_lang_js = ({
55311
55335
  __name: 'ESelect',
55312
55336
  props: {
@@ -55594,7 +55618,9 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
55594
55618
  */
55595
55619
  dropdownAppendTo: {
55596
55620
  type: String,
55597
- default: null
55621
+ default: () => {
55622
+ return (0,external_commonjs_vue_commonjs2_vue_root_Vue_.inject)('$exosSelectConfig')?.dropdownAppendTo ?? null;
55623
+ }
55598
55624
  },
55599
55625
  /**
55600
55626
  * Dirección de apertura del dropdown de opciones.
@@ -56037,42 +56063,89 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56037
56063
  }
56038
56064
  });
56039
56065
  };
56066
+ const getScrollParent = element => {
56067
+ if (!element) return null;
56068
+ let parent = element.parentElement;
56069
+ while (parent) {
56070
+ const style = window.getComputedStyle(parent);
56071
+ const overflowY = style.overflowY;
56072
+ if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {
56073
+ return parent;
56074
+ }
56075
+ parent = parent.parentElement;
56076
+ }
56077
+ return null;
56078
+ };
56079
+
56080
+ /**
56081
+ * Rect del viewport (para medir espacio arriba/abajo cuando se usa Teleport).
56082
+ */
56083
+ const getViewportRect = () => ({
56084
+ top: 0,
56085
+ bottom: window.innerHeight
56086
+ });
56087
+
56088
+ /**
56089
+ * Decide si el dropdown debe abrir hacia arriba según openDirection y espacio disponible.
56090
+ * En auto: compara topSpace y bottomSpace con la altura del dropdown (no umbral fijo).
56091
+ * @param {{ referenceEl: HTMLElement, dropdownHeight: number, useViewport: boolean }} opts
56092
+ * @returns {boolean} true = abrir arriba, false = abrir abajo
56093
+ */
56094
+ const shouldOpenAbove = ({
56095
+ referenceEl,
56096
+ dropdownHeight,
56097
+ useViewport
56098
+ }) => {
56099
+ if (props.openDirection === 'up') return true;
56100
+ if (props.openDirection === 'down') return false;
56101
+ const rect = referenceEl.getBoundingClientRect();
56102
+ const containerRect = useViewport ? getViewportRect() : (() => {
56103
+ const scrollParent = getScrollParent(mainSelectComponent.value);
56104
+ return scrollParent ? scrollParent.getBoundingClientRect() : getViewportRect();
56105
+ })();
56106
+ const topSpace = rect.top - containerRect.top;
56107
+ const bottomSpace = containerRect.bottom - rect.bottom;
56108
+ const needHeight = dropdownHeight + DROPDOWN_GAP_PX;
56109
+ if (bottomSpace >= needHeight) return false;
56110
+ if (topSpace >= needHeight) return true;
56111
+ return topSpace >= bottomSpace;
56112
+ };
56040
56113
  const calculateDropPositionY = () => {
56041
56114
  if (isSelectOpened.value) {
56042
56115
  const refComp = instance.refs[`mainSelectComp-${instance.uid}`];
56043
- const openAbove = props.openDirection === 'up' || props.openDirection === 'auto' && calculateAvailableSpaceY(refComp);
56116
+ const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * DROPDOWN_MAX_HEIGHT_VH;
56117
+ const openAbove = refComp ? shouldOpenAbove({
56118
+ referenceEl: refComp,
56119
+ dropdownHeight,
56120
+ useViewport: useTeleport.value
56121
+ }) : false;
56044
56122
  if (openAbove) {
56045
56123
  showPosition.value = false;
56046
- // Gap solo cuando hay valor seleccionado para que el label flotante no quede tapado
56047
56124
  return {
56048
56125
  top: 'auto',
56049
56126
  left: '0',
56050
- bottom: hasInfo.value ? 'calc(100% + 0.8rem)' : '100%'
56051
- };
56052
- } else {
56053
- showPosition.value = true;
56054
- return {
56055
- left: '0',
56056
- top: selectHeight.value + 'px' // Despliega hacia abajo
56127
+ bottom: `calc(100% + ${DROPDOWN_LABEL_GAP_PX}px)`
56057
56128
  };
56058
56129
  }
56130
+ showPosition.value = true;
56131
+ return {
56132
+ left: '0',
56133
+ top: selectHeight.value + 'px'
56134
+ };
56059
56135
  }
56060
-
56061
- // Otro manejo si es necesario cuando el dropdown no está abierto
56062
56136
  return {};
56063
56137
  };
56064
- const calculateAvailableSpaceY = referenceComp => {
56065
- const rect = referenceComp.getBoundingClientRect();
56066
- const bottomSpace = window.innerHeight - rect.bottom;
56067
- return bottomSpace < 200 ? true : false; // arriba : abajo
56068
- };
56069
56138
  const getTriggerEl = () => mainSelectComponent.value?.querySelector('[data-testid="select-default"], [data-testid="select-suggest"]');
56070
56139
  const updateDropdownPositionFixed = () => {
56071
56140
  const triggerEl = getTriggerEl();
56072
56141
  if (!triggerEl) return;
56073
56142
  const rect = triggerEl.getBoundingClientRect();
56074
- const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * 0.4;
56075
- const openAbove = props.openDirection === 'up' || props.openDirection === 'auto' && calculateAvailableSpaceY(triggerEl);
56143
+ const dropdownHeight = contentSelect.value?.offsetHeight ?? window.innerHeight * DROPDOWN_MAX_HEIGHT_VH;
56144
+ const openAbove = shouldOpenAbove({
56145
+ referenceEl: triggerEl,
56146
+ dropdownHeight,
56147
+ useViewport: true
56148
+ });
56076
56149
  const style = {
56077
56150
  position: 'fixed',
56078
56151
  left: rect.left + 'px',
@@ -56082,8 +56155,7 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56082
56155
  zIndex: 9999
56083
56156
  };
56084
56157
  if (openAbove) {
56085
- const gapLabel = hasInfo.value ? 8 : 0;
56086
- style.top = Math.max(0, rect.top - dropdownHeight - gapLabel) + 'px';
56158
+ style.top = Math.max(0, rect.top - dropdownHeight - DROPDOWN_LABEL_GAP_PX) + 'px';
56087
56159
  } else {
56088
56160
  style.top = rect.bottom + 'px';
56089
56161
  }
@@ -56439,7 +56511,7 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_14 = {
56439
56511
 
56440
56512
  // Watch effects
56441
56513
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_.watch)(() => props.modelValue, newVal => {
56442
- if (newVal !== null && newVal !== undefined) {
56514
+ if (newVal !== null && newVal !== undefined && newVal !== '') {
56443
56515
  validate();
56444
56516
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_.nextTick)(() => {
56445
56517
  calculateWidthLabelSelected();
@@ -62571,7 +62643,15 @@ const ExosLibraryComponents = {
62571
62643
  }
62572
62644
  }
62573
62645
  }
62646
+ let defaultSelectConfig = {
62647
+ dropdownAppendTo: null
62648
+ };
62649
+ let selectConfig = defaultSelectConfig;
62650
+ if (options.select?.dropdownAppendTo) {
62651
+ selectConfig.dropdownAppendTo = options.select.dropdownAppendTo;
62652
+ }
62574
62653
  app.provide('$exosDialogConfig', dialogConfig);
62654
+ app.provide('$exosSelectConfig', selectConfig);
62575
62655
  app.provide('$loading', loading);
62576
62656
  app.provide('$notify', notify);
62577
62657
  app.provide('$showMessage', message);