fix(carousel): en RTL el rail se movia AL CONTRARIO del dedo durante el arrastre

El usuario vio a mano lo que mi simulacion de puntero no consiguio disparar en
cinco intentos. El swipe estaba mal, y no donde yo habia mirado.

La decision de finishDrag SI voltea (isRtlHorizontal ? -offset : offset) y por
eso la daba por buena. El defecto estaba en el PINTADO durante el arrastre:

  translate          = base + dragOffset      // base = indice, dragOffset = dedo
  itemGroupTransform = translate * flip       // el flip caia sobre LOS DOS

El recorrido por indice es LOGICO —N->N+1 mueve el rail a la izquierda en LTR y a
la derecha en RTL— y debe voltear. `dragOffset` es el desplazamiento FISICO del
puntero y no debe. Al voltear ambos, en RTL el rail se movia en sentido contrario
al dedo: arrastras a la derecha y el contenido huye a la izquierda. Separados:
solo baseTranslate lleva el signo.

A/B con git stash, midiendo el transform A MITAD del arrastre —que no exige
superar el umbral de gesto, y por eso ahora si es medible—: sin el arreglo, con
el dedo a +100px el rail da Δ-100 (huye); con el arreglo, Δ+100 (lo sigue). LTR
intacto en los dos casos.

NOTA DE METODO, anotada en el handoff: para un gesto con umbral de distancia y
velocidad, no intentes completar el swipe con page.mouse; mide el estado
INTERMEDIO con el puntero aun abajo. Es lo que me faltaba en §6.8.

DESCARTADOS CON MEDIDA, no se tocan:

scroll-area FUNCIONA. Confirmado por el usuario y medido: con el toggle en auto,
ES->ltr y AR->rtl, y el elemento pasa a direction:rtl. Su resolvedDir sigue
siendo codigo muerto y el uso de scrollLeft sin verificar en un eje horizontal
real, pero el componente responde a la direccion.

