pixelon 0.5.0 → 0.5.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/dist/components/molecules/chat/StructuredContent/components/LocationField/usePlaces.d.ts +11 -0
- package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/ContextoDeLaAccion.d.ts +8 -0
- package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/types.d.ts +28 -1
- package/dist/components/molecules/chat/StructuredContent/components/ServicesMap/types.d.ts +6 -0
- package/dist/components/organisms/chat/Chat/hooks/useAutoScroll.d.ts +8 -1
- package/dist/components/templates/lumi/Crud/helpers/confirmSummary.d.ts +13 -0
- package/dist/components/templates/lumi/Crud/types.d.ts +6 -0
- package/dist/index.esm.js +193 -16
- package/dist/index.js +193 -16
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10945,6 +10945,7 @@ const Drawer = ({
|
|
|
10945
10945
|
"data-testid": dataTestId,
|
|
10946
10946
|
scrollIndicatorOffset = 1
|
|
10947
10947
|
}) => {
|
|
10948
|
+
const idTitulo = React.useId();
|
|
10948
10949
|
const [isMobile, setIsMobile] = React.useState(false);
|
|
10949
10950
|
const contentRef = React.useRef(null);
|
|
10950
10951
|
const [showScrollIndicator, setShowScrollIndicator] = React.useState(false);
|
|
@@ -11068,14 +11069,14 @@ const Drawer = ({
|
|
|
11068
11069
|
style: getDrawerStyles(),
|
|
11069
11070
|
role: "dialog",
|
|
11070
11071
|
"aria-modal": "true",
|
|
11071
|
-
"aria-labelledby": title ?
|
|
11072
|
+
"aria-labelledby": title ? idTitulo : void 0,
|
|
11072
11073
|
"data-testid": dataTestId,
|
|
11073
11074
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}drawer__viewport`, children: [
|
|
11074
11075
|
(title || showCloseButton || headerExtra) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}drawer__header`, children: [
|
|
11075
11076
|
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11076
11077
|
"h2",
|
|
11077
11078
|
{
|
|
11078
|
-
id:
|
|
11079
|
+
id: idTitulo,
|
|
11079
11080
|
className: `${COMPONENT_PREFIX}drawer__title`,
|
|
11080
11081
|
children: title
|
|
11081
11082
|
}
|
|
@@ -13188,7 +13189,9 @@ function mapFieldsToFormFields(fields, mode) {
|
|
|
13188
13189
|
readOnly: field.readOnly,
|
|
13189
13190
|
placeholder: field.placeholder,
|
|
13190
13191
|
uiProps: field.uiProps,
|
|
13191
|
-
transformOnSubmit: field.parseValue
|
|
13192
|
+
transformOnSubmit: field.parseValue,
|
|
13193
|
+
gridSpan: field.gridSpan
|
|
13194
|
+
// SPEC-131 T7
|
|
13192
13195
|
}));
|
|
13193
13196
|
}
|
|
13194
13197
|
|
|
@@ -13368,6 +13371,47 @@ function calculateTotalPages(totalRecords, limit) {
|
|
|
13368
13371
|
return Math.ceil(totalRecords / limit);
|
|
13369
13372
|
}
|
|
13370
13373
|
|
|
13374
|
+
function etiquetaDeOpcion(valor, uiProps) {
|
|
13375
|
+
const opciones = uiProps?.options;
|
|
13376
|
+
if (!Array.isArray(opciones)) return null;
|
|
13377
|
+
const encontrada = opciones.find((o) => o && String(o.value) === String(valor));
|
|
13378
|
+
return encontrada?.label != null ? String(encontrada.label) : null;
|
|
13379
|
+
}
|
|
13380
|
+
function esNumerico(moleculeType) {
|
|
13381
|
+
return moleculeType === "INPUT_CURRENCY" || moleculeType === "INPUT_NUMBER";
|
|
13382
|
+
}
|
|
13383
|
+
function aTexto$1(valor, uiProps, moleculeType) {
|
|
13384
|
+
if (valor == null || valor === "") return "";
|
|
13385
|
+
if (typeof valor === "boolean") return valor ? "S\xED" : "No";
|
|
13386
|
+
if (esNumerico(moleculeType)) {
|
|
13387
|
+
const n = Number(String(valor).replace(/[^\d.-]/g, ""));
|
|
13388
|
+
if (Number.isFinite(n)) return n.toLocaleString();
|
|
13389
|
+
}
|
|
13390
|
+
if (typeof File !== "undefined" && valor instanceof File) return valor.name;
|
|
13391
|
+
if (Array.isArray(valor)) {
|
|
13392
|
+
const partes = valor.map((v) => aTexto$1(v, uiProps, moleculeType)).filter(Boolean);
|
|
13393
|
+
return partes.join(", ");
|
|
13394
|
+
}
|
|
13395
|
+
if (typeof valor === "object") {
|
|
13396
|
+
const o = valor;
|
|
13397
|
+
for (const clave of ["label", "address", "name", "text", "value"]) {
|
|
13398
|
+
if (typeof o[clave] === "string" && o[clave]) return o[clave];
|
|
13399
|
+
}
|
|
13400
|
+
return "";
|
|
13401
|
+
}
|
|
13402
|
+
return etiquetaDeOpcion(valor, uiProps) ?? String(valor);
|
|
13403
|
+
}
|
|
13404
|
+
function resumenParaConfirmar(campos, datos) {
|
|
13405
|
+
if (!datos) return [];
|
|
13406
|
+
const lineas = [];
|
|
13407
|
+
for (const campo of campos) {
|
|
13408
|
+
const valor = aTexto$1(datos[campo.name], campo.uiProps, campo.moleculeType);
|
|
13409
|
+
if (!valor) continue;
|
|
13410
|
+
lineas.push({ label: campo.label ?? campo.name, valor });
|
|
13411
|
+
}
|
|
13412
|
+
return lineas;
|
|
13413
|
+
}
|
|
13414
|
+
|
|
13371
13415
|
const useAnchoPropio = () => {
|
|
13372
13416
|
const ref = React.useRef(null);
|
|
13373
13417
|
const [ancho, setAncho] = React.useState(null);
|
|
@@ -13812,6 +13856,10 @@ function Crud({
|
|
|
13812
13856
|
return "Formulario";
|
|
13813
13857
|
}
|
|
13814
13858
|
};
|
|
13859
|
+
const resumenConfirmacion = React.useMemo(
|
|
13860
|
+
() => confirmAction === "create" || confirmAction === "edit" ? resumenParaConfirmar(formFields, pendingFormData) : [],
|
|
13861
|
+
[confirmAction, formFields, pendingFormData]
|
|
13862
|
+
);
|
|
13815
13863
|
const getConfirmMessage = () => {
|
|
13816
13864
|
switch (confirmAction) {
|
|
13817
13865
|
case "create":
|
|
@@ -13951,6 +13999,10 @@ function Crud({
|
|
|
13951
13999
|
className: `${COMPONENT_PREFIX}crud__confirm-drawer`,
|
|
13952
14000
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-content`, children: [
|
|
13953
14001
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `${COMPONENT_PREFIX}crud__confirm-message`, children: getConfirmMessage() }),
|
|
14002
|
+
resumenConfirmacion.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("dl", { className: `${COMPONENT_PREFIX}crud__confirm-summary`, children: resumenConfirmacion.map((linea) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-summary-row`, children: [
|
|
14003
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: linea.label }),
|
|
14004
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: linea.valor })
|
|
14005
|
+
] }, linea.label)) }),
|
|
13954
14006
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-actions`, children: [
|
|
13955
14007
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13956
14008
|
Button,
|
|
@@ -14125,7 +14177,7 @@ function getFormatter(key) {
|
|
|
14125
14177
|
}
|
|
14126
14178
|
|
|
14127
14179
|
const OPTIONS_LOAD_ERROR = "No se pudieron cargar las opciones. Int\xE9ntalo de nuevo.";
|
|
14128
|
-
function unwrap$
|
|
14180
|
+
function unwrap$5(raw) {
|
|
14129
14181
|
if (raw && typeof raw === "object" && "response" in raw) {
|
|
14130
14182
|
return raw.response;
|
|
14131
14183
|
}
|
|
@@ -14146,7 +14198,7 @@ function resolveBody(body, ctx) {
|
|
|
14146
14198
|
}
|
|
14147
14199
|
async function loadOptions(eventBus, ep, ctx) {
|
|
14148
14200
|
const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, resolveBody(ep.body, ctx));
|
|
14149
|
-
let data = unwrap$
|
|
14201
|
+
let data = unwrap$5(raw);
|
|
14150
14202
|
if (ep.response_path && data && typeof data === "object" && !Array.isArray(data)) {
|
|
14151
14203
|
data = data[ep.response_path];
|
|
14152
14204
|
}
|
|
@@ -14184,7 +14236,7 @@ const mensajeDeError = (e, respaldo) => {
|
|
|
14184
14236
|
};
|
|
14185
14237
|
|
|
14186
14238
|
const REFRESH_INTERVAL_MS = 2e4;
|
|
14187
|
-
function unwrap$
|
|
14239
|
+
function unwrap$4(raw) {
|
|
14188
14240
|
if (raw && typeof raw === "object" && "response" in raw) {
|
|
14189
14241
|
return raw.response;
|
|
14190
14242
|
}
|
|
@@ -14233,7 +14285,7 @@ function useReactiveCrud(config, eventBus) {
|
|
|
14233
14285
|
filters
|
|
14234
14286
|
});
|
|
14235
14287
|
assertOk(raw);
|
|
14236
|
-
const data = unwrap$
|
|
14288
|
+
const data = unwrap$4(raw);
|
|
14237
14289
|
let rows = [];
|
|
14238
14290
|
let total = 0;
|
|
14239
14291
|
if (Array.isArray(data)) {
|
|
@@ -14437,6 +14489,72 @@ const FileField = ({
|
|
|
14437
14489
|
] });
|
|
14438
14490
|
};
|
|
14439
14491
|
|
|
14492
|
+
function unwrap$3(raw) {
|
|
14493
|
+
if (raw && typeof raw === "object" && "response" in raw) {
|
|
14494
|
+
return raw.response;
|
|
14495
|
+
}
|
|
14496
|
+
return raw;
|
|
14497
|
+
}
|
|
14498
|
+
function aTexto(valor, locale, moleculeType) {
|
|
14499
|
+
if (valor == null || valor === "") return "";
|
|
14500
|
+
if (moleculeType === "INPUT_CURRENCY" || moleculeType === "INPUT_NUMBER") {
|
|
14501
|
+
const n = Number(valor);
|
|
14502
|
+
if (!Number.isNaN(n)) return n.toLocaleString(locale || "es-CO");
|
|
14503
|
+
}
|
|
14504
|
+
if (typeof valor === "object") {
|
|
14505
|
+
const o = valor;
|
|
14506
|
+
for (const clave of ["label", "address", "name", "text", "value"]) {
|
|
14507
|
+
if (typeof o[clave] === "string" && o[clave]) return o[clave];
|
|
14508
|
+
}
|
|
14509
|
+
return "";
|
|
14510
|
+
}
|
|
14511
|
+
return String(valor);
|
|
14512
|
+
}
|
|
14513
|
+
function ContextoDeLaAccion({ contexto, row, eventBus }) {
|
|
14514
|
+
const { locale } = useChatConfig();
|
|
14515
|
+
const [urlAdjunto, setUrlAdjunto] = React.useState("");
|
|
14516
|
+
const [estadoAdjunto, setEstadoAdjunto] = React.useState("idle");
|
|
14517
|
+
const adjunto = contexto.adjunto;
|
|
14518
|
+
const idAdjunto = adjunto ? String(row[adjunto.idField] ?? "") : "";
|
|
14519
|
+
React.useEffect(() => {
|
|
14520
|
+
if (!adjunto || !idAdjunto || !eventBus) return;
|
|
14521
|
+
let vivo = true;
|
|
14522
|
+
setEstadoAdjunto("cargando");
|
|
14523
|
+
void (async () => {
|
|
14524
|
+
try {
|
|
14525
|
+
const crudo = unwrap$3(
|
|
14526
|
+
await fetchViaEventBus(eventBus, adjunto.media.service, adjunto.media.urlPath.replace("{id}", idAdjunto), "GET")
|
|
14527
|
+
);
|
|
14528
|
+
const url = typeof crudo === "string" ? crudo : String(crudo?.url ?? "");
|
|
14529
|
+
if (!vivo) return;
|
|
14530
|
+
setUrlAdjunto(url);
|
|
14531
|
+
setEstadoAdjunto(url ? "idle" : "error");
|
|
14532
|
+
} catch {
|
|
14533
|
+
if (vivo) setEstadoAdjunto("error");
|
|
14534
|
+
}
|
|
14535
|
+
})();
|
|
14536
|
+
return () => {
|
|
14537
|
+
vivo = false;
|
|
14538
|
+
};
|
|
14539
|
+
}, [adjunto, idAdjunto, eventBus]);
|
|
14540
|
+
const lineas = (contexto.campos ?? []).map((c) => ({ label: c.label, valor: aTexto(row[c.field], locale, c.moleculeType) })).filter((l) => l.valor);
|
|
14541
|
+
if (lineas.length === 0 && !adjunto) return null;
|
|
14542
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud__contexto", children: [
|
|
14543
|
+
lineas.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "px-chat-reactive-crud__contexto-datos", children: lineas.map((l) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud__contexto-linea", children: [
|
|
14544
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: l.label }),
|
|
14545
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: l.valor })
|
|
14546
|
+
] }, l.label)) }),
|
|
14547
|
+
adjunto && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud__contexto-adjunto", children: [
|
|
14548
|
+
adjunto.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-reactive-crud__contexto-adjunto-label", children: adjunto.label }),
|
|
14549
|
+
estadoAdjunto === "cargando" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-reactive-crud__contexto-nota", children: "Cargando\u2026" }),
|
|
14550
|
+
estadoAdjunto === "error" && /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "px-chat-reactive-crud__contexto-nota", children: "No se pudo cargar el archivo. M\xEDralo en \xABVer\xBB antes de confirmar." }),
|
|
14551
|
+
urlAdjunto && estadoAdjunto === "idle" && // Se abre aparte porque el comprobante de una transferencia puede ser un PDF o una
|
|
14552
|
+
// captura larga, y aquí solo cabe un vistazo.
|
|
14553
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: urlAdjunto, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: urlAdjunto, alt: adjunto.label ?? "Archivo adjunto", className: "px-chat-reactive-crud__contexto-imagen" }) })
|
|
14554
|
+
] })
|
|
14555
|
+
] });
|
|
14556
|
+
}
|
|
14557
|
+
|
|
14440
14558
|
const RESPALDO = { miles: ".", decimal: "," };
|
|
14441
14559
|
function separadoresDelLocale(locale) {
|
|
14442
14560
|
if (!locale) return RESPALDO;
|
|
@@ -14592,6 +14710,7 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
|
|
|
14592
14710
|
};
|
|
14593
14711
|
const uploading = Object.values(fileEntries).some((list) => list.some((e) => e.status === "uploading"));
|
|
14594
14712
|
return /* @__PURE__ */ jsxRuntime.jsx(Drawer$1, { isOpen: true, onClose, title: action.label, placement: "right", children: loadingOptions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-reactive-crud__action-loading", children: "Cargando\u2026" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud__action-form", children: [
|
|
14713
|
+
action.form?.contexto && /* @__PURE__ */ jsxRuntime.jsx(ContextoDeLaAccion, { contexto: action.form.contexto, row, eventBus }),
|
|
14595
14714
|
fileFields.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
14596
14715
|
FileField,
|
|
14597
14716
|
{
|
|
@@ -15896,7 +16015,8 @@ function usePlaceSuggestions(query, enabled) {
|
|
|
15896
16015
|
return {
|
|
15897
16016
|
lat: typeof loc.lat === "function" ? loc.lat() : loc.lat,
|
|
15898
16017
|
lng: typeof loc.lng === "function" ? loc.lng() : loc.lng,
|
|
15899
|
-
address: String(place.formattedAddress ?? "")
|
|
16018
|
+
address: limpiarDireccion(String(place.formattedAddress ?? ""))
|
|
16019
|
+
// SPEC-131 T6
|
|
15900
16020
|
};
|
|
15901
16021
|
} catch {
|
|
15902
16022
|
return null;
|
|
@@ -15906,13 +16026,27 @@ function usePlaceSuggestions(query, enabled) {
|
|
|
15906
16026
|
);
|
|
15907
16027
|
return { suggestions, resolve, clear: () => setSuggestions([]) };
|
|
15908
16028
|
}
|
|
16029
|
+
function limpiarDireccion(address) {
|
|
16030
|
+
const normal = (s) => s.trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
16031
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
16032
|
+
const tramos = [];
|
|
16033
|
+
for (const tramo of address.split(",")) {
|
|
16034
|
+
const limpio = tramo.trim();
|
|
16035
|
+
if (!limpio) continue;
|
|
16036
|
+
const clave = normal(limpio);
|
|
16037
|
+
if (vistos.has(clave)) continue;
|
|
16038
|
+
vistos.add(clave);
|
|
16039
|
+
tramos.push(limpio);
|
|
16040
|
+
}
|
|
16041
|
+
return tramos.join(", ");
|
|
16042
|
+
}
|
|
15909
16043
|
function reverseGeocode(lat, lng) {
|
|
15910
16044
|
return new Promise((resolve) => {
|
|
15911
16045
|
const maps = window.google?.maps;
|
|
15912
16046
|
if (!maps?.Geocoder) return resolve(null);
|
|
15913
16047
|
new maps.Geocoder().geocode({ location: { lat, lng } }, (results, status) => {
|
|
15914
16048
|
if (status !== "OK" || !results?.length) return resolve(null);
|
|
15915
|
-
resolve(String(results[0].formatted_address ?? "") || null);
|
|
16049
|
+
resolve(limpiarDireccion(String(results[0].formatted_address ?? "")) || null);
|
|
15916
16050
|
});
|
|
15917
16051
|
});
|
|
15918
16052
|
}
|
|
@@ -16069,6 +16203,10 @@ function toRenderField(f, loaded, eventBus, mapsApiKey, loadError) {
|
|
|
16069
16203
|
showInModes: f.showInModes,
|
|
16070
16204
|
required: f.required,
|
|
16071
16205
|
placeholder: f.placeholder,
|
|
16206
|
+
// SPEC-131 T7 — la fila entera: al elegir la dirección se despliega el mapa de verificación
|
|
16207
|
+
// y, a media fila, su campo hermano se quedaba con ~400px de blanco al lado. Medido entre
|
|
16208
|
+
// «¿En qué situación está?» y «¿A dónde lo llevamos?» del formulario de solicitud.
|
|
16209
|
+
gridSpan: 2,
|
|
16072
16210
|
uiProps: {
|
|
16073
16211
|
// SPEC-074 — `error` viene del render de CUSTOM (DynamicForm ya lo pasaba) y aquí se
|
|
16074
16212
|
// desechaba: el campo obligatorio más importante era el único mudo.
|
|
@@ -16693,6 +16831,8 @@ const ServicesMap = ({ config, eventBus, apiKey, filter, fill }) => {
|
|
|
16693
16831
|
const { service, url, method, payloadMap } = config.data.list;
|
|
16694
16832
|
void fetchViaEventBus(eventBus, service, url, method ?? "POST", {
|
|
16695
16833
|
...payloadMap,
|
|
16834
|
+
// SPEC-131 T6 — el del config primero, el de la instancia después: quien acota manda.
|
|
16835
|
+
...config.defaultFilter ?? {},
|
|
16696
16836
|
...filter ?? {}
|
|
16697
16837
|
}).then((res) => {
|
|
16698
16838
|
if (!alive) return;
|
|
@@ -18846,6 +18986,10 @@ const useInputFocus = ({
|
|
|
18846
18986
|
};
|
|
18847
18987
|
};
|
|
18848
18988
|
|
|
18989
|
+
function reservaNecesaria(clientHeight, scrollHeight, targetTop) {
|
|
18990
|
+
const debajoDelObjetivo = scrollHeight - targetTop;
|
|
18991
|
+
return Math.max(0, clientHeight - debajoDelObjetivo);
|
|
18992
|
+
}
|
|
18849
18993
|
const useAutoScroll = ({
|
|
18850
18994
|
messagesEndRef,
|
|
18851
18995
|
messagesContainerRef
|
|
@@ -18864,11 +19008,12 @@ const useAutoScroll = ({
|
|
|
18864
19008
|
const container = messagesContainerRef.current;
|
|
18865
19009
|
const spacer = messagesEndRef.current;
|
|
18866
19010
|
if (!container || !spacer || !target) return;
|
|
18867
|
-
spacer.style.minHeight =
|
|
19011
|
+
spacer.style.minHeight = "0px";
|
|
18868
19012
|
const containerRect = container.getBoundingClientRect();
|
|
18869
19013
|
const msgRect = target.getBoundingClientRect();
|
|
18870
19014
|
const padTop = parseFloat(getComputedStyle(container).paddingTop) || 0;
|
|
18871
19015
|
const targetTop = msgRect.top - containerRect.top + container.scrollTop - padTop;
|
|
19016
|
+
spacer.style.minHeight = `${reservaNecesaria(container.clientHeight, container.scrollHeight, targetTop)}px`;
|
|
18872
19017
|
container.scrollTo({ top: targetTop, behavior: "smooth" });
|
|
18873
19018
|
}, [messagesContainerRef, messagesEndRef]);
|
|
18874
19019
|
const last = React.useCallback((selector) => {
|
|
@@ -18910,15 +19055,33 @@ const useAutoScroll = ({
|
|
|
18910
19055
|
});
|
|
18911
19056
|
}, 150);
|
|
18912
19057
|
}, [doScroll]);
|
|
18913
|
-
const
|
|
19058
|
+
const aterrizarAlFinal = React.useCallback(() => {
|
|
19059
|
+
const container = messagesContainerRef.current;
|
|
19060
|
+
if (!container) return;
|
|
19061
|
+
resetSpacer();
|
|
19062
|
+
const hasta = Date.now() + 1e3;
|
|
19063
|
+
let ultimoAlto = -1;
|
|
19064
|
+
const pegar = () => {
|
|
19065
|
+
const c = messagesContainerRef.current;
|
|
19066
|
+
if (!c) return;
|
|
19067
|
+
if (c.scrollHeight !== ultimoAlto) {
|
|
19068
|
+
ultimoAlto = c.scrollHeight;
|
|
19069
|
+
c.scrollTop = c.scrollHeight;
|
|
19070
|
+
}
|
|
19071
|
+
if (Date.now() < hasta) requestAnimationFrame(pegar);
|
|
19072
|
+
};
|
|
19073
|
+
requestAnimationFrame(pegar);
|
|
19074
|
+
}, [messagesContainerRef, resetSpacer]);
|
|
19075
|
+
const scrollToBottom = React.useCallback((suave = true) => {
|
|
18914
19076
|
cancelPending();
|
|
18915
19077
|
resetSpacer();
|
|
18916
19078
|
requestAnimationFrame(() => {
|
|
19079
|
+
const comportamiento = suave ? "smooth" : "auto";
|
|
18917
19080
|
const container = messagesContainerRef.current;
|
|
18918
19081
|
if (container) {
|
|
18919
|
-
container.scrollTo({ top: container.scrollHeight, behavior:
|
|
19082
|
+
container.scrollTo({ top: container.scrollHeight, behavior: comportamiento });
|
|
18920
19083
|
} else {
|
|
18921
|
-
messagesEndRef.current?.scrollIntoView({ behavior:
|
|
19084
|
+
messagesEndRef.current?.scrollIntoView({ behavior: comportamiento, block: "end" });
|
|
18922
19085
|
}
|
|
18923
19086
|
});
|
|
18924
19087
|
}, [cancelPending, resetSpacer, messagesContainerRef, messagesEndRef]);
|
|
@@ -18926,7 +19089,9 @@ const useAutoScroll = ({
|
|
|
18926
19089
|
scrollToLastUserMessage,
|
|
18927
19090
|
pinLastUserMessage,
|
|
18928
19091
|
scrollToProactive,
|
|
18929
|
-
scrollToBottom
|
|
19092
|
+
scrollToBottom,
|
|
19093
|
+
aterrizarAlFinal
|
|
19094
|
+
// SPEC-131 T3
|
|
18930
19095
|
};
|
|
18931
19096
|
};
|
|
18932
19097
|
|
|
@@ -19479,7 +19644,7 @@ const Chat = ({
|
|
|
19479
19644
|
usePreventOverscroll();
|
|
19480
19645
|
const { showThinking, scrollButtonState, scrollToTopPending } = useChatStore();
|
|
19481
19646
|
const { attachments, handleAttachmentsChange, clearAttachments } = useAttachments();
|
|
19482
|
-
const { scrollToBottom, scrollToLastUserMessage, pinLastUserMessage, scrollToProactive } = useAutoScroll({
|
|
19647
|
+
const { scrollToBottom, scrollToLastUserMessage, pinLastUserMessage, scrollToProactive, aterrizarAlFinal } = useAutoScroll({
|
|
19483
19648
|
messagesEndRef,
|
|
19484
19649
|
messagesContainerRef
|
|
19485
19650
|
});
|
|
@@ -19507,11 +19672,23 @@ const Chat = ({
|
|
|
19507
19672
|
}
|
|
19508
19673
|
}, [scrollToTopPending, scrollToLastUserMessage]);
|
|
19509
19674
|
React.useEffect(() => {
|
|
19675
|
+
if (messages[messages.length - 1]?.proactive) return;
|
|
19510
19676
|
const spacer = messagesEndRef.current;
|
|
19511
19677
|
if (spacer && spacer.style.minHeight && spacer.style.minHeight !== "0px") {
|
|
19512
19678
|
pinLastUserMessage();
|
|
19513
19679
|
}
|
|
19514
19680
|
}, [messages]);
|
|
19681
|
+
const yaAterrizo = React.useRef(false);
|
|
19682
|
+
React.useEffect(() => {
|
|
19683
|
+
if (messages.length === 0) {
|
|
19684
|
+
yaAterrizo.current = false;
|
|
19685
|
+
return;
|
|
19686
|
+
}
|
|
19687
|
+
if (yaAterrizo.current) return;
|
|
19688
|
+
yaAterrizo.current = true;
|
|
19689
|
+
if (messages[messages.length - 1]?.proactive) return;
|
|
19690
|
+
aterrizarAlFinal();
|
|
19691
|
+
}, [messages, aterrizarAlFinal]);
|
|
19515
19692
|
const lastProactiveIdRef = React.useRef(void 0);
|
|
19516
19693
|
React.useEffect(() => {
|
|
19517
19694
|
const last = messages[messages.length - 1];
|
|
@@ -19629,7 +19806,7 @@ const Chat = ({
|
|
|
19629
19806
|
{
|
|
19630
19807
|
className: `${COMPONENT_PREFIX$1}chat__scroll-to-bottom`,
|
|
19631
19808
|
variant: "circle",
|
|
19632
|
-
onClick: scrollToBottom,
|
|
19809
|
+
onClick: () => scrollToBottom(),
|
|
19633
19810
|
"aria-label": "Ir al final",
|
|
19634
19811
|
icon: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
19635
19812
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v8", strokeWidth: "2", strokeLinecap: "round" }),
|