@ihabdevteam/core 0.212.0 → 0.213.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.
@@ -1626,7 +1626,7 @@ export declare function copyText(text: string): Promise<boolean>;
1626
1626
  *
1627
1627
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
1628
1628
  */
1629
- export declare const CORE_VERSION = "0.212.0";
1629
+ export declare const CORE_VERSION = "0.213.0";
1630
1630
 
1631
1631
  /** @public */
1632
1632
  declare const coreDefault: Record<string, unknown>;
@@ -10360,7 +10360,7 @@ export declare interface WordSearchResultWord {
10360
10360
  * 표시(items)·편집(editCells) 두 모드를 지원한다.
10361
10361
  * @public
10362
10362
  */
10363
- export declare function WordTable({ title, titleIcon, disc, className, orientation, contentTypes, cutLine, showOutline, showDivLine, items, editCells, count, activeCell, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd, contentRef, buttonSize, pageNumber, pageNumberPosition, fitHeight, scoring, marks, onMarkClick, scoreIndexOffset, }: WordTableProps): JSX.Element;
10363
+ export declare function WordTable({ title, titleIcon, disc, className, orientation, contentTypes, cutLine, showOutline, showDivLine, items, editCells, count, activeCell, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd, contentRef, buttonSize, brand, code, pageNumber, pageNumberPosition, fitHeight, scoring, marks, onMarkClick, scoreIndexOffset, }: WordTableProps): JSX.Element;
10364
10364
 
10365
10365
  export declare function WordTableBlock({ title, titleIcon, disc, filterTitle, filterCollapsible, filterDefaultCollapsed, filterCollapsed, onFilterCollapsedChange, filterHeaderSummary, items, onSaveComplete, onLoad, onRemoveItem, onReset, resetLabel, saveButtonLabel, secondarySaveButtonLabel, onReorder, showCellCount, onAddItem, className, renderSavePopup, renderWordSearch, hideControls, saveOnly, tableName, nameValue, onNameChange, descriptionValue, onDescriptionChange, namePlaceholder, descriptionPlaceholder, existingNames, editingName, onCancel, initialItemsPerPage, renderCellAction, actionsLeftExtra }: WordTableBlockProps): JSX.Element;
10366
10366
 
@@ -10741,6 +10741,40 @@ export declare interface WordTableProps {
10741
10741
  * 쪽의 시작 번호를 받아 표 전체에서 유일한 번호로 만든다.
10742
10742
  */
10743
10743
  scoreIndexOffset?: number;
10744
+ /**
10745
+ * **종이 위·아래에 띠를 두르고** 로고·표 번호·주소·쪽 번호를 담는다(사용자 지시 2026-09-30).
10746
+ *
10747
+ * 자리는 넷이다 — 위 왼쪽 말귀 로고 · 위 오른쪽 `code` · 아래 왼쪽 ihab 로고와 저작권 ·
10748
+ * 아래 오른쪽 주소와 쪽 번호.
10749
+ *
10750
+ * **안 주면 종이가 한 픽셀도 안 바뀐다.** 띠는 격자 위아래를 비워 만드는 것이라(쪽 번호가
10751
+ * 쓰던 방식과 같다) 칸이 그만큼 낮아진다 — 안 쓰는 표까지 낮아지면 안 된다.
10752
+ *
10753
+ * 띠는 **격자 안**에 있다(자리만 절대 배치로 잡는다). 인쇄는 격자 하나만 복사해 가므로
10754
+ * (`contentRef` → `printWordTableElements`) 격자 밖에 두면 **종이에는 안 찍힌다** —
10755
+ * 화면에서는 멀쩡해 보이고 종이에서만 사라지는 꼴이라 눈치채기 어렵다.
10756
+ */
10757
+ brand?: {
10758
+ /** 로고와 저작권을 세운다. 나머지 자리는 각자 값이 있을 때만 선다. */
10759
+ show?: boolean;
10760
+ /**
10761
+ * 아래 오른쪽 — **이 표로 가는 주소**. 회사 홈 주소가 아니라 그 표의 공유 링크다
10762
+ * (사용자 지시 2026-09-30). 종이를 든 사람이 이 주소로 같은 표를 화면에서 연다.
10763
+ *
10764
+ * 종이라 누를 수 없으니 글자 그대로 찍는다 — 그래서 **사람이 옮겨 칠 수 있는 길이**
10765
+ * 여야 한다. 긴 주소를 그대로 주면 넘쳐 …로 잘리고, 잘린 주소는 옮겨 칠 수 없다.
10766
+ * 짧은 주소를 쓰거나, 표 번호로 찾는 길을 함께 두는 편이 낫다.
10767
+ */
10768
+ link?: string;
10769
+ /** 저작권 글월 — 안 주면 `© IHAB, Inc. All rights reserved.` */
10770
+ copyright?: string;
10771
+ };
10772
+ /**
10773
+ * **짧은 식별 글** — 표 번호(`1043-E`). `brand` 를 줬을 때 위 오른쪽에 선다.
10774
+ * 종이에 찍힌 번호로 나중에 그 표를 찾는다(`WordTableButton` 의 같은 이름 프롭과 짝이다).
10775
+ * 종이 글씨는 전부 한 글꼴이라 여기서는 고정폭을 안 쓴다 — 목록 카드 쪽은 고정폭이다.
10776
+ */
10777
+ code?: string;
10744
10778
  /** 페이지 분할 시 현재/전체 페이지 — 지정하면 종이 안에 페이지 번호를 찍는다 */
10745
10779
  pageNumber?: {
10746
10780
  current: number;
@@ -105,6 +105,13 @@ export function captureHtml(els, paperW, paperH, autoPrint = false, fileName = '
105
105
  아무도 opacity 를 되돌리지 않아 **빈칸으로 인쇄**되고, 새 창·내려받은 HTML 에서는 스피너가 영영 돈다(말귀 내 단어표
106
106
  '인쇄' 는 열자마자 100ms 뒤 인쇄해 처음 보는 표는 거의 늘 이랬다). */
107
107
  clone.querySelectorAll('.word-table-cell__spinner').forEach(s => s.remove());
108
+ /* **머리·바닥 띠의 배율 되돌림을 걷는다.**
109
+ 화면의 표는 미리보기 상자에 맞춰 줄어 있고, 띠가 그만큼 줄지 않도록 격자에
110
+ `--wt-unscale`(1/배율)이 **인라인으로** 박혀 있다. 이 문서는 그 격자를 인라인 스타일째
111
+ 복사해 오는데 여기서는 종이가 제 크기다 — 그대로 두면 미리보기가 좁았을수록 로고와
112
+ 글씨가 그 배수만큼 커진다(0.5 로 보고 있었으면 두 배). CSS 로 덮으려면 인라인을
113
+ 이겨야 해 `!important` 가 필요하니, 아예 지운다. */
114
+ clone.style.removeProperty('--wt-unscale');
108
115
  /* 불러오는 중이던 그림은 opacity 를 **그대로 두고** 받아지면 보이고 실패하면 숨게 한다 — 지우기만 하면 없는 그림(404, 화면에서는
109
116
  onError 로 칸이 사라짐)이 인쇄 문서에서 깨진 그림 아이콘과 alt 글자로 찍혔다. autoPrint 는 그림 로드를 기다린 뒤 인쇄한다. */
110
117
  clone.querySelectorAll('img').forEach(i => {
@@ -63,6 +63,100 @@
63
63
  pointer-events: none;
64
64
  }
65
65
 
66
+ /* ── 종이 머리·바닥 띠 ────────────────────────────────────────────────
67
+ 쪽 번호가 쓰던 수를 위아래로 넓힌 것이다. 격자가 그만큼 비우고 띠는 그 빈 자리에
68
+ 절대 배치로 선다 — **격자 안**이라 인쇄가 격자만 복사해 가도 함께 간다. */
69
+ .word-table-block__content--brand {
70
+ /* 띠 높이에 딱 붙인다 — 남는 틈은 4px 뿐이다(사용자 지시 2026-09-30: 여백이 많다).
71
+ 비운 자리가 띠보다 크면 로고와 칸 사이가 벌어져 종이가 헐거워 보인다. */
72
+ padding-top: calc(32px * var(--wt-unscale, 1));
73
+ padding-bottom: calc(24px * var(--wt-unscale, 1));
74
+ }
75
+ /* 절취선 바탕·외곽선은 칸이 있는 데까지만 — 띠까지 두르면 로고가 표 안에 든 것처럼 보인다
76
+ (쪽 번호 때와 같은 까닭). 아래는 쪽 번호 규칙과 겹치므로 여기서 다시 적어 이긴다. */
77
+ .word-table-block__content--brand.word-table-block__content--cut-show {
78
+ background-clip: content-box;
79
+ }
80
+ .word-table-block__content--brand.word-table-block__content--outline::after {
81
+ top: calc(32px * var(--wt-unscale, 1));
82
+ bottom: calc(24px * var(--wt-unscale, 1));
83
+ }
84
+
85
+ .word-table__band {
86
+ position: absolute;
87
+ left: 0;
88
+ right: 0;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: space-between;
92
+ gap: calc(12px * var(--wt-unscale, 1));
93
+ z-index: 2;
94
+ pointer-events: none;
95
+ color: var(--color-gray);
96
+ /* **body-3 급**이다(사용자 지시 2026-09-30) — 손으로 적은 13px 대신 디자인 시스템 값을
97
+ 쓴다. 클래스(`.body-3`)를 그냥 붙이면 크기가 고정돼 배율 되돌림이 안 먹으므로,
98
+ 같은 토큰에 되돌림만 곱한다.
99
+ **이 토큰은 보는 사람 쪽 설정을 탄다** — 좁은 화면이면 13px, 큰 글씨 설정이면 16~18px
100
+ 이라 종이 글씨가 그만큼 달라진다. 화면과 종이를 한 급으로 맞추기로 한 값이다. */
101
+ font-size: calc(var(--text-body-3-size, 14px) * var(--wt-unscale, 1));
102
+ line-height: calc(var(--text-body-3-line, 22px) * var(--wt-unscale, 1));
103
+ font-weight: var(--text-body-3-weight, 400);
104
+ }
105
+ /* 비워 둔 44px 안에 든다 */
106
+ .word-table__band--top { top: 0; height: calc(28px * var(--wt-unscale, 1)); }
107
+ .word-table__band--bottom { bottom: 0; height: calc(20px * var(--wt-unscale, 1)); }
108
+
109
+ .word-table__brand-logo {
110
+ height: calc(28px * var(--wt-unscale, 1));
111
+ width: auto;
112
+ display: block;
113
+ /* 말귀 로고는 컬러다 — 브라우저 인쇄 기본값은 바탕·색을 빼므로 그대로 두면 종이에서
114
+ 흐려지거나 사라진다(절취선 바탕이 쓰는 것과 같은 수다). */
115
+ print-color-adjust: exact;
116
+ -webkit-print-color-adjust: exact;
117
+ }
118
+ /* 만든 곳 로고 — 저작권 글씨와 같은 무게로 물러나 있는다 */
119
+ .word-table__brand-logo--small { height: calc(14px * var(--wt-unscale, 1)); }
120
+
121
+ /* 만든 곳 — 로고와 저작권이 한 줄로 붙는다 */
122
+ .word-table__brand-by {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: calc(8px * var(--wt-unscale, 1));
126
+ min-width: 0;
127
+ }
128
+ .word-table__copyright {
129
+ white-space: nowrap;
130
+ overflow: hidden;
131
+ text-overflow: ellipsis;
132
+ }
133
+
134
+ /* 바닥 오른쪽 — 이 표의 주소와 쪽 번호. 주소가 길면(짧은 주소가 아닐 때) 먼저 양보한다. */
135
+ .word-table__band-end {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: calc(12px * var(--wt-unscale, 1));
139
+ min-width: 0;
140
+ }
141
+
142
+ /* 표 번호·주소 — **종이 글씨는 전부 한 글꼴이다**(사용자 지시 2026-09-30). 고정폭으로
143
+ 찍어 봤는데 한 장 안에 글꼴이 둘 섞여 보였다. 종이가 한 벌로 읽히는 쪽을 골랐다 —
144
+ 목록 카드(`WordTableButton`)의 번호는 청한 대로 고정폭 그대로다. */
145
+ .word-table__code {
146
+ color: var(--color-text-primary);
147
+ flex-shrink: 0;
148
+ white-space: nowrap;
149
+ }
150
+ /* 이 표로 가는 주소 — 길 수 있으므로 넘치면 …로 자른다(쪽 번호는 안 밀린다) */
151
+ .word-table__link {
152
+ color: var(--color-gray);
153
+ min-width: 0;
154
+ overflow: hidden;
155
+ text-overflow: ellipsis;
156
+ white-space: nowrap;
157
+ }
158
+ .word-table__page-count { flex-shrink: 0; white-space: nowrap; }
159
+
66
160
  .word-table__page-number--left { left: 12px; }
67
161
  .word-table__page-number--right { right: 12px; }
68
162
  .word-table__page-number--center { left: 50%; transform: translateX(-50%); }
@@ -456,6 +550,13 @@
456
550
  transform: none !important;
457
551
  }
458
552
 
553
+ /* 표가 제 크기로 돌아오니 띠의 배율 되돌림도 끈다 — 안 끄면 화면에서 좁게 보던 만큼
554
+ 로고와 글씨가 커져 찍힌다. 인라인으로 박힌 값이라 `!important` 라야 이긴다
555
+ (위 `transform` 과 같은 까닭). */
556
+ .word-table-block__content {
557
+ --wt-unscale: 1 !important;
558
+ }
559
+
459
560
  .word-table-block__card-close,
460
561
  .word-table-block__card-actions,
461
562
  .word-table-block__card-handle { display: none; }
@@ -83,6 +83,40 @@ export interface WordTableProps {
83
83
  * 쪽의 시작 번호를 받아 표 전체에서 유일한 번호로 만든다.
84
84
  */
85
85
  scoreIndexOffset?: number;
86
+ /**
87
+ * **종이 위·아래에 띠를 두르고** 로고·표 번호·주소·쪽 번호를 담는다(사용자 지시 2026-09-30).
88
+ *
89
+ * 자리는 넷이다 — 위 왼쪽 말귀 로고 · 위 오른쪽 `code` · 아래 왼쪽 ihab 로고와 저작권 ·
90
+ * 아래 오른쪽 주소와 쪽 번호.
91
+ *
92
+ * **안 주면 종이가 한 픽셀도 안 바뀐다.** 띠는 격자 위아래를 비워 만드는 것이라(쪽 번호가
93
+ * 쓰던 방식과 같다) 칸이 그만큼 낮아진다 — 안 쓰는 표까지 낮아지면 안 된다.
94
+ *
95
+ * 띠는 **격자 안**에 있다(자리만 절대 배치로 잡는다). 인쇄는 격자 하나만 복사해 가므로
96
+ * (`contentRef` → `printWordTableElements`) 격자 밖에 두면 **종이에는 안 찍힌다** —
97
+ * 화면에서는 멀쩡해 보이고 종이에서만 사라지는 꼴이라 눈치채기 어렵다.
98
+ */
99
+ brand?: {
100
+ /** 로고와 저작권을 세운다. 나머지 자리는 각자 값이 있을 때만 선다. */
101
+ show?: boolean;
102
+ /**
103
+ * 아래 오른쪽 — **이 표로 가는 주소**. 회사 홈 주소가 아니라 그 표의 공유 링크다
104
+ * (사용자 지시 2026-09-30). 종이를 든 사람이 이 주소로 같은 표를 화면에서 연다.
105
+ *
106
+ * 종이라 누를 수 없으니 글자 그대로 찍는다 — 그래서 **사람이 옮겨 칠 수 있는 길이**
107
+ * 여야 한다. 긴 주소를 그대로 주면 넘쳐 …로 잘리고, 잘린 주소는 옮겨 칠 수 없다.
108
+ * 짧은 주소를 쓰거나, 표 번호로 찾는 길을 함께 두는 편이 낫다.
109
+ */
110
+ link?: string;
111
+ /** 저작권 글월 — 안 주면 `© IHAB, Inc. All rights reserved.` */
112
+ copyright?: string;
113
+ };
114
+ /**
115
+ * **짧은 식별 글** — 표 번호(`1043-E`). `brand` 를 줬을 때 위 오른쪽에 선다.
116
+ * 종이에 찍힌 번호로 나중에 그 표를 찾는다(`WordTableButton` 의 같은 이름 프롭과 짝이다).
117
+ * 종이 글씨는 전부 한 글꼴이라 여기서는 고정폭을 안 쓴다 — 목록 카드 쪽은 고정폭이다.
118
+ */
119
+ code?: string;
86
120
  /** 페이지 분할 시 현재/전체 페이지 — 지정하면 종이 안에 페이지 번호를 찍는다 */
87
121
  pageNumber?: {
88
122
  current: number;
@@ -114,4 +148,4 @@ export interface WordTableProps {
114
148
  * 표시(items)·편집(editCells) 두 모드를 지원한다.
115
149
  * @public
116
150
  */
117
- export default function WordTable({ title, titleIcon, disc, className, orientation, contentTypes, cutLine, showOutline, showDivLine, items, editCells, count, activeCell, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd, contentRef, buttonSize, pageNumber, pageNumberPosition, fitHeight, scoring, marks, onMarkClick, scoreIndexOffset, }: WordTableProps): import("react").JSX.Element;
151
+ export default function WordTable({ title, titleIcon, disc, className, orientation, contentTypes, cutLine, showOutline, showDivLine, items, editCells, count, activeCell, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd, contentRef, buttonSize, brand, code, pageNumber, pageNumberPosition, fitHeight, scoring, marks, onMarkClick, scoreIndexOffset, }: WordTableProps): import("react").JSX.Element;
@@ -14,6 +14,12 @@ import { getPronunciationResolver } from '../utils/pronunciationConfig';
14
14
  import { useWordImage } from '../hooks/useWordImage';
15
15
  import useMediaQuery from '../hooks/useMediaQuery';
16
16
  import { isTouchPointer, recentlyTouched, useTouchToggle } from '../hooks/touchToggle';
17
+ /* `Footer` 가 하듯 곧장 가져온다 — 묶개가 알아서 옮겨 주고, 인쇄 창은 `<img>` 가 다 받아진
18
+ 뒤에 인쇄한다. 말귀는 **컬러 판**이다(사용자 지시 2026-09-30) — 종이에서도 색으로 나가야
19
+ 하므로 아래 CSS 가 `print-color-adjust: exact` 를 준다. 만든 곳(ihab)은 검정 판이라
20
+ 글씨와 같은 무게로 물러나 있는다. */
21
+ import 말귀로고 from '../logo/Logo_speechear_kr.svg';
22
+ import ihab로고 from '../logo/Logo_IHAB_en_black.svg';
17
23
  import { WORD_TABLE_PAPER, gridFor, CUT_CLASS, POS_LABEL, WORD_TYPE_LABEL, expandSlashLabel, dotDashStyle, REF_W_BOTH, REF_W_TEXT_ONLY, REF_W_IMAGE_ONLY, REF_H_BOTH, REF_H_TEXT_ONLY, REF_H_IMAGE_ONLY, } from './wordTableLayout';
18
24
  import { useFitScale } from './useFitScale';
19
25
  import { claimSound } from './Training/currentSound';
@@ -184,7 +190,7 @@ function 어종이름(t) {
184
190
  * 표시(items)·편집(editCells) 두 모드를 지원한다.
185
191
  * @public
186
192
  */
187
- export default function WordTable({ title, titleIcon, disc, className, orientation = 'landscape', contentTypes = ['이미지', '단어', '표준 발음'], cutLine = '표시', showOutline = true, showDivLine = true, items, editCells, count, activeCell = null, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd = false, contentRef, buttonSize = 40, pageNumber, pageNumberPosition = 'right', fitHeight = false, scoring = false, marks, onMarkClick, scoreIndexOffset = 0, }) {
193
+ export default function WordTable({ title, titleIcon, disc, className, orientation = 'landscape', contentTypes = ['이미지', '단어', '표준 발음'], cutLine = '표시', showOutline = true, showDivLine = true, items, editCells, count, activeCell = null, onActivateCell, onRemoveCell, onRemoveItem, onReorder, renderCellAction, showFillerAdd = false, contentRef, buttonSize = 40, brand, code, pageNumber, pageNumberPosition = 'right', fitHeight = false, scoring = false, marks, onMarkClick, scoreIndexOffset = 0, }) {
188
194
  const clickMark = useCallback((cellIndex, clicked) => {
189
195
  onMarkClick?.(scoreIndexOffset + cellIndex, clicked);
190
196
  }, [onMarkClick, scoreIndexOffset]);
@@ -363,9 +369,16 @@ export default function WordTable({ title, titleIcon, disc, className, orientati
363
369
  const hasPairs = isEdit
364
370
  ? (editCells ?? []).some(c => c !== null && c.length > 1)
365
371
  : (items ?? []).some(item => (item.words ?? []).length > 1);
372
+ /* 로고를 세우기로 한 때만 띠를 두른다. `code`·`link` 만 주고 `show` 를 안 주면 띠가 없으니
373
+ 그 값들도 설 자리가 없다 — 켜는 스위치를 하나로 둔다. */
374
+ const 띠쓰나 = Boolean(brand?.show);
375
+ const 저작권 = brand?.copyright ?? '© IHAB, Inc. All rights reserved.';
366
376
  const contentClass = cx('word-table-block__content', CUT_CLASS[cutLine], showOutline && 'word-table-block__content--outline', showDivLine && hasPairs && 'word-table-block__content--div-line',
367
377
  /* 번호가 설 자리를 비운다 — 번호가 없으면 비우지 않는다(표가 공연히 낮아지지 않게) */
368
- pageNumber && pageNumberPosition !== 'none' && 'word-table-block__content--page-number');
378
+ pageNumber && pageNumberPosition !== 'none' && 'word-table-block__content--page-number',
379
+ /* 띠가 설 자리를 위아래로 비운다 — 같은 까닭으로 띠가 없으면 안 비운다.
380
+ 바닥 띠가 쪽 번호 자리를 겸하므로 둘이 겹쳐도 아래는 한 번만 비운다(CSS 가 덮어쓴다). */
381
+ 띠쓰나 && 'word-table-block__content--brand');
369
382
  // 빈칸(추가버튼)은 스케일 역보정으로 실제 크기 유지
370
383
  const counterScale = { transform: `scale(${1 / scale})`, transformOrigin: 'center center' };
371
384
  /* **채점 단추는 표와 함께 줄어들면 안 된다.**
@@ -378,7 +391,16 @@ export default function WordTable({ title, titleIcon, disc, className, orientati
378
391
  const 채점되돌림 = Math.max(1, Math.min(1 / scale, (width / cols) / 채점행폭));
379
392
  const grid = (_jsxs("div", { ref: scalerRef, className: "word-table__scaler", style: { height: height * scale }, children: [_jsx("div", {
380
393
  /* title 로 Section 에 감쌀 때는 className 을 바깥 한 곳에만 — 두 곳에 붙어 소비자 스타일이 두 번 걸렸다 */
381
- className: cx('word-table', !wrapped && className), style: { width, height, transform: `scale(${scale})`, transformOrigin: 'top left', marginLeft: offset }, children: _jsxs("div", { ref: 내용ref, className: contentClass, style: { gridTemplateColumns: `repeat(${cols}, 1fr)`, gridTemplateRows: `repeat(${rows}, 1fr)` }, children: [isEdit ? (_jsx(_Fragment, { children: (editCells ?? []).map((cell, i) => (_jsx("div", { className: cx('word-table-block__edit-cell', cell ? 'word-table-block__edit-cell--filled' : 'word-table-block__edit-cell--empty', activeCell === i ? 'word-table-block__edit-cell--active' : ''), style: dotDashStyle(i, cols, rows, cutLine), children: cell ? (_jsxs(_Fragment, { children: [cell.map((e, wi) => (_jsxs(Fragment, { children: [wi > 0 && _jsx("div", { className: "word-table-block__pair-divider" }), _jsx(WordCell, { label: e.word, subLabel: e.pronunciation,
394
+ className: cx('word-table', !wrapped && className), style: { width, height, transform: `scale(${scale})`, transformOrigin: 'top left', marginLeft: offset }, children: _jsxs("div", { ref: 내용ref, className: contentClass, style: {
395
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
396
+ gridTemplateRows: `repeat(${rows}, 1fr)`,
397
+ /* **띠는 표와 함께 줄지 않는다**(사용자 지시 2026-09-30). 종이는 미리보기 상자에
398
+ 맞춰 통째로 줄어드는데(`scale`), 그러면 로고와 글씨가 상자 폭마다 달라 보인다.
399
+ 띠 안의 치수를 배율로 나눠 두면 줄어든 만큼 되돌아와 **화면 크기가 한결같다.**
400
+ 자리를 비우는 여백도 같이 나눠야 띠가 칸을 안 침범한다.
401
+ 배율이 1인 종이(인쇄)에서는 1 이라 아무것도 안 달라진다. */
402
+ ...(띠쓰나 ? { ['--wt-unscale']: String(1 / (scale || 1)) } : {}),
403
+ }, children: [isEdit ? (_jsx(_Fragment, { children: (editCells ?? []).map((cell, i) => (_jsx("div", { className: cx('word-table-block__edit-cell', cell ? 'word-table-block__edit-cell--filled' : 'word-table-block__edit-cell--empty', activeCell === i ? 'word-table-block__edit-cell--active' : ''), style: dotDashStyle(i, cols, rows, cutLine), children: cell ? (_jsxs(_Fragment, { children: [cell.map((e, wi) => (_jsxs(Fragment, { children: [wi > 0 && _jsx("div", { className: "word-table-block__pair-divider" }), _jsx(WordCell, { label: e.word, subLabel: e.pronunciation,
382
404
  // 편집 칸도 구어체 발음을 넘긴다 — 빠져 있어 '구어체 발음'을 켜도 편집 모드에서만 비었다.
383
405
  colloquialLabel: e.colloquialPronunciation, homonum: e.homonum, showWord: show.word, showImage: show.image, showPronunciation: show.pron, showColloquial: show.colloq, buttonSize: buttonSize })] }, wi))), scoring && renderScore(i, cell.map(e => e.word).join(' / ')), onRemoveCell && (_jsx("div", { className: "word-table-block__card-close", children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "x", variant: "transparent-lightGray", useBorder: false, showTooltipInfo: cell.length > 1 ? t('wordPrint.removePair', '대립쌍 빼기') : t('wordPrint.removeWord', '단어 빼기'), ariaLabel: cell.length > 1 ? t('wordPrint.removePair', '대립쌍 빼기') : t('wordPrint.removeWord', '단어 빼기'), onClick: () => onRemoveCell(i) }) }))] })) : (onActivateCell && (_jsx("div", { style: counterScale, children: _jsx(Button, { htmlType: "button", size: "48", type: "bar", icon: "plus", label: t('wordTable.addWord', '추가하기'), onClick: () => onActivateCell(i) }) }))) }, i))) })) : (_jsxs(_Fragment, { children: [(items ?? []).map((item, index) => (_jsxs("div", { "data-wordtable-cell": onReorder ? index : undefined, className: `word-table-block__card${onReorder ? ' word-table-block__card--draggable' : ''}${dragOver === index ? ' word-table-block__card--drag-over' : ''}${끌기 === index ? ' word-table-block__card--dragging' : ''}`, style: dotDashStyle(index, cols, rows, cutLine), draggable: !!onReorder, onPointerDown: onReorder ? (e) => 길게눌러끌기(e, index) : undefined, onKeyDown: onReorder ? (e) => 키로옮기기(e, index) : undefined, onDragStart: onReorder ? () => { dragIndex.current = index; } : undefined, onDragOver: onReorder ? (e) => { e.preventDefault(); setDragOver(index); } : undefined, onDragLeave: onReorder ? () => setDragOver(null) : undefined, onDrop: onReorder ? (e) => { e.preventDefault(); if (dragIndex.current !== null && dragIndex.current !== index) {
384
406
  onReorder(dragIndex.current, index);
@@ -397,7 +419,7 @@ export default function WordTable({ title, titleIcon, disc, className, orientati
397
419
  _jsxs("div", { className: "word-table-block__card-actions", children: [renderCellAction?.(index, item), onRemoveItem && (_jsx("div", { className: "word-table-block__card-close", children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "x", variant: "transparent-lightGray", useBorder: false, showTooltipInfo: (item.words ?? []).length > 1 ? t('wordPrint.removePair', '대립쌍 빼기') : t('wordPrint.removeWord', '단어 빼기'), ariaLabel: (item.words ?? []).length > 1 ? t('wordPrint.removePair', '대립쌍 빼기') : t('wordPrint.removeWord', '단어 빼기'), onClick: () => onRemoveItem(item.id) }) }))] }))] }, item.id))), Array.from({ length: Math.max(0, cols * rows - (items?.length ?? 0)) }).map((_, i) => {
398
420
  const isWithinCount = (items?.length ?? 0) + i < cellCount;
399
421
  return (_jsx("div", { className: "word-table-block__card word-table-block__card--filler", children: showFillerAdd && onActivateCell && isWithinCount && (_jsx("div", { style: counterScale, children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "bar", icon: "plus", label: t('wordTable.addWord', '추가하기'), onClick: () => onActivateCell((items?.length ?? 0) + i) }) })) }, `filler-${i}`));
400
- })] })), pageNumber && pageNumberPosition !== 'none' && (_jsx("span", { className: `word-table__page-number body-3 word-table__page-number--${pageNumberPosition}`, children: t('wordTable.filter.pageIndicator', { current: pageNumber.current, total: pageNumber.total }) }))] }) }), onReorder && _jsx("span", { role: "status", style: 보이지않게, children: 옮김알림 })] }));
422
+ })] })), 띠쓰나 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "word-table__band word-table__band--top", children: [_jsx("img", { className: "word-table__brand-logo", src: 말귀로고, alt: "\uB9D0\uADC0" }), code && _jsx("span", { className: "word-table__code", children: code })] }), _jsxs("div", { className: "word-table__band word-table__band--bottom", children: [_jsxs("span", { className: "word-table__brand-by", children: [_jsx("img", { className: "word-table__brand-logo word-table__brand-logo--small", src: ihab로고, alt: "IHAB" }), _jsx("span", { className: "word-table__copyright", children: 저작권 })] }), _jsxs("span", { className: "word-table__band-end", children: [brand?.link && _jsx("span", { className: "word-table__link", children: brand.link }), pageNumber && pageNumberPosition !== 'none' && (_jsx("span", { className: "word-table__page-count", children: t('wordTable.filter.pageIndicator', { current: pageNumber.current, total: pageNumber.total }) }))] })] })] })), !띠쓰나 && pageNumber && pageNumberPosition !== 'none' && (_jsx("span", { className: `word-table__page-number body-3 word-table__page-number--${pageNumberPosition}`, children: t('wordTable.filter.pageIndicator', { current: pageNumber.current, total: pageNumber.total }) }))] }) }), onReorder && _jsx("span", { role: "status", style: 보이지않게, children: 옮김알림 })] }));
401
423
  /* 담는 틀 — 높이를 받은 자리에서만 두른다. `useFitScale` 은 **스케일러의 부모**를 재므로
402
424
  이 틀이 곧 그 자(척도)가 된다. 가운데 맞춤은 남는 세로를 위아래로 나눈다 — 표가 상자보다
403
425
  납작할 때 아래로만 쏠려 보이지 않게. */
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export declare const CORE_VERSION = "0.212.0";
49
+ export declare const CORE_VERSION = "0.213.0";
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export const CORE_VERSION = '0.212.0';
49
+ export const CORE_VERSION = '0.213.0';