devnonla-ui 0.3.0 → 0.4.1
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/README.md +3 -3
- package/package.json +2 -1
- package/src/app/App.tsx +4 -4
- package/src/button/Button.tsx +1 -1
- package/src/button/ButtonCopy.tsx +11 -21
- package/src/calendar/Calendar.tsx +1 -1
- package/src/chat/AgentPanel.tsx +3 -3
- package/src/chat/index.ts +35 -51
- package/src/chat/message-ui/ChatError.tsx +2 -4
- package/src/chat/message-ui/ChatInput.tsx +5 -9
- package/src/chat/message-ui/ChatThinking.tsx +2 -3
- package/src/chat/message-ui/ChatUserMessage.tsx +4 -5
- package/src/chat/message-ui/ChatWelcome.tsx +3 -8
- package/src/chat/message-ui/MermaidBlock.tsx +7 -47
- package/src/chat/tool-ui/BackgroundTaskToolUI.tsx +8 -11
- package/src/chat/tool-ui/BackgroundTasksBar.tsx +13 -14
- package/src/chat/tool-ui/CallAgentToolUI.tsx +10 -4
- package/src/chat/tool-ui/ChatToolCall.tsx +11 -17
- package/src/chat/tool-ui/GetCurrentTimeToolUI.tsx +2 -2
- package/src/chat/tool-ui/ReadSkillToolUI.tsx +3 -3
- package/src/chat/tool-ui/RunJsToolUI.tsx +2 -2
- package/src/chat/tool-ui/ToolUiTrailing.tsx +3 -7
- package/src/chat/tool-ui/WebFetchToolUI.tsx +22 -13
- package/src/checkbox/Checkbox.tsx +1 -1
- package/src/codeblock/CodeBlock.tsx +17 -19
- package/src/colorpicker/ColorPicker.tsx +7 -7
- package/src/datepicker/DatePicker.tsx +3 -3
- package/src/desktop/DesktopHeader.tsx +15 -20
- package/src/desktop/DesktopIcon.tsx +3 -9
- package/src/desktop/DesktopWindow.tsx +54 -30
- package/src/desktop/MeadowDesktop.tsx +1 -1
- package/src/form/Form.tsx +1 -1
- package/src/form/FormItem.tsx +2 -2
- package/src/form/index.ts +23 -24
- package/src/index.ts +167 -199
- package/src/input/Input.tsx +14 -12
- package/src/input/SearchInput.tsx +5 -2
- package/src/lib/sizes.ts +1 -1
- package/src/modal/Modal.tsx +3 -0
- package/src/pagination/Pagination.tsx +528 -51
- package/src/scroll/OverlayScroll.tsx +58 -37
- package/src/select/Select.tsx +2 -2
- package/src/spin/Spin.tsx +14 -17
- package/src/splitter/Splitter.tsx +3 -3
- package/src/styles.css +13 -11
- package/src/switch/Switch.tsx +1 -1
- package/src/table/Table.tsx +43 -18
- package/src/timepicker/TimePicker.tsx +2 -2
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { type PointerEvent as ReactPointerEvent, type
|
|
1
|
+
import { type ReactNode, type PointerEvent as ReactPointerEvent, type Ref, type UIEventHandler, useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import { cn } from "../lib/cn";
|
|
3
3
|
|
|
4
4
|
export type OverlayScrollVisibility = "hover" | "always";
|
|
5
5
|
|
|
6
6
|
const HIDE_MS = 1000;
|
|
7
|
+
const MIN_THUMB = 32;
|
|
7
8
|
|
|
8
9
|
export type OverlayScrollProps = {
|
|
9
10
|
/** `hover` shows the thumb on hover / while scrolling. `always` keeps it visible. */
|
|
10
11
|
visibility?: OverlayScrollVisibility;
|
|
12
|
+
/** Size to content (honors max-height on `className`) instead of filling the parent. */
|
|
13
|
+
autoHeight?: boolean;
|
|
11
14
|
className?: string;
|
|
12
15
|
innerClassName?: string;
|
|
13
16
|
children?: ReactNode;
|
|
@@ -15,7 +18,10 @@ export type OverlayScrollProps = {
|
|
|
15
18
|
scrollRef?: Ref<HTMLDivElement>;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
|
-
type Thumb = {
|
|
21
|
+
type Thumb = { offset: number; size: number; shown: boolean };
|
|
22
|
+
type Axis = "x" | "y";
|
|
23
|
+
|
|
24
|
+
const HIDDEN: Thumb = { offset: 0, size: 0, shown: false };
|
|
19
25
|
|
|
20
26
|
function assignRef(ref: Ref<HTMLDivElement> | undefined, node: HTMLDivElement | null) {
|
|
21
27
|
if (!ref) return;
|
|
@@ -23,27 +29,36 @@ function assignRef(ref: Ref<HTMLDivElement> | undefined, node: HTMLDivElement |
|
|
|
23
29
|
else ref.current = node;
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
function measure(scroll: number, scrollSize: number, client: number): Thumb {
|
|
33
|
+
const overflow = scrollSize - client;
|
|
34
|
+
if (overflow <= 1) return HIDDEN;
|
|
35
|
+
const size = Math.max(MIN_THUMB, (client / scrollSize) * client);
|
|
36
|
+
const offset = (scroll / overflow) * (client - size);
|
|
37
|
+
return { offset, size, shown: true };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function canScrollX(el: HTMLDivElement) {
|
|
41
|
+
const overflow = getComputedStyle(el).overflowX;
|
|
42
|
+
return overflow === "auto" || overflow === "scroll";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function OverlayScroll({ visibility = "hover", autoHeight = false, className, innerClassName, children, onScroll, scrollRef }: OverlayScrollProps) {
|
|
27
46
|
const elRef = useRef<HTMLDivElement | null>(null);
|
|
28
47
|
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
29
|
-
const
|
|
48
|
+
const vThumbRef = useRef<Thumb>(HIDDEN);
|
|
49
|
+
const hThumbRef = useRef<Thumb>(HIDDEN);
|
|
30
50
|
const [scrolling, setScrolling] = useState(false);
|
|
31
51
|
const [dragging, setDragging] = useState(false);
|
|
32
|
-
const [
|
|
33
|
-
|
|
52
|
+
const [vThumb, setVThumb] = useState<Thumb>(HIDDEN);
|
|
53
|
+
const [hThumb, setHThumb] = useState<Thumb>(HIDDEN);
|
|
54
|
+
vThumbRef.current = vThumb;
|
|
55
|
+
hThumbRef.current = hThumb;
|
|
34
56
|
|
|
35
57
|
const updateThumb = useCallback(() => {
|
|
36
58
|
const el = elRef.current;
|
|
37
59
|
if (!el) return;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (overflow <= 1) {
|
|
41
|
-
setThumb((prev) => (prev.shown ? { top: 0, height: 0, shown: false } : prev));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const height = Math.max(32, (clientHeight / scrollHeight) * clientHeight);
|
|
45
|
-
const top = (scrollTop / overflow) * (clientHeight - height);
|
|
46
|
-
setThumb({ top, height, shown: true });
|
|
60
|
+
setVThumb(measure(el.scrollTop, el.scrollHeight, el.clientHeight));
|
|
61
|
+
setHThumb(canScrollX(el) ? measure(el.scrollLeft, el.scrollWidth, el.clientWidth) : HIDDEN);
|
|
47
62
|
}, []);
|
|
48
63
|
|
|
49
64
|
const setNode = useCallback(
|
|
@@ -64,7 +79,7 @@ export function OverlayScroll({ visibility = "hover", className, innerClassName,
|
|
|
64
79
|
const child = el.firstElementChild;
|
|
65
80
|
if (child) ro.observe(child);
|
|
66
81
|
return () => ro.disconnect();
|
|
67
|
-
}, [updateThumb]);
|
|
82
|
+
}, [updateThumb, innerClassName, autoHeight]);
|
|
68
83
|
|
|
69
84
|
useLayoutEffect(() => {
|
|
70
85
|
return () => {
|
|
@@ -82,27 +97,30 @@ export function OverlayScroll({ visibility = "hover", className, innerClassName,
|
|
|
82
97
|
onScroll?.(e);
|
|
83
98
|
};
|
|
84
99
|
|
|
85
|
-
const onThumbPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {
|
|
100
|
+
const onThumbPointerDown = (axis: Axis) => (e: ReactPointerEvent<HTMLDivElement>) => {
|
|
86
101
|
const el = elRef.current;
|
|
87
|
-
const current =
|
|
102
|
+
const current = axis === "y" ? vThumbRef.current : hThumbRef.current;
|
|
88
103
|
if (!el || !current.shown || e.button !== 0) return;
|
|
89
104
|
e.preventDefault();
|
|
90
105
|
e.stopPropagation();
|
|
91
106
|
|
|
92
|
-
const overflow = el.scrollHeight - el.clientHeight;
|
|
93
|
-
const track = el.clientHeight - current.
|
|
107
|
+
const overflow = axis === "y" ? el.scrollHeight - el.clientHeight : el.scrollWidth - el.clientWidth;
|
|
108
|
+
const track = (axis === "y" ? el.clientHeight : el.clientWidth) - current.size;
|
|
94
109
|
if (overflow <= 0 || track <= 0) return;
|
|
95
110
|
|
|
96
|
-
const
|
|
97
|
-
const
|
|
111
|
+
const start = axis === "y" ? e.clientY : e.clientX;
|
|
112
|
+
const startOffset = current.offset;
|
|
98
113
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
99
114
|
setDragging(true);
|
|
100
115
|
setScrolling(true);
|
|
101
116
|
|
|
102
117
|
const onMove = (ev: PointerEvent) => {
|
|
103
118
|
if (ev.pointerId !== e.pointerId) return;
|
|
104
|
-
const
|
|
105
|
-
|
|
119
|
+
const delta = (axis === "y" ? ev.clientY : ev.clientX) - start;
|
|
120
|
+
const next = Math.min(track, Math.max(0, startOffset + delta));
|
|
121
|
+
const pos = (next / track) * overflow;
|
|
122
|
+
if (axis === "y") el.scrollTop = pos;
|
|
123
|
+
else el.scrollLeft = pos;
|
|
106
124
|
};
|
|
107
125
|
const onUp = (ev: PointerEvent) => {
|
|
108
126
|
if (ev.pointerId !== e.pointerId) return;
|
|
@@ -119,24 +137,27 @@ export function OverlayScroll({ visibility = "hover", className, innerClassName,
|
|
|
119
137
|
};
|
|
120
138
|
|
|
121
139
|
const thumbVisible = visibility === "always" || dragging || scrolling;
|
|
140
|
+
const thumbClass = cn(
|
|
141
|
+
"nonla-overlay-thumb absolute z-20 rounded-full transition-opacity duration-150",
|
|
142
|
+
visibility === "always" || dragging
|
|
143
|
+
? "pointer-events-auto opacity-100"
|
|
144
|
+
: "pointer-events-none opacity-0 group-hover/scroll:pointer-events-auto group-hover/scroll:opacity-100 group-data-scrolling/scroll:pointer-events-auto group-data-scrolling/scroll:opacity-100",
|
|
145
|
+
);
|
|
122
146
|
|
|
123
147
|
return (
|
|
124
148
|
<div className={cn("group/scroll relative min-h-0 min-w-0 w-full overflow-hidden", className)} data-scrolling={thumbVisible ? "true" : undefined}>
|
|
125
|
-
<div
|
|
149
|
+
<div
|
|
150
|
+
ref={setNode}
|
|
151
|
+
onScroll={handleScroll}
|
|
152
|
+
className={cn("nonla-scroll-hidden overflow-y-auto overflow-x-hidden [overflow-anchor:none]", autoHeight ? "max-h-[inherit]" : "absolute inset-0", innerClassName)}
|
|
153
|
+
>
|
|
126
154
|
{children}
|
|
127
155
|
</div>
|
|
128
|
-
{
|
|
129
|
-
<div
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"nonla-overlay-thumb absolute z-20 rounded-full transition-opacity duration-150",
|
|
134
|
-
visibility === "always" || dragging
|
|
135
|
-
? "pointer-events-auto opacity-100"
|
|
136
|
-
: "pointer-events-none opacity-0 group-hover/scroll:pointer-events-auto group-hover/scroll:opacity-100 group-data-scrolling/scroll:pointer-events-auto group-data-scrolling/scroll:opacity-100",
|
|
137
|
-
)}
|
|
138
|
-
style={{ right: 2, width: "var(--nonla-scrollbar-size)", height: thumb.height, transform: `translateY(${thumb.top}px)` }}
|
|
139
|
-
/>
|
|
156
|
+
{vThumb.shown ? (
|
|
157
|
+
<div aria-hidden onPointerDown={onThumbPointerDown("y")} className={thumbClass} style={{ right: 2, width: "var(--nonla-scrollbar-size)", height: vThumb.size, transform: `translateY(${vThumb.offset}px)` }} />
|
|
158
|
+
) : null}
|
|
159
|
+
{hThumb.shown ? (
|
|
160
|
+
<div aria-hidden onPointerDown={onThumbPointerDown("x")} className={thumbClass} style={{ bottom: 2, height: "var(--nonla-scrollbar-size)", width: hThumb.size, transform: `translateX(${hThumb.offset}px)` }} />
|
|
140
161
|
) : null}
|
|
141
162
|
</div>
|
|
142
163
|
);
|
package/src/select/Select.tsx
CHANGED
package/src/spin/Spin.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { type ReactNode, useEffect, useState } from "react";
|
|
2
2
|
import { cn } from "../lib/cn";
|
|
3
|
-
import { type ControlSize, getSizeTokens, useControlSize } from "../lib/sizes";
|
|
3
|
+
import { type ControlSize, getSizeTokens, normalizeSize, useControlSize } from "../lib/sizes";
|
|
4
4
|
|
|
5
|
-
export type SpinVariant = "default" | "agent"
|
|
5
|
+
export type SpinVariant = "default" | "agent";
|
|
6
6
|
|
|
7
7
|
export type SpinProps = {
|
|
8
8
|
spinning?: boolean;
|
|
9
9
|
tip?: ReactNode;
|
|
10
10
|
size?: ControlSize;
|
|
11
|
-
/** Visual style — agent
|
|
11
|
+
/** Visual style — `agent` is the 3×3 snake for AI run states. */
|
|
12
12
|
variant?: SpinVariant;
|
|
13
13
|
/** Replace the built-in indicator entirely. */
|
|
14
14
|
indicator?: ReactNode;
|
|
@@ -75,11 +75,17 @@ function stepSnake(snake: Snake): Snake {
|
|
|
75
75
|
return [next, head, mid];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/** 3×3 metrics so `small` is visibly smaller than default (icon-aligned). */
|
|
79
|
+
function matrixMetrics(size: ControlSize | undefined): { cell: number; gap: number } {
|
|
80
|
+
const s = normalizeSize(size);
|
|
81
|
+
if (s === "small") return { cell: 3, gap: 1.5 };
|
|
82
|
+
if (s === "large") return { cell: 6, gap: 3 };
|
|
83
|
+
return { cell: 4, gap: 2 };
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
/** 3×3 fixed dots — 3 adjacent lit cells crawl randomly (snake). */
|
|
79
|
-
function MatrixSnake({ size, tickMs =
|
|
80
|
-
const
|
|
81
|
-
const cell = Math.max(4, Math.round(5 * scale));
|
|
82
|
-
const gap = Math.max(2, Math.round(2 * scale));
|
|
87
|
+
function MatrixSnake({ size, tickMs = 240 }: { size: ControlSize | undefined; tickMs?: number }) {
|
|
88
|
+
const { cell, gap } = matrixMetrics(size);
|
|
83
89
|
const dim = cell * 3 + gap * 2;
|
|
84
90
|
const [snake, setSnake] = useState<Snake>(() => randomSnake());
|
|
85
91
|
|
|
@@ -112,17 +118,8 @@ function MatrixSnake({ size, tickMs = 220 }: { size: ControlSize | undefined; ti
|
|
|
112
118
|
);
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
function AgentSpinner({ size }: { size: ControlSize | undefined }) {
|
|
116
|
-
return <MatrixSnake size={size} tickMs={240} />;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function SubAgentSpinner({ size }: { size: ControlSize | undefined }) {
|
|
120
|
-
return <MatrixSnake size={size} tickMs={180} />;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
121
|
function Spinner({ size, variant }: { size: ControlSize | undefined; variant: SpinVariant }) {
|
|
124
|
-
if (variant === "agent") return <
|
|
125
|
-
if (variant === "subAgent") return <SubAgentSpinner size={size} />;
|
|
122
|
+
if (variant === "agent") return <MatrixSnake size={size} />;
|
|
126
123
|
return <DefaultSpinner size={size} />;
|
|
127
124
|
}
|
|
128
125
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Children,
|
|
3
|
-
Fragment,
|
|
4
3
|
type CSSProperties,
|
|
4
|
+
Fragment,
|
|
5
|
+
isValidElement,
|
|
5
6
|
type KeyboardEvent,
|
|
6
7
|
type PointerEvent,
|
|
7
8
|
type ReactElement,
|
|
8
9
|
type ReactNode,
|
|
9
|
-
isValidElement,
|
|
10
10
|
useCallback,
|
|
11
11
|
useEffect,
|
|
12
12
|
useLayoutEffect,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
useState,
|
|
16
16
|
} from "react";
|
|
17
17
|
import { cn } from "../lib/cn";
|
|
18
|
-
import { applyDrag, distributeSizes, parseSplitterSize,
|
|
18
|
+
import { applyDrag, distributeSizes, parseSplitterSize, type SplitterSize, scaleSizes } from "./sizes";
|
|
19
19
|
|
|
20
20
|
export type { SplitterSize };
|
|
21
21
|
export type SplitterOrientation = "horizontal" | "vertical";
|
package/src/styles.css
CHANGED
|
@@ -232,6 +232,8 @@
|
|
|
232
232
|
--color-ink-active: var(--ink-active);
|
|
233
233
|
--color-ink-line: var(--ink-line);
|
|
234
234
|
--color-chat: var(--nonla-chat-bg);
|
|
235
|
+
--color-well: color-mix(in oklab, var(--foreground) 5.5%, var(--card));
|
|
236
|
+
--color-well-strong: color-mix(in oklab, var(--foreground) 10%, var(--card));
|
|
235
237
|
|
|
236
238
|
--shadow-card: none;
|
|
237
239
|
--shadow-whisper: none;
|
|
@@ -402,7 +404,7 @@
|
|
|
402
404
|
}
|
|
403
405
|
}
|
|
404
406
|
|
|
405
|
-
/* ── Spin: agent
|
|
407
|
+
/* ── Spin: agent (3×3 random snake) ────────────────────────────────────── */
|
|
406
408
|
.nonla-spin-matrix {
|
|
407
409
|
align-content: center;
|
|
408
410
|
justify-content: center;
|
|
@@ -413,12 +415,12 @@
|
|
|
413
415
|
width: 100%;
|
|
414
416
|
height: 100%;
|
|
415
417
|
border-radius: 999px;
|
|
416
|
-
background: var(--
|
|
418
|
+
background: color-mix(in oklab, var(--brand) 10%, transparent);
|
|
417
419
|
transition: background 120ms linear;
|
|
418
420
|
}
|
|
419
421
|
|
|
420
422
|
.nonla-spin-matrix-cell.is-on {
|
|
421
|
-
background: var(--
|
|
423
|
+
background: var(--brand);
|
|
422
424
|
}
|
|
423
425
|
|
|
424
426
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -631,14 +633,6 @@
|
|
|
631
633
|
backdrop-filter: blur(40px) saturate(108%);
|
|
632
634
|
}
|
|
633
635
|
|
|
634
|
-
/* Fluent Color desktop tile — same frost as the window, macOS squircle. */
|
|
635
|
-
.nonla-desktop-icon-plate {
|
|
636
|
-
overflow: hidden;
|
|
637
|
-
border: 0;
|
|
638
|
-
border-radius: 22%;
|
|
639
|
-
box-shadow: none;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
636
|
.nonla-btn-glass {
|
|
643
637
|
border-color: var(--glass-border);
|
|
644
638
|
background: var(--glass);
|
|
@@ -864,6 +858,14 @@
|
|
|
864
858
|
animation: nonla-pop-out 100ms var(--nonla-ease-in);
|
|
865
859
|
}
|
|
866
860
|
|
|
861
|
+
.nonla-chat-tool-result-ok {
|
|
862
|
+
background-image: linear-gradient(
|
|
863
|
+
165deg,
|
|
864
|
+
color-mix(in oklab, var(--success) 14%, var(--card)) 0%,
|
|
865
|
+
color-mix(in oklab, var(--success) 5%, var(--card)) 100%
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
867
869
|
.nonla-codeblock-pre code,
|
|
868
870
|
.nonla-codeblock-pre .hljs {
|
|
869
871
|
color: var(--foreground);
|
package/src/switch/Switch.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
2
|
-
import { type
|
|
2
|
+
import { type ComponentPropsWithoutRef, type CSSProperties, forwardRef } from "react";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
4
|
import { type ControlSize, controlRadiusVar, useControlSize } from "../lib/sizes";
|
|
5
5
|
|
package/src/table/Table.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { Checkbox } from "../checkbox/Checkbox";
|
|
|
3
3
|
import { Empty } from "../empty/Empty";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
5
|
import { type CanonicalSize, type ControlSize, useControlSize } from "../lib/sizes";
|
|
6
|
-
import { Pagination } from "../pagination/Pagination";
|
|
6
|
+
import { Pagination, type PaginationProps } from "../pagination/Pagination";
|
|
7
7
|
import { Spin } from "../spin/Spin";
|
|
8
8
|
|
|
9
9
|
export type SortOrder = "ascend" | "descend" | null;
|
|
@@ -29,14 +29,29 @@ export type ColumnType<T> = {
|
|
|
29
29
|
|
|
30
30
|
export type ColumnsType<T> = ColumnType<T>[];
|
|
31
31
|
|
|
32
|
-
export type TablePaginationConfig =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
export type TablePaginationConfig = Pick<
|
|
33
|
+
PaginationProps,
|
|
34
|
+
| "align"
|
|
35
|
+
| "current"
|
|
36
|
+
| "defaultCurrent"
|
|
37
|
+
| "pageSize"
|
|
38
|
+
| "defaultPageSize"
|
|
39
|
+
| "total"
|
|
40
|
+
| "onChange"
|
|
41
|
+
| "onShowSizeChange"
|
|
42
|
+
| "hideOnSinglePage"
|
|
43
|
+
| "className"
|
|
44
|
+
| "showSizeChanger"
|
|
45
|
+
| "showQuickJumper"
|
|
46
|
+
| "showTotal"
|
|
47
|
+
| "simple"
|
|
48
|
+
| "disabled"
|
|
49
|
+
| "size"
|
|
50
|
+
| "pageSizeOptions"
|
|
51
|
+
| "showLessItems"
|
|
52
|
+
| "showTitle"
|
|
53
|
+
| "itemRender"
|
|
54
|
+
>;
|
|
40
55
|
|
|
41
56
|
export type TableRowSelection<T> = {
|
|
42
57
|
selectedRowKeys?: Key[];
|
|
@@ -157,8 +172,8 @@ export function Table<T extends object = Record<string, unknown>>({ columns = []
|
|
|
157
172
|
const [innerSelected, setInnerSelected] = useState<Key[]>(() => rowSelection?.defaultSelectedRowKeys ?? []);
|
|
158
173
|
const selectedKeys = selectionControlled ? (rowSelection?.selectedRowKeys ?? []) : innerSelected;
|
|
159
174
|
|
|
160
|
-
const [innerPage, setInnerPage] = useState(1);
|
|
161
|
-
const [innerPageSize, setInnerPageSize] = useState(10);
|
|
175
|
+
const [innerPage, setInnerPage] = useState(() => (typeof pagination === "object" && pagination?.defaultCurrent) || 1);
|
|
176
|
+
const [innerPageSize, setInnerPageSize] = useState(() => (typeof pagination === "object" && pagination?.defaultPageSize) || 10);
|
|
162
177
|
|
|
163
178
|
const initialSort = useMemo(() => {
|
|
164
179
|
const col = columns.find((c) => c.defaultSortOrder);
|
|
@@ -179,8 +194,9 @@ export function Table<T extends object = Record<string, unknown>>({ columns = []
|
|
|
179
194
|
const paginationOff = pagination === false;
|
|
180
195
|
const pageConfig: TablePaginationConfig = paginationOff ? {} : (pagination ?? {});
|
|
181
196
|
const pageControlled = pageConfig.current !== undefined;
|
|
197
|
+
const pageSizeControlled = pageConfig.pageSize !== undefined;
|
|
182
198
|
const current = pageControlled ? (pageConfig.current ?? 1) : innerPage;
|
|
183
|
-
const pageSize = pageConfig.pageSize ??
|
|
199
|
+
const pageSize = pageSizeControlled ? (pageConfig.pageSize ?? 10) : innerPageSize;
|
|
184
200
|
const serverSide = pageConfig.total !== undefined;
|
|
185
201
|
|
|
186
202
|
const sortedData = useMemo(() => {
|
|
@@ -227,10 +243,8 @@ export function Table<T extends object = Record<string, unknown>>({ columns = []
|
|
|
227
243
|
};
|
|
228
244
|
|
|
229
245
|
const handlePageChange = (page: number, nextSize: number) => {
|
|
230
|
-
if (!pageControlled)
|
|
231
|
-
|
|
232
|
-
setInnerPageSize(nextSize);
|
|
233
|
-
}
|
|
246
|
+
if (!pageControlled) setInnerPage(page);
|
|
247
|
+
if (!pageSizeControlled) setInnerPageSize(nextSize);
|
|
234
248
|
pageConfig.onChange?.(page, nextSize);
|
|
235
249
|
onChange?.({ current: page, pageSize: nextSize, total }, {}, { columnKey: sortState.key ?? undefined, order: sortState.order });
|
|
236
250
|
};
|
|
@@ -377,8 +391,19 @@ export function Table<T extends object = Record<string, unknown>>({ columns = []
|
|
|
377
391
|
</Spin>
|
|
378
392
|
|
|
379
393
|
{showPagination ? (
|
|
380
|
-
<div className={cn("mt-4
|
|
381
|
-
<Pagination
|
|
394
|
+
<div className={cn("mt-4", pageConfig.className)}>
|
|
395
|
+
<Pagination
|
|
396
|
+
{...pageConfig}
|
|
397
|
+
className={undefined}
|
|
398
|
+
hideOnSinglePage={false}
|
|
399
|
+
align={pageConfig.align ?? "end"}
|
|
400
|
+
current={current}
|
|
401
|
+
pageSize={pageSize}
|
|
402
|
+
total={total}
|
|
403
|
+
size={pageConfig.size ?? "small"}
|
|
404
|
+
showSizeChanger={pageConfig.showSizeChanger ?? false}
|
|
405
|
+
onChange={handlePageChange}
|
|
406
|
+
/>
|
|
382
407
|
</div>
|
|
383
408
|
) : null}
|
|
384
409
|
</div>
|
|
@@ -2,11 +2,11 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
|
2
2
|
import {
|
|
3
3
|
type CSSProperties,
|
|
4
4
|
type FocusEvent,
|
|
5
|
+
Fragment,
|
|
6
|
+
forwardRef,
|
|
5
7
|
type KeyboardEvent,
|
|
6
8
|
type MouseEvent,
|
|
7
9
|
type PointerEvent,
|
|
8
|
-
Fragment,
|
|
9
|
-
forwardRef,
|
|
10
10
|
useCallback,
|
|
11
11
|
useEffect,
|
|
12
12
|
useRef,
|