@iyulab/flex-table 0.44.0 → 0.45.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.45.0] - 2026-10-05
4
+
5
+ ### Added
6
+
7
+ - **Press targets follow `--u-target-size`** (the host's minimum target size from `@iyulab/components`).
8
+ When it is set, rows (and so the header row) are at least that tall — it wins over `row-height` and
9
+ `--ft-row-height`, because it is a floor — and the row-selection column is at least that wide. Column
10
+ menu buttons, menu items, filter fields and the find panel's fields and buttons follow too. Unset,
11
+ nothing changes. The column resize handle and the hidden-column marker keep their size: the column menu
12
+ does the same jobs (Wider · Narrower · Fit to content · Show …) with full-size targets.
13
+
14
+ ### Changed
15
+
16
+ - **The whole row-selection cell toggles the checkbox**, not just the 16px box (it is now a label). The box
17
+ itself looks the same.
18
+
19
+ ### Fixed
20
+
21
+ - **The column filter dropdown is no longer cut off by the table.** It was positioned inside the table's
22
+ scroll area, so in a short table its lower controls were hidden and could not be pressed. It now floats
23
+ like the column menu does, below the header (or above it when there is no room).
24
+
3
25
  ## [0.44.0] - 2026-10-05
4
26
 
5
27
  ### Added
@@ -467,8 +467,9 @@ var y = r`
467
467
  display: inline-flex;
468
468
  align-items: center;
469
469
  justify-content: center;
470
- inline-size: 24px;
471
- block-size: 24px;
470
+ /* 호스트 하한(--u-target-size)이 있으면 그 값 — 미설정이면 24px. */
471
+ inline-size: max(24px, var(--u-target-size, 0px));
472
+ block-size: max(24px, var(--u-target-size, 0px));
472
473
  margin-left: auto;
473
474
  flex-shrink: 0;
474
475
  border-radius: 4px;
@@ -496,22 +497,23 @@ var y = r`
496
497
  opacity: 1;
497
498
  }
498
499
 
500
+ /* 표 밖에 띄운다 — 자리는 머리 칸에 맞춰 스크립트가 정한다(_adjustFilterDropdown). 표의 스크롤 영역에 잘리지 않는다. */
499
501
  .ft-filter-dropdown {
500
- position: absolute;
501
- top: 100%;
502
- left: 0;
502
+ position: fixed;
503
503
  min-width: 200px;
504
504
  background: var(--ft-bg);
505
505
  border: 1px solid var(--ft-border-color);
506
506
  border-radius: 4px;
507
507
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
508
508
  padding: 8px;
509
- z-index: 10;
509
+ z-index: var(--u-layer-floating, 1000);
510
510
  box-sizing: border-box;
511
511
  }
512
512
 
