-
Notifications
You must be signed in to change notification settings - Fork 0
UI v0.20.1 모달고정헤더푸터및배경스크롤잠금 #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "ui-v0.20.1-\uBAA8\uB2EC\uACE0\uC815\uD5E4\uB354\uD478\uD130\uBC0F\uBC30\uACBD\uC2A4\uD06C\uB864\uC7A0\uAE08"
Changes from all commits
ba57dc3
47e1cc3
e4e72e1
6e3a94f
ad1d323
443dcea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { useEffect } from 'react' | ||
|
|
||
| /** | ||
| * 배경(body) 스크롤 잠금 훅. | ||
| * | ||
| * 모달/다이얼로그처럼 배경 조작을 막아야 하는 오버레이가 열려 있는 동안 | ||
| * body 스크롤을 잠급니다. 여러 오버레이가 겹쳐 떠도 올바르게 동작하도록 | ||
| * 모듈 단위 참조 카운팅을 사용합니다. (마지막 하나가 닫힐 때만 잠금 해제) | ||
| * | ||
| * 스크롤바가 사라지며 생기는 레이아웃 이동(콘텐츠 밀림)을 막기 위해 | ||
| * 사라진 스크롤바 폭만큼 body에 padding-right를 보정합니다. | ||
| */ | ||
| let lockCount = 0 | ||
| let originalOverflow = '' | ||
| let originalPaddingRight = '' | ||
|
|
||
| export function useScrollLock() { | ||
| useEffect(() => { | ||
|
Comment on lines
+17
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| const body = document.body | ||
|
|
||
| if (lockCount === 0) { | ||
| const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth | ||
|
|
||
| originalOverflow = body.style.overflow | ||
| originalPaddingRight = body.style.paddingRight | ||
|
|
||
| body.style.overflow = 'hidden' | ||
| if (scrollbarWidth > 0) { | ||
| const currentPaddingRight = parseInt(window.getComputedStyle(body).paddingRight, 10) || 0 | ||
| body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px` | ||
| } | ||
| } | ||
|
|
||
| lockCount += 1 | ||
|
|
||
| return () => { | ||
| lockCount -= 1 | ||
| if (lockCount === 0) { | ||
| body.style.overflow = originalOverflow | ||
| body.style.paddingRight = originalPaddingRight | ||
| } | ||
| } | ||
| }, []) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,15 +12,37 @@ | |
| .yds-modal { | ||
| background-color: var(--color-background-secondary); | ||
| border-radius: var(--yds-border-radius); | ||
| padding: var(--yds-content-padding); | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow-y: auto; | ||
| /* 박스 전체 스크롤 제거 → 내부 body 영역만 스크롤 */ | ||
| overflow: hidden; | ||
| z-index: var(--z-index-modal); | ||
| } | ||
|
|
||
| /* 상단 고정 영역 (스크롤되지 않음) */ | ||
| .yds-modal-header { | ||
| flex-shrink: 0; | ||
| padding: var(--yds-content-padding); | ||
| } | ||
|
|
||
| .yds-modal-title { | ||
| margin: 0; | ||
| } | ||
|
|
||
| /* 스크롤되는 본문 영역 */ | ||
| .yds-modal-body { | ||
| flex: 1 1 auto; | ||
| /* flex 자식이 넘칠 때 스크롤되도록 하는 필수 조건 */ | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| padding: var(--yds-content-padding); | ||
| } | ||
|
|
||
| /* 하단 고정 영역 (스크롤되지 않음) */ | ||
| .yds-modal-footer { | ||
| flex-shrink: 0; | ||
| padding: var(--yds-content-padding); | ||
| } | ||
|
Comment on lines
+22
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 모달 헤더/바디/푸터 여백 중첩 개선 (하위 호환성 유지)현재 설정으로는 기존에 /* 상단 고정 영역 (스크롤되지 않음) */
.yds-modal-header {
flex-shrink: 0;
padding: var(--yds-content-padding) var(--yds-content-padding) 0;
}
.yds-modal-title {
margin: 0;
}
/* 스크롤되는 본문 영역 */
.yds-modal-body {
flex: 1 1 auto;
/* flex 자식이 넘칠 때 스크롤되도록 하는 필수 조건 */
min-height: 0;
overflow-y: auto;
padding: var(--yds-content-padding);
}
/* 헤더가 있을 경우 본문 상단 패딩 조절 (여백 중첩 방지) */
.yds-modal-header + .yds-modal-body {
padding-top: calc(var(--yds-content-padding) / 2);
}
/* 푸터가 있을 경우 본문 하단 패딩 조절 (여백 중첩 방지) */
.yds-modal-body:not(:last-child) {
padding-bottom: calc(var(--yds-content-padding) / 2);
}
/* 하단 고정 영역 (스크롤되지 않음) */
.yds-modal-footer {
flex-shrink: 0;
padding: 0 var(--yds-content-padding) var(--yds-content-padding);
} |
||
|
|
||
|
|
||
| .yds-modal-size-sm { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚡ SSR 대응 및 레이아웃 흔들림 방지를 위한
useLayoutEffect도입useEffect는 브라우저가 화면을 그린(paint) 후에 비동기적으로 실행되므로, 모달이 열릴 때 아주 잠깐 스크롤바가 보였다가 사라지면서 레이아웃이 미세하게 흔들리는 현상(Flicker)이 발생할 수 있습니다.화면을 그리기 전에 동기적으로 스크롤을 잠그기 위해
useLayoutEffect를 사용하는 것이 좋습니다. 다만, SSR(Server-Side Rendering) 환경에서useLayoutEffect를 그대로 사용하면 경고가 발생하므로, 환경에 따라 안전하게 대체되는useSafeLayoutEffect패턴을 제안합니다.