@ihabdevteam/core 0.220.0 → 0.221.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.
@@ -9796,6 +9796,13 @@ export declare interface UseWordTablePrintOptions {
9796
9796
  onPopupBlocked?: () => void;
9797
9797
  /** 종이 맨 위에 찍을 한 줄(누구의·언제·무슨 표). 인쇄되는 것은 격자뿐이라
9798
9798
  * 이것이 없으면 뽑아 둔 종이에 아무 단서도 안 남는다. */
9799
+ /**
9800
+ * 쪽마다 종이 **맨 위**에 찍는 한 줄(누구의 · 언제 · 무슨 표).
9801
+ *
9802
+ * **띠(`brand`)를 쓰면 대개 이것은 안 준다** — 띠 위 오른쪽이 표 이름·기관·번호를
9803
+ * 이미 싣는다. 둘 다 주면 번호가 종이에 **두 번** 찍힌다(말귀가 그 잔재를 걷었다,
9804
+ * 2026-09-30). 띠를 안 쓰는 종이(채점지 등)에는 이 줄이 유일한 식별자다.
9805
+ */
9799
9806
  header?: string;
9800
9807
  }
9801
9808
 
@@ -11,6 +11,13 @@ export interface UseWordTablePrintOptions {
11
11
  onPopupBlocked?: () => void;
12
12
  /** 종이 맨 위에 찍을 한 줄(누구의·언제·무슨 표). 인쇄되는 것은 격자뿐이라
13
13
  * 이것이 없으면 뽑아 둔 종이에 아무 단서도 안 남는다. */
14
+ /**
15
+ * 쪽마다 종이 **맨 위**에 찍는 한 줄(누구의 · 언제 · 무슨 표).
16
+ *
17
+ * **띠(`brand`)를 쓰면 대개 이것은 안 준다** — 띠 위 오른쪽이 표 이름·기관·번호를
18
+ * 이미 싣는다. 둘 다 주면 번호가 종이에 **두 번** 찍힌다(말귀가 그 잔재를 걷었다,
19
+ * 2026-09-30). 띠를 안 쓰는 종이(채점지 등)에는 이 줄이 유일한 식별자다.
20
+ */
14
21
  header?: string;
15
22
  }
16
23
  /** @public */
