@ihabdevteam/core 0.254.0 → 0.256.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 +97 -0
- package/dist/components.cjs.js +24 -24
- package/dist/components.esm.css +2 -2
- package/dist/hooks.cjs.js +3 -3
- package/dist/i18n.cjs.js +3 -3
- package/dist/ihabdevteam-core.cjs.js +14 -14
- package/dist/ihabdevteam-core.d.ts +24 -3
- package/dist/packages/core/src/components/Calendar.css +6 -0
- package/dist/packages/core/src/components/Calendar.js +23 -1
- package/dist/packages/core/src/components/Toast.css +27 -3
- package/dist/packages/core/src/components/Toast.js +1 -1
- package/dist/packages/core/src/components/Training/TrainingCard.d.ts +15 -1
- package/dist/packages/core/src/components/Training/TrainingCard.js +2 -2
- package/dist/packages/core/src/components/Training/TrainingCardGroup.d.ts +8 -1
- package/dist/packages/core/src/components/Training/TrainingCardGroup.js +10 -2
- package/dist/packages/core/src/i18n/locales/en.json +5 -1
- package/dist/packages/core/src/i18n/locales/ko.json +5 -1
- 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
|
@@ -1651,7 +1651,7 @@ export declare function copyText(text: string): Promise<boolean>;
|
|
|
1651
1651
|
*
|
|
1652
1652
|
* 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
|
|
1653
1653
|
*/
|
|
1654
|
-
export declare const CORE_VERSION = "0.
|
|
1654
|
+
export declare const CORE_VERSION = "0.256.0";
|
|
1655
1655
|
|
|
1656
1656
|
/** @public */
|
|
1657
1657
|
declare const coreDefault: Record<string, unknown>;
|
|
@@ -8323,7 +8323,7 @@ export declare interface TrainingBlockProps {
|
|
|
8323
8323
|
}
|
|
8324
8324
|
|
|
8325
8325
|
/** @public */
|
|
8326
|
-
export declare function TrainingCard({ title, titleLevel, description, topActions, children, footerLeft, footerRight, onClick, draggable, onDragStart, onDragEnd, className, }: TrainingCardProps): JSX.Element;
|
|
8326
|
+
export declare function TrainingCard({ title, titleLevel, description, topActions, children, footerLeft, footerRight, ariaLabel, onClick, draggable, onDragStart, onDragEnd, className, }: TrainingCardProps): JSX.Element;
|
|
8327
8327
|
|
|
8328
8328
|
/** @public */
|
|
8329
8329
|
export declare function TrainingCardGroup({ title, titleLevel, count, collapsed, onToggle, sort, actions, empty, emptyContent, dragOver, onDragOver, onDragLeave, onDrop, children, className, }: TrainingCardGroupProps): JSX.Element;
|
|
@@ -8337,7 +8337,14 @@ declare type TrainingCardGroupProps = {
|
|
|
8337
8337
|
* 안 잡혔다. 그 안의 `접기`·`정렬` 은 그룹마다 같은 이름이라, 낭독기로 단추 목록을
|
|
8338
8338
|
* 부르면 `접기` 가 세 번 선다 — **어느 그룹의 것인지 가릴 길이 없었다.**
|
|
8339
8339
|
*
|
|
8340
|
-
* 이름 있는 지역이 서면 단추 이름은 짧은 채로 둬도 된다(지역이 가린다)
|
|
8340
|
+
* **전에 여기 "이름 있는 지역이 서면 단추 이름은 짧은 채로 둬도 된다(지역이 가린다)" 고
|
|
8341
|
+
* 적어 두었는데, 그 말이 틀렸다**(2026-10-02 바로잡음). `role="group"` 은 **landmark 가
|
|
8342
|
+
* 아니다.** 초점이 묶음 안으로 들어가 읽을 때는 묶음 이름이 먼저 읽히니 맞지만,
|
|
8343
|
+
* **낭독기로 단추 목록을 훑는 길**에서는 묶음 맥락이 안 따라온다 — 바로 윗줄에 적은
|
|
8344
|
+
* "`접기` 가 세 번 선다" 가 그대로 남는다. 말귀가 그 한 줄을 믿고 앱 세 파일에서 그룹
|
|
8345
|
+
* 이름을 걷으려다 물어 와서 드러났다.
|
|
8346
|
+
* 그래서 **이 조각이 제 단추에 묶음 이름을 직접 단다**(아래 `접기`·`정렬`). 부르는 쪽이
|
|
8347
|
+
* actions 로 넘기는 단추에도 묶음 이름을 다는 쪽이 맞다 — 걷지 마라.
|
|
8341
8348
|
* `0` 을 주면 전과 같이 `span` 으로 그린다. */
|
|
8342
8349
|
titleLevel?: 0 | 2 | 3 | 4 | 5 | 6;
|
|
8343
8350
|
/** 그룹 제목 — 문자열이면 제목으로 감싸고, 노드면 그대로 렌더(예: 인라인 이름변경 입력) */
|
|
@@ -8411,6 +8418,20 @@ export declare type TrainingCardProps = {
|
|
|
8411
8418
|
description?: ReactNode;
|
|
8412
8419
|
/** 우측 상단 액션 영역 (좋아요/다운로드, ⋯ 메뉴 등) — 클릭이 카드로 전파되지 않음 */
|
|
8413
8420
|
topActions?: ReactNode;
|
|
8421
|
+
/**
|
|
8422
|
+
* **카드가 눌릴 때 읽히는 이름**(말귀 요청 (65), 2026-10-02).
|
|
8423
|
+
*
|
|
8424
|
+
* 안 주면 `title` 을 쓴다. 안 쓰고 두면 `role="button"` 의 이름이 **안 글자 전부**로
|
|
8425
|
+
* 지어지는데, 이 카드는 안에 `topActions`(진짜 `<button>`)를 품으므로 **그 단추의 이름까지
|
|
8426
|
+
* 삼킨다** — 말귀 실측(나의 단어훈련 375px, 카드 여섯 장 전부):
|
|
8427
|
+
* `훈련하자!! (복사) 훈련하자!! (복사) 더보기 음절 2개 합치기 1 …` 로, 제목이 두 번 읽히고
|
|
8428
|
+
* 사이에 `더보기` 가 꼈다. `TrainingBlockButton` 이 이미 같은 길로 풀어 둔 자리다.
|
|
8429
|
+
*
|
|
8430
|
+
* **값을 치른다** — 갈래별 수와 마지막 쓴 날이 이름에서 빠진다. 그것까지 들려주려면
|
|
8431
|
+
* 부르는 쪽이 여기에 **갖춰 쓴 이름**을 넘겨라(`'훈련하자!! · 음절 합치기 1 · 3개월 전'`).
|
|
8432
|
+
* 코어는 children 의 뜻을 모르므로 제목까지만 지을 수 있다.
|
|
8433
|
+
*/
|
|
8434
|
+
ariaLabel?: string;
|
|
8414
8435
|
/** 본문 영역 콘텐츠 (타입 칩, 단어 뱃지, 빈 상태 등) */
|
|
8415
8436
|
children?: ReactNode;
|
|
8416
8437
|
/** 풋터 좌측 (게시자·날짜 등) */
|
|
@@ -147,6 +147,12 @@
|
|
|
147
147
|
background: currentColor;
|
|
148
148
|
flex-shrink: 0;
|
|
149
149
|
}
|
|
150
|
+
/* **범례와 같은 세 색**((66), 2026-10-02). 전에는 `currentColor` 하나라 예약·미방문도
|
|
151
|
+
'방문' 색으로 섰다 — 머리 바로 위 범례는 셋이라고 적어 둔 채로. 범례의
|
|
152
|
+
`--main`·`--sub`·`--disabled` 와 **같은 값을 쓴다**: 둘이 갈리면 범례가 거짓말이 된다. */
|
|
153
|
+
.calendar-cell__visit-dot--visited { background: var(--color-main); }
|
|
154
|
+
.calendar-cell__visit-dot--scheduled { background: var(--color-sub); }
|
|
155
|
+
.calendar-cell__visit-dot--noVisit { background: var(--color-disabled); }
|
|
150
156
|
/* 이름표 한 줄("13시 33분 · 지한")이 담기려면 100px 은 있어야 한다 — 그 밑에서는 접는다.
|
|
151
157
|
창이 아니라 **칸 제 폭**으로 정한다: 같은 창에서도 담긴 카드가 좁으면 칸이 좁아진다.
|
|
152
158
|
재는 값은 칸의 **안쪽**(여백 8px씩 뺀 값)이라, 118px 짜리 칸이면 102 로 이 문턱을 넘는다. */
|
|
@@ -85,7 +85,29 @@ export default function Calendar({ visits = [], onVisitClick, onVisitMove, onDay
|
|
|
85
85
|
dragVisitId.current = null;
|
|
86
86
|
}
|
|
87
87
|
setDragOverDate(null);
|
|
88
|
-
} : undefined, children: [_jsx("div", { className: "calendar-cell__number body-3", children: day }), dayVisits.length > 0 && (
|
|
88
|
+
} : undefined, children: [_jsx("div", { className: "calendar-cell__number body-3", children: day }), dayVisits.length > 0 && (() => {
|
|
89
|
+
/* **접힌 꼴이 세 갈래를 하나로 뭉갰다**(말귀 요청 (66), 2026-10-02).
|
|
90
|
+
점은 늘 `--color-main`(= 범례의 '방문') 이고 이름표는 늘 `방문 N건` 이라,
|
|
91
|
+
**아직 안 온 예약과 안 온 것(미방문)을 치료사가 "왔다" 로 읽었다** —
|
|
92
|
+
말귀 실측: 그 달 자취가 예약 하나뿐인데 `방문 1건` · `rgb(73,156,223)`.
|
|
93
|
+
드문 자리가 아니다: 375 에서 칸이 37px, **1189 에서도 111px** 이라 둘 다
|
|
94
|
+
이 꼴이 선다. 넓은 칸의 이름표는 멀쩡해서 그쪽만 보면 안 보인다.
|
|
95
|
+
갈래가 하나면 그 갈래 말로, 섞였으면 갈라 센다. */
|
|
96
|
+
const 갈래 = [
|
|
97
|
+
{ key: 'visited', 말: t('calendar.legend.visited', '방문'),
|
|
98
|
+
수: dayVisits.filter(v => (v.status ?? 'visited') === 'visited').length },
|
|
99
|
+
{ key: 'scheduled', 말: t('calendar.legend.scheduled', '예약'),
|
|
100
|
+
수: dayVisits.filter(v => v.status === 'scheduled').length },
|
|
101
|
+
{ key: 'noVisit', 말: t('calendar.legend.noVisit', '미방문'),
|
|
102
|
+
수: dayVisits.filter(v => v.status === 'noVisit').length },
|
|
103
|
+
].filter(g => g.수 > 0);
|
|
104
|
+
/* 방문뿐인 날은 전 글귀 그대로 간다 — 그 열쇠의 뜻("방문 N건")이 실제로 맞는
|
|
105
|
+
자리이고, 부르는 쪽이 덮어 둔 글도 그대로 산다. */
|
|
106
|
+
const 센말 = 갈래.length === 1 && 갈래[0].key === 'visited'
|
|
107
|
+
? t('calendar.visitCount', { count: dayVisits.length, defaultValue: '방문 {{count}}건' })
|
|
108
|
+
: 갈래.map(g => t('calendar.statusCount', { label: g.말, count: g.수, defaultValue: '{{label}} {{count}}건' })).join(' · ');
|
|
109
|
+
return (_jsxs("span", { className: "calendar-cell__visit-count caption-1", "aria-label": `${날짜말} ${센말}`, children: [갈래.map(g => (_jsx("span", { className: `calendar-cell__visit-dot calendar-cell__visit-dot--${g.key}`, "aria-hidden": "true" }, g.key))), dayVisits.length] }));
|
|
110
|
+
})(), _jsx("div", { className: "calendar-cell__badges", children: dayVisits.map((v, vi) => (_jsxs("div", { draggable: !!onVisitMove, className: `calendar-visit-badge${v.status === 'noVisit' ? ' calendar-visit-badge--no-visit' : v.status === 'scheduled' ? ' calendar-visit-badge--scheduled' : ''}${onVisitClick ? ' calendar-visit-badge--clickable' : ''}${onVisitMove ? ' calendar-visit-badge--draggable' : ''}`, onDragStart: onVisitMove ? (e) => {
|
|
89
111
|
dragVisitId.current = v.id;
|
|
90
112
|
e.dataTransfer.effectAllowed = 'move';
|
|
91
113
|
} : undefined, onDragEnd: onVisitMove ? () => {
|
|
@@ -23,14 +23,38 @@
|
|
|
23
23
|
box-sizing:border-box;
|
|
24
24
|
max-width: min(520px, calc(100vw - 32px));
|
|
25
25
|
margin: 0;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/* **긴 말은 줄을 넘긴다**(말귀 요청 (64), 2026-10-02).
|
|
27
|
+
전에는 `white-space: nowrap` + `text-overflow: ellipsis` 라 한 줄에 안 들어가는 말이
|
|
28
|
+
잘렸다. 그런데 이 앱의 오류 문구는 거의 다 **"무엇이 잘못됐나 + 무엇을 하라"** 두
|
|
29
|
+
문장이고(코어 `toUserErrorMessage` 가 내주는 말도 그 꼴이다), 잘리는 쪽은 늘 **뒷문장 —
|
|
30
|
+
할 일** 이었다: `네트워크 연결이 불안정합니다. 연결 상태를 확…` 까지만 읽혔다.
|
|
31
|
+
말귀 실측(`scrollWidth - clientWidth`): 320 에서 98~125px, 375 에서 70~98px,
|
|
32
|
+
**1440 에서도** 가장 긴 말이 `max-width: 520px` 에 닿아 19px 잘렸다 —
|
|
33
|
+
좁은 화면만의 일이 아니었다.
|
|
34
|
+
가운데 맞춤이라 **말줄임표가 눈에 안 띄어** 오래 안 드러난 자리이기도 하다.
|
|
35
|
+
`pre-line` 인 까닭: 부르는 쪽이 넣은 줄바꿈 문자를 살린다. */
|
|
36
|
+
white-space: pre-line;
|
|
37
|
+
overflow: hidden; /* 둥근 모서리·테두리 그라디언트를 지킨다 */
|
|
29
38
|
text-align:center;
|
|
30
39
|
min-height: 60px;
|
|
31
40
|
min-width: min(360px, calc(100vw - 32px));
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
/* 글자는 **따로 한 겹**이다 — 줄 수 상한(`-webkit-line-clamp`)은 `display: -webkit-box` 를
|
|
44
|
+
요구하는데, 겉칸은 가운데 맞춤 때문에 `display: flex` 여야 한다. 둘을 한 상자에 못 담는다.
|
|
45
|
+
상한을 두는 까닭: 알림이 화면을 덮으면 그것대로 곤란하다. 세 줄까지 보이고 그 뒤는
|
|
46
|
+
말줄임이 된다 — 두 문장짜리는 두 줄에 다 들어가므로 실제로 잘릴 말은 아주 긴 것뿐이다. */
|
|
47
|
+
.toast__text{
|
|
48
|
+
display: -webkit-box;
|
|
49
|
+
-webkit-box-orient: vertical;
|
|
50
|
+
-webkit-line-clamp: 3;
|
|
51
|
+
line-clamp: 3;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
white-space: pre-line;
|
|
54
|
+
/* 끊을 자리 없는 긴 토막(URL·토큰)이 칸을 밀고 나가지 않게 */
|
|
55
|
+
overflow-wrap: anywhere;
|
|
56
|
+
}
|
|
57
|
+
|
|
34
58
|
.toast__inner--normal {
|
|
35
59
|
border: 2px solid transparent;
|
|
36
60
|
background:
|
|
@@ -36,7 +36,7 @@ onClose = () => { }, duration = 2000, // 0 = persistent until message cleared
|
|
|
36
36
|
쪽이 미리 정하는 것이고, 읽는 사람이 **그 자리에서** 멈출 수는 없었다.
|
|
37
37
|
다시 움직일 때는 **남은 시간부터** 돈다 — 처음부터가 아니다. */
|
|
38
38
|
_jsx("div", { ref: popupRef, className: `toast toast--${position} ${showing ? 'is-visible' : ''}`, role: "status", "aria-live": "polite", onPointerEnter: (e) => { 마지막포인터.current = e.pointerType; }, onMouseEnter: () => { if (마지막포인터.current !== 'touch')
|
|
39
|
-
pause(); }, onMouseLeave: resume, onFocusCapture: pause, onBlurCapture: resume, children: _jsx("div", { className: `toast__inner toast__inner--${normalizedType} body-2`, "aria-hidden": 읽힘 || showing ? undefined : true, children: message }) }));
|
|
39
|
+
pause(); }, onMouseLeave: resume, onFocusCapture: pause, onBlurCapture: resume, children: _jsx("div", { className: `toast__inner toast__inner--${normalizedType} body-2`, "aria-hidden": 읽힘 || showing ? undefined : true, children: _jsx("span", { className: "toast__text", children: message }) }) }));
|
|
40
40
|
if (typeof document !== 'undefined') {
|
|
41
41
|
return createPortal(el, document.body);
|
|
42
42
|
}
|
|
@@ -16,6 +16,20 @@ export type TrainingCardProps = {
|
|
|
16
16
|
description?: ReactNode;
|
|
17
17
|
/** 우측 상단 액션 영역 (좋아요/다운로드, ⋯ 메뉴 등) — 클릭이 카드로 전파되지 않음 */
|
|
18
18
|
topActions?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* **카드가 눌릴 때 읽히는 이름**(말귀 요청 (65), 2026-10-02).
|
|
21
|
+
*
|
|
22
|
+
* 안 주면 `title` 을 쓴다. 안 쓰고 두면 `role="button"` 의 이름이 **안 글자 전부**로
|
|
23
|
+
* 지어지는데, 이 카드는 안에 `topActions`(진짜 `<button>`)를 품으므로 **그 단추의 이름까지
|
|
24
|
+
* 삼킨다** — 말귀 실측(나의 단어훈련 375px, 카드 여섯 장 전부):
|
|
25
|
+
* `훈련하자!! (복사) 훈련하자!! (복사) 더보기 음절 2개 합치기 1 …` 로, 제목이 두 번 읽히고
|
|
26
|
+
* 사이에 `더보기` 가 꼈다. `TrainingBlockButton` 이 이미 같은 길로 풀어 둔 자리다.
|
|
27
|
+
*
|
|
28
|
+
* **값을 치른다** — 갈래별 수와 마지막 쓴 날이 이름에서 빠진다. 그것까지 들려주려면
|
|
29
|
+
* 부르는 쪽이 여기에 **갖춰 쓴 이름**을 넘겨라(`'훈련하자!! · 음절 합치기 1 · 3개월 전'`).
|
|
30
|
+
* 코어는 children 의 뜻을 모르므로 제목까지만 지을 수 있다.
|
|
31
|
+
*/
|
|
32
|
+
ariaLabel?: string;
|
|
19
33
|
/** 본문 영역 콘텐츠 (타입 칩, 단어 뱃지, 빈 상태 등) */
|
|
20
34
|
children?: ReactNode;
|
|
21
35
|
/** 풋터 좌측 (게시자·날짜 등) */
|
|
@@ -29,4 +43,4 @@ export type TrainingCardProps = {
|
|
|
29
43
|
className?: string;
|
|
30
44
|
};
|
|
31
45
|
/** @public */
|
|
32
|
-
export default function TrainingCard({ title, titleLevel, description, topActions, children, footerLeft, footerRight, onClick, draggable, onDragStart, onDragEnd, className, }: TrainingCardProps): import("react").JSX.Element;
|
|
46
|
+
export default function TrainingCard({ title, titleLevel, description, topActions, children, footerLeft, footerRight, ariaLabel, onClick, draggable, onDragStart, onDragEnd, className, }: TrainingCardProps): import("react").JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { cx } from '../../utils/cx';
|
|
|
4
4
|
import './TrainingCard.css';
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
/** @public */
|
|
7
|
-
export default function TrainingCard({ title, titleLevel = 3, description, topActions, children, footerLeft, footerRight, onClick, draggable, onDragStart, onDragEnd, className, }) {
|
|
7
|
+
export default function TrainingCard({ title, titleLevel = 3, description, topActions, children, footerLeft, footerRight, ariaLabel, onClick, draggable, onDragStart, onDragEnd, className, }) {
|
|
8
8
|
const 제목태그 = titleLevel ? `h${titleLevel}` : 'div';
|
|
9
9
|
const hasFooter = footerLeft != null || footerRight != null;
|
|
10
10
|
const hasBody = children != null && children !== false;
|
|
@@ -20,7 +20,7 @@ export default function TrainingCard({ title, titleLevel = 3, description, topAc
|
|
|
20
20
|
onClick();
|
|
21
21
|
}
|
|
22
22
|
}, [onClick]);
|
|
23
|
-
return (_jsxs("div", { className: cx('training-card', onClick ? 'training-card--clickable' : '', className), onClick: onClick, onKeyDown: onClick ? handleKeyDown : undefined, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, draggable: draggable, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsxs("div", { className: "training-card__header", children: [_jsxs("div", { className: "training-card__title-row", children: [typeof title === 'string'
|
|
23
|
+
return (_jsxs("div", { className: cx('training-card', onClick ? 'training-card--clickable' : '', className), onClick: onClick, onKeyDown: onClick ? handleKeyDown : undefined, role: onClick ? 'button' : undefined, "aria-label": onClick ? (ariaLabel ?? (typeof title === 'string' ? title : undefined)) : undefined, tabIndex: onClick ? 0 : undefined, draggable: draggable, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsxs("div", { className: "training-card__header", children: [_jsxs("div", { className: "training-card__title-row", children: [typeof title === 'string'
|
|
24
24
|
? _jsx(제목태그, { className: "training-card__title h4", children: title })
|
|
25
25
|
: title, topActions != null && (_jsx("div", { className: "training-card__top-actions", onClick: (e) => e.stopPropagation(), children: topActions }))] }), description != null
|
|
26
26
|
? (typeof description === 'string'
|
|
@@ -20,7 +20,14 @@ export type TrainingCardGroupProps = {
|
|
|
20
20
|
* 안 잡혔다. 그 안의 `접기`·`정렬` 은 그룹마다 같은 이름이라, 낭독기로 단추 목록을
|
|
21
21
|
* 부르면 `접기` 가 세 번 선다 — **어느 그룹의 것인지 가릴 길이 없었다.**
|
|
22
22
|
*
|
|
23
|
-
* 이름 있는 지역이 서면 단추 이름은 짧은 채로 둬도 된다(지역이 가린다)
|
|
23
|
+
* **전에 여기 "이름 있는 지역이 서면 단추 이름은 짧은 채로 둬도 된다(지역이 가린다)" 고
|
|
24
|
+
* 적어 두었는데, 그 말이 틀렸다**(2026-10-02 바로잡음). `role="group"` 은 **landmark 가
|
|
25
|
+
* 아니다.** 초점이 묶음 안으로 들어가 읽을 때는 묶음 이름이 먼저 읽히니 맞지만,
|
|
26
|
+
* **낭독기로 단추 목록을 훑는 길**에서는 묶음 맥락이 안 따라온다 — 바로 윗줄에 적은
|
|
27
|
+
* "`접기` 가 세 번 선다" 가 그대로 남는다. 말귀가 그 한 줄을 믿고 앱 세 파일에서 그룹
|
|
28
|
+
* 이름을 걷으려다 물어 와서 드러났다.
|
|
29
|
+
* 그래서 **이 조각이 제 단추에 묶음 이름을 직접 단다**(아래 `접기`·`정렬`). 부르는 쪽이
|
|
30
|
+
* actions 로 넘기는 단추에도 묶음 이름을 다는 쪽이 맞다 — 걷지 마라.
|
|
24
31
|
* `0` 을 주면 전과 같이 `span` 으로 그린다. */
|
|
25
32
|
titleLevel?: 0 | 2 | 3 | 4 | 5 | 6;
|
|
26
33
|
/** 그룹 제목 — 문자열이면 제목으로 감싸고, 노드면 그대로 렌더(예: 인라인 이름변경 입력) */
|
|
@@ -19,9 +19,17 @@ export default function TrainingCardGroup({ title, titleLevel = 3, count, collap
|
|
|
19
19
|
잡혀, 안의 `접기`·`정렬` 이 그룹마다 같은 이름으로 서도 가릴 길이 없었다 —
|
|
20
20
|
`ChartSection` 에 (51) 로 한 것과 같은 자다. 글자 제목일 때만 건다(마디를 가리키므로
|
|
21
21
|
글이 아닌 제목이면 이름이 안 선다). */
|
|
22
|
-
_jsxs("div", { className: cls, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, role: typeof title === 'string' && title ? 'group' : undefined, "aria-labelledby": typeof title === 'string' && title ? 제목id : undefined, children: [_jsxs("header", { className: "training-card-group__header", children: [접을수있다 && (_jsx(Button, { htmlType: "button", size: 40, type: "dot", variant: "transparent-gray", useBorder: false, icon: collapsed ? 'chevron-right' : 'chevron-down',
|
|
22
|
+
_jsxs("div", { className: cls, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, role: typeof title === 'string' && title ? 'group' : undefined, "aria-labelledby": typeof title === 'string' && title ? 제목id : undefined, children: [_jsxs("header", { className: "training-card-group__header", children: [접을수있다 && (_jsx(Button, { htmlType: "button", size: 40, type: "dot", variant: "transparent-gray", useBorder: false, icon: collapsed ? 'chevron-right' : 'chevron-down',
|
|
23
|
+
/* **묶음 이름을 단다** — 그룹이 셋 서면 `접기` 도 셋이라 단추 목록에서 못 가린다.
|
|
24
|
+
지역(`role="group"`)이 가려 주지 않는다(위 `titleLevel` 머리글). 제목이 글자일
|
|
25
|
+
때만 — 마디 제목이면 이름을 못 짓는다. */
|
|
26
|
+
ariaLabel: typeof title === 'string' && title
|
|
27
|
+
? (collapsed ? t('common.expandFor', { name: title }) : t('common.collapseFor', { name: title }))
|
|
28
|
+
: (collapsed ? t('common.expand', '펼치기') : t('common.collapse', '접기')), className: "training-card-group__chevron", "aria-expanded": !collapsed, onClick: onToggle })), typeof title === 'string'
|
|
23
29
|
? _jsx(제목태그, { id: 제목id, className: "training-card-group__title h5b", children: title })
|
|
24
|
-
: title, count !== undefined && _jsx("span", { className: "training-card-group__count body-3b", children: count }), (sort || actions != null) && (_jsxs("span", { className: "training-card-group__actions", onClick: e => e.stopPropagation(), children: [sort && (_jsx(Dropdown, { size: 40, options: sort.options, value: sort.value, placeholder: sort.placeholder, onChange: sort.onChange
|
|
30
|
+
: title, count !== undefined && _jsx("span", { className: "training-card-group__count body-3b", children: count }), (sort || actions != null) && (_jsxs("span", { className: "training-card-group__actions", onClick: e => e.stopPropagation(), children: [sort && (_jsx(Dropdown, { size: 40, options: sort.options, value: sort.value, placeholder: sort.placeholder, onChange: sort.onChange,
|
|
31
|
+
/* 접기와 같은 까닭으로 묶음 이름을 단다 — 그룹마다 `정렬` 이 하나씩 선다 */
|
|
32
|
+
ariaLabel: typeof title === 'string' && title ? t('common.sortFor', { name: title }) : undefined })), actions] }))] }), !collapsed && (empty
|
|
25
33
|
? _jsx("div", { className: "training-card-group__empty body-3", children: emptyContent })
|
|
26
34
|
: _jsx("div", { className: "training-card-group__grid", children: children }))] }));
|
|
27
35
|
}
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
"selectedCount_one": "{{count}} selected",
|
|
6
6
|
"selectedCount_other": "{{count}} selected",
|
|
7
7
|
"moreFor": "More options for {{name}}",
|
|
8
|
+
"collapseFor": "Collapse {{name}}",
|
|
9
|
+
"expandFor": "Expand {{name}}",
|
|
10
|
+
"sortFor": "Sort {{name}}",
|
|
8
11
|
"cancel": "Cancel",
|
|
9
12
|
"info": "Info",
|
|
10
13
|
"errorState": {
|
|
@@ -121,7 +124,8 @@
|
|
|
121
124
|
"noVisit": "No Show"
|
|
122
125
|
},
|
|
123
126
|
"periodSelect": "Period unit",
|
|
124
|
-
"visitCount": "{{count}} visit(s)"
|
|
127
|
+
"visitCount": "{{count}} visit(s)",
|
|
128
|
+
"statusCount": "{{count}} {{label}}"
|
|
125
129
|
},
|
|
126
130
|
"passCard": {
|
|
127
131
|
"planSuffix": "Plan",
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
"loadMore": "더보기",
|
|
40
40
|
"more": "더보기",
|
|
41
41
|
"moreFor": "{{name}} 더보기",
|
|
42
|
+
"collapseFor": "{{name}} 접기",
|
|
43
|
+
"expandFor": "{{name}} 펼치기",
|
|
44
|
+
"sortFor": "{{name}} 정렬",
|
|
42
45
|
"retry": "다시 시도",
|
|
43
46
|
"prevPage": "이전 페이지",
|
|
44
47
|
"nextPage": "다음 페이지",
|
|
@@ -119,7 +122,8 @@
|
|
|
119
122
|
"noVisit": "미방문"
|
|
120
123
|
},
|
|
121
124
|
"periodSelect": "기간 단위",
|
|
122
|
-
"visitCount": "방문 {{count}}건"
|
|
125
|
+
"visitCount": "방문 {{count}}건",
|
|
126
|
+
"statusCount": "{{label}} {{count}}건"
|
|
123
127
|
},
|
|
124
128
|
"passCard": {
|
|
125
129
|
"planSuffix": "이용권",
|