513
513
  .ft-filter-input {
514
514
  width: 100%;
515
+ /* 호스트 하한(--u-target-size, 미설정 = 0) — 아래 필터·찾기 패널의 입력·버튼도 같다. */
516
+ min-height: var(--u-target-size, 0px);
515
517
  padding: 4px 8px;
516
518
  font: inherit;
517
519
  font-size: 13px;
@@ -547,6 +549,12 @@ var y = r`
547
549
  color: var(--ft-sort-indicator-color);
548
550
  padding: 2px 8px;
549
551
  border-radius: 3px;
552
+ box-sizing: border-box;
553
+ display: inline-flex;
554
+ align-items: center;
555
+ justify-content: center;
556
+ min-height: var(--u-target-size, 0px);
557
+ min-width: var(--u-target-size, 0px);
550
558
  }
551
559
 
552
560
  .ft-filter-clear:hover {
@@ -562,6 +570,9 @@ var y = r`
562
570
 
563
571
  .ft-filter-mode-select {
564
572
  flex: 1;
573
+ box-sizing: border-box;
574
+ min-height: var(--u-target-size, 0px);
575
+ min-width: var(--u-target-size, 0px);
565
576
  padding: 3px 6px;
566
577
  font: inherit;
567
578
  font-size: 12px;
@@ -616,7 +627,7 @@ var y = r`
616
627
  .ft-checkbox-header {
617
628
  background: var(--ft-header-bg);
618
629
  border-bottom: 2px solid var(--ft-border-color);
619
- }
630
+ }\n .ft-checkbox-header .ft-checkbox-hit {\n margin-block-end: -2px;\n }
620
631
 
621
632
  .ft-checkbox-cell {
622
633
  border-bottom: 1px solid var(--ft-border-color);
@@ -630,6 +641,19 @@ var y = r`
630
641
  .ft-row-selected .ft-cell { background: var(--ft-selection-bg) !important; }
631
642
  .ft-row-selected .ft-row-num { background: var(--ft-selection-bg); }
632
643
 
644
+ /* 누르는 자리는 칸 전체다(라벨이 입력을 토글한다) — 체크 상자는 16px 그대로. 칸의 좌우 여백(4px)까지 덮는다. */
645
+ .ft-checkbox-hit {
646
+ flex: 1;
647
+ align-self: stretch;
648
+ margin-inline: -4px;
649
+ /* 칸 아래 경계선(본문 1px · 머리 2px)까지 — 행 높이가 곧 누르는 높이가 되게. */
650
+ margin-block-end: -1px;
651
+ display: flex;
652
+ align-items: center;
653
+ justify-content: center;
654
+ cursor: pointer;
655
+ }
656
+
633
657
  .ft-checkbox-header input[type="checkbox"],
634
658
  .ft-checkbox-cell input[type="checkbox"] {
635
659
  width: 16px;
@@ -679,7 +703,7 @@ var y = r`
679
703
  all: unset;
680
704
  box-sizing: border-box;
681
705
  display: block;
682
- min-block-size: 28px;
706
+ min-block-size: max(28px, var(--u-target-size, 0px));
683
707
  padding: 6px 12px;
684
708
  cursor: pointer;
685
709
  font-size: var(--ft-font-size, 14px);
@@ -743,7 +767,7 @@ var y = r`
743
767
  box-sizing: border-box;
744
768
  display: block;
745
769
  inline-size: 100%;
746
- min-block-size: 28px;
770
+ min-block-size: max(28px, var(--u-target-size, 0px));
747
771
  padding: 6px 14px;
748
772
  cursor: pointer;
749
773
  font-size: var(--ft-font-size, 14px);
@@ -859,6 +883,8 @@ var y = r`
859
883
  .ft-find-input, .ft-find-replace-input {
860
884
  flex: 1;
861
885
  min-width: 120px;
886
+ /* content-box 다(최소 폭 120px 의 뜻을 바꾸지 않으려고) — 여백 3px ×2 와 테두리 1px ×2 를 뺀다. */
887
+ min-height: max(0px, calc(var(--u-target-size, 0px) - 8px));
862
888
  padding: 3px 6px;
863
889
  border: 1px solid var(--ft-border-color);
864
890
  border-radius: 3px;
@@ -881,6 +907,9 @@ var y = r`
881
907
  }
882
908
 
883
909
  .ft-find-panel button {
910
+ box-sizing: border-box;
911
+ min-height: var(--u-target-size, 0px);
912
+ min-width: var(--u-target-size, 0px);
884
913
  padding: 2px 8px;
885
914
  border: 1px solid var(--ft-border-color);
886
915
  border-radius: 3px;
@@ -902,6 +931,8 @@ var y = r`
902
931
  .ft-find-panel label {
903
932
  display: flex;
904
933
  align-items: center;
934
+ min-height: var(--u-target-size, 0px);
935
+ min-width: var(--u-target-size, 0px);
905
936
  gap: 2px;
906
937
  font-size: 12px;
907
938
  cursor: pointer;
@@ -1790,7 +1821,7 @@ var q = {
1790
1821
  lte: "≤"
1791
1822
  }, J = 120, Y = 40, X = 20, Z = 32, Q = 5, Je = 200, $ = class extends n {
1792
1823
  constructor(...e) {
1793
- super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.emptyMessage = "No data", this.noMatchingMessage = "No matching data", this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new re(), this._editing = new ie(), this._rowSelection = new j(), this._undo = new M(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowIndex = null, this._checkboxShiftPending = !1, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._wasHeaderDrag = !1, this._wasRowDrag = !1, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._warnedUnconstrained = !1, this._onCommentPopupOutsideClick = () => {
1824
+ super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this._targetFloor = 0, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.emptyMessage = "No data", this.noMatchingMessage = "No matching data", this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new re(), this._editing = new ie(), this._rowSelection = new j(), this._undo = new M(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowIndex = null, this._checkboxShiftPending = !1, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._wasHeaderDrag = !1, this._wasRowDrag = !1, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._warnedUnconstrained = !1, this._onCommentPopupOutsideClick = () => {
1794
1825
  this._commitCommentPopup();
1795
1826
  }, this._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map(), this._emptyFilterState = /* @__PURE__ */ new Map();
1796
1827
  }
@@ -1805,7 +1836,8 @@ var q = {
1805
1836
  this._data = e, n && !this._inUndoRedo && (this.clearUndoOnDataChange && this._undo.clear(), this.clearSelectionOnDataChange && this._rowSelection.selectedCount > 0 && (this._rowSelection.deselectAll(), this._rowSelectionVersion++, this._dispatchRowSelectionEvent())), this.requestUpdate("data", t);
1806
1837
  }
1807
1838
  get rowHeight() {
1808
- return this._rowHeightExplicit ?? this._rowHeightFromCss ?? Z;
1839
+ let e = this._rowHeightExplicit ?? this._rowHeightFromCss ?? Z;
1840
+ return Math.max(e, this._targetFloor);
1809
1841
  }
1810
1842
  set rowHeight(e) {
1811
1843
  let t = this.rowHeight;
@@ -1826,9 +1858,12 @@ var q = {
1826
1858
  get visibleColumns() {
1827
1859
  return this.columns.filter((e) => !e.hidden);
1828
1860
  }
1861
+ get _checkboxColWidth() {
1862
+ return Math.max(36, this._targetFloor);
1863
+ }
1829
1864
  get _prefixWidth() {
1830
1865
  let e = 0;
1831
- return this.selectable && (e += 36), this.showRowNumbers && (e += 48), e;
1866
+ return this.selectable && (e += this._checkboxColWidth), this.showRowNumbers && (e += 48), e;
1832
1867
  }
1833
1868
  get canUndo() {
1834
1869
  return this._undo.canUndo;
@@ -2372,12 +2407,11 @@ var q = {
2372
2407
  this.shadowRoot && Array.isArray(this.shadowRoot.adoptedStyleSheets) && (this.shadowRoot.adoptedStyleSheets = [...this._baseStyleSheets, ...this.stylesheets]);
2373
2408
  }
2374
2409
  _readDensityTokens() {
2375
- let e = getComputedStyle(this).getPropertyValue("--ft-row-height").trim();
2376
- if (!e) return;
2377
- let t = parseFloat(e);
2378
- if (!Number.isFinite(t) || t <= 0 || !/px\s*$/.test(e) || t === this._rowHeightFromCss) return;
2379
- let n = this.rowHeight;
2380
- this._rowHeightFromCss = t, this._rowHeightExplicit === void 0 && this.requestUpdate("rowHeight", n);
2410
+ let e = getComputedStyle(this), t = (t) => {
2411
+ let n = e.getPropertyValue(t).trim(), r = parseFloat(n);
2412
+ return n && Number.isFinite(r) && r > 0 && /px\s*$/.test(n) ? r : void 0;
2413
+ }, n = this.rowHeight, r = t("--ft-row-height");
2414
+ r !== void 0 && (this._rowHeightFromCss = r), this._targetFloor = t("--u-target-size") ?? 0, this.rowHeight !== n && this.requestUpdate("rowHeight", n);
2381
2415
  }
2382
2416
  _updateColOffsets() {
2383
2417
  let e = this.visibleColumns, t = [], n = this._prefixWidth;
@@ -3102,7 +3136,7 @@ var q = {
3102
3136
  }
3103
3137
  _getPinnedLeft(e) {
3104
3138
  let t = this.visibleColumns, n = 0;
3105
- this.selectable && (n += 36), this.showRowNumbers && (n += 48);
3139
+ this.selectable && (n += this._checkboxColWidth), this.showRowNumbers && (n += 48);
3106
3140
  for (let r = 0; r < e; r++) t[r].pinned === "left" && (n += this._getColWidth(t[r]));
3107
3141
  return n;
3108
3142
  }
@@ -3389,11 +3423,12 @@ var q = {
3389
3423
  }
3390
3424
  _adjustFilterDropdown() {
3391
3425
  if (!this._openFilterKey) return;
3392
- let e = this.shadowRoot?.querySelector(".ft-filter-dropdown");
3393
- if (!e) return;
3394
- e.style.top = "", e.style.bottom = "";
3395
- let t = e.getBoundingClientRect(), n = window.innerHeight;
3396
- t.bottom > n && (e.style.top = "auto", e.style.bottom = "100%");
3426
+ let e = this.shadowRoot?.querySelector(".ft-filter-dropdown"), t = e?.previousElementSibling;
3427
+ if (!e || !t) return;
3428
+ let n = t.getBoundingClientRect(), r = e.getBoundingClientRect(), i = document.documentElement.clientWidth, a = document.documentElement.clientHeight, o = n.bottom;
3429
+ o + r.height > a - 4 && (o = n.top - r.height >= 4 ? n.top - r.height : Math.max(4, a - r.height - 4));
3430
+ let s = Math.max(4, Math.min(n.left, i - r.width - 4));
3431
+ e.style.left = `${s}px`, e.style.top = `${o}px`;
3397
3432
  }
3398
3433
  _renderFilterDropdown(e) {
3399
3434
  let t = e.type ?? "text";
@@ -4132,15 +4167,16 @@ var q = {
4132
4167
  let r = this.headerHeight, o = this._totalRowWidth, { start: s, end: c } = this.visibleColRange, l = [];
4133
4168
  for (let t = 0; t < e.length; t++) (e[t].pinned === "left" || e[t].pinned === "right") && (t < s || t >= c) && l.push(t);
4134
4169
  let u = 0, d = this._scrollLeft, f = this.selectable ? i`<div class="ft-checkbox-header"
4135
- style="position: absolute; top: 0; left: ${d + u}px; width: 36px; height: ${r}px; z-index: 4;">
4170
+ style="position: absolute; top: 0; left: ${d + u}px; width: ${this._checkboxColWidth}px; height: ${r}px; z-index: 4;">
4136
4171
  ${this._rowSelection.mode === "multi" ? i`
4137
- <input type="checkbox"
4172
+ <!-- 칸 전체가 누르는 자리다(라벨) — 체크 상자는 16px 그대로. -->
4173
+ <label class="ft-checkbox-hit"><input type="checkbox"
4138
4174
  .checked=${this._rowSelection.isAllSelected}
4139
4175
  .indeterminate=${this._rowSelection.isSomeSelected}
4140
- @change=${this._onSelectAllChange}>
4176
+ @change=${this._onSelectAllChange}></label>
4141
4177
  ` : ""}
4142
4178
  </div>` : "";
4143
- this.selectable && (u += 36);
4179
+ this.selectable && (u += this._checkboxColWidth);
4144
4180
  let p = this.showRowNumbers ? i`<div class="ft-row-num-header"
4145
4181
  style="position: absolute; top: 0; left: ${d + u}px; width: 48px; height: ${r}px; z-index: 4;">#</div>` : "", m = [];
4146
4182
  for (let t of l) m.push(this._renderHeaderCell(e[t], t));
@@ -4205,8 +4241,8 @@ var q = {
4205
4241
  _renderFooter(e, t, n) {
4206
4242
  if (!this.footerData) return "";
4207
4243
  let r = this.visibleColumns, a = this.rowHeight, o = this._totalRowWidth, s = this._scrollLeft, c = 0, l = this.selectable ? i`<div class="ft-footer-cell ft-checkbox-cell"
4208
- style="position: absolute; top: 0; left: ${s + c}px; width: 36px; height: ${a}px; z-index: 2;"></div>` : "";
4209
- this.selectable && (c += 36);
4244
+ style="position: absolute; top: 0; left: ${s + c}px; width: ${this._checkboxColWidth}px; height: ${a}px; z-index: 2;"></div>` : "";
4245
+ this.selectable && (c += this._checkboxColWidth);
4210
4246
  let u = this.showRowNumbers ? i`<div class="ft-footer-cell ft-row-num"
4211
4247
  style="position: absolute; top: 0; left: ${s + c}px; width: 48px; height: ${a}px; z-index: 2;"></div>` : "", d = [];
4212
4248
  for (let e of n) {
@@ -4233,14 +4269,14 @@ var q = {
4233
4269
  _renderRow(e, t, n, r, a) {
4234
4270
  let o = this.visibleColumns, s = this._toDataIndex(e), c = this.data[s], l = a ?? e * this.rowHeight, u = this.rowHeight, d = this._totalRowWidth, f = e % 2 == 0 ? "ft-row-even" : "ft-row-odd", p = this.selectable && this._rowSelection.isSelected(e), m = this._scrollLeft, h = 0, g = this.selectable ? i`
4235
4271
  <div class="ft-checkbox-cell"
4236
- style="position: absolute; top: 0; left: ${m + h}px; width: 36px; height: ${u}px; z-index: 2;">
4237
- <input type="checkbox"
4272
+ style="position: absolute; top: 0; left: ${m + h}px; width: ${this._checkboxColWidth}px; height: ${u}px; z-index: 2;">
4273
+ <label class="ft-checkbox-hit"><input type="checkbox"
4238
4274
  .checked=${p}
4239
4275
  @click=${(e) => this._onRowCheckboxClick(e)}
4240
- @change=${(t) => this._onRowCheckboxChange(t, e)}>
4276
+ @change=${(t) => this._onRowCheckboxChange(t, e)}></label>
4241
4277
  </div>
4242
4278
  ` : "";
4243
- this.selectable && (h += 36);
4279
+ this.selectable && (h += this._checkboxColWidth);
4244
4280
  let _ = this._rowDrag?.active && this._rowDrag.rowIndex === e, v = this.showRowNumbers ? i`
4245
4281
  <div class="ft-row-num ${_ ? "ft-col-dragging" : ""}"
4246
4282
  style="position: absolute; top: 0; left: ${m + h}px; width: 48px; height: ${u}px; z-index: 2; cursor: grab;"
@@ -52,6 +52,8 @@ export declare class FlexTable extends LitElement {
52
52
  private _rowHeightExplicit?;
53
53
  /** `--ft-row-height` 판독 결과. 판독 전·판독 실패 시 기본값. */
54
54
  private _rowHeightFromCss;
55
+ /** 호스트 하한 `--u-target-size`(px) 판독 결과 — 행(그러므로 머리행)과 선택 열 폭의 하한. 미설정이면 0. */
56
+ private _targetFloor;
55
57
  showRowNumbers: boolean;
56
58
  theme: 'light' | 'dark' | undefined;
57
59
  maxRows: number;
@@ -147,6 +149,8 @@ export declare class FlexTable extends LitElement {
147
149
  private _comments;
148
150
  private _isDragOver;
149
151
  get visibleColumns(): ColumnDefinition[];
152
+ /** 행 선택 열의 폭 — 36px, 호스트 하한(`--u-target-size`)이 더 크면 그 값. */
153
+ private get _checkboxColWidth();
150
154
  private get _prefixWidth();
151
155
  /** Whether an undo operation is available. */
152
156
  get canUndo(): boolean;
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-MnSI0Ogl.js";
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-Bv41ls8b.js";
2
2
  export { o as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, r as flexTableLocale, a as renderCell };
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./flex-table-MnSI0Ogl.js";
1
+ import { t as e } from "./flex-table-Bv41ls8b.js";
2
2
  import { i as t, t as n } from "./query-Dw4iO0ir.js";
3
3
  import { t as r } from "./view-DiLr-TdY.js";
4
4
  import i, { useCallback as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",