chart NO NECESITA ARREGLO y las referencias lo respaldan. Medido: chartDir,
legendDir y el texto de los ejes pasan de ltr a rtl; el eje numerico no se
voltea. Es el consenso: Chart.js implemento RTL SOLO para leyendas y tooltips y
mantiene abierto el issue de ejes/etiquetas (#11937, PR #6460), y el patron
documentado es eje numerico LTR con textos RTL. amCharts es de los pocos con RTL
integral.

Y el sintoma «la demo no reacciona al idioma», reportado tres veces
(virtual-list, carousel, scroll-area): medido en las tres, con el toggle en AUTO
las tres reaccionan. Si el toggle esta en ltr/rtl explicito el idioma no manda,
porque el intent gana sobre la derivacion. No es defecto, pero despista de forma
sistematica: parece un interruptor de dos posiciones y es un ciclo de tres donde
solo uno cede el mando al idioma, y el estado solo se lee en el title. Cerrarlo
seria trabajo de la shell, no del eje.

Verificado: check 74 = linea base exacta. 4/4 en carousel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent f420c74920
commit 714a1ddb59

@ -563,8 +563,54 @@ click y el arrastre) sin tratar que en RTL los navegadores lo devuelven
está medido porque el scroll-area de la demo no tiene eje horizontal
(`maxScroll: 0`) — hace falta un ejemplo que sí lo tenga.
**Riesgo real que queda SIN MIRAR**: `scroll-area` (arriba) y `chart` (¿el eje X
se voltea? decisión de diseño).
### 6.13 · El swipe del carousel SÍ estaba mal — el rail huía del dedo
Lo que §6.8 no consiguió disparar por simulación, el usuario lo vio a mano. El
defecto no estaba en la decisión de `finishDrag` (que sí voltea con
`isRtlHorizontal`) sino en el pintado durante el arrastre:
```ts
translate = base + dragOffset // base = índice, dragOffset = dedo
itemGroupTransform = translate * flip // el flip caía sobre LOS DOS
```
El recorrido por índice es LÓGICO y debe voltear; `dragOffset` es el
desplazamiento FÍSICO del puntero y no. Al voltear ambos, en RTL el rail se movía
en sentido contrario al dedo. Separados: sólo `baseTranslate` lleva el signo.
A/B con `git stash`, midiendo el transform A MITAD del arrastre (que no exige
superar el umbral de gesto, y por eso ahora sí es medible): sin el arreglo, RTL
da Δ−100 con el dedo a +100 —huye—; con él, Δ+100 —lo sigue—. LTR intacto en
ambos casos.
**MÉTODO**: para un gesto con umbral de distancia/velocidad, no intentes
completar el swipe; mide el estado INTERMEDIO con el puntero aún abajo.
### 6.14 · Descartados con medida — no tocar
- **`scroll-area` funciona.** Confirmado por el usuario y medido: con el toggle
en `auto`, ES→ltr y AR→rtl, y el elemento pasa a `direction: rtl`. Su
`resolvedDir` sigue siendo código muerto (una sola aparición) y el uso de
`scrollLeft` sigue sin verificarse en un eje horizontal real — pero el
componente responde a la dirección.
- **`chart` no necesita arreglo, y las referencias lo respaldan.** Medido:
`chartDir`, `legendDir` y el texto de los ejes pasan de `ltr` a `rtl`; el eje
numérico no se voltea. Es exactamente el consenso: Chart.js implementó RTL
SÓLO para leyendas y tooltips y mantiene abierto el issue de ejes/etiquetas
(chartjs/Chart.js#11937, PR #6460); el patrón documentado es eje numérico LTR
con textos RTL. amCharts es de los pocos con RTL integral.
### 6.15 · El síntoma «la demo no reacciona al idioma» — es el TOGGLE
Reportado tres veces (virtual-list, carousel, scroll-area) y medido en las tres:
**con el toggle en `auto`, las tres reaccionan al idioma**. Si el toggle está en
`ltr`/`rtl` EXPLÍCITO, el idioma no manda — el intent gana sobre la derivación,
que es lo ratificado en `013ceac57`.
No es un defecto, pero **despista de forma sistemática**: el control parece un
interruptor de dos posiciones y en realidad es un ciclo de tres donde sólo uno
cede el mando al idioma, y el estado sólo se lee en el `title`. Si se quiere
cerrar, es trabajo de la shell (hacer visible el estado), no del eje.
### 6.5 · Suelto, para quien siga

@ -257,17 +257,22 @@ export class CarouselProvider {
* the reading-direction-appropriate way (slides advance leftward in LTR,
* rightward in RTL).
*/
readonly translate = $derived.by(() => {
const base = -this.opts.index.current * this.slideSize + this.alignOffset;
return base + this.dragOffset;
});
/** Travel from the active index alone — LOGICAL, so RTL flips its sign. */
readonly baseTranslate = $derived.by(
() => -this.opts.index.current * this.slideSize + this.alignOffset
);
readonly translate = $derived.by(() => this.baseTranslate + this.dragOffset);
readonly itemGroupTransform = $derived.by(() => {
const v = this.translate;
const horizontal = this.opts.orientation.current === 'horizontal';
if (!horizontal) return `translate3d(0, ${v}px, 0)`;
if (!horizontal) return `translate3d(0, ${this.translate}px, 0)`;
// Only the INDEX travel flips. `dragOffset` is the pointer's physical
// displacement, and flipping it too made the rail run AWAY from the finger
// in RTL — drag right, content goes left. The commit decision in
// `finishDrag` mirrors separately, on the raw offset.
const flip = this.resolvedDir === 'rtl' ? -1 : 1;
return `translate3d(${v * flip}px, 0, 0)`;
return `translate3d(${this.baseTranslate * flip + this.dragOffset}px, 0, 0)`;
});
// ── Navigation ───────────────────────────────────────────────────────────

Loading…
Cancel
Save

Powered by TurnKey Linux.