@xonic0506/sonic-ui 0.4.3 → 0.6.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.
@@ -0,0 +1,413 @@
1
+ "use client";
2
+ import { cn as e } from "../lib/cn.js";
3
+ import { Button as t } from "./button.js";
4
+ import { Checkbox as n } from "./checkbox.js";
5
+ import { Switch as r } from "./switch.js";
6
+ import { Skeleton as i } from "./skeleton.js";
7
+ import { Menu as a, MenuItem as o, MenuPopup as s, MenuSeparator as c, MenuTrigger as l } from "./menu.js";
8
+ import { Badge as u } from "./badge.js";
9
+ import { TableBody as d, TableCell as f, TableHead as p, TableHeader as ee, TableRow as m, tableClass as te } from "./table.js";
10
+ import { formatDate as h, formatDateTime as g, formatNumber as _ } from "../lib/format.js";
11
+ import { Pagination as ne } from "./pagination.js";
12
+ import * as v from "react";
13
+ import { Ellipsis as y, GripVertical as b } from "lucide-react";
14
+ import { Fragment as re, jsx as x, jsxs as S } from "react/jsx-runtime";
15
+ import { columnOrderingFeature as C, columnPinningFeature as w, columnSizingFeature as T, createPaginatedRowModel as ie, createSortedRowModel as E, rowPaginationFeature as D, rowSortingFeature as O, tableFeatures as k, useTable as ae } from "@tanstack/react-table";
16
+ import { useVirtualizer as oe } from "@tanstack/react-virtual";
17
+ import { DndContext as se, KeyboardSensor as ce, PointerSensor as le, closestCenter as ue, useSensor as de, useSensors as fe } from "@dnd-kit/core";
18
+ import { SortableContext as pe, arrayMove as me, horizontalListSortingStrategy as he, sortableKeyboardCoordinates as ge, useSortable as A, verticalListSortingStrategy as _e } from "@dnd-kit/sortable";
19
+ //#region src/components/data-grid.tsx
20
+ var j = "__select", M = "__handle", ve = 100, ye = k({
21
+ rowSortingFeature: O,
22
+ sortedRowModel: E(),
23
+ rowPaginationFeature: D,
24
+ paginatedRowModel: ie(),
25
+ columnOrderingFeature: C,
26
+ columnSizingFeature: T,
27
+ columnPinningFeature: w
28
+ }), be = (e, t, n) => {
29
+ let r = e.getValue(n), i = t.getValue(n);
30
+ return r == null || i == null ? r == null ? i == null ? 0 : -1 : 1 : typeof r == "number" && typeof i == "number" ? r - i : typeof r == "boolean" ? Number(r) - Number(i) : String(r).localeCompare(String(i), "ko", { numeric: !0 });
31
+ }, N = (e) => "field" in e ? e.id ?? e.field : e.id, xe = {
32
+ before: "sn:shadow-[inset_0_2px_0_var(--sn-color-border-focus)]",
33
+ after: "sn:shadow-[inset_0_-2px_0_var(--sn-color-border-focus)]"
34
+ }, P = {
35
+ before: "sn:shadow-[inset_2px_0_0_var(--sn-color-border-focus)]",
36
+ after: "sn:shadow-[inset_-2px_0_0_var(--sn-color-border-focus)]"
37
+ }, F = "sn:flex sn:size-4 sn:cursor-grab sn:items-center sn:justify-center sn:rounded-md sn:border-0 sn:bg-transparent sn:p-0 sn:text-muted sn:hover:text-default sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus sn:[&_svg]:size-4";
38
+ function I(e, t) {
39
+ let n = (e) => t().indexOf(String(e)) + 1;
40
+ return {
41
+ onDragStart: ({ active: t }) => `${e}을 들었어요. 지금 ${n(t.id)}번째예요.`,
42
+ onDragOver: ({ over: e }) => e ? `${n(e.id)}번째 위치예요.` : "놓을 수 없는 위치예요.",
43
+ onDragEnd: ({ over: e }) => e ? `${n(e.id)}번째에 놓았어요.` : "옮기지 않았어요.",
44
+ onDragCancel: () => "옮기기를 취소했어요."
45
+ };
46
+ }
47
+ var Se = { draggable: "Space로 들고, 화살표로 옮긴 다음 Space로 놓아요. Esc로 취소해요." };
48
+ function Ce(e, t) {
49
+ let [n, r] = v.useState(null);
50
+ return {
51
+ drop: n,
52
+ onDragOver: ({ active: t, over: n }) => {
53
+ if (!n || n.id === t.id) return r(null);
54
+ r({
55
+ id: String(n.id),
56
+ side: e.indexOf(String(n.id)) > e.indexOf(String(t.id)) ? "after" : "before"
57
+ });
58
+ },
59
+ onDragEnd: ({ active: n, over: i }) => {
60
+ r(null), i && i.id !== n.id && t(me(e, e.indexOf(String(n.id)), e.indexOf(String(i.id))));
61
+ },
62
+ onDragCancel: () => r(null)
63
+ };
64
+ }
65
+ function L(e, t, n) {
66
+ let [r, i] = v.useState(t);
67
+ return [e === void 0 ? r : e, (t) => {
68
+ e === void 0 && i(t), n?.(t);
69
+ }];
70
+ }
71
+ function we(e, t) {
72
+ let n = e.getIsPinned(), r = t || n ? e.getSize() : void 0;
73
+ return {
74
+ width: r,
75
+ minWidth: r,
76
+ left: n === "start" ? e.getStart("start") : void 0,
77
+ right: n === "end" ? e.getAfter("end") : void 0
78
+ };
79
+ }
80
+ function Te({ actions: e, row: n }) {
81
+ if (e.length === 1) {
82
+ let [r] = e, i = r.icon;
83
+ return i ? /* @__PURE__ */ x(t, {
84
+ size: "icon-sm",
85
+ variant: "ghost",
86
+ "aria-label": r.label,
87
+ onClick: () => r.onClick(n),
88
+ children: /* @__PURE__ */ x(i, {})
89
+ }) : /* @__PURE__ */ x(t, {
90
+ size: "sm",
91
+ variant: "ghost",
92
+ onClick: () => r.onClick(n),
93
+ children: r.label
94
+ });
95
+ }
96
+ let r = e.filter((e) => e.tone !== "danger"), i = e.filter((e) => e.tone === "danger"), u = (e) => {
97
+ let t = e.icon;
98
+ return /* @__PURE__ */ S(o, {
99
+ tone: e.tone,
100
+ onClick: () => e.onClick(n),
101
+ children: [t && /* @__PURE__ */ x(t, {}), e.label]
102
+ }, e.label);
103
+ };
104
+ return /* @__PURE__ */ S(a, { children: [/* @__PURE__ */ x(l, {
105
+ render: /* @__PURE__ */ x(t, {
106
+ size: "icon-sm",
107
+ variant: "ghost",
108
+ "aria-label": "더 보기"
109
+ }),
110
+ children: /* @__PURE__ */ x(y, {})
111
+ }), /* @__PURE__ */ S(s, { children: [
112
+ r.map(u),
113
+ r.length > 0 && i.length > 0 && /* @__PURE__ */ x(c, {}),
114
+ i.map(u)
115
+ ] })] });
116
+ }
117
+ function Ee(e, t, n) {
118
+ switch (e.type) {
119
+ case "text": return n == null ? "" : String(n);
120
+ case "number": return _(n);
121
+ case "date": return h(n);
122
+ case "datetime": return g(n);
123
+ case "badge": {
124
+ let t = n == null ? "" : String(n);
125
+ return /* @__PURE__ */ x(u, {
126
+ tone: e.tones[t] ?? "neutral",
127
+ children: e.labels?.[t] ?? t
128
+ });
129
+ }
130
+ case "switch": return /* @__PURE__ */ x(r, {
131
+ checked: !!n,
132
+ onCheckedChange: (n) => e.onChange(t, n),
133
+ children: /* @__PURE__ */ x("span", {
134
+ className: "sn:sr-only",
135
+ children: e.header
136
+ })
137
+ });
138
+ case "actions": return /* @__PURE__ */ x(Te, {
139
+ actions: e.actions,
140
+ row: t
141
+ });
142
+ }
143
+ }
144
+ function De({ id: t, label: n, drop: r, className: i, ...a }) {
145
+ let { setNodeRef: o, setActivatorNodeRef: s, attributes: c, listeners: l, isDragging: u } = A({
146
+ id: t,
147
+ attributes: { roleDescription: "옮길 수 있는 열" }
148
+ });
149
+ return /* @__PURE__ */ x(p, {
150
+ ref: o,
151
+ className: e(i, u && "sn:bg-subtle", r && P[r]),
152
+ handle: /* @__PURE__ */ x("button", {
153
+ type: "button",
154
+ ref: s,
155
+ ...c,
156
+ ...l,
157
+ "aria-label": `${n} 열 옮기기`,
158
+ className: F,
159
+ children: /* @__PURE__ */ x(b, {})
160
+ }),
161
+ ...a
162
+ });
163
+ }
164
+ function Oe({ id: e, draggable: t, renderCells: n, ...r }) {
165
+ let { setNodeRef: i, setActivatorNodeRef: a, attributes: o, listeners: s, isDragging: c } = A({
166
+ id: e,
167
+ disabled: !t,
168
+ attributes: { roleDescription: "옮길 수 있는 행" }
169
+ }), l = t ? /* @__PURE__ */ x("button", {
170
+ type: "button",
171
+ ref: a,
172
+ ...o,
173
+ ...s,
174
+ "aria-label": "행 옮기기",
175
+ className: F,
176
+ children: /* @__PURE__ */ x(b, {})
177
+ }) : null;
178
+ return /* @__PURE__ */ x(m, {
179
+ ref: i,
180
+ interactive: !0,
181
+ dragging: c,
182
+ ...r,
183
+ children: n(l)
184
+ });
185
+ }
186
+ function ke({ rows: t, getRowId: r, columns: a, "aria-label": o, sorting: s, defaultSorting: c = null, onSortingChange: l, selection: u, onSelectionChange: h, columnOrder: g, onColumnOrderChange: _, onRowOrderChange: y, loading: b, emptyState: C, errorState: w, pagination: T, className: ie }) {
187
+ let E = u !== void 0 || h !== void 0, D = y !== void 0, O = _ !== void 0, [k, me] = L(s, c, l), [A, P] = L(u, [], h), [F, Te] = L(g, a.map(N), _), [ke, R] = L(T?.page, T?.defaultPage ?? 1, T?.onPageChange), [z, Ae] = L(T?.pageSize, T?.defaultPageSize ?? 20, T?.onPageSizeChange), B = T?.rowCount !== void 0, je = T ? Math.max(1, Math.ceil((B ? T.rowCount : t.length) / z)) : 1, V = Math.min(ke, je), H = v.useMemo(() => new Map(a.map((e) => [N(e), e])), [a]), Me = v.useMemo(() => {
188
+ let e = [];
189
+ E && e.push({
190
+ id: j,
191
+ size: 40,
192
+ enableSorting: !1
193
+ }), D && e.push({
194
+ id: M,
195
+ size: 40,
196
+ enableSorting: !1
197
+ });
198
+ for (let t of a) {
199
+ let n = t.width ?? (t.type === "actions" ? 56 : void 0);
200
+ e.push("field" in t ? {
201
+ id: N(t),
202
+ accessorFn: (e) => e[t.field],
203
+ enableSorting: !!t.sortable,
204
+ sortFn: be,
205
+ ...n && { size: n }
206
+ } : {
207
+ id: t.id,
208
+ enableSorting: !1,
209
+ ...n && { size: n }
210
+ });
211
+ }
212
+ return e;
213
+ }, [
214
+ a,
215
+ E,
216
+ D
217
+ ]), Ne = v.useMemo(() => ({
218
+ start: [
219
+ ...E ? [j] : [],
220
+ ...D ? [M] : [],
221
+ ...a.filter((e) => e.pinned === "left").map(N)
222
+ ],
223
+ end: a.filter((e) => e.pinned === "right").map(N)
224
+ }), [
225
+ a,
226
+ E,
227
+ D
228
+ ]), U = ae({
229
+ features: ye,
230
+ columns: Me,
231
+ data: t,
232
+ getRowId: (e) => r(e),
233
+ state: {
234
+ sorting: k ? [k] : [],
235
+ columnOrder: F,
236
+ columnPinning: Ne,
237
+ pagination: {
238
+ pageIndex: V - 1,
239
+ pageSize: T ? z : t.length
240
+ }
241
+ },
242
+ manualPagination: !T || B,
243
+ manualSorting: B
244
+ }), W = [
245
+ ...U.getStartFlatHeaders(),
246
+ ...U.getCenterFlatHeaders(),
247
+ ...U.getEndFlatHeaders()
248
+ ], G = U.getRowModel().rows, Pe = t.map(r), K = G.map((e) => e.id), q = U.getCenterFlatHeaders().map((e) => e.column.id), Fe = D && !k, J = v.useRef(null), Y = !T && G.length > ve, Ie = oe({
249
+ count: Y ? G.length : 0,
250
+ getScrollElement: () => J.current,
251
+ estimateSize: () => 40,
252
+ overscan: 8
253
+ }), X = Y ? Ie.getVirtualItems() : null, Le = X ? X.map((e) => G[e.index]) : G, Re = X?.[0]?.start ?? 0, ze = X ? Ie.getTotalSize() - (X.at(-1)?.end ?? 0) : 0, Be = typeof document > "u" ? void 0 : document.body, Ve = fe(de(le, { activationConstraint: { distance: 4 } }), de(ce, { coordinateGetter: ge })), Z = Ce(Pe, (e) => y?.(e)), Q = Ce(q, (e) => Te([...F.filter((t) => !e.includes(t)), ...e])), He = (e) => {
254
+ me(k?.id === e ? k.desc ? null : {
255
+ id: e,
256
+ desc: !0
257
+ } : {
258
+ id: e,
259
+ desc: !1
260
+ }), T && V !== 1 && R(1);
261
+ }, Ue = K.length > 0 && K.every((e) => A.includes(e)), We = !Ue && K.some((e) => A.includes(e)), Ge = (e) => P([...A.filter((e) => !K.includes(e)), ...e ? K : []]), Ke = (t) => {
262
+ let { column: r } = t, i = r.id, a = r.getIsPinned(), o = a === "start" ? "left" : a === "end" ? "right" : void 0, s = {
263
+ pinned: o,
264
+ style: we(r, i === j || i === M || H.get(i)?.width !== void 0),
265
+ className: e("sn:sticky sn:top-0 sn:z-20 sn:bg-surface", o && "sn:z-30")
266
+ };
267
+ if (i === j) return /* @__PURE__ */ x(p, {
268
+ ...s,
269
+ children: /* @__PURE__ */ x(n, {
270
+ checked: Ue,
271
+ indeterminate: We,
272
+ onCheckedChange: Ge,
273
+ children: /* @__PURE__ */ x("span", {
274
+ className: "sn:sr-only",
275
+ children: "모든 행 선택"
276
+ })
277
+ })
278
+ }, i);
279
+ if (i === M) return /* @__PURE__ */ x(p, {
280
+ ...s,
281
+ children: /* @__PURE__ */ x("span", {
282
+ className: "sn:sr-only",
283
+ children: "순서"
284
+ })
285
+ }, i);
286
+ let c = H.get(i), l = c.header ?? "", u = "sortable" in c && c.sortable ? {
287
+ sort: k?.id === i ? k.desc ? "desc" : "asc" : "none",
288
+ onSort: () => He(i)
289
+ } : {}, d = c.type === "actions" && !c.header ? /* @__PURE__ */ x("span", {
290
+ className: "sn:sr-only",
291
+ children: "동작"
292
+ }) : l, f = c.type === "number" || c.type === "actions" ? "end" : "start";
293
+ return O && !o ? /* @__PURE__ */ x(De, {
294
+ id: i,
295
+ label: l || "동작",
296
+ align: f,
297
+ drop: Q.drop?.id === i ? Q.drop.side : void 0,
298
+ ...s,
299
+ ...u,
300
+ children: d
301
+ }, i) : /* @__PURE__ */ x(p, {
302
+ align: f,
303
+ ...s,
304
+ ...u,
305
+ children: d
306
+ }, i);
307
+ }, qe = (e) => /* @__PURE__ */ x(m, { children: /* @__PURE__ */ x(f, {
308
+ colSpan: W.length,
309
+ className: "sn:h-auto sn:p-0",
310
+ children: e
311
+ }) }), $;
312
+ return $ = w ? qe(w) : b ? Array.from({ length: 5 }, (e, t) => /* @__PURE__ */ x(m, { children: W.map((e) => /* @__PURE__ */ x(f, { children: e.column.id === j || e.column.id === M ? null : /* @__PURE__ */ x(i, {
313
+ shape: "text",
314
+ className: "sn:w-2/3"
315
+ }) }, e.id)) }, t)) : G.length === 0 ? C ? qe(C) : null : /* @__PURE__ */ S(re, { children: [
316
+ Re > 0 && /* @__PURE__ */ x("tr", {
317
+ "aria-hidden": !0,
318
+ style: { height: Re }
319
+ }),
320
+ Le.map((t) => {
321
+ let r = A.includes(t.id), i = Z.drop?.id === t.id ? xe[Z.drop.side] : void 0, a = [
322
+ ...t.getStartVisibleCells(),
323
+ ...t.getCenterVisibleCells(),
324
+ ...t.getEndVisibleCells()
325
+ ];
326
+ return /* @__PURE__ */ x(Oe, {
327
+ id: t.id,
328
+ draggable: Fe,
329
+ selected: r,
330
+ "aria-selected": E ? r : void 0,
331
+ "aria-rowindex": Y ? t.index + 2 : void 0,
332
+ renderCells: (o) => a.map((a) => {
333
+ let { column: s } = a, c = s.id, l = s.getIsPinned(), u = l === "start" ? "left" : l === "end" ? "right" : void 0, d = H.get(c), p;
334
+ return p = c === j ? /* @__PURE__ */ x(n, {
335
+ checked: r,
336
+ onCheckedChange: (e) => P(e ? [...A, t.id] : A.filter((e) => e !== t.id)),
337
+ children: /* @__PURE__ */ x("span", {
338
+ className: "sn:sr-only",
339
+ children: "행 선택"
340
+ })
341
+ }) : c === M ? o : Ee(d, t.original, a.getValue()), /* @__PURE__ */ x(f, {
342
+ pinned: u,
343
+ style: we(s, c === j || c === M || d?.width !== void 0),
344
+ align: d?.type === "number" ? "end" : "start",
345
+ className: e((d?.type === "date" || d?.type === "datetime") && "sn:font-mono", d?.type === "actions" && "sn:text-right", i),
346
+ children: p
347
+ }, a.id);
348
+ })
349
+ }, t.id);
350
+ }),
351
+ ze > 0 && /* @__PURE__ */ x("tr", {
352
+ "aria-hidden": !0,
353
+ style: { height: ze }
354
+ })
355
+ ] }), /* @__PURE__ */ S("div", {
356
+ className: e("sn:flex sn:w-full sn:flex-col sn:gap-4", ie),
357
+ children: [/* @__PURE__ */ x("div", {
358
+ ref: J,
359
+ className: "sn:min-h-0 sn:w-full sn:flex-1 sn:overflow-auto",
360
+ children: /* @__PURE__ */ S("table", {
361
+ "aria-label": o,
362
+ "aria-busy": b || void 0,
363
+ "aria-rowcount": Y ? G.length + 1 : void 0,
364
+ className: e(te, "sn:bg-surface"),
365
+ children: [/* @__PURE__ */ x(se, {
366
+ sensors: Ve,
367
+ collisionDetection: ue,
368
+ accessibility: {
369
+ container: Be,
370
+ announcements: I("열", () => q),
371
+ screenReaderInstructions: Se
372
+ },
373
+ onDragOver: Q.onDragOver,
374
+ onDragEnd: Q.onDragEnd,
375
+ onDragCancel: Q.onDragCancel,
376
+ children: /* @__PURE__ */ x(ee, { children: /* @__PURE__ */ x(pe, {
377
+ items: O ? q : [],
378
+ strategy: he,
379
+ children: /* @__PURE__ */ x(m, { children: W.map(Ke) })
380
+ }) })
381
+ }), /* @__PURE__ */ x(se, {
382
+ sensors: Ve,
383
+ collisionDetection: ue,
384
+ accessibility: {
385
+ container: Be,
386
+ announcements: I("행", () => K),
387
+ screenReaderInstructions: Se
388
+ },
389
+ onDragOver: Z.onDragOver,
390
+ onDragEnd: Z.onDragEnd,
391
+ onDragCancel: Z.onDragCancel,
392
+ children: /* @__PURE__ */ x(pe, {
393
+ items: Fe ? K : [],
394
+ strategy: _e,
395
+ children: /* @__PURE__ */ x(d, { children: $ })
396
+ })
397
+ })]
398
+ })
399
+ }), T && !w && (b || G.length > 0) && /* @__PURE__ */ x(ne, {
400
+ page: V,
401
+ pageCount: je,
402
+ onPageChange: R,
403
+ pageSize: z,
404
+ onPageSizeChange: (e) => {
405
+ Ae(e), V !== 1 && R(1);
406
+ },
407
+ pageSizeOptions: T.pageSizeOptions,
408
+ showFirstLast: T.showFirstLast
409
+ })]
410
+ });
411
+ }
412
+ //#endregion
413
+ export { ke as DataGrid };
@@ -14,7 +14,7 @@ function a({ status: a, title: o = i[a][0], description: s = i[a][1], action: c,
14
14
  className: e("sn:box-border sn:flex sn:w-full sn:flex-col sn:items-center sn:gap-6 sn:px-6 sn:py-16 sn:text-center sn:font-sans", l),
15
15
  children: [
16
16
  /* @__PURE__ */ n("img", {
17
- src: t[a],
17
+ src: t[`error-${a}`],
18
18
  alt: "",
19
19
  width: 320,
20
20
  height: 213,
@@ -1,7 +1,7 @@
1
1
  export declare const illustrations: {
2
2
  empty: string;
3
3
  error: string;
4
- 404: string;
5
- 403: string;
6
- 500: string;
4
+ 'error-404': string;
5
+ 'error-403': string;
6
+ 'error-500': string;
7
7
  };
@@ -2,9 +2,9 @@
2
2
  var e = {
3
3
  empty: new URL("../illustrations/empty.webp", import.meta.url).href,
4
4
  error: new URL("../illustrations/error.webp", import.meta.url).href,
5
- 404: new URL("../illustrations/error-404.webp", import.meta.url).href,
6
- 403: new URL("../illustrations/error-403.webp", import.meta.url).href,
7
- 500: new URL("../illustrations/error-500.webp", import.meta.url).href
5
+ "error-404": new URL("../illustrations/error-404.webp", import.meta.url).href,
6
+ "error-403": new URL("../illustrations/error-403.webp", import.meta.url).href,
7
+ "error-500": new URL("../illustrations/error-500.webp", import.meta.url).href
8
8
  };
9
9
  //#endregion
10
10
  export { e as illustrations };
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ export interface PaginationProps {
3
+ /** 현재 페이지 (1부터) */
4
+ page: number;
5
+ pageCount: number;
6
+ onPageChange: (page: number) => void;
7
+ /** onPageSizeChange를 넘기면 왼쪽에 페이지당 행 수 Select가 생겨요 */
8
+ pageSize?: number;
9
+ onPageSizeChange?: (pageSize: number) => void;
10
+ pageSizeOptions?: number[];
11
+ /** 처음·마지막 버튼 */
12
+ showFirstLast?: boolean;
13
+ 'aria-label'?: string;
14
+ className?: string;
15
+ }
16
+ /** 처음·마지막 + 현재 양옆 1개, 나머지는 줄임표. 항상 최대 7칸이라 너비가 흔들리지 않아요 */
17
+ export declare function getPageItems(page: number, pageCount: number): Array<number | 'ellipsis'>;
18
+ export declare function Pagination({ page, pageCount, onPageChange, pageSize, onPageSizeChange, pageSizeOptions, showFirstLast, 'aria-label': ariaLabel, className, }: PaginationProps): React.JSX.Element;
@@ -0,0 +1,101 @@
1
+ "use client";
2
+ import { cn as e } from "../lib/cn.js";
3
+ import { Button as t } from "./button.js";
4
+ import { Select as n } from "./select.js";
5
+ import "react";
6
+ import { ChevronLeft as r, ChevronRight as i, ChevronsLeft as a, ChevronsRight as o } from "lucide-react";
7
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
8
+ //#region src/components/pagination.tsx
9
+ function l(e, t) {
10
+ return t <= 7 ? Array.from({ length: t }, (e, t) => t + 1) : e <= 4 ? [
11
+ 1,
12
+ 2,
13
+ 3,
14
+ 4,
15
+ 5,
16
+ "ellipsis",
17
+ t
18
+ ] : e >= t - 3 ? [
19
+ 1,
20
+ "ellipsis",
21
+ ...Array.from({ length: 5 }, (e, n) => t - 4 + n)
22
+ ] : [
23
+ 1,
24
+ "ellipsis",
25
+ e - 1,
26
+ e,
27
+ e + 1,
28
+ "ellipsis",
29
+ t
30
+ ];
31
+ }
32
+ var u = "sn:box-border sn:flex sn:h-7 sn:min-w-7 sn:items-center sn:justify-center sn:rounded-md sn:border-0 sn:px-1 sn:font-mono sn:text-body";
33
+ function d({ page: d, pageCount: f, onPageChange: p, pageSize: m, onPageSizeChange: h, pageSizeOptions: g = [
34
+ 10,
35
+ 20,
36
+ 50
37
+ ], showFirstLast: _ = !0, "aria-label": v = "페이지", className: y }) {
38
+ let b = Math.max(1, f), x = Math.min(Math.max(1, d), b), S = (e) => e !== x && p(e), C = (e, n, r, i) => /* @__PURE__ */ s(t, {
39
+ size: "icon-sm",
40
+ variant: "ghost",
41
+ "aria-label": e,
42
+ disabled: r,
43
+ focusableWhenDisabled: !0,
44
+ onClick: () => S(n),
45
+ children: /* @__PURE__ */ s(i, {})
46
+ });
47
+ return /* @__PURE__ */ c("div", {
48
+ className: e("sn:flex sn:items-center sn:justify-between sn:gap-4 sn:font-sans", y),
49
+ children: [h && /* @__PURE__ */ c("div", {
50
+ className: "sn:flex sn:items-center sn:gap-2",
51
+ children: [/* @__PURE__ */ s("span", {
52
+ "aria-hidden": !0,
53
+ className: "sn:hidden sn:text-label sn:text-muted sn:sm:inline",
54
+ children: "페이지당 행"
55
+ }), /* @__PURE__ */ s(n, {
56
+ "aria-label": "페이지당 행",
57
+ className: "sn:w-auto",
58
+ options: g.map((e) => ({
59
+ value: String(e),
60
+ label: String(e)
61
+ })),
62
+ value: m === void 0 ? null : String(m),
63
+ onValueChange: (e) => e && h(Number(e))
64
+ })]
65
+ }), /* @__PURE__ */ c("nav", {
66
+ "aria-label": v,
67
+ className: "sn:ml-auto sn:flex sn:items-center sn:gap-1",
68
+ children: [
69
+ _ && C("처음 페이지", 1, x <= 1, a),
70
+ C("이전 페이지", x - 1, x <= 1, r),
71
+ /* @__PURE__ */ s("ul", {
72
+ className: "sn:m-0 sn:hidden sn:list-none sn:items-center sn:gap-1 sn:p-0 sn:sm:flex",
73
+ children: l(x, b).map((t, n) => t === "ellipsis" ? /* @__PURE__ */ s("li", {
74
+ "aria-hidden": !0,
75
+ className: e(u, "sn:text-muted"),
76
+ children: "…"
77
+ }, `e${n}`) : /* @__PURE__ */ s("li", { children: /* @__PURE__ */ s("button", {
78
+ type: "button",
79
+ "aria-label": `${t}페이지`,
80
+ "aria-current": t === x ? "page" : void 0,
81
+ onClick: () => S(t),
82
+ className: e(u, "sn:cursor-pointer sn:transition-colors sn:duration-(--sn-duration-fast) sn:ease-standard sn:motion-reduce:transition-none", "sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus", t === x ? "sn:bg-subtle sn:text-default" : "sn:bg-transparent sn:text-muted sn:hover:bg-subtle sn:hover:text-default"),
83
+ children: t
84
+ }) }, t))
85
+ }),
86
+ /* @__PURE__ */ c("span", {
87
+ className: "sn:px-1 sn:font-mono sn:text-caption sn:text-muted sn:sm:hidden",
88
+ children: [
89
+ x,
90
+ " / ",
91
+ b
92
+ ]
93
+ }),
94
+ C("다음 페이지", x + 1, x >= b, i),
95
+ _ && C("마지막 페이지", b, x >= b, o)
96
+ ]
97
+ })]
98
+ });
99
+ }
100
+ //#endregion
101
+ export { d as Pagination, l as getPageItems };
@@ -1,4 +1,6 @@
1
1
  import * as React from 'react';
2
+ type Pinned = 'left' | 'right';
3
+ export declare const tableClass = "sn:w-full sn:border-separate sn:border-spacing-0 sn:font-sans sn:text-body sn:text-default";
2
4
  /** 넓으면 가로로 스크롤해요 */
3
5
  export declare const Table: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLTableElement>, "className"> & {
4
6
  className?: string;
@@ -11,14 +13,29 @@ export declare const TableBody: React.ForwardRefExoticComponent<Omit<React.HTMLA
11
13
  } & React.RefAttributes<HTMLTableSectionElement>>;
12
14
  export declare const TableRow: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLTableRowElement>, "className"> & {
13
15
  className?: string;
16
+ } & {
17
+ /** 선택된 행 (bg/selected) */
18
+ selected?: boolean;
19
+ /** hover 바탕을 보여 줘요 (DataGrid처럼 행을 고르거나 옮길 때) */
20
+ interactive?: boolean;
21
+ /** 옮기는 중인 행 */
22
+ dragging?: boolean;
14
23
  } & React.RefAttributes<HTMLTableRowElement>>;
15
- export declare const TableHead: React.ForwardRefExoticComponent<Omit<React.TdHTMLAttributes<HTMLTableCellElement>, "align" | "className"> & {
16
- /** 숫자 열은 end */
24
+ export declare const TableHead: React.ForwardRefExoticComponent<Omit<React.ThHTMLAttributes<HTMLTableCellElement>, "align" | "className"> & {
17
25
  align?: 'start' | 'end';
26
+ pinned?: Pinned;
27
+ /** 정렬 표시. onSort를 넘기면 머리글이 버튼이 돼요 */
28
+ sort?: 'asc' | 'desc' | 'none';
29
+ onSort?: () => void;
30
+ /** 컬럼 이동 손잡이. hover·포커스 때만 보이고, 정렬 반대쪽 끝에 놓여요 */
31
+ handle?: React.ReactNode;
18
32
  className?: string;
19
33
  } & React.RefAttributes<HTMLTableCellElement>>;
20
34
  export declare const TableCell: React.ForwardRefExoticComponent<Omit<React.TdHTMLAttributes<HTMLTableCellElement>, "align" | "className"> & {
21
35
  /** 숫자 열은 end */
22
36
  align?: 'start' | 'end';
37
+ /** 가로 스크롤 중에도 붙어 있어요. 여러 열을 고정하면 style.left·right로 위치를 넘겨요 */
38
+ pinned?: Pinned;
23
39
  className?: string;
24
40
  } & React.RefAttributes<HTMLTableCellElement>>;
41
+ export {};