@ncds/ui-admin 1.8.4 → 1.8.6

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.
Files changed (187) hide show
  1. package/dist/cjs/assets/scripts/featuredIcon.js +87 -0
  2. package/dist/cjs/assets/scripts/notification/FloatingNotification.js +178 -0
  3. package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +133 -0
  4. package/dist/cjs/assets/scripts/notification/MessageNotification.js +159 -0
  5. package/dist/cjs/assets/scripts/notification/Notification.js +120 -0
  6. package/dist/cjs/assets/scripts/notification/const/classNames.js +50 -0
  7. package/dist/cjs/assets/scripts/notification/const/icons.js +31 -0
  8. package/dist/cjs/assets/scripts/notification/const/index.js +87 -0
  9. package/dist/cjs/assets/scripts/notification/const/sizes.js +46 -0
  10. package/dist/cjs/assets/scripts/notification/const/types.js +14 -0
  11. package/dist/cjs/assets/scripts/notification/index.js +116 -0
  12. package/dist/cjs/assets/scripts/notification/positionSync.js +180 -0
  13. package/dist/cjs/assets/scripts/notification/utils.js +122 -0
  14. package/dist/cjs/assets/scripts/shared/ButtonCloseX.js +45 -0
  15. package/dist/cjs/assets/scripts/utils/sanitize.js +39 -0
  16. package/dist/cjs/src/components/data-display/data-grid/DataGrid.js +5 -1
  17. package/dist/cjs/src/components/data-display/table/Table.js +118 -96
  18. package/dist/cjs/src/components/data-display/table/useTableScrollbars.js +187 -0
  19. package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
  20. package/dist/cjs/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
  21. package/dist/cjs/src/components/forms-and-input/select-box/SelectBox.js +67 -29
  22. package/dist/cjs/src/components/forms-and-input/slider/Slider.js +2 -3
  23. package/dist/cjs/src/components/overlays/dropdown/Dropdown.js +47 -19
  24. package/dist/cjs/src/components/overlays/notification/CalloutNotification.js +25 -0
  25. package/dist/cjs/src/components/overlays/notification/FloatingNotification.js +86 -13
  26. package/dist/cjs/src/components/overlays/notification/Notification.js +7 -0
  27. package/dist/cjs/src/components/overlays/notification/host.js +12 -0
  28. package/dist/cjs/src/components/overlays/tooltip/Tooltip.js +57 -44
  29. package/dist/cjs/src/components/select-dropdown/SelectDropdown.js +2 -1
  30. package/dist/cjs/src/contexts/FloatingContext.js +11 -0
  31. package/dist/cjs/src/contexts/index.js +16 -0
  32. package/dist/cjs/src/hooks/index.js +11 -0
  33. package/dist/cjs/src/hooks/useFloatingPosition.js +78 -0
  34. package/dist/cjs/src/hooks/usePortalState.js +17 -0
  35. package/dist/cjs/src/types/component-meta.js +8 -1
  36. package/dist/cjs/src/utils/dropdown/maxSelection.js +35 -0
  37. package/dist/cjs/src/utils/dropdown/multiSelect.js +72 -15
  38. package/dist/esm/assets/scripts/featuredIcon.js +80 -0
  39. package/dist/esm/assets/scripts/notification/FloatingNotification.js +171 -0
  40. package/dist/esm/assets/scripts/notification/FullWidthNotification.js +126 -0
  41. package/dist/esm/assets/scripts/notification/MessageNotification.js +152 -0
  42. package/dist/esm/assets/scripts/notification/Notification.js +113 -0
  43. package/dist/esm/assets/scripts/notification/const/classNames.js +44 -0
  44. package/dist/esm/assets/scripts/notification/const/icons.js +25 -0
  45. package/dist/esm/assets/scripts/notification/const/index.js +4 -0
  46. package/dist/esm/assets/scripts/notification/const/sizes.js +40 -0
  47. package/dist/esm/assets/scripts/notification/const/types.js +8 -0
  48. package/dist/esm/assets/scripts/notification/index.js +10 -0
  49. package/dist/esm/assets/scripts/notification/positionSync.js +171 -0
  50. package/dist/esm/assets/scripts/notification/utils.js +109 -0
  51. package/dist/esm/assets/scripts/shared/ButtonCloseX.js +37 -0
  52. package/dist/esm/assets/scripts/utils/sanitize.js +31 -0
  53. package/dist/esm/src/components/data-display/data-grid/DataGrid.js +5 -1
  54. package/dist/esm/src/components/data-display/table/Table.js +118 -96
  55. package/dist/esm/src/components/data-display/table/useTableScrollbars.js +179 -0
  56. package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
  57. package/dist/esm/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
  58. package/dist/esm/src/components/forms-and-input/select-box/SelectBox.js +67 -29
  59. package/dist/esm/src/components/forms-and-input/slider/Slider.js +1 -2
  60. package/dist/esm/src/components/overlays/dropdown/Dropdown.js +47 -19
  61. package/dist/esm/src/components/overlays/notification/CalloutNotification.js +19 -0
  62. package/dist/esm/src/components/overlays/notification/FloatingNotification.js +86 -14
  63. package/dist/esm/src/components/overlays/notification/Notification.js +7 -0
  64. package/dist/esm/src/components/overlays/notification/host.js +9 -0
  65. package/dist/esm/src/components/overlays/tooltip/Tooltip.js +58 -45
  66. package/dist/esm/src/components/select-dropdown/SelectDropdown.js +2 -1
  67. package/dist/esm/src/contexts/FloatingContext.js +4 -0
  68. package/dist/esm/src/contexts/index.js +1 -0
  69. package/dist/esm/src/hooks/index.js +1 -0
  70. package/dist/esm/src/hooks/useFloatingPosition.js +71 -0
  71. package/dist/esm/src/hooks/usePortalState.js +10 -0
  72. package/dist/esm/src/types/component-meta.js +5 -1
  73. package/dist/esm/src/utils/dropdown/maxSelection.js +27 -0
  74. package/dist/esm/src/utils/dropdown/multiSelect.js +70 -14
  75. package/dist/temp/assets/scripts/featuredIcon.d.ts +22 -0
  76. package/dist/temp/assets/scripts/featuredIcon.js +79 -0
  77. package/dist/temp/assets/scripts/notification/FloatingNotification.d.ts +24 -0
  78. package/dist/temp/assets/scripts/notification/FloatingNotification.js +156 -0
  79. package/dist/temp/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
  80. package/dist/temp/assets/scripts/notification/FullWidthNotification.js +111 -0
  81. package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +22 -0
  82. package/dist/temp/assets/scripts/notification/MessageNotification.js +140 -0
  83. package/dist/temp/assets/scripts/notification/Notification.d.ts +22 -0
  84. package/dist/temp/assets/scripts/notification/Notification.js +112 -0
  85. package/dist/temp/assets/scripts/notification/const/classNames.d.ts +43 -0
  86. package/dist/temp/assets/scripts/notification/const/classNames.js +44 -0
  87. package/dist/temp/assets/scripts/notification/const/icons.d.ts +25 -0
  88. package/dist/temp/assets/scripts/notification/const/icons.js +25 -0
  89. package/dist/temp/assets/scripts/notification/const/index.d.ts +5 -0
  90. package/dist/temp/assets/scripts/notification/const/index.js +4 -0
  91. package/dist/temp/assets/scripts/notification/const/sizes.d.ts +32 -0
  92. package/dist/temp/assets/scripts/notification/const/sizes.js +40 -0
  93. package/dist/temp/assets/scripts/notification/const/types.d.ts +19 -0
  94. package/dist/temp/assets/scripts/notification/const/types.js +8 -0
  95. package/dist/temp/assets/scripts/notification/index.d.ts +8 -0
  96. package/dist/temp/assets/scripts/notification/index.js +10 -0
  97. package/dist/temp/assets/scripts/notification/positionSync.d.ts +50 -0
  98. package/dist/temp/assets/scripts/notification/positionSync.js +170 -0
  99. package/dist/temp/assets/scripts/notification/utils.d.ts +8 -0
  100. package/dist/temp/assets/scripts/notification/utils.js +115 -0
  101. package/dist/temp/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
  102. package/dist/temp/assets/scripts/shared/ButtonCloseX.js +33 -0
  103. package/dist/temp/assets/scripts/utils/sanitize.d.ts +22 -0
  104. package/dist/temp/assets/scripts/utils/sanitize.js +31 -0
  105. package/dist/temp/src/components/data-display/data-grid/DataGrid.js +1 -1
  106. package/dist/temp/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
  107. package/dist/temp/src/components/data-display/table/Table.d.ts +4 -1
  108. package/dist/temp/src/components/data-display/table/Table.js +53 -68
  109. package/dist/temp/src/components/data-display/table/types.d.ts +18 -0
  110. package/dist/temp/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
  111. package/dist/temp/src/components/data-display/table/useTableScrollbars.js +136 -0
  112. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
  113. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +7 -11
  114. package/dist/temp/src/components/forms-and-input/image-file-input/ImageFileInput.js +1 -1
  115. package/dist/temp/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
  116. package/dist/temp/src/components/forms-and-input/select-box/SelectBox.js +30 -3
  117. package/dist/temp/src/components/forms-and-input/slider/Slider.d.ts +0 -1
  118. package/dist/temp/src/components/forms-and-input/slider/Slider.js +0 -1
  119. package/dist/temp/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
  120. package/dist/temp/src/components/overlays/dropdown/Dropdown.js +35 -11
  121. package/dist/temp/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
  122. package/dist/temp/src/components/overlays/notification/CalloutNotification.js +6 -0
  123. package/dist/temp/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
  124. package/dist/temp/src/components/overlays/notification/FloatingNotification.js +81 -13
  125. package/dist/temp/src/components/overlays/notification/Notification.d.ts +18 -3
  126. package/dist/temp/src/components/overlays/notification/Notification.js +4 -0
  127. package/dist/temp/src/components/overlays/notification/host.d.ts +9 -0
  128. package/dist/temp/src/components/overlays/notification/host.js +9 -0
  129. package/dist/temp/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
  130. package/dist/temp/src/components/overlays/tooltip/Tooltip.js +25 -22
  131. package/dist/temp/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
  132. package/dist/temp/src/components/select-dropdown/SelectDropdown.js +2 -2
  133. package/dist/temp/src/contexts/FloatingContext.d.ts +6 -0
  134. package/dist/temp/src/contexts/FloatingContext.js +4 -0
  135. package/dist/temp/src/contexts/index.d.ts +1 -0
  136. package/dist/temp/src/contexts/index.js +1 -0
  137. package/dist/temp/src/hooks/index.d.ts +1 -0
  138. package/dist/temp/src/hooks/index.js +1 -0
  139. package/dist/temp/src/hooks/useFloatingPosition.d.ts +19 -0
  140. package/dist/temp/src/hooks/useFloatingPosition.js +55 -0
  141. package/dist/temp/src/hooks/usePortalState.d.ts +6 -0
  142. package/dist/temp/src/hooks/usePortalState.js +7 -0
  143. package/dist/temp/src/types/component-meta.d.ts +6 -2
  144. package/dist/temp/src/types/component-meta.js +14 -1
  145. package/dist/temp/src/utils/dropdown/maxSelection.d.ts +24 -0
  146. package/dist/temp/src/utils/dropdown/maxSelection.js +28 -0
  147. package/dist/temp/src/utils/dropdown/multiSelect.d.ts +42 -2
  148. package/dist/temp/src/utils/dropdown/multiSelect.js +66 -13
  149. package/dist/types/assets/scripts/featuredIcon.d.ts +22 -0
  150. package/dist/types/assets/scripts/notification/FloatingNotification.d.ts +24 -0
  151. package/dist/types/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
  152. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +22 -0
  153. package/dist/types/assets/scripts/notification/Notification.d.ts +22 -0
  154. package/dist/types/assets/scripts/notification/const/classNames.d.ts +43 -0
  155. package/dist/types/assets/scripts/notification/const/icons.d.ts +25 -0
  156. package/dist/types/assets/scripts/notification/const/index.d.ts +5 -0
  157. package/dist/types/assets/scripts/notification/const/sizes.d.ts +32 -0
  158. package/dist/types/assets/scripts/notification/const/types.d.ts +19 -0
  159. package/dist/types/assets/scripts/notification/index.d.ts +8 -0
  160. package/dist/types/assets/scripts/notification/positionSync.d.ts +50 -0
  161. package/dist/types/assets/scripts/notification/utils.d.ts +8 -0
  162. package/dist/types/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
  163. package/dist/types/assets/scripts/utils/sanitize.d.ts +22 -0
  164. package/dist/types/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
  165. package/dist/types/src/components/data-display/table/Table.d.ts +4 -1
  166. package/dist/types/src/components/data-display/table/types.d.ts +18 -0
  167. package/dist/types/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
  168. package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
  169. package/dist/types/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
  170. package/dist/types/src/components/forms-and-input/slider/Slider.d.ts +0 -1
  171. package/dist/types/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
  172. package/dist/types/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
  173. package/dist/types/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
  174. package/dist/types/src/components/overlays/notification/Notification.d.ts +18 -3
  175. package/dist/types/src/components/overlays/notification/host.d.ts +9 -0
  176. package/dist/types/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
  177. package/dist/types/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
  178. package/dist/types/src/contexts/FloatingContext.d.ts +6 -0
  179. package/dist/types/src/contexts/index.d.ts +1 -0
  180. package/dist/types/src/hooks/index.d.ts +1 -0
  181. package/dist/types/src/hooks/useFloatingPosition.d.ts +19 -0
  182. package/dist/types/src/hooks/usePortalState.d.ts +6 -0
  183. package/dist/types/src/types/component-meta.d.ts +6 -2
  184. package/dist/types/src/utils/dropdown/maxSelection.d.ts +24 -0
  185. package/dist/types/src/utils/dropdown/multiSelect.d.ts +42 -2
  186. package/dist/ui-admin/assets/styles/style.css +312 -64
  187. package/package.json +1 -1
