Conversation
* **feat: `PrezelTooltipBox` 컴포넌트 구현**
* Skydoves의 Balloon 라이브러리를 활용하여 커스텀 툴팁 컴포넌트를 구현했습니다.
* 디자인 가이드에 맞춰 배경색(`bgMedium`), 화살표 크기, 코너 라운드, 패딩 등 `Balloon.Builder` 설정을 적용했습니다.
* 툴팁 내부 콘텐츠 구성을 위한 `TooltipContent`를 분리하고, 선택적인 닫기 아이콘 표시 기능을 추가했습니다.
* 클릭 시 툴팁이 노출되도록 `Modifier.balloon` 및 `rememberBalloonState`를 연동했습니다.
* **build: Balloon 라이브러리 의존성 추가**
* 툴팁 기능 구현을 위해 `core:designsystem` 모듈에 `balloon-compose` 의존성을 추가했습니다.
* **style: PrezelTooltipBox의 Balloon 화살표 배치 설정 수정**
* 툴팁 화살표가 기준이 되는 요소(Anchor)의 중앙에 정확히 위치하도록 `setArrowPosition(0.5f)` 및 `setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR)` 설정을 추가했습니다.
* **feat: `PrezelTooltipBox` 외부 터치 시 닫힘 방지 및 클릭 동작 수정**
* Balloon 설정에 `setDismissWhenTouchOutside(false)`를 추가하여 외부 영역 터치 시 툴팁이 자동으로 닫히지 않도록 변경했습니다.
* 툴팁 컨텐츠를 클릭했을 때 툴팁이 닫히도록 `TooltipContent`에 클릭 이벤트를 추가했습니다.
* **refactor: `Modifier.noRippleClick` 확장 함수 추가 및 적용**
* 리플 효과 없이 클릭 이벤트를 처리하기 위한 `noRippleClick` 확장 함수를 정의했습니다.
* `PrezelTooltipBox`의 표시(show) 및 닫기(dismiss) 로직에 해당 함수를 적용하여 불필요한 인디케이션을 제거했습니다.
* **refactor: `TooltipContent` 컴포저블 구조 개선**
* 상위 수준에서 레이아웃 및 이벤트를 제어할 수 있도록 `modifier` 파라미터를 추가하고 내부 `Row`에 적용했습니다.
* **feat: 툴팁 노출 상태 및 화면 내 가시성 처리 로직 추가**
* `onGloballyPositioned`와 `getWindowVisibleDisplayFrame`을 사용하여 툴팁의 앵커가 현재 화면(Window) 내에 위치하는지 감지하는 로직을 구현했습니다.
* `shouldRestoreTooltip` 상태를 추가하여 사용자가 명시적으로 툴팁을 닫았는지 여부를 추적하고, 앵커가 화면에 보일 때만 툴팁이 유지되도록 `LaunchedEffect`로 제어합니다.
* 툴팁 내부 클릭 시 `shouldRestoreTooltip`을 `false`로 설정하여 의도치 않게 다시 뜨는 현상을 방지했습니다.
* **refactor: 툴팁 컴포넌트 구조 및 설정 최적화**
* `TooltipContent`의 가시성을 `private`으로 제한하여 캡슐화를 강화했습니다.
* `BalloonAnimation.NONE`을 적용하여 툴팁 표시 시 불필요한 애니메이션을 제거했습니다.
* 툴팁의 닫기 아이콘에 접근성을 위한 `contentDescription`을 추가하고 관련 문자열 리소스를 정의했습니다.
* 앵커 위치 계산을 위해 `Rect.intersects` 확장 함수를 추가했습니다.
* **style: 프리뷰 코드 정리**
* `PrezelTooltipBoxPreview`에서 불필요한 `innerPadding` 참조를 제거하고 레이아웃을 정돈했습니다.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 Walkthrough워크스루이 PR은 Skydoves Balloon 라이브러리를 기반으로 한 새로운 Jetpack Compose 툴팁 컴포넌트 PrezelTooltipBox를 추가합니다. 컴포넌트는 앵커의 윈도우 가시성을 추적하고 사용자 상호작용(탭)에 따라 툴팁을 표시하거나 복원합니다. 변경 사항PrezelTooltip 기능 구현
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/feedback/tooltip/PrezelTooltipBox.kt (1)
72-74: ⚡ Quick win
onGloballyPositioned내부 Rect 할당은 재사용으로 줄이는 편이 좋습니다.현재 콜백 호출마다
android.graphics.Rect()를 생성하므로, 스크롤 중 불필요한 객체 할당이 누적될 수 있습니다.♻️ 제안 diff
fun PrezelTooltipBox( text: String, modifier: Modifier = Modifier, showDismissIcon: Boolean = true, showArrow: Boolean = true, content: `@Composable` BoxScope.() -> Unit, ) { val builder = rememberBalloonBuilder(showArrow) val state = rememberBalloonState(builder) val view = LocalView.current + val visibleFrame = remember { android.graphics.Rect() } var shouldRestoreTooltip by remember { mutableStateOf(false) } var isAnchorVisibleInWindow by remember { mutableStateOf(true) } LaunchedEffect(shouldRestoreTooltip, isAnchorVisibleInWindow) { if (shouldRestoreTooltip && isAnchorVisibleInWindow) state.showAlignTop() else state.dismiss() @@ modifier = modifier .onGloballyPositioned { coordinates -> - val visibleFrame = android.graphics.Rect() view.getWindowVisibleDisplayFrame(visibleFrame) isAnchorVisibleInWindow = coordinates.boundsInWindow().intersects(visibleFrame.toComposeRect()) }.noRippleClick { shouldRestoreTooltip = true } .balloon(state) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/feedback/tooltip/PrezelTooltipBox.kt` around lines 72 - 74, The callback currently allocates a new android.graphics.Rect on every onGloballyPositioned call which causes churn; instead create a single reusable Rect and reuse it inside the callback (e.g. remember { android.graphics.Rect() } or a private field) and pass that instance to view.getWindowVisibleDisplayFrame(...) before computing isAnchorVisibleInWindow via coordinates.boundsInWindow().intersects(visibleFrame.toComposeRect()); ensure you call getWindowVisibleDisplayFrame on the same Rect each invocation rather than creating a new one.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/feedback/tooltip/PrezelTooltipBox.kt`:
- Around line 72-74: The callback currently allocates a new
android.graphics.Rect on every onGloballyPositioned call which causes churn;
instead create a single reusable Rect and reuse it inside the callback (e.g.
remember { android.graphics.Rect() } or a private field) and pass that instance
to view.getWindowVisibleDisplayFrame(...) before computing
isAnchorVisibleInWindow via
coordinates.boundsInWindow().intersects(visibleFrame.toComposeRect()); ensure
you call getWindowVisibleDisplayFrame on the same Rect each invocation rather
than creating a new one.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: dc9677fa-7692-4b61-aae9-09f2b9062501
📒 Files selected for processing (3)
Prezel/core/designsystem/build.gradle.ktsPrezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/feedback/tooltip/PrezelTooltipBox.ktPrezel/core/designsystem/src/main/res/values/strings.xml
* **refactor: `onGloballyPositioned` 내 객체 할당 최적화**
* 뷰의 가시 영역을 계산할 때 사용되는 `android.graphics.Rect` 객체를 `remember`를 사용하여 재사용하도록 변경함으로써, 레이아웃 계산 시 발생하는 불필요한 객체 생성을 방지했습니다.
* `modifier` 내 `noRippleClick`과 `onGloballyPositioned`의 호출 순서를 변경하여 코드를 정리했습니다.
* **build: Balloon Compose 라이브러리 정의 및 참조 방식 변경**
* `libs.versions.toml` 파일에 `balloonCompose` 버전 정보와 라이브러리 선언을 추가했습니다.
* `core:designsystem` 모듈의 `build.gradle.kts`에서 하드코딩되어 있던 `balloon-compose` 의존성을 Version Catalog 참조(`libs.balloon.compose`) 방식으로 수정했습니다.
📌 작업 내용
PrezelTooltipBox컴포넌트를 추가했습니다.balloon-compose의존성을 도입해 툴팁 UI를 구성했습니다.외부 입력으로 dismiss 되지 않는다.라는 요구 사항으로 인해 스크롤 시 화면을 벗어나면 숨김 처리🧩 관련 이슈
📸 스크린샷
Screen_recording_20260510_004200.webm
📢 논의하고 싶은 내용
Material 3 TooltipBox를 사용하려고 했으나 현재 Caret(Arrow) 부분이 늦게 그려지는 문제가 있었습니다.
해결하기 위해 여러 방법을 사용해봤지만 해결하지 못해서 skydoves/Balloon 라이브러리를 활용하여 구현하였습니다.
해당 이슈는 알려진 문제로 수정이 진행중인 것으로 보입니다.
Summary by CodeRabbit
새로운 기능
작업(Chores)
문구