@ihabdevteam/core 0.300.0 → 0.302.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 +62 -0
- package/dist/components.cjs.js +9 -9
- package/dist/hooks.cjs.js +2 -2
- package/dist/i18n.cjs.js +2 -2
- package/dist/ihabdevteam-core.cjs.js +45 -45
- package/dist/ihabdevteam-core.d.ts +25 -2
- package/dist/packages/core/src/components/Accordion.js +1 -1
- package/dist/packages/core/src/components/FilterPanel.js +6 -6
- package/dist/packages/core/src/components/Table.js +1 -1
- package/dist/packages/core/src/components/Training/TrainingCardGroup.js +1 -1
- package/dist/packages/core/src/components/Training/TrainingStep.d.ts +9 -1
- package/dist/packages/core/src/components/Training/TrainingStep.js +3 -2
- package/dist/packages/core/src/components/Training/TrainingStepConfirmWithText.js +1 -1
- package/dist/packages/core/src/components/Training/trainingBlockButtonTypes.d.ts +15 -0
- package/dist/packages/core/src/i18n/locales/ko.json +3 -3
- 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.302.0";
|
|
1655
1655
|
|
|
1656
1656
|
/** @public */
|
|
1657
1657
|
declare const coreDefault: Record<string, unknown>;
|
|
@@ -8264,6 +8264,21 @@ export declare interface TrainingBlockButtonPopupProps {
|
|
|
8264
8264
|
correct: number | null;
|
|
8265
8265
|
total: number | null;
|
|
8266
8266
|
}) => void;
|
|
8267
|
+
/**
|
|
8268
|
+
* **판이 끝난 그 순간** 호출 — 닫기와 무관하다(`TrainingPagePopup` 으로 그대로 전달).
|
|
8269
|
+
*
|
|
8270
|
+
* `onComplete` 는 **닫을 때** 불린다. 숙제처럼 *"이 문항 풀었다"* 를 세는 쪽은 닫기를
|
|
8271
|
+
* 기다릴 수 없다 — 다 풀고 안 닫고 떠나면 통째로 날아갔다(말귀 요청 (100), 0.300.0).
|
|
8272
|
+
*
|
|
8273
|
+
* **값은 `...rest` 로 흘러 이미 갔는데 타입만 빠져 있었다**(0.300.0). 쓰는 쪽은 통과시킬
|
|
8274
|
+
* 길이 없어 `as` 로 덮어야 했다 — (74) 가 앱까지 안 닿았던 것과 **같은 꼴**이다:
|
|
8275
|
+
* 가운데 한 겹이 안 따라갔다.
|
|
8276
|
+
*/
|
|
8277
|
+
onRoundFinished?: (result: {
|
|
8278
|
+
correct: number | null;
|
|
8279
|
+
total: number | null;
|
|
8280
|
+
attempt?: number;
|
|
8281
|
+
}) => void;
|
|
8267
8282
|
popupTitle: string;
|
|
8268
8283
|
popupSubTitle?: string;
|
|
8269
8284
|
depth?: string;
|
|
@@ -8937,7 +8952,7 @@ export declare type TrainingScenario = {
|
|
|
8937
8952
|
};
|
|
8938
8953
|
|
|
8939
8954
|
/** @public */
|
|
8940
|
-
declare function TrainingStep_2({ index, stepIndex, isLast, title, titleProps, children, className, onDone, useTitleFaded }: TrainingStepProps): JSX.Element;
|
|
8955
|
+
declare function TrainingStep_2({ index, stepIndex, isLast, title, titleProps, children, className, onDone, useTitleFaded, lockedHint }: TrainingStepProps): JSX.Element;
|
|
8941
8956
|
export { TrainingStep_2 as TrainingStep }
|
|
8942
8957
|
|
|
8943
8958
|
/** @public */
|
|
@@ -9148,6 +9163,14 @@ export declare type TrainingStepProps = {
|
|
|
9148
9163
|
className?: string;
|
|
9149
9164
|
onDone?: (payload?: unknown) => void;
|
|
9150
9165
|
useTitleFaded?: boolean;
|
|
9166
|
+
/**
|
|
9167
|
+
* **왜 아직 못 누르는지 알리는 한 줄** — 잠긴 단계 **바로 다음 차례**에만 선다
|
|
9168
|
+
* (말귀 요청 (99), 2026-10-08).
|
|
9169
|
+
*
|
|
9170
|
+
* 안 주면 *"다 들으면 고를 수 있어요."* 다 — 이 저장소의 단계는 앞이 늘 **듣는 단계**다.
|
|
9171
|
+
* 기다리는 것이 다른 단계면 그 말을 주고, **`null` 을 주면 안 세운다.**
|
|
9172
|
+
*/
|
|
9173
|
+
lockedHint?: ReactNode | null;
|
|
9151
9174
|
};
|
|
9152
9175
|
|
|
9153
9176
|
/** @public */
|
|
@@ -30,7 +30,7 @@ function Item({ title, body, open = false, onToggle, avatar, titleClassName = 'b
|
|
|
30
30
|
/* **접기 손잡이의 말은 저장소 전체가 하나다**(사용자 지시 "전체 통일해", 2026-10-07).
|
|
31
31
|
열쇠는 `accordion.*` 로 따로 남는다(소비자가 아코디언만 달리 부르고 싶을 수 있다) —
|
|
32
32
|
대신 **값이 갈라지지 않는지**를 못이 센다(`filterPanelCollapseAffordance`). */
|
|
33
|
-
label: open ? t('accordion.collapse', '접기') : t('accordion.expand', '
|
|
33
|
+
label: open ? t('accordion.collapse', '접기') : t('accordion.expand', '펼치기'), className: "accordion-item__chevron-icon", imgClassName: "accordion-item__chevron-image" }) })] }), open && (_jsx("div", { className: `accordion-item__body ${bodyClassName}`, children: typeof body === 'string' ? _jsx("p", { children: body }) : body }))] }));
|
|
34
34
|
}
|
|
35
35
|
// ── Accordion ─────────────────────────────────────────────────────────────
|
|
36
36
|
/** @public */
|
|
@@ -73,12 +73,12 @@ export default function FilterPanel({ title, titleLevel = 2, headerAction, heade
|
|
|
73
73
|
그리고 `common.close`(대화상자 닫기)와 **섞지 않는다** — 섞으면 접기 손잡이와
|
|
74
74
|
닫기 단추가 한 결정에 묶여, 한쪽을 고칠 때 다른 쪽이 따라 움직인다.
|
|
75
75
|
|
|
76
|
-
>
|
|
77
|
-
>
|
|
78
|
-
>
|
|
79
|
-
>
|
|
80
|
-
>
|
|
81
|
-
ariaLabel: collapsed ? t('common.expand', '
|
|
76
|
+
> **짝은 `펼치기`/`접기` 다**(사용자 결정 2026-10-08). 하루 사이에 말이
|
|
77
|
+
> 두 번 움직였다 — `펼치기/접기`(처음) → `열기/닫기`(10-07) → `열기/접기`
|
|
78
|
+
> (10-08, `닫기` 만 고침) → **`펼치기/접기`**(10-08, 짝을 맞춤).
|
|
79
|
+
> 섞인 하루를 **제 짐작으로 안 맞춘 것**이 맞았다: 두 말 다 사장님이
|
|
80
|
+
> 고르신 것이라, 어느 쪽으로 모을지도 사장님이 정하실 일이었다. */
|
|
81
|
+
ariaLabel: collapsed ? t('common.expand', '펼치기') : t('common.collapse', '접기'), showTooltipInfo: collapsed ? t('common.expand', '펼치기') : t('common.collapse', '접기'), "aria-expanded": !collapsed, onClick: toggleCollapsed }))] })] })), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { className: cx('filter-panel__sections', isRow ? 'filter-panel__sections--row' : ''), children: sections.map(section => {
|
|
82
82
|
const sel = selected[section.label];
|
|
83
83
|
const isAllSelected = sel === undefined || (Array.isArray(sel) ? sel.length === 0 : false);
|
|
84
84
|
// section.items에 빈 항목(null/undefined)이 섞여 들어오면 item.value 접근에서
|
|
@@ -528,7 +528,7 @@ export default function Table({ fields = [], headerGroups, items = [], renderRow
|
|
|
528
528
|
* 손으로 붙이던 `onKeyDown` 은 **지웠다** — 진짜 단추라 Enter·Space 는 브라우저가
|
|
529
529
|
* 처리하고, "머리 안 단추에서 누른 키는 그 단추 몫" 이라는 가드도 저절로 선다.
|
|
530
530
|
*/
|
|
531
|
-
role: "caption", children: [onToggleCollapse && (_jsx(Button, { htmlType: "button", size: 40, type: "dot", variant: "transparent-gray", useBorder: false, icon: collapsed ? 'chevron-right' : 'chevron-down', ariaLabel: collapsed ? t('common.expand', '
|
|
531
|
+
role: "caption", children: [onToggleCollapse && (_jsx(Button, { htmlType: "button", size: 40, type: "dot", variant: "transparent-gray", useBorder: false, icon: collapsed ? 'chevron-right' : 'chevron-down', ariaLabel: collapsed ? t('common.expand', '펼치기') : t('common.collapse', '접기'),
|
|
532
532
|
/* 여닫힘은 **여는 것**이 말한다 — 전에는 겉의 `div` 에 달려 있었다(위 머리글) */
|
|
533
533
|
ariaExpanded: !collapsed, className: "table-header__chevron" })), icon && (_jsx(Icon, { icon: icon, label: typeof title === 'string' ? title : undefined, className: "table-header__icon" })), _jsx("span", { id: 제목id, className: "table-header__title h5b", children: title }), count !== undefined && _jsx("span", { className: "table-header__count body-3b", children: count }), headerActions && (_jsx("span", { className: "table-header__actions", onClick: e => e.stopPropagation(), children: headerActions }))] })), !collapsed && (_jsxs(_Fragment, { children: [headerGroups && headerGroups.length > 0 && (_jsx("div", { role: "row", className: "table-fields table-fields--groups", children: headerGroups.map((g, i) => (_jsx("div", { role: "columnheader", "aria-colspan": g.span, className: cx('table-field body-3', g.className), style: { gridColumn: `span ${g.span}` }, children: g.label }, i))) })), _jsxs("div", { role: "presentation", className: cx('table-fields-strip', 폭조절 && 'table-fields-strip--resizable'), children: [_jsx("div", { role: "row", className: "table-fields", ref: fieldsRef, children: fields.map((f, i) => (_jsx(TableField, { onClick: f.onClick, className: f.className, sortDir: f.sortDir, ariaSort: ariaSortOf(i),
|
|
534
534
|
/* fit 모드는 오른쪽 이웃에서 폭을 덜어 오므로, 덜어 올 컬럼이 없는 자리에는
|
|
@@ -25,7 +25,7 @@ export default function TrainingCardGroup({ title, titleLevel = 3, count, collap
|
|
|
25
25
|
때만 — 마디 제목이면 이름을 못 짓는다. */
|
|
26
26
|
ariaLabel: typeof title === 'string' && title
|
|
27
27
|
? (collapsed ? t('common.expandFor', { name: title }) : t('common.collapseFor', { name: title }))
|
|
28
|
-
: (collapsed ? t('common.expand', '
|
|
28
|
+
: (collapsed ? t('common.expand', '펼치기') : t('common.collapse', '접기')), className: "training-card-group__chevron", "aria-expanded": !collapsed, onClick: onToggle })), typeof title === 'string'
|
|
29
29
|
? _jsx(제목태그, { id: 제목id, className: "training-card-group__title h5b", children: title })
|
|
30
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
31
|
/* 접기와 같은 까닭으로 묶음 이름을 단다 — 그룹마다 `정렬` 이 하나씩 선다 */
|
|
@@ -10,6 +10,14 @@ export type TrainingStepProps = {
|
|
|
10
10
|
className?: string;
|
|
11
11
|
onDone?: (payload?: unknown) => void;
|
|
12
12
|
useTitleFaded?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* **왜 아직 못 누르는지 알리는 한 줄** — 잠긴 단계 **바로 다음 차례**에만 선다
|
|
15
|
+
* (말귀 요청 (99), 2026-10-08).
|
|
16
|
+
*
|
|
17
|
+
* 안 주면 *"다 들으면 고를 수 있어요."* 다 — 이 저장소의 단계는 앞이 늘 **듣는 단계**다.
|
|
18
|
+
* 기다리는 것이 다른 단계면 그 말을 주고, **`null` 을 주면 안 세운다.**
|
|
19
|
+
*/
|
|
20
|
+
lockedHint?: ReactNode | null;
|
|
13
21
|
};
|
|
14
22
|
/** @public */
|
|
15
|
-
export default function TrainingStep({ index, stepIndex, isLast, title, titleProps, children, className, onDone, useTitleFaded }: TrainingStepProps): import("react").JSX.Element;
|
|
23
|
+
export default function TrainingStep({ index, stepIndex, isLast, title, titleProps, children, className, onDone, useTitleFaded, lockedHint }: TrainingStepProps): import("react").JSX.Element;
|
|
@@ -18,7 +18,7 @@ function computeState(index, sIndex) {
|
|
|
18
18
|
return "disabled";
|
|
19
19
|
}
|
|
20
20
|
/** @public */
|
|
21
|
-
export default function TrainingStep({ index, stepIndex, isLast = false, title = "", titleProps = {}, children, className = "", onDone = undefined, useTitleFaded = undefined }) {
|
|
21
|
+
export default function TrainingStep({ index, stepIndex, isLast = false, title = "", titleProps = {}, children, className = "", onDone = undefined, useTitleFaded = undefined, lockedHint }) {
|
|
22
22
|
const { t } = useTranslation();
|
|
23
23
|
const [currentState, setCurrentState] = useState(() => computeState(index, stepIndex));
|
|
24
24
|
useEffect(() => {
|
|
@@ -87,5 +87,6 @@ export default function TrainingStep({ index, stepIndex, isLast = false, title =
|
|
|
87
87
|
}
|
|
88
88
|
return child;
|
|
89
89
|
});
|
|
90
|
-
return (_jsxs("div", { className: `training-step training-step--${currentState} ${isLast ? "training-step--last" : ""} ${className}`, "aria-current": currentState === 'active', children: [_jsx(TrainingStepBullet, { state: currentState }), _jsxs("div", { className: "training-step__content", children: [_jsx(TrainingStepTitle, { state: currentState, title: title, ...titleProps, useFaded: useTitleFaded }),
|
|
90
|
+
return (_jsxs("div", { className: `training-step training-step--${currentState} ${isLast ? "training-step--last" : ""} ${className}`, "aria-current": currentState === 'active', children: [_jsx(TrainingStepBullet, { state: currentState }), _jsxs("div", { className: "training-step__content", children: [_jsx(TrainingStepTitle, { state: currentState, title: title, ...titleProps, useFaded: useTitleFaded }), currentState === 'disabled' && lockedHint !== null
|
|
91
|
+
&& typeof index === 'number' && typeof stepIndex === 'number' && index === stepIndex + 1 && (_jsx("p", { className: "training-step__locked-hint caption-1", role: "status", children: lockedHint ?? t('training.answerLockedHint', '다 들으면 고를 수 있어요.') })), mappedChildren] }), tooltipOpen ? (_jsx(Tooltip, { anchorRect: tooltipOpts.anchorRect, backdrop: tooltipOpts.backdrop, onClose: hideTooltip, ariaLabel: t('tooltip.textbox', '본문'), children: _jsx(TextBoxTooltip, { text: tooltipOpts.text, onClose: hideTooltip }) })) : null] }));
|
|
91
92
|
}
|
|
@@ -70,7 +70,7 @@ export default function TrainingStepConfirmWithText({ index, stepIndex, isLast =
|
|
|
70
70
|
setSelectedAnswer(true);
|
|
71
71
|
handleEndedLocal(true);
|
|
72
72
|
}, [handleEndedLocal, currentState]);
|
|
73
|
-
return (_jsxs(TrainingStep, { index: index, stepIndex: stepIndex, isLast: isLast, title: resolvedTitle, onDone: onDone, useTitleFaded: false, children: [_jsx(TrainingStepTextBox, { context: context, state: currentState }),
|
|
73
|
+
return (_jsxs(TrainingStep, { index: index, stepIndex: stepIndex, isLast: isLast, title: resolvedTitle, onDone: onDone, useTitleFaded: false, children: [_jsx(TrainingStepTextBox, { context: context, state: currentState }), _jsx("div", { className: "training-step__buttons", children: Array.isArray(choices) && choices.length > 0 ? (choices.slice(0, 6).map((c, i) => (_jsx(Button, { htmlType: "button", size: 56, type: "bar", onClick: () => {
|
|
74
74
|
if (currentState !== 'active')
|
|
75
75
|
return;
|
|
76
76
|
setSelectedAnswer(i);
|
|
@@ -10,6 +10,21 @@ export interface TrainingBlockButtonPopupProps {
|
|
|
10
10
|
correct: number | null;
|
|
11
11
|
total: number | null;
|
|
12
12
|
}) => void;
|
|
13
|
+
/**
|
|
14
|
+
* **판이 끝난 그 순간** 호출 — 닫기와 무관하다(`TrainingPagePopup` 으로 그대로 전달).
|
|
15
|
+
*
|
|
16
|
+
* `onComplete` 는 **닫을 때** 불린다. 숙제처럼 *"이 문항 풀었다"* 를 세는 쪽은 닫기를
|
|
17
|
+
* 기다릴 수 없다 — 다 풀고 안 닫고 떠나면 통째로 날아갔다(말귀 요청 (100), 0.300.0).
|
|
18
|
+
*
|
|
19
|
+
* **값은 `...rest` 로 흘러 이미 갔는데 타입만 빠져 있었다**(0.300.0). 쓰는 쪽은 통과시킬
|
|
20
|
+
* 길이 없어 `as` 로 덮어야 했다 — (74) 가 앱까지 안 닿았던 것과 **같은 꼴**이다:
|
|
21
|
+
* 가운데 한 겹이 안 따라갔다.
|
|
22
|
+
*/
|
|
23
|
+
onRoundFinished?: (result: {
|
|
24
|
+
correct: number | null;
|
|
25
|
+
total: number | null;
|
|
26
|
+
attempt?: number;
|
|
27
|
+
}) => void;
|
|
13
28
|
popupTitle: string;
|
|
14
29
|
popupSubTitle?: string;
|
|
15
30
|
depth?: string;
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"more": "더보기",
|
|
41
41
|
"moreFor": "{{name}} 더보기",
|
|
42
42
|
"collapseFor": "{{name}} 접기",
|
|
43
|
-
"expandFor": "{{name}}
|
|
43
|
+
"expandFor": "{{name}} 펼치기",
|
|
44
44
|
"sortFor": "{{name}} 정렬",
|
|
45
45
|
"retry": "다시 시도",
|
|
46
46
|
"prevPage": "이전 페이지",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"pageSizeLabel": "{{n}}개씩 보기",
|
|
64
64
|
"totalCountPrefix": "총 ",
|
|
65
65
|
"totalCountSuffix": "개",
|
|
66
|
-
"expand": "
|
|
66
|
+
"expand": "펼치기",
|
|
67
67
|
"collapse": "접기",
|
|
68
68
|
"popup": "팝업",
|
|
69
69
|
"codeBlock": "코드 블록",
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
},
|
|
160
160
|
"accordion": {
|
|
161
161
|
"collapse": "접기",
|
|
162
|
-
"expand": "
|
|
162
|
+
"expand": "펼치기"
|
|
163
163
|
},
|
|
164
164
|
"header": {
|
|
165
165
|
"biArea": "BI 영역",
|