@ihabdevteam/core 0.173.0 → 0.174.0
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/CHANGELOG.md +8 -0
- package/dist/components.cjs.js +179 -179
- package/dist/hooks.cjs.js +1 -1
- package/dist/i18n.cjs.js +1 -1
- package/dist/ihabdevteam-core.cjs.js +182 -182
- package/dist/ihabdevteam-core.d.ts +18 -3
- package/dist/packages/core/src/components/BottomNav.d.ts +9 -1
- package/dist/packages/core/src/components/BottomNav.js +9 -3
- package/dist/packages/core/src/components/Header.d.ts +9 -1
- package/dist/packages/core/src/components/Header.js +2 -2
- package/dist/packages/core/src/components/SideNav.d.ts +1 -0
- package/dist/packages/core/src/components/SideNav.js +2 -26
- package/dist/packages/core/src/utils/navScroll.d.ts +11 -0
- package/dist/packages/core/src/utils/navScroll.js +27 -0
- package/dist/packages/core/src/version.d.ts +1 -1
- package/dist/packages/core/src/version.js +1 -1
- package/dist/utils.cjs.js +1 -1
- package/package.json +1 -1
|
@@ -543,7 +543,7 @@ export declare interface BoardProps<T> {
|
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
/** @public */
|
|
546
|
-
export declare function BottomNav({ items, activeKey, onChange, appMode, onAppModeChange, moreItems, }: {
|
|
546
|
+
export declare function BottomNav({ items, activeKey, onChange, appMode, onAppModeChange, moreItems, scrollContainer, }: {
|
|
547
547
|
items?: NavItem[];
|
|
548
548
|
activeKey?: string;
|
|
549
549
|
onChange?: (key: string) => void;
|
|
@@ -551,6 +551,13 @@ export declare function BottomNav({ items, activeKey, onChange, appMode, onAppMo
|
|
|
551
551
|
onAppModeChange?: (mode: 'malgwi' | 'manager') => void;
|
|
552
552
|
/** nav 항목과 별개로 항상 더보기 메뉴에 노출되는 유틸(알림·설정·계정 등) */
|
|
553
553
|
moreItems?: BottomNavMoreItem[];
|
|
554
|
+
/**
|
|
555
|
+
* 갈래를 고를 때 **맨 위로 되돌릴 본문 스크롤 통** — CSS 고르개나 ref. `SideNav` 와 같은 뜻이다.
|
|
556
|
+
*
|
|
557
|
+
* 모바일에서도 갈래를 바꾸면 본문이 위에서 시작해야 한다 — 전에는 데스크톱(SideNav)만 되돌려, 같은 앱에서
|
|
558
|
+
* 큰 화면은 위로 가고 작은 화면은 앞 화면의 스크롤 자리에 남았다. 유틸(더보기 메뉴의 알림·설정 등)은 갈래가 아니라 되돌리지 않는다.
|
|
559
|
+
*/
|
|
560
|
+
scrollContainer?: string | RefObject<HTMLElement | null>;
|
|
554
561
|
}): JSX.Element;
|
|
555
562
|
|
|
556
563
|
/**
|
|
@@ -1556,7 +1563,7 @@ export declare function copyText(text: string): Promise<boolean>;
|
|
|
1556
1563
|
*
|
|
1557
1564
|
* 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
|
|
1558
1565
|
*/
|
|
1559
|
-
export declare const CORE_VERSION = "0.
|
|
1566
|
+
export declare const CORE_VERSION = "0.174.0";
|
|
1560
1567
|
|
|
1561
1568
|
/** @public */
|
|
1562
1569
|
declare const coreDefault: Record<string, unknown>;
|
|
@@ -2682,7 +2689,7 @@ export declare function hasStructureContrast(A: Decomp, B: Decomp): boolean;
|
|
|
2682
2689
|
*
|
|
2683
2690
|
* @public
|
|
2684
2691
|
*/
|
|
2685
|
-
export declare function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange }: {
|
|
2692
|
+
export declare function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange, scrollContainer }: {
|
|
2686
2693
|
activeKey?: string | null;
|
|
2687
2694
|
onChange?: (key?: string) => void;
|
|
2688
2695
|
/** 지금 모드의 내비 항목. 데스크톱은 SideNav, 모바일은 BottomNav 가 이 목록을 그린다. */
|
|
@@ -2702,6 +2709,13 @@ export declare function Header({ activeKey, onChange, items, malgwiItems: _malgw
|
|
|
2702
2709
|
onHelp?: () => void;
|
|
2703
2710
|
appMode?: 'malgwi' | 'manager';
|
|
2704
2711
|
onAppModeChange?: (mode: 'malgwi' | 'manager') => void;
|
|
2712
|
+
/**
|
|
2713
|
+
* 갈래를 고를 때 **맨 위로 되돌릴 본문 스크롤 통** — CSS 고르개나 ref. 안쪽 `SideNav`·`BottomNav` 에 그대로 넘긴다.
|
|
2714
|
+
*
|
|
2715
|
+
* 본문이 `body` 가 아니라 제 통에서 구르는 셸(`body { overflow: hidden }`)이면 **반드시 준다** — 창 굴리기는 그 앱에서 아무 일도 하지 않는다.
|
|
2716
|
+
* **ref 로 주는 것이 가장 낫다**: 고르개 글자로 주면 코어가 소비자 클래스 이름을 다시 붙잡는 꼴이고, ref 면 그 얽힘이 아예 없다.
|
|
2717
|
+
*/
|
|
2718
|
+
scrollContainer?: string | RefObject<HTMLElement | null>;
|
|
2705
2719
|
}): JSX.Element;
|
|
2706
2720
|
|
|
2707
2721
|
/** @public */
|
|
@@ -5926,6 +5940,7 @@ export declare type ShuffledItem = {
|
|
|
5926
5940
|
|
|
5927
5941
|
declare type Side = '좌' | '우';
|
|
5928
5942
|
|
|
5943
|
+
/** @public */
|
|
5929
5944
|
export declare function SideNav(props: SideNavProps): React_2.JSX.Element;
|
|
5930
5945
|
|
|
5931
5946
|
/** @public */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
1
2
|
import './BottomNav.css';
|
|
2
3
|
import './Tooltip/Tooltip.css';
|
|
3
4
|
/** @public */
|
|
@@ -27,7 +28,7 @@ export type BottomNavMoreItem = {
|
|
|
27
28
|
onClick?: () => void;
|
|
28
29
|
};
|
|
29
30
|
/** @public */
|
|
30
|
-
export default function BottomNav({ items, activeKey, onChange, appMode, onAppModeChange, moreItems, }: {
|
|
31
|
+
export default function BottomNav({ items, activeKey, onChange, appMode, onAppModeChange, moreItems, scrollContainer, }: {
|
|
31
32
|
items?: NavItem[];
|
|
32
33
|
activeKey?: string;
|
|
33
34
|
onChange?: (key: string) => void;
|
|
@@ -35,4 +36,11 @@ export default function BottomNav({ items, activeKey, onChange, appMode, onAppMo
|
|
|
35
36
|
onAppModeChange?: (mode: 'malgwi' | 'manager') => void;
|
|
36
37
|
/** nav 항목과 별개로 항상 더보기 메뉴에 노출되는 유틸(알림·설정·계정 등) */
|
|
37
38
|
moreItems?: BottomNavMoreItem[];
|
|
39
|
+
/**
|
|
40
|
+
* 갈래를 고를 때 **맨 위로 되돌릴 본문 스크롤 통** — CSS 고르개나 ref. `SideNav` 와 같은 뜻이다.
|
|
41
|
+
*
|
|
42
|
+
* 모바일에서도 갈래를 바꾸면 본문이 위에서 시작해야 한다 — 전에는 데스크톱(SideNav)만 되돌려, 같은 앱에서
|
|
43
|
+
* 큰 화면은 위로 가고 작은 화면은 앞 화면의 스크롤 자리에 남았다. 유틸(더보기 메뉴의 알림·설정 등)은 갈래가 아니라 되돌리지 않는다.
|
|
44
|
+
*/
|
|
45
|
+
scrollContainer?: string | RefObject<HTMLElement | null>;
|
|
38
46
|
}): import("react").JSX.Element;
|
|
@@ -12,12 +12,13 @@ import MenuTooltip from './Tooltip/MenuTooltip';
|
|
|
12
12
|
import './Tooltip/Tooltip.css';
|
|
13
13
|
import { useAnchoredMenu, anchoredMenuZIndex } from '../hooks/useAnchoredMenu';
|
|
14
14
|
import { useTranslation } from 'react-i18next';
|
|
15
|
+
import { scrollNavTargetToTop } from '../utils/navScroll';
|
|
15
16
|
const BTN_SIZE = 52;
|
|
16
17
|
const BTN_GAP = 10;
|
|
17
18
|
/** 배지 숫자 — SideNav·MenuTooltip 과 같이 99 에서 자른다. 0 이하·없음은 0. */
|
|
18
19
|
const badgeOf = (n) => (n && n > 0 ? n : 0);
|
|
19
20
|
/** @public */
|
|
20
|
-
export default function BottomNav({ items = [], activeKey, onChange, appMode, onAppModeChange, moreItems = [], }) {
|
|
21
|
+
export default function BottomNav({ items = [], activeKey, onChange, appMode, onAppModeChange, moreItems = [], scrollContainer, }) {
|
|
21
22
|
const { t } = useTranslation();
|
|
22
23
|
const nextMode = appMode === 'manager' ? 'malgwi' : 'manager';
|
|
23
24
|
const modeIcon = appMode === 'manager' ? 'arrow-left' : 'arrow-right';
|
|
@@ -91,8 +92,10 @@ export default function BottomNav({ items = [], activeKey, onChange, appMode, on
|
|
|
91
92
|
onClick: () => {
|
|
92
93
|
if (it.state === 'disabled')
|
|
93
94
|
return;
|
|
94
|
-
if (it.key && onChange)
|
|
95
|
+
if (it.key && onChange) {
|
|
95
96
|
onChange(it.key);
|
|
97
|
+
scrollNavTargetToTop(scrollContainer, 'BottomNav');
|
|
98
|
+
}
|
|
96
99
|
handleCloseMore();
|
|
97
100
|
},
|
|
98
101
|
})),
|
|
@@ -115,6 +118,9 @@ export default function BottomNav({ items = [], activeKey, onChange, appMode, on
|
|
|
115
118
|
const count = badgeOf(it.badgeCount);
|
|
116
119
|
return (
|
|
117
120
|
// key 는 선택 prop 이다 — 없으면 자리 번호로(형제 key 가 모두 undefined 가 되던 것)
|
|
118
|
-
_jsxs("span", { className: "bottom-nav__slot", children: [_jsx(Button, { htmlType: "button", size: 52, type: "dot", icon: it.icon, ariaLabel: nameWithCount(it.label, count), selected: !!it.key && it.key === activeKey, disabled: it.state === 'disabled', className: "bottom-nav-item", onClick: () =>
|
|
121
|
+
_jsxs("span", { className: "bottom-nav__slot", children: [_jsx(Button, { htmlType: "button", size: 52, type: "dot", icon: it.icon, ariaLabel: nameWithCount(it.label, count), selected: !!it.key && it.key === activeKey, disabled: it.state === 'disabled', className: "bottom-nav-item", onClick: () => { if (it.key) {
|
|
122
|
+
onChange?.(it.key);
|
|
123
|
+
scrollNavTargetToTop(scrollContainer, 'BottomNav');
|
|
124
|
+
} } }), count > 0 && (_jsx("span", { "aria-hidden": "true", children: _jsx(Badge, { type: "dot", position: "top-right", number: Math.min(count, 99), size: 24 }) }))] }, it.key ?? `__slot-${i}`));
|
|
119
125
|
}) }), showMore && (_jsxs(_Fragment, { children: [_jsx("div", { className: "bottom-nav__divider", "aria-hidden": "true" }), _jsx("div", { className: "bottom-nav__right", children: _jsxs("div", { ref: moreRef, className: "bottom-nav__slot", children: [_jsx(Button, { htmlType: "button", size: 52, type: "dot", icon: activeMore?.icon ?? 'ellipsis', ariaLabel: nameWithCount(activeMore?.label ?? t('nav.more', '더보기'), moreBadge), selected: !!activeMore, className: "bottom-nav-item", onClick: handleToggleMore, "aria-expanded": moreOpen, "aria-controls": moreOpen ? moreMenuId : undefined }), moreBadge > 0 && (_jsx("span", { "aria-hidden": "true", children: _jsx(Badge, { type: "dot", position: "top-right", number: Math.min(moreBadge, 99), size: 24 }) })), moreOpen && createPortal(_jsx("div", { id: moreMenuId, role: "group", "aria-label": t('nav.moreMenu', '더보기 메뉴'), className: "tooltip-content", style: menuStyle, ref: menuRef, children: _jsx(MenuTooltip, { columns: moreColumns, onClose: handleCloseMore }) }), document.body)] }) })] }))] }));
|
|
120
126
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
1
2
|
import './Tooltip/Tooltip.css';
|
|
2
3
|
import { type SideNavItem } from './SideNav';
|
|
3
4
|
import './Header.css';
|
|
@@ -18,7 +19,7 @@ export declare function BrandIdentity({ appMode }: {
|
|
|
18
19
|
*
|
|
19
20
|
* @public
|
|
20
21
|
*/
|
|
21
|
-
export default function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange }: {
|
|
22
|
+
export default function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange, scrollContainer }: {
|
|
22
23
|
activeKey?: string | null;
|
|
23
24
|
onChange?: (key?: string) => void;
|
|
24
25
|
/** 지금 모드의 내비 항목. 데스크톱은 SideNav, 모바일은 BottomNav 가 이 목록을 그린다. */
|
|
@@ -38,6 +39,13 @@ export default function Header({ activeKey, onChange, items, malgwiItems: _malgw
|
|
|
38
39
|
onHelp?: () => void;
|
|
39
40
|
appMode?: 'malgwi' | 'manager';
|
|
40
41
|
onAppModeChange?: (mode: 'malgwi' | 'manager') => void;
|
|
42
|
+
/**
|
|
43
|
+
* 갈래를 고를 때 **맨 위로 되돌릴 본문 스크롤 통** — CSS 고르개나 ref. 안쪽 `SideNav`·`BottomNav` 에 그대로 넘긴다.
|
|
44
|
+
*
|
|
45
|
+
* 본문이 `body` 가 아니라 제 통에서 구르는 셸(`body { overflow: hidden }`)이면 **반드시 준다** — 창 굴리기는 그 앱에서 아무 일도 하지 않는다.
|
|
46
|
+
* **ref 로 주는 것이 가장 낫다**: 고르개 글자로 주면 코어가 소비자 클래스 이름을 다시 붙잡는 꼴이고, ref 면 그 얽힘이 아예 없다.
|
|
47
|
+
*/
|
|
48
|
+
scrollContainer?: string | RefObject<HTMLElement | null>;
|
|
41
49
|
}): import("react").JSX.Element;
|
|
42
50
|
/**
|
|
43
51
|
* `Header`의 alias입니다. 프로젝트 내 `Header`라는 이름이 이미 사용 중일 때 충돌 없이 임포트할 수 있습니다.
|
|
@@ -42,7 +42,7 @@ export function BrandIdentity({ appMode }) {
|
|
|
42
42
|
*
|
|
43
43
|
* @public
|
|
44
44
|
*/
|
|
45
|
-
export default function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange }) {
|
|
45
|
+
export default function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange, scrollContainer }) {
|
|
46
46
|
const { t } = useTranslation();
|
|
47
47
|
const isMobile = useMediaQuery("(max-width: 767px)");
|
|
48
48
|
/* **모바일 하단 내비에 SideNav 항목을 옮겨 적는다.** 예전에는 `SideNavItem` 을 `NavItem` 으로
|
|
@@ -118,7 +118,7 @@ export default function Header({ activeKey, onChange, items, malgwiItems: _malgw
|
|
|
118
118
|
}], [bottomMoreItems, activeKey, closeUtilMenu]);
|
|
119
119
|
return (_jsxs("aside", { className: `header${appMode === 'manager' ? ' header--manager' : ''}`, children: [_jsxs("div", { className: "header__bi", children: [_jsx(BrandIdentity, { appMode: appMode }), _jsxs("div", { className: "header__menu-button", ref: utilTriggerRef, children: [_jsx(Button, { htmlType: "button", size: 48, type: "dot", shape: "pill", variant: "default", icon: 'menu', ariaLabel: onNotification && unreadCount && unreadCount > 0
|
|
120
120
|
? t('nav.badgeLabel', { label: t('header.menuOpen', '메뉴 열기'), count: unreadCount, defaultValue: '{{label}}, {{count}}개' })
|
|
121
|
-
: t('header.menuOpen', '메뉴 열기'), onClick: toggleUtilMenu, "aria-expanded": utilOpen, "aria-controls": utilOpen ? utilMenuId : undefined }), !!onNotification && !!unreadCount && unreadCount > 0 && (_jsx(Badge, { type: "dot", number: Math.min(unreadCount, 99), size: 24, "aria-hidden": true })), utilOpen && createPortal(_jsx("div", { id: utilMenuId, role: "group", "aria-label": t('header.utilMenu', '유틸리티 메뉴'), className: "tooltip-content", style: utilMenuStyle, ref: utilMenuRef, children: _jsx(MenuTooltip, { columns: utilColumns, onClose: closeUtilMenu }) }), document.body)] })] }), _jsx(SideNav, { items: items, activeKey: activeKey ?? undefined, onChange: onChange, unreadCount: unreadCount, onDebug: onDebug, onNotification: onNotification, onAccount: onAccount, onSetting: onSetting, onHelp: onHelp, appMode: appMode, onAppModeChange: onAppModeChange }), isMobile && _jsx(BottomNav, { items: bottomItems, activeKey: activeKey ?? undefined, onChange: handleBottomChange, appMode: appMode, onAppModeChange: onAppModeChange })] }));
|
|
121
|
+
: t('header.menuOpen', '메뉴 열기'), onClick: toggleUtilMenu, "aria-expanded": utilOpen, "aria-controls": utilOpen ? utilMenuId : undefined }), !!onNotification && !!unreadCount && unreadCount > 0 && (_jsx(Badge, { type: "dot", number: Math.min(unreadCount, 99), size: 24, "aria-hidden": true })), utilOpen && createPortal(_jsx("div", { id: utilMenuId, role: "group", "aria-label": t('header.utilMenu', '유틸리티 메뉴'), className: "tooltip-content", style: utilMenuStyle, ref: utilMenuRef, children: _jsx(MenuTooltip, { columns: utilColumns, onClose: closeUtilMenu }) }), document.body)] })] }), _jsx(SideNav, { scrollContainer: scrollContainer, items: items, activeKey: activeKey ?? undefined, onChange: onChange, unreadCount: unreadCount, onDebug: onDebug, onNotification: onNotification, onAccount: onAccount, onSetting: onSetting, onHelp: onHelp, appMode: appMode, onAppModeChange: onAppModeChange }), isMobile && _jsx(BottomNav, { items: bottomItems, activeKey: activeKey ?? undefined, onChange: handleBottomChange, scrollContainer: scrollContainer, appMode: appMode, onAppModeChange: onAppModeChange })] }));
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* `Header`의 alias입니다. 프로젝트 내 `Header`라는 이름이 이미 사용 중일 때 충돌 없이 임포트할 수 있습니다.
|
|
@@ -7,7 +7,7 @@ import Badge from './Badge';
|
|
|
7
7
|
import React, { useCallback } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
import { isSafeOpenHref } from '../utils/safeHref';
|
|
10
|
-
import {
|
|
10
|
+
import { scrollNavTargetToTop } from '../utils/navScroll';
|
|
11
11
|
/** 단추 이름에 개수를 붙인다 — 뱃지는 aria-hidden 이라 개수는 이름으로만 전해진다(BottomNav 와 같은 문구). */
|
|
12
12
|
function nameWithCount(t, label, count) {
|
|
13
13
|
return count && count > 0 ? t('nav.badgeLabel', { label, count, defaultValue: '{{label}}, {{count}}개' }) : label;
|
|
@@ -56,30 +56,6 @@ function SideNavButton(props) {
|
|
|
56
56
|
return (_jsxs("button", { type: "button", className: btnClass, onClick: handleClick, "aria-label": typeof text === 'string' ? nameWithCount(t, text, badgeCount) : undefined, "aria-current": active ? 'page' : undefined, style: style, "aria-disabled": isDisabled, children: [iconElement, _jsx("span", { className: `side-nav__label button-1 f`, style: labelStyle, children: text }), badgeCount > 0 && (_jsx(Badge, { type: "dot", position: "inline", number: Math.min(badgeCount, 99), size: 24, "aria-hidden": true })), state === 'loading' && (_jsx("span", { className: "side-nav__spinner", role: "img", "aria-label": t('loader.loading', '불러오는 중…') }))] }));
|
|
57
57
|
}
|
|
58
58
|
/** @public */
|
|
59
|
-
/** 기본 고르개 — 소비자가 `scrollContainer` 를 안 줄 때 찾아 보는 이름(옛 동작 유지). @internal */
|
|
60
|
-
const 기본통고르개 = '.content-area';
|
|
61
|
-
/**
|
|
62
|
-
* 갈래를 고른 뒤 본문을 맨 위로. 통을 못 찾으면 창을 굴리는데, 본문이 제 통에서 구르는 셸에서는
|
|
63
|
-
* 그것이 **아무 일도 하지 않는다** — 그때는 조용히 죽는 대신 개발 모드에 한 번 알린다.
|
|
64
|
-
*/
|
|
65
|
-
function 맨위로(scrollContainer) {
|
|
66
|
-
try {
|
|
67
|
-
const el = typeof scrollContainer === 'string'
|
|
68
|
-
? document.querySelector(scrollContainer)
|
|
69
|
-
: scrollContainer?.current ?? document.querySelector(기본통고르개);
|
|
70
|
-
if (el) {
|
|
71
|
-
el.scrollTop = 0;
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const 문서 = document.scrollingElement ?? document.documentElement;
|
|
75
|
-
/* 창도 구를 수 없으면(본문이 다른 통에서 구른다) 이 기능은 조용히 죽는다 — 그 사실을 알린다 */
|
|
76
|
-
if (문서 && 문서.scrollHeight <= 문서.clientHeight) {
|
|
77
|
-
devWarnOnce('side-nav-scroll-container', `[SideNav] 본문을 맨 위로 되돌릴 통을 못 찾았다(기본 ${기본통고르개}) — 창도 구를 수 없어 아무 일도 하지 않는다. scrollContainer 로 통을 알려 준다.`);
|
|
78
|
-
}
|
|
79
|
-
window.scrollTo({ top: 0 });
|
|
80
|
-
}
|
|
81
|
-
catch (_) { }
|
|
82
|
-
}
|
|
83
59
|
export default function SideNav(props) {
|
|
84
60
|
const { t } = useTranslation();
|
|
85
61
|
const { items = [], activeKey, onChange, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange, scrollContainer } = props || {};
|
|
@@ -105,7 +81,7 @@ export default function SideNav(props) {
|
|
|
105
81
|
return;
|
|
106
82
|
}
|
|
107
83
|
onChange?.(it.key);
|
|
108
|
-
|
|
84
|
+
scrollNavTargetToTop(scrollContainer, 'SideNav');
|
|
109
85
|
/* 키보드로 고르면 포커스가 레일 안에 남으므로 태블릿 레일도 펼친 채 둔다(:focus-within) —
|
|
110
86
|
아래 강제 접힘은 마우스가 들어와야 풀리므로 키보드 사용자에게 걸면 레일이 닫힌 채 포커스만 남는다. */
|
|
111
87
|
if (!fromPointer)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
/** 소비자가 통을 안 알려 줄 때 찾아 보는 이름 — 옛 동작 유지(말귀 셸의 본문). @internal */
|
|
3
|
+
export declare const 기본통고르개 = ".content-area";
|
|
4
|
+
/**
|
|
5
|
+
* 갈래를 고른 뒤 본문을 맨 위로 — 내비(SideNav·BottomNav)가 함께 쓴다. @internal
|
|
6
|
+
*
|
|
7
|
+
* 통을 못 찾으면 창을 굴리는데, 본문이 제 통에서 구르는 셸(`body { overflow: hidden }`)에서는
|
|
8
|
+
* 그것이 **아무 일도 하지 않는다** — 그때는 조용히 죽는 대신 개발 모드에 한 번 알린다.
|
|
9
|
+
* 코어가 `.content-area`(소비자 클래스 이름)를 붙잡고 있던 자리라, 부르는 쪽이 통을 알려 주는 것이 정본이다.
|
|
10
|
+
*/
|
|
11
|
+
export declare function scrollNavTargetToTop(scrollContainer?: string | RefObject<HTMLElement | null>, 어디서?: string): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { devWarnOnce } from './devWarn';
|
|
2
|
+
/** 소비자가 통을 안 알려 줄 때 찾아 보는 이름 — 옛 동작 유지(말귀 셸의 본문). @internal */
|
|
3
|
+
export const 기본통고르개 = '.content-area';
|
|
4
|
+
/**
|
|
5
|
+
* 갈래를 고른 뒤 본문을 맨 위로 — 내비(SideNav·BottomNav)가 함께 쓴다. @internal
|
|
6
|
+
*
|
|
7
|
+
* 통을 못 찾으면 창을 굴리는데, 본문이 제 통에서 구르는 셸(`body { overflow: hidden }`)에서는
|
|
8
|
+
* 그것이 **아무 일도 하지 않는다** — 그때는 조용히 죽는 대신 개발 모드에 한 번 알린다.
|
|
9
|
+
* 코어가 `.content-area`(소비자 클래스 이름)를 붙잡고 있던 자리라, 부르는 쪽이 통을 알려 주는 것이 정본이다.
|
|
10
|
+
*/
|
|
11
|
+
export function scrollNavTargetToTop(scrollContainer, 어디서 = 'Nav') {
|
|
12
|
+
try {
|
|
13
|
+
const el = typeof scrollContainer === 'string'
|
|
14
|
+
? document.querySelector(scrollContainer)
|
|
15
|
+
: scrollContainer?.current ?? document.querySelector(기본통고르개);
|
|
16
|
+
if (el) {
|
|
17
|
+
el.scrollTop = 0;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const 문서 = document.scrollingElement ?? document.documentElement;
|
|
21
|
+
if (문서 && 문서.scrollHeight <= 문서.clientHeight) {
|
|
22
|
+
devWarnOnce('nav-scroll-container', `[${어디서}] 본문을 맨 위로 되돌릴 통을 못 찾았다(기본 ${기본통고르개}) — 창도 구를 수 없어 아무 일도 하지 않는다. scrollContainer 로 통을 알려 준다.`);
|
|
23
|
+
}
|
|
24
|
+
window.scrollTo({ top: 0 });
|
|
25
|
+
}
|
|
26
|
+
catch (_) { }
|
|
27
|
+
}
|