@@ -1,13 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown, ChevronSelectorVertical, ChevronUp } from '@ncds/ui-admin-icon';
3
3
  import classNames from 'classnames';
4
- import { Children, forwardRef, useEffect, useRef } from 'react';
5
- // ──────────────────────────────────────────────
6
- // $table-header-height 동기화 sticky thead가 차지하는 높이를 maxHeight에 보상
7
- const TABLE_HEADER_HEIGHT = 40;
8
- // 스크롤바 트랙 상하 여백 합계 (top 8px + bottom 8px) — SCSS &--fixed-header &__scrollbar 오프셋과 동기화
9
- const SCROLLBAR_TRACK_OFFSET = 16;
10
- const SCROLLBAR_THUMB_MIN_HEIGHT = 40;
4
+ import { Children, forwardRef, useRef } from 'react';
5
+ import { FloatingProvider } from '../../../contexts/FloatingContext';
6
+ import { TABLE_HEADER_HEIGHT, useTableHorizontalScrollbar, useTableVerticalScrollbar } from './useTableScrollbars';
7
+ // 가로 스크롤 디자인 기준 폭 — 14인치 모니터 + LNB 고려한 디자인 권장 너비
8
+ const DEFAULT_HORIZONTAL_SCROLL_MIN_WIDTH = 1140;
9
+ const FLOATING_PORTAL_VALUE = {
10
+ preferPortal: true
11
+ };
12
+ // TABLE_HEADER_HEIGHT·DEFAULT_HORIZONTAL_SCROLL_MIN_WIDTH를 CSS 커스텀 프로퍼티로 주입 — SCSS fallback 단일 소스
13
+ const WRAPPER_STYLE = {
14
+ '--ncua-table-header-height': `${TABLE_HEADER_HEIGHT}px`,
15
+ '--ncua-table-default-min-width': `${DEFAULT_HORIZONTAL_SCROLL_MIN_WIDTH}px`
16
+ };
11
17
  // Sort Icons (@ncds/ui-admin-icon)