@@ -199,6 +199,43 @@ export function captureHtml(els, paperW, paperH, autoPrint = false, fileName = '
199
199
  scale();
200
200
  window.addEventListener('resize', scale);
201
201
 
202
+ /* **칸 글씨를 이 종이 칸에 맞춰 다시 센다.**
203
+ 화면의 칸은 'ResizeObserver' 로 재서 'transform: scale(n)' 을 칸 안쪽에 직접 써
204
+ 둔다. 그 값이 클론을 타고 여기까지 따라오는데, 여기 칸은 화면 칸이 아니다 —
205
+ 종이(paper-wrap)는 제 치수를 갖고, 인쇄가 걸리면 다시 100% x 100vh 로 바뀐다.
206
+ 사람이 인쇄 대화상자에서 A4 아닌 종이를 고르면 그 100vh 가 또 달라진다. 굳은
207
+ 배율을 그대로 쓰면 화면에서 칸을 꽉 채우던 글씨가 종이에서 작아진다(Letter 가로
208
+ 실측 -3.4%, 사용자 신고 2026-09-30).
209
+ 그래서 칸마다 새겨 둔 기준값(data-ref-w/h)으로 **화면과 똑같은 셈**을 여기서 다시
210
+ 한다 — 'Math.min(w/refW, h/refH)', 1 을 넘겨도 천장을 안 씌운다(2026-09-22 결정).
211
+ 'clientWidth' 는 배치 좌표라 종이에 걸린 'transform: scale()' 에 안 흔들린다. */
212
+ function refit() {
213
+ var cells = document.querySelectorAll('.word-table-cell[data-ref-w]');
214
+ Array.prototype.forEach.call(cells, function (cell) {
215
+ var inner = cell.querySelector('.word-table-cell__inner');
216
+ if (!inner) return;
217
+ var rw = parseFloat(cell.getAttribute('data-ref-w'));
218
+ var rh = parseFloat(cell.getAttribute('data-ref-h'));
219
+ if (!(rw > 0) || !(rh > 0)) return;
220
+ var cs = getComputedStyle(cell);
221
+ var w = cell.clientWidth - parseFloat(cs.paddingLeft || 0) - parseFloat(cs.paddingRight || 0);
222
+ var h = cell.clientHeight - parseFloat(cs.paddingTop || 0) - parseFloat(cs.paddingBottom || 0);
223
+ /* 아직 자리를 못 잡은 칸(0)은 건드리지 않는다 — 화면이 준 배율이 0 보다는 낫다 */
224
+ if (!(w > 0) || !(h > 0)) return;
225
+ inner.style.transform = 'scale(' + Math.min(w / rw, h / rh) + ')';
226
+ });
227
+ }
228
+ refit();
229
+ window.addEventListener('resize', refit);
230
+ /* 인쇄 배치로 바뀌는 순간을 브라우저마다 다른 신호로 알린다 — 크롬은 beforeprint,
231
+ 파이어폭스는 print 매체 질의가 먼저 켜진다. 둘 다 걸어 두고 두 번 세도 값이 같다. */
232
+ window.addEventListener('beforeprint', refit);
233
+ try {
234
+ var mq = window.matchMedia('print');
235
+ if (mq.addEventListener) mq.addEventListener('change', refit);
236
+ else if (mq.addListener) mq.addListener(refit);
237
+ } catch (e) { /* matchMedia 가 없는 환경 — 위 둘로 충분하다 */ }
238
+
202
239
  function resetWrap(w) {
203
240
  var pp = w.querySelector('[data-play-state="play"]');
204
241
  var pa = w.querySelector('[data-play-state="pause"]');
@@ -246,6 +283,8 @@ export function captureHtml(els, paperW, paperH, autoPrint = false, fileName = '
246
283
  if (printed) return;
247
284
  printed = true;
248
285
  requestAnimationFrame(function () {
286
+ /* 그림·글꼴이 다 온 뒤 한 번 더 — 그림이 늦게 오면 칸 높이가 달라진다 */
287
+ refit();
249
288
  requestAnimationFrame(function () { window.print(); });
250
289
  });
251
290
  }
@@ -160,7 +160,7 @@ function WordCell({ label, subLabel, colloquialLabel, homonum, showWord = true,
160
160
  obs.observe(el);
161
161
  return () => obs.disconnect();
162
162
  }, [refW, refH]);
163
- return (_jsxs("div", { ref: outerRef, className: cx('word-table-cell', hasImage ? 'word-table-cell--with-image' : ''), children: [_jsxs("div", { className: "word-table-cell__inner", style: { transform: `scale(${scale})` }, children: [hasImage && (_jsxs("div", { className: "word-table-cell__image-wrap", children: [imgLoading && _jsx("div", { className: "word-table-cell__spinner" }), _jsx("img", { ref: imgRef, className: "word-table-cell__image", src: imageUrl, alt: label, draggable: false, style: { opacity: imgLoading ? 0 : 1 }, onLoad: handleImgLoad, onError: handleImgError })] })), showWord && _jsx("span", { className: "word-table-cell__word h0", children: label }), showPronunciation && pron && _jsxs("span", { className: "word-table-cell__sub h3", children: ["[", pron, "]"] }), showColloquial && colloquialLabel && _jsxs("span", { className: "word-table-cell__colloquial h5", children: ["[", colloquialLabel, "]"] })] }), _jsxs("div", { className: "word-table-cell__play-button", "data-audio-src": audioUrl, children: [_jsx("span", { "data-play-state": "play", style: { display: playing ? 'none' : undefined }, children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "play", variant: "transparent-lightGray", useBorder: false, showTooltipInfo: t('common.play', '재생'), onClick: handlePlay }) }), _jsx("span", { "data-play-state": "pause", style: { display: playing ? undefined : 'none' }, children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "pause", useBorder: false, selected: true, showTooltipInfo: t('common.pause', '멈춤'), onClick: handlePlay }) })] }), wordInfo && (_jsxs("div", { ref: infoRef, className: "word-table-cell__info-button", onPointerEnter: (e) => { if (!isTouchPointer(e))
163
+ return (_jsxs("div", { ref: outerRef, className: cx('word-table-cell', hasImage ? 'word-table-cell--with-image' : ''), "data-ref-w": refW, "data-ref-h": refH, children: [_jsxs("div", { className: "word-table-cell__inner", style: { transform: `scale(${scale})` }, children: [hasImage && (_jsxs("div", { className: "word-table-cell__image-wrap", children: [imgLoading && _jsx("div", { className: "word-table-cell__spinner" }), _jsx("img", { ref: imgRef, className: "word-table-cell__image", src: imageUrl, alt: label, draggable: false, style: { opacity: imgLoading ? 0 : 1 }, onLoad: handleImgLoad, onError: handleImgError })] })), showWord && _jsx("span", { className: "word-table-cell__word h0", children: label }), showPronunciation && pron && _jsxs("span", { className: "word-table-cell__sub h3", children: ["[", pron, "]"] }), showColloquial && colloquialLabel && _jsxs("span", { className: "word-table-cell__colloquial h5", children: ["[", colloquialLabel, "]"] })] }), _jsxs("div", { className: "word-table-cell__play-button", "data-audio-src": audioUrl, children: [_jsx("span", { "data-play-state": "play", style: { display: playing ? 'none' : undefined }, children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "play", variant: "transparent-lightGray", useBorder: false, showTooltipInfo: t('common.play', '재생'), onClick: handlePlay }) }), _jsx("span", { "data-play-state": "pause", style: { display: playing ? undefined : 'none' }, children: _jsx(Button, { htmlType: "button", size: buttonSize, type: "dot", icon: "pause", useBorder: false, selected: true, showTooltipInfo: t('common.pause', '멈춤'), onClick: handlePlay }) })] }), wordInfo && (_jsxs("div", { ref: infoRef, className: "word-table-cell__info-button", onPointerEnter: (e) => { if (!isTouchPointer(e))
164
164
  handleInfoEnter(); }, onPointerLeave: (e) => { if (!isTouchPointer(e))
165
165
  handleInfoLeave(); },
166
166
  // 이 div 자체는 포커스 대상이 아니지만 안의 Button이 포커스를 받으면
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihabdevteam/core",
3
- "version": "0.220.0",
3
+ "version": "0.221.0",
4
4
  "private": false,
5
5
  "main": "dist/ihabdevteam-core.cjs.js",
6
6
  "module": "dist/packages/core/src/index.js",