From 36c69b27ae5b0d5124c5f5af6420a5364682b36a Mon Sep 17 00:00:00 2001 From: Gabriel-Pereira1788 Date: Mon, 7 Sep 2026 20:32:28 -0300 Subject: [PATCH 1/4] fix(overlay): forward card positioning data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit renderCard only received step/palette/labels fields, never the layout/width/containerHeight/isExiting that TourCard uses to place itself next to the target — a custom card rendered pinned to the top-left with no positioning at all. Folds those fields into the single cardProps object shared by both call sites. Fixes #4 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_014wCD2QPtx1V5YbA9YgT8c8 --- .../hooks/useCiceroneOverlayViewModel/index.ts | 6 +++++- src/components/CiceroneOverlay/index.tsx | 11 +---------- src/components/TourCard/types/ITourCardProps.ts | 7 +------ src/types/ICiceroneCardProps.ts | 6 ++++++ 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/index.ts b/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/index.ts index 44e8a08..3737016 100644 --- a/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/index.ts +++ b/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/index.ts @@ -84,12 +84,16 @@ export const useCiceroneOverlayViewModel = (props: ICiceroneOverlayProps) => { placement, palette, labels, + layout, + width: cardWidth, + containerHeight: screen.height, + isExiting: props.isExiting, next: props.next, previous: props.previous, skip: props.skip, stop: props.stop, }), - [step, props, placement, palette, labels], + [step, props, placement, palette, labels, layout, cardWidth, screen.height], ); const overlayPress = options.overlayPress ?? 'next'; diff --git a/src/components/CiceroneOverlay/index.tsx b/src/components/CiceroneOverlay/index.tsx index 4b2a1c4..86bb1ef 100644 --- a/src/components/CiceroneOverlay/index.tsx +++ b/src/components/CiceroneOverlay/index.tsx @@ -10,8 +10,6 @@ export const CiceroneOverlay: React.FC = (props) => { const { theme, isHighlight, - cardWidth, - layout, cardProps, geometry, screen, @@ -44,14 +42,7 @@ export const CiceroneOverlay: React.FC = (props) => { {options.renderCard ? ( options.renderCard(cardProps) ) : ( - + )} ); diff --git a/src/components/TourCard/types/ITourCardProps.ts b/src/components/TourCard/types/ITourCardProps.ts index b6ea323..edfc1d3 100644 --- a/src/components/TourCard/types/ITourCardProps.ts +++ b/src/components/TourCard/types/ITourCardProps.ts @@ -1,11 +1,6 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import type { ICardLayout, ICiceroneCardProps } from '@/types'; +import type { ICiceroneCardProps } from '@/types'; export interface ITourCardProps extends ICiceroneCardProps { - layout: ICardLayout; - width: number; - /** The overlay's box, which the layout was measured against. */ - containerHeight: number; - isExiting: boolean; style?: StyleProp; } diff --git a/src/types/ICiceroneCardProps.ts b/src/types/ICiceroneCardProps.ts index cac6dfa..d0400c8 100644 --- a/src/types/ICiceroneCardProps.ts +++ b/src/types/ICiceroneCardProps.ts @@ -1,3 +1,4 @@ +import type { ICardLayout } from './ICardLayout'; import type { ICiceroneCardPalette } from './ICiceroneCardPalette'; import type { ICiceroneLabels } from './ICiceroneLabels'; import type { ICiceronePlacement } from './ICiceronePlacement'; @@ -12,6 +13,11 @@ export interface ICiceroneCardProps { placement: ICiceronePlacement; palette: ICiceroneCardPalette; labels: ICiceroneLabels; + layout: ICardLayout; + width: number; + /** The overlay's box, which the layout was measured against. */ + containerHeight: number; + isExiting: boolean; next: () => void; previous: () => void; skip: () => void; From 387b51b53cc34b552ccac143194e2d200fcc8323 Mon Sep 17 00:00:00 2001 From: Gabriel-Pereira1788 Date: Mon, 7 Sep 2026 20:32:41 -0300 Subject: [PATCH 2/4] test(overlay): cover card positioning props Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_014wCD2QPtx1V5YbA9YgT8c8 --- .../__tests__/useCiceroneOverlayViewModel.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/__tests__/useCiceroneOverlayViewModel.test.ts b/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/__tests__/useCiceroneOverlayViewModel.test.ts index 2e4bb0b..53b35be 100644 --- a/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/__tests__/useCiceroneOverlayViewModel.test.ts +++ b/src/components/CiceroneOverlay/hooks/useCiceroneOverlayViewModel/__tests__/useCiceroneOverlayViewModel.test.ts @@ -111,6 +111,19 @@ describe('useCiceroneOverlayViewModel', () => { }); }); + describe('cardProps', () => { + it('Carries the positioning data renderCard needs to place itself', async () => { + const { result } = await renderHook(() => + useCiceroneOverlayViewModel(mountProps()), + ); + + expect(result.current.cardProps.layout).toEqual(result.current.layout); + expect(result.current.cardProps.width).toBe(result.current.cardWidth); + expect(result.current.cardProps.containerHeight).toBe(result.current.screen.height); + expect(result.current.cardProps.isExiting).toBe(false); + }); + }); + describe('cardWidth', () => { it('Uses the prototype width unless the consumer overrides it', async () => { const { result } = await renderHook(() => From 5137656d469cb012b53b6809ed17750f46b91fb0 Mon Sep 17 00:00:00 2001 From: Gabriel-Pereira1788 Date: Mon, 7 Sep 2026 20:32:54 -0300 Subject: [PATCH 3/4] docs(api): document card positioning fields The ICiceroneCardProps table and the renderCard example were both incomplete in the same way as the bug: neither showed how to position a custom card, so exposing the fields alone would not have helped a consumer use them. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_014wCD2QPtx1V5YbA9YgT8c8 --- website/docs/api.md | 27 +++++++++++------ website/docs/theming.md | 30 +++++++++++-------- .../current/api.md | 27 +++++++++++------ .../current/theming.md | 30 +++++++++++-------- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/website/docs/api.md b/website/docs/api.md index cf5970e..f012497 100644 --- a/website/docs/api.md +++ b/website/docs/api.md @@ -85,12 +85,21 @@ up chasing an `undefined` three frames later. What `renderCard` receives. -| Field | Type | -| ------------------------------------- | ---------------------- | -| `step` | `ICiceroneStep` | -| `index` / `total` | `number` | -| `isFirst` / `isLast` | `boolean` | -| `placement` | `'top' \| 'bottom'` | -| `palette` | `ICiceroneCardPalette` | -| `labels` | `ICiceroneLabels` | -| `next` / `previous` / `skip` / `stop` | `() => void` | +| Field | Type | +| -------------------------------------- | ---------------------- | +| `step` | `ICiceroneStep` | +| `index` / `total` | `number` | +| `isFirst` / `isLast` | `boolean` | +| `placement` | `'top' \| 'bottom'` | +| `palette` | `ICiceroneCardPalette` | +| `labels` | `ICiceroneLabels` | +| `layout` | `ICardLayout` | +| `width` | `number` | +| `containerHeight` | `number` | +| `isExiting` | `boolean` | +| `next` / `previous` / `skip` / `stop` | `() => void` | + +`layout`, `width` and `containerHeight` are what the built-in card uses to position itself +next to the target — `left`/`arrowLeft` from `layout`, anchored against `containerHeight` +depending on `placement`. `isExiting` drives the exit animation. A custom `renderCard` needs +all four to place itself the same way; see [Replacing the card](./theming.md#replacing-the-card). diff --git a/website/docs/theming.md b/website/docs/theming.md index 96c311b..9a4ab99 100644 --- a/website/docs/theming.md +++ b/website/docs/theming.md @@ -102,18 +102,24 @@ you draw your own. The spotlight, ring and placement stay as they are. ```tsx ( - - )} + renderCard={({ step, index, total, isLast, next, skip, placement, layout, width, containerHeight }) => { + const anchorY = placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); + + return ( + + ); + }} /> ``` -Positioning is on you. The `placement` and `layout` you get tell you which side the tour -picked and where it would have put its own card. +Positioning is on you. `placement` tells you which side the tour picked; `layout`, `width` +and `containerHeight` are the same numbers `TourCard` itself uses to land there — see +[`ICiceroneCardProps`](./api.md#iciceronecardprops) for what each one means. diff --git a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/api.md b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/api.md index 3331946..1b960be 100644 --- a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/api.md +++ b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/api.md @@ -85,12 +85,21 @@ ficar caçando um `undefined` três frames depois. O que o `renderCard` recebe. -| Field | Type | -| ------------------------------------- | ---------------------- | -| `step` | `ICiceroneStep` | -| `index` / `total` | `number` | -| `isFirst` / `isLast` | `boolean` | -| `placement` | `'top' \| 'bottom'` | -| `palette` | `ICiceroneCardPalette` | -| `labels` | `ICiceroneLabels` | -| `next` / `previous` / `skip` / `stop` | `() => void` | +| Field | Type | +| -------------------------------------- | ---------------------- | +| `step` | `ICiceroneStep` | +| `index` / `total` | `number` | +| `isFirst` / `isLast` | `boolean` | +| `placement` | `'top' \| 'bottom'` | +| `palette` | `ICiceroneCardPalette` | +| `labels` | `ICiceroneLabels` | +| `layout` | `ICardLayout` | +| `width` | `number` | +| `containerHeight` | `number` | +| `isExiting` | `boolean` | +| `next` / `previous` / `skip` / `stop` | `() => void` | + +`layout`, `width` e `containerHeight` são o que o card embutido usa para se posicionar ao +lado do alvo — `left`/`arrowLeft` vêm de `layout`, ancorado em `containerHeight` conforme o +`placement`. `isExiting` controla a animação de saída. Um `renderCard` customizado precisa +dos quatro para se posicionar do mesmo jeito; veja [Trocando o card](./theming.md#trocando-o-card). diff --git a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md index 1d9b09d..c84d9a8 100644 --- a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md +++ b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md @@ -103,18 +103,24 @@ desenhar o seu. O holofote, o anel e o posicionamento continuam como estão. ```tsx ( - - )} + renderCard={({ step, index, total, isLast, next, skip, placement, layout, width, containerHeight }) => { + const anchorY = placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); + + return ( + + ); + }} /> ``` -O posicionamento fica com você. O `placement` e o `layout` que chegam dizem qual lado o tour -escolheu e onde ele teria posto o próprio card. +O posicionamento fica com você. `placement` diz qual lado o tour escolheu; `layout`, `width` +e `containerHeight` são os mesmos números que o próprio `TourCard` usa para se posicionar — +veja [`ICiceroneCardProps`](./api.md#iciceronecardprops) para o que cada um significa. From ee829a6e9629d6be535c3025ce415d9220163090 Mon Sep 17 00:00:00 2001 From: Gabriel-Pereira1788 Date: Mon, 7 Sep 2026 21:14:32 -0300 Subject: [PATCH 4/4] style(docs): run prettier on card docs CI's format:check caught table alignment and unwrapped destructuring that yarn format wasn't run on before the previous commit. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_014wCD2QPtx1V5YbA9YgT8c8 --- website/docs/api.md | 26 +++++++++---------- website/docs/theming.md | 24 ++++++++++++++--- .../current/api.md | 26 +++++++++---------- .../current/theming.md | 24 ++++++++++++++--- 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/website/docs/api.md b/website/docs/api.md index f012497..12ff00d 100644 --- a/website/docs/api.md +++ b/website/docs/api.md @@ -85,19 +85,19 @@ up chasing an `undefined` three frames later. What `renderCard` receives. -| Field | Type | -| -------------------------------------- | ---------------------- | -| `step` | `ICiceroneStep` | -| `index` / `total` | `number` | -| `isFirst` / `isLast` | `boolean` | -| `placement` | `'top' \| 'bottom'` | -| `palette` | `ICiceroneCardPalette` | -| `labels` | `ICiceroneLabels` | -| `layout` | `ICardLayout` | -| `width` | `number` | -| `containerHeight` | `number` | -| `isExiting` | `boolean` | -| `next` / `previous` / `skip` / `stop` | `() => void` | +| Field | Type | +| ------------------------------------- | ---------------------- | +| `step` | `ICiceroneStep` | +| `index` / `total` | `number` | +| `isFirst` / `isLast` | `boolean` | +| `placement` | `'top' \| 'bottom'` | +| `palette` | `ICiceroneCardPalette` | +| `labels` | `ICiceroneLabels` | +| `layout` | `ICardLayout` | +| `width` | `number` | +| `containerHeight` | `number` | +| `isExiting` | `boolean` | +| `next` / `previous` / `skip` / `stop` | `() => void` | `layout`, `width` and `containerHeight` are what the built-in card uses to position itself next to the target — `left`/`arrowLeft` from `layout`, anchored against `containerHeight` diff --git a/website/docs/theming.md b/website/docs/theming.md index 9a4ab99..ec849cc 100644 --- a/website/docs/theming.md +++ b/website/docs/theming.md @@ -102,12 +102,30 @@ you draw your own. The spotlight, ring and placement stay as they are. ```tsx { - const anchorY = placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); + renderCard={({ + step, + index, + total, + isLast, + next, + skip, + placement, + layout, + width, + containerHeight, + }) => { + const anchorY = + placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); return ( void` | +| Field | Type | +| ------------------------------------- | ---------------------- | +| `step` | `ICiceroneStep` | +| `index` / `total` | `number` | +| `isFirst` / `isLast` | `boolean` | +| `placement` | `'top' \| 'bottom'` | +| `palette` | `ICiceroneCardPalette` | +| `labels` | `ICiceroneLabels` | +| `layout` | `ICardLayout` | +| `width` | `number` | +| `containerHeight` | `number` | +| `isExiting` | `boolean` | +| `next` / `previous` / `skip` / `stop` | `() => void` | `layout`, `width` e `containerHeight` são o que o card embutido usa para se posicionar ao lado do alvo — `left`/`arrowLeft` vêm de `layout`, ancorado em `containerHeight` conforme o diff --git a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md index c84d9a8..80f4676 100644 --- a/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md +++ b/website/i18n/pt-BR/docusaurus-plugin-content-docs/current/theming.md @@ -103,12 +103,30 @@ desenhar o seu. O holofote, o anel e o posicionamento continuam como estão. ```tsx { - const anchorY = placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); + renderCard={({ + step, + index, + total, + isLast, + next, + skip, + placement, + layout, + width, + containerHeight, + }) => { + const anchorY = + placement === 'bottom' ? (layout.top ?? 0) : containerHeight - (layout.bottom ?? 0); return (