12
18
  // ──────────────────────────────────────────────
13
19
  const SORT_ICONS = {
@@ -72,6 +78,7 @@ const HeaderCell = /*#__PURE__*/forwardRef((_ref4, ref) => {
72
78
  sortDirection,
73
79
  onSort,
74
80
  width,
81
+ minWidth,
75
82
  style,
76
83
  ...rest
77
84
  } = _ref4;
@@ -84,7 +91,8 @@ const HeaderCell = /*#__PURE__*/forwardRef((_ref4, ref) => {
84
91
  }),
85
92
  style: {
86
93
  ...style,
87
- width
94
+ width,
95
+ minWidth
88
96
  },
89
97
  "aria-sort": isSortable ? ARIA_SORT_MAP[sortDirection] : undefined,
90
98
  onClick: isSortable ? onSort : undefined,
@@ -153,10 +161,11 @@ const Pagination = _ref7 => {
153
161
  Pagination.displayName = 'Table.Pagination';
154
162
  const ColGroup = _ref8 => {
155
163
  let {
156
- widths
164
+ widths,
165
+ minWidths
157
166
  } = _ref8;
158
167
  const resolveColWidth = width => {
159
- if (width === 'auto') return undefined;
168
+ if (width === undefined || width === 'auto') return undefined;
160
169
  if (typeof width === 'number') return `${width}px`;
161
170
  return width;
162
171
  };
@@ -165,7 +174,8 @@ const ColGroup = _ref8 => {
165
174
  // biome-ignore lint/suspicious/noArrayIndexKey: colgroup columns never reorder or change
166
175
  _jsx("col", {
167
176
  style: {
168
- width: resolveColWidth(width)
177
+ width: resolveColWidth(width),
178
+ minWidth: resolveColWidth(minWidths?.[index])
169
179
  }
170
180
  }, index))
171
181
  });
@@ -227,6 +237,8 @@ const TableComponent = /*#__PURE__*/forwardRef((_ref0, ref) => {
227
237
  maxHeight,
228
238
  hoverable = true,
229
239
  selectable = false,
240
+ horizontalScroll = false,
241
+ minWidth,
230
242
  children,
231
243
  className,
232
244
  ...rest
@@ -251,85 +263,99 @@ const TableComponent = /*#__PURE__*/forwardRef((_ref0, ref) => {
251
263
  // Custom scrollbar refs (used only in fixed-header mode)
252
264
  const scrollContainerRef = useRef(null);
253
265
  const scrollAreaRef = useRef(null);
266
+ const scrollbarRef = useRef(null);
254
267
  const thumbRef = useRef(null);
255
- useEffect(() => {
256
- if (!fixedHeader || !maxHeight) return;
257
- const scrollEl = scrollContainerRef.current;
258
- const thumbEl = thumbRef.current;
259
- if (!scrollEl || !thumbEl) return;
260
- const update = () => {
261
- const {
262
- scrollTop,
263
- scrollHeight,
264
- clientHeight
265
- } = scrollEl;
266
- if (scrollHeight <= clientHeight) {
267
- thumbEl.style.height = '0';
268
- return;
269
- }
270
- const trackHeight = (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
271
- const thumbHeight = Math.max(SCROLLBAR_THUMB_MIN_HEIGHT, clientHeight / scrollHeight * trackHeight);
272
- const thumbTop = scrollTop / (scrollHeight - clientHeight) * (trackHeight - thumbHeight);
273
- thumbEl.style.height = `${thumbHeight}px`;
274
- thumbEl.style.transform = `translateY(${thumbTop}px)`;
275
- };
276
- scrollEl.addEventListener('scroll', update, {
277
- passive: true
278
- });
279
- const observer = new ResizeObserver(update);
280
- observer.observe(scrollEl);
281
- update();
282
- return () => {
283
- scrollEl.removeEventListener('scroll', update);
284
- observer.disconnect();
285
- };
286
- }, [fixedHeader, maxHeight]);
287
- const handleThumbMouseDown = e => {
288
- e.preventDefault();
289
- const scrollEl = scrollContainerRef.current;
290
- const thumbEl = thumbRef.current;
291
- const areaEl = scrollAreaRef.current;
292
- if (!scrollEl || !thumbEl) return;
293
- areaEl?.setAttribute('data-dragging', '');
294
- const startY = e.clientY;
295
- const startScrollTop = scrollEl.scrollTop;
296
- const {
297
- scrollHeight,
298
- clientHeight
299
- } = scrollEl;
300
- const thumbHeight = thumbEl.offsetHeight;
301
- const scrollRatio = (scrollHeight - clientHeight) / (clientHeight - thumbHeight);
302
- const onMove = ev => {
303
- scrollEl.scrollTop = startScrollTop + (ev.clientY - startY) * scrollRatio;
304
- };
305
- const onUp = () => {
306
- areaEl?.removeAttribute('data-dragging');
307
- document.removeEventListener('mousemove', onMove);
308
- document.removeEventListener('mouseup', onUp);
309
- };
310
- document.addEventListener('mousemove', onMove);
311
- document.addEventListener('mouseup', onUp);
268
+ // 가로 스크롤바 refs (horizontalScroll 모드)
269
+ const hScrollContainerRef = useRef(null);
270
+ const hScrollbarRef = useRef(null);
271
+ const hThumbRef = useRef(null);
272
+ const fixedScrollEnabled = !!(fixedHeader && maxHeight);
273
+ const {
274
+ handleThumbMouseDown
275
+ } = useTableVerticalScrollbar({
276
+ enabled: fixedScrollEnabled,
277
+ scrollContainerRef,
278
+ scrollAreaRef,
279
+ thumbRef
280
+ });
281
+ const {
282
+ handleHThumbMouseDown
283
+ } = useTableHorizontalScrollbar({
284
+ enabled: horizontalScroll,
285
+ hScrollContainerRef,
286
+ hScrollbarRef,
287
+ hThumbRef
288
+ });
289
+ // <colgroup> + <thead> + <tbody> 묶음 — fixed-header 분기와 horizontalScroll 분기 모두에서 재사용
290
+ const renderTable = () => _jsxs("table", {
291
+ className: "ncua-table__table",
292
+ role: "table",
293
+ children: [colGroupContent, headerContent, tableContent]
294
+ });
295
+ // fixed-header scroll-area + scrollbar 래핑, 아니면 <table> 그대로.
296
+ // withScrollbar=false 이면 scrollbar를 제외 — horizontalScroll 분기에서 scrollbar를
297
+ // h-scroll-container 형제 위치에 별도 렌더해 가로 스크롤 시 viewport 우측에 자연 고정.
298
+ const renderScrollableArea = function () {
299
+ let includeVerticalScrollbar = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
300
+ return fixedScrollEnabled ? _jsxs("div", {
301
+ ref: scrollAreaRef,
302
+ className: "ncua-table__scroll-area",
303
+ children: [_jsx("div", {
304
+ ref: scrollContainerRef,
305
+ className: "ncua-table__scroll-container",
306
+ style: scrollStyle,
307
+ children: renderTable()
308
+ }), includeVerticalScrollbar && _jsx("div", {
309
+ ref: scrollbarRef,
310
+ className: "ncua-table__scrollbar",
311
+ "aria-hidden": "true",
312
+ children: _jsx("div", {
313
+ ref: thumbRef,
314
+ className: "ncua-table__scrollbar-thumb",
315
+ onMouseDown: handleThumbMouseDown
316
+ })
317
+ })]
318
+ }) : renderTable();
312
319
  };
313
- if (fixedHeader && maxHeight) {
314
- return _jsxs("div", {
315
- ref: ref,
316
- className: "ncua-table-wrapper",
317
- children: [_jsxs("div", {
318
- className: tableClasses,
319
- ...rest,
320
+ // horizontalScroll=true 외곽 wrapper + FloatingProvider 부착.
321
+ // 핵심 — __h-scroll-container 는 <table>(또는 scroll-area) 만 감싸고, footer/pagination 은
322
+ // 그 바깥에서 항상 고정 위치. 세로 스크롤바는 h-scroll-container 형제로 배치되어
323
+ // 가로 스크롤에 영향받지 않고 .ncua-table 우측에 absolute 고정된다.
324
+ if (horizontalScroll) {
325
+ const resolvedMinWidth = minWidth ?? DEFAULT_HORIZONTAL_SCROLL_MIN_WIDTH;
326
+ // CSS 변수로 전달 — SCSS 에서 max(100%, var(--ncua-table-min-width)) 로 부모 너비를 항상 보장한다.
327
+ // (inline min-width 를 직접 주면 부모보다 작은 값에서 wrapper 가 좁아져 콘텐츠가 깨짐)
328
+ const innerStyle = {
329
+ '--ncua-table-min-width': typeof resolvedMinWidth === 'number' ? `${resolvedMinWidth}px` : resolvedMinWidth
330
+ };
331
+ return _jsx(FloatingProvider, {
332
+ value: FLOATING_PORTAL_VALUE,
333
+ children: _jsxs("div", {
334
+ ref: ref,
335
+ className: "ncua-table-wrapper",
336
+ style: WRAPPER_STYLE,
320
337
  children: [_jsxs("div", {
321
- ref: scrollAreaRef,
322
- className: "ncua-table__scroll-area",
323
- children: [_jsx("div", {
324
- ref: scrollContainerRef,
325
- className: "ncua-table__scroll-container",
326
- style: scrollStyle,
327
- children: _jsxs("table", {
328
- className: "ncua-table__table",
329
- role: "table",
330
- children: [colGroupContent, headerContent, tableContent]
331
- })
332
- }), _jsx("div", {
338
+ className: tableClasses,
339
+ ...rest,
340
+ children: [_jsxs("div", {
341
+ ref: hScrollContainerRef,
342
+ className: "ncua-table__h-scroll-container",
343
+ children: [_jsx("div", {
344
+ className: "ncua-table__h-scroll-inner",
345
+ style: innerStyle,
346
+ children: renderScrollableArea(false)
347
+ }), _jsx("div", {
348
+ ref: hScrollbarRef,
349
+ className: "ncua-table__h-scrollbar",
350
+ "aria-hidden": "true",
351
+ children: _jsx("div", {
352
+ ref: hThumbRef,
353
+ className: "ncua-table__h-scrollbar-thumb",
354
+ onMouseDown: handleHThumbMouseDown
355
+ })
356
+ })]
357
+ }), fixedScrollEnabled && _jsx("div", {
358
+ ref: scrollbarRef,
333
359
  className: "ncua-table__scrollbar",
334
360
  "aria-hidden": "true",
335
361
  children: _jsx("div", {
@@ -337,23 +363,19 @@ const TableComponent = /*#__PURE__*/forwardRef((_ref0, ref) => {
337
363
  className: "ncua-table__scrollbar-thumb",
338
364
  onMouseDown: handleThumbMouseDown
339
365
  })
340
- })]
341
- }), footerContent]
342
- }), paginationContent]
366
+ }), footerContent]
367
+ }), paginationContent]
368
+ })
343
369
  });
344
370
  }
345
- const tableElement = _jsxs("table", {
346
- className: "ncua-table__table",
347
- role: "table",
348
- children: [colGroupContent, headerContent, tableContent]
349
- });
350
371
  return _jsxs("div", {
351
372
  ref: ref,
352
373
  className: "ncua-table-wrapper",
374
+ style: WRAPPER_STYLE,
353
375
  children: [_jsxs("div", {
354
376
  className: tableClasses,
355
377
  ...rest,
356
- children: [tableElement, footerContent]
378
+ children: [renderScrollableArea(), footerContent]
357
379
  }), paginationContent]
358
380
  });
359
381
  });
@@ -0,0 +1,179 @@
1
+ import { useEffect } from 'react';
2
+ // ──────────────────────────────────────────────
3
+ // 상수 — Table.tsx 와 _table.scss 양쪽에서 동기화 필요
4
+ // ──────────────────────────────────────────────
5
+ // $table-header-height
6
+ export const TABLE_HEADER_HEIGHT = 40;
7
+ // 세로/가로 thumb 최소 크기
8
+ export const SCROLLBAR_THUMB_MIN_HEIGHT = 40;
9
+ export const H_SCROLLBAR_THUMB_MIN_WIDTH = 40;
10
+ // SCSS .ncua-table__h-scrollbar { left/right: var(--spacing-s) = 8px } 와 동기화
11
+ export const H_SCROLLBAR_SIDE_GAP = 8;
12
+ // 세로 트랙 상하 여백 합계 — top 8 + bottom 8 = 16 (header 회피분 40 은 별도 처리)
13
+ // biome-ignore lint/style/useExportsLast: 상수는 문서 주석과 함께 상단에 정의
14
+ export const SCROLLBAR_TRACK_OFFSET = 16;
15
+ const startDrag = (e, options) => {
16
+ e.preventDefault();
17
+ const {
18
+ axis,
19
+ scrollEl,
20
+ thumbEl,
21
+ draggingTarget = scrollEl,
22
+ sideGap = 0
23
+ } = options;
24
+ draggingTarget.setAttribute('data-dragging', '');
25
+ if (axis === 'y') {
26
+ const startY = e.clientY;
27
+ const startScrollTop = scrollEl.scrollTop;
28
+ const {
29
+ scrollHeight,
30
+ clientHeight
31
+ } = scrollEl;
32
+ const thumbHeight = thumbEl.offsetHeight;
33
+ const ratio = (scrollHeight - clientHeight) / (clientHeight - thumbHeight);
34
+ const onMove = ev => {
35
+ scrollEl.scrollTop = startScrollTop + (ev.clientY - startY) * ratio;
36
+ };
37
+ const onUp = () => {
38
+ draggingTarget.removeAttribute('data-dragging');
39
+ document.removeEventListener('mousemove', onMove);
40
+ document.removeEventListener('mouseup', onUp);
41
+ };
42
+ document.addEventListener('mousemove', onMove);
43
+ document.addEventListener('mouseup', onUp);
44
+ return;
45
+ }
46
+ const startX = e.clientX;
47
+ const startScrollLeft = scrollEl.scrollLeft;
48
+ const {
49
+ scrollWidth,
50
+ clientWidth
51
+ } = scrollEl;
52
+ const thumbWidth = thumbEl.offsetWidth;
53
+ const trackWidth = clientWidth - sideGap * 2;
54
+ const ratio = (scrollWidth - clientWidth) / (trackWidth - thumbWidth);
55
+ const onMove = ev => {
56
+ scrollEl.scrollLeft = startScrollLeft + (ev.clientX - startX) * ratio;
57
+ };
58
+ const onUp = () => {
59
+ draggingTarget.removeAttribute('data-dragging');
60
+ document.removeEventListener('mousemove', onMove);
61
+ document.removeEventListener('mouseup', onUp);
62
+ };
63
+ document.addEventListener('mousemove', onMove);
64
+ document.addEventListener('mouseup', onUp);
65
+ };
66
+ export const useTableVerticalScrollbar = _ref => {
67
+ let {
68
+ enabled,
69
+ scrollContainerRef,
70
+ scrollAreaRef,
71
+ thumbRef
72
+ } = _ref;
73
+ useEffect(() => {
74
+ if (!enabled) return;
75
+ const scrollEl = scrollContainerRef.current;
76
+ const thumbEl = thumbRef.current;
77
+ if (!scrollEl || !thumbEl) return;
78
+ const update = () => {
79
+ const {
80
+ scrollTop,
81
+ scrollHeight,
82
+ clientHeight
83
+ } = scrollEl;
84
+ if (scrollHeight <= clientHeight) {
85
+ thumbEl.style.height = '0';
86
+ return;
87
+ }
88
+ const trackHeight = (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
89
+ const thumbHeight = Math.max(SCROLLBAR_THUMB_MIN_HEIGHT, clientHeight / scrollHeight * trackHeight);
90
+ const thumbTop = scrollTop / (scrollHeight - clientHeight) * (trackHeight - thumbHeight);
91
+ thumbEl.style.height = `${thumbHeight}px`;
92
+ thumbEl.style.transform = `translateY(${thumbTop}px)`;
93
+ };
94
+ scrollEl.addEventListener('scroll', update, {
95
+ passive: true
96
+ });
97
+ const observer = new ResizeObserver(update);
98
+ observer.observe(scrollEl);
99
+ if (scrollAreaRef.current) observer.observe(scrollAreaRef.current);
100
+ update();
101
+ return () => {
102
+ scrollEl.removeEventListener('scroll', update);
103
+ observer.disconnect();
104
+ };
105
+ }, [enabled, scrollContainerRef, scrollAreaRef, thumbRef]);
106
+ const handleThumbMouseDown = e => {
107
+ const scrollEl = scrollContainerRef.current;
108
+ const thumbEl = thumbRef.current;
109
+ const areaEl = scrollAreaRef.current;
110
+ if (!scrollEl || !thumbEl) return;
111
+ startDrag(e, {
112
+ axis: 'y',
113
+ scrollEl,
114
+ thumbEl,
115
+ draggingTarget: areaEl ?? scrollEl
116
+ });
117
+ };
118
+ return {
119
+ handleThumbMouseDown
120
+ };
121
+ };
122
+ export const useTableHorizontalScrollbar = _ref2 => {
123
+ let {
124
+ enabled,
125
+ hScrollContainerRef,
126
+ hScrollbarRef,
127
+ hThumbRef
128
+ } = _ref2;
129
+ useEffect(() => {
130
+ if (!enabled) return;
131
+ const hScrollEl = hScrollContainerRef.current;
132
+ const hScrollbarEl = hScrollbarRef.current;
133
+ const hThumbEl = hThumbRef.current;
134
+ if (!hScrollEl || !hScrollbarEl || !hThumbEl) return;
135
+ const update = () => {
136
+ const {
137
+ scrollLeft,
138
+ scrollWidth,
139
+ clientWidth
140
+ } = hScrollEl;
141
+ if (scrollWidth <= clientWidth) {
142
+ hThumbEl.style.width = '0';
143
+ return;
144
+ }
145
+ // transform으로 스크롤 오프셋 보정 — reflow 없이 compositor-only 이동
146
+ hScrollbarEl.style.transform = `translateX(${scrollLeft}px)`;
147
+ hScrollbarEl.style.width = `${clientWidth - H_SCROLLBAR_SIDE_GAP * 2}px`;
148
+ const trackWidth = clientWidth - H_SCROLLBAR_SIDE_GAP * 2;
149
+ const thumbWidth = Math.max(H_SCROLLBAR_THUMB_MIN_WIDTH, clientWidth / scrollWidth * trackWidth);
150
+ const thumbLeft = scrollLeft / (scrollWidth - clientWidth) * (trackWidth - thumbWidth);
151
+ hThumbEl.style.width = `${thumbWidth}px`;
152
+ hThumbEl.style.transform = `translateX(${thumbLeft}px)`;
153
+ };
154
+ hScrollEl.addEventListener('scroll', update, {
155
+ passive: true
156
+ });
157
+ const ro = new ResizeObserver(update);
158
+ ro.observe(hScrollEl);
159
+ update();
160
+ return () => {
161
+ hScrollEl.removeEventListener('scroll', update);
162
+ ro.disconnect();
163
+ };
164
+ }, [enabled, hScrollContainerRef, hScrollbarRef, hThumbRef]);
165
+ const handleHThumbMouseDown = e => {
166
+ const hScrollEl = hScrollContainerRef.current;
167
+ const hThumbEl = hThumbRef.current;
168
+ if (!hScrollEl || !hThumbEl) return;
169
+ startDrag(e, {
170
+ axis: 'x',
171
+ scrollEl: hScrollEl,
172
+ thumbEl: hThumbEl,
173
+ sideGap: H_SCROLLBAR_SIDE_GAP
174
+ });
175
+ };
176
+ return {
177
+ handleHThumbMouseDown
178
+ };
179
+ };
@@ -10,13 +10,6 @@ import { SelectDropdown } from '../../select-dropdown';
10
10
  import { HintText } from '../../shared/hintText/HintText';
11
11
  import { InputBase } from '../input-base/InputBase';
12
12
  const defaultMaxHeight = 275;
13
- const toggleMultiSelectValue = (currentValue, optionId) => {
14
- const currentValues = Array.isArray(currentValue) ? currentValue : [];
15
- if (currentValues.includes(optionId)) {
16
- return currentValues.filter(v => v !== optionId);
17
- }
18
- return [...currentValues, optionId];
19
- };
20
13
  const notifyFormChange = (register, multiple, newValue) => {
21
14
  if (register?.onChange) {
22
15
  register.onChange({
@@ -48,6 +41,7 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
48
41
  required = false,
49
42
  multiple = false,
50
43
  showFooterButtons = false,
44
+ maxSelection,
51
45
  onEdit,
52
46
  ...props
53
47
  } = _ref;
@@ -60,7 +54,8 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
60
54
  const handleOptionSelect = option => {
61
55
  if (disabled) return;
62
56
  if (multiple) {
63
- const newValue = toggleMultiSelectValue(value ?? [], option.id);
57
+ const newValue = tryToggle(option.id, Array.isArray(value) ? value : []);
58
+ if (newValue === null) return;
64
59
  onChange?.(newValue);
65
60
  notifyFormChange(register, multiple, newValue);
66
61
  return;
@@ -137,6 +132,7 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
137
132
  // 나머지는 useDropdown 훅에서 처리
138
133
  dropdownHandleKeyDown(e);
139
134
  };
135
+ // biome-ignore lint/style/noNonNullAssertion: forwardRef 패턴에서 internalRef는 첫 렌더 후 항상 존재
140
136
  useImperativeHandle(ref, () => internalRef.current, []);
141
137
  const trailingElement = {
142
138
  type: 'custom',
@@ -166,8 +162,12 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
166
162
  buttonText: selectAllButtonText,
167
163
  toggleSelectAll,
168
164
  getSelectedTagsData,
169
- removeTag
170
- } = useMultiSelect(currentSelectedValues, optionItems);
165
+ removeTag,
166
+ isMaxSelectionActive,
167
+ tryToggle
168
+ } = useMultiSelect(currentSelectedValues, optionItems, {
169
+ maxSelection
170
+ });
171
171
  const handleSelectAll = () => {
172
172
  if (!multiple || !onChange) return;
173
173
  const newSelectedValues = toggleSelectAll();
@@ -253,6 +253,7 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
253
253
  multiple: multiple,
254
254
  showFooterButtons: showFooter,
255
255
  selectAllButtonText: selectAllButtonText,
256
+ showSelectAllAction: !isMaxSelectionActive,
256
257
  onSelectAll: handleSelectAll,
257
258
  onEdit: handleEdit,
258
259
  onComplete: handleComplete,
@@ -132,9 +132,12 @@ export const ImageFileInput = /*#__PURE__*/forwardRef((_ref, ref) => {
132
132
  onClick: handleBrowseClick,
133
133
  disabled: disabled,
134
134
  label: imagePreviewTooltipLabel
135
- }), isButtonHovered && !disabled && _jsx(Tooltip, {
135
+ }), _jsx(Tooltip, {
136
136
  content: imagePreviewTooltipLabel,
137
- position: "bottom"
137
+ position: "bottom",
138
+ tooltipType: "black",
139
+ forceVisible: isButtonHovered && !disabled,
140
+ disablePortal: true
138
141
  })]
139
142
  })]
140
143
  });