listpage-next 0.0.235 → 0.0.237
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/dist/components/Card/index.d.ts +1 -1
- package/dist/components/PageLayout/components/PageModal/useActions.js +11 -3
- package/dist/components/PageLayout/components/PageModal/useCloseButton.d.ts +0 -4
- package/dist/components/PageLayout/components/PageModal/useCloseButton.js +1 -5
- package/dist/components/PageLayout/components/PageProvider/float.d.ts +1 -1
- package/dist/features/ChatClient/ui/ConversationDropdownMenu/index.d.ts +3 -0
- package/dist/features/ChatClient/ui/ConversationDropdownMenu/index.js +85 -21
- package/dist/features/ChatClient/ui/ConversationDropdownMenu/utils.d.ts +1 -0
- package/dist/features/ChatClient/ui/ConversationDropdownMenu/utils.js +36 -0
- package/dist/features/JsonEditor/CodeEditor.d.ts +6 -0
- package/dist/features/JsonEditor/CodeEditor.js +122 -0
- package/dist/features/JsonEditor/ErrorMessage.d.ts +3 -0
- package/dist/features/JsonEditor/ErrorMessage.js +67 -0
- package/dist/features/JsonEditor/JsonEditor.d.ts +10 -0
- package/dist/features/JsonEditor/JsonEditor.js +127 -0
- package/dist/features/JsonEditor/Toolbar.d.ts +8 -0
- package/dist/features/JsonEditor/Toolbar.js +61 -0
- package/dist/features/JsonEditor/index.d.ts +1 -1
- package/dist/features/JsonEditor/index.js +1 -1
- package/dist/features/JsonEditor/types.d.ts +8 -0
- package/dist/features/JsonEditor/types.js +0 -0
- package/dist/features/JsonEditor/utils.d.ts +6 -2
- package/dist/features/JsonEditor/utils.js +47 -10
- package/dist/features/ListPage/hooks/useFloat.js +4 -3
- package/package.json +3 -2
- package/dist/features/JsonEditor/Container.d.ts +0 -12
- package/dist/features/JsonEditor/Container.js +0 -122
- package/dist/features/JsonEditor/LineNumberEditor.d.ts +0 -10
- package/dist/features/JsonEditor/LineNumberEditor.js +0 -186
- package/dist/features/JsonEditor/style.d.ts +0 -13
- package/dist/features/JsonEditor/style.js +0 -185
|
@@ -71,12 +71,20 @@ function useActions(props) {
|
|
|
71
71
|
})
|
|
72
72
|
]
|
|
73
73
|
});
|
|
74
|
-
return /*#__PURE__*/ jsxs(
|
|
74
|
+
if ('bottom' === actionPosition) return /*#__PURE__*/ jsxs(HeaderContainer, {
|
|
75
75
|
children: [
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
/*#__PURE__*/ jsxs(FlexContainer, {
|
|
77
|
+
children: [
|
|
78
|
+
icon,
|
|
79
|
+
title
|
|
80
|
+
]
|
|
81
|
+
}),
|
|
82
|
+
/*#__PURE__*/ jsx(ExtraContainer, {
|
|
83
|
+
children: closeButton
|
|
84
|
+
})
|
|
78
85
|
]
|
|
79
86
|
});
|
|
87
|
+
return /*#__PURE__*/ jsx(Fragment, {});
|
|
80
88
|
}, [
|
|
81
89
|
actionPosition,
|
|
82
90
|
icon,
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { PageModalProps } from '.';
|
|
2
2
|
export declare function useCloseButton(props: PageModalProps): {
|
|
3
3
|
closeButton: import("react/jsx-runtime").JSX.Element | null;
|
|
4
|
-
closable: boolean;
|
|
5
|
-
} | {
|
|
6
|
-
closeButton: import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
-
closable?: undefined;
|
|
8
4
|
};
|
|
@@ -4,7 +4,7 @@ import { CloseOutlined } from "@ant-design/icons";
|
|
|
4
4
|
import { styled } from "styled-components";
|
|
5
5
|
import { Button } from "antd";
|
|
6
6
|
function useCloseButton(props) {
|
|
7
|
-
const {
|
|
7
|
+
const { onCancel, closable = true } = props;
|
|
8
8
|
const closeButton = useMemo(()=>{
|
|
9
9
|
if (true === closable) return /*#__PURE__*/ jsx(CloseButton, {
|
|
10
10
|
type: "text",
|
|
@@ -23,10 +23,6 @@ function useCloseButton(props) {
|
|
|
23
23
|
closable,
|
|
24
24
|
onCancel
|
|
25
25
|
]);
|
|
26
|
-
if ('top' === actionPosition) return {
|
|
27
|
-
closeButton,
|
|
28
|
-
closable: false
|
|
29
|
-
};
|
|
30
26
|
return {
|
|
31
27
|
closeButton
|
|
32
28
|
};
|
|
@@ -12,7 +12,7 @@ export declare const FloatProvider: (props: {
|
|
|
12
12
|
export type FloatComponentProps<T extends Record<string, any> = any> = {
|
|
13
13
|
record: T;
|
|
14
14
|
visible: boolean;
|
|
15
|
-
onClose: () => void;
|
|
15
|
+
onClose: (code?: number) => void;
|
|
16
16
|
};
|
|
17
17
|
export type PageFloatProps<T extends Record<string, any> = any> = {
|
|
18
18
|
key: string;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
type BaseConversation = {
|
|
2
2
|
id: string;
|
|
3
3
|
title: string;
|
|
4
|
+
updatedAt?: string | number | Date;
|
|
5
|
+
createdAt?: string | number | Date;
|
|
4
6
|
};
|
|
5
7
|
export interface ConversationDropdownMenuProps<T extends BaseConversation = BaseConversation> {
|
|
6
8
|
request: () => Promise<{
|
|
7
9
|
list: T[];
|
|
8
10
|
}>;
|
|
9
11
|
onSelect?: (item: T) => void;
|
|
12
|
+
onDelete?: (item: T) => void | Promise<void>;
|
|
10
13
|
activeKey?: string;
|
|
11
14
|
}
|
|
12
15
|
export declare const ConversationDropdownMenu: <T extends BaseConversation = any>(props: ConversationDropdownMenuProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,28 +1,58 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { HistoryOutlined } from "@ant-design/icons";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DeleteOutlined, HistoryOutlined } from "@ant-design/icons";
|
|
3
3
|
import { PageLoading } from "../../../../components/index.js";
|
|
4
4
|
import { useRequest } from "ahooks";
|
|
5
5
|
import { Button, Dropdown, Tooltip } from "antd";
|
|
6
6
|
import styled_components from "styled-components";
|
|
7
|
+
import { getConversationsGroup } from "./utils.js";
|
|
7
8
|
const ConversationDropdownMenu = (props)=>{
|
|
8
|
-
const { request, onSelect, activeKey } = props;
|
|
9
|
-
const { data, run: refresh, loading } = useRequest(()=>request(), {
|
|
9
|
+
const { request, onSelect, onDelete, activeKey } = props;
|
|
10
|
+
const { data, run: refresh, loading, mutate } = useRequest(()=>request(), {
|
|
10
11
|
manual: true
|
|
11
12
|
});
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
label:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
const toMenuItem = (item)=>({
|
|
14
|
+
key: item.id,
|
|
15
|
+
label: /*#__PURE__*/ jsxs(MenuItemLabel, {
|
|
16
|
+
children: [
|
|
17
|
+
/*#__PURE__*/ jsx("span", {
|
|
18
|
+
children: item.title || item
|
|
19
|
+
}),
|
|
20
|
+
/*#__PURE__*/ jsx(Button, {
|
|
21
|
+
type: "text",
|
|
22
|
+
size: "small",
|
|
23
|
+
danger: true,
|
|
24
|
+
icon: /*#__PURE__*/ jsx(DeleteOutlined, {}),
|
|
25
|
+
onClick: async (e)=>{
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
await onDelete?.(item);
|
|
29
|
+
const list = (data?.list ?? []).filter((x)=>x.id !== item.id);
|
|
30
|
+
mutate?.({
|
|
31
|
+
list
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
className: "delete-btn"
|
|
35
|
+
})
|
|
36
|
+
]
|
|
37
|
+
}),
|
|
38
|
+
type: 'item',
|
|
39
|
+
onClick: ()=>{
|
|
40
|
+
if (item.id !== activeKey) onSelect?.(item);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const groups = getConversationsGroup(data?.list ?? []);
|
|
44
|
+
const groupEntries = Object.entries(groups).filter(([_, arr])=>arr.length);
|
|
45
|
+
const menus = [];
|
|
46
|
+
groupEntries.forEach(([label, arr], index)=>{
|
|
47
|
+
menus.push({
|
|
48
|
+
type: 'group',
|
|
49
|
+
label,
|
|
50
|
+
children: arr.map((it)=>toMenuItem(it))
|
|
51
|
+
});
|
|
52
|
+
if (index < groupEntries.length - 1) menus.push({
|
|
53
|
+
type: 'divider'
|
|
54
|
+
});
|
|
55
|
+
});
|
|
26
56
|
return /*#__PURE__*/ jsx(Tooltip, {
|
|
27
57
|
title: "历史会话",
|
|
28
58
|
children: /*#__PURE__*/ jsx(Dropdown, {
|
|
@@ -36,11 +66,15 @@ const ConversationDropdownMenu = (props)=>{
|
|
|
36
66
|
popupRender: (originNode)=>{
|
|
37
67
|
if (loading || !data?.list?.length) {
|
|
38
68
|
const content = loading ? /*#__PURE__*/ jsx(PageLoading, {}) : '暂无历史会话';
|
|
39
|
-
return /*#__PURE__*/ jsx(
|
|
40
|
-
children:
|
|
69
|
+
return /*#__PURE__*/ jsx(MenuPanelContainer, {
|
|
70
|
+
children: /*#__PURE__*/ jsx(DefaultPanelContainer, {
|
|
71
|
+
children: content
|
|
72
|
+
})
|
|
41
73
|
});
|
|
42
74
|
}
|
|
43
|
-
return
|
|
75
|
+
return /*#__PURE__*/ jsx(MenuPanelContainer, {
|
|
76
|
+
children: originNode
|
|
77
|
+
});
|
|
44
78
|
},
|
|
45
79
|
children: /*#__PURE__*/ jsx(Button, {
|
|
46
80
|
onClick: refresh,
|
|
@@ -59,6 +93,36 @@ const DefaultPanelContainer = styled_components.div`
|
|
|
59
93
|
border-radius: 8px;
|
|
60
94
|
min-height: 100px;
|
|
61
95
|
min-width: 200px;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
box-shadow:
|
|
100
|
+
0px 0px 4px 0px rgba(0, 0, 0, 0.02),
|
|
101
|
+
0px 6px 12px 0px rgba(47, 53, 64, 0.12);
|
|
102
|
+
`;
|
|
103
|
+
const MenuItemLabel = styled_components.div`
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: space-between;
|
|
107
|
+
gap: 8px;
|
|
108
|
+
width: 100%;
|
|
109
|
+
.delete-btn {
|
|
110
|
+
opacity: 0;
|
|
111
|
+
visibility: hidden;
|
|
112
|
+
transition: opacity 0.2s ease;
|
|
113
|
+
}
|
|
114
|
+
&:hover .delete-btn {
|
|
115
|
+
opacity: 1;
|
|
116
|
+
visibility: visible;
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
const MenuPanelContainer = styled_components.div`
|
|
120
|
+
max-height: 450px;
|
|
121
|
+
overflow: auto;
|
|
122
|
+
min-width: 200px;
|
|
123
|
+
background: #fff;
|
|
124
|
+
border: 1px solid rgba(5, 5, 5, 0.06);
|
|
125
|
+
border-radius: 8px;
|
|
62
126
|
box-shadow:
|
|
63
127
|
0px 0px 4px 0px rgba(0, 0, 0, 0.02),
|
|
64
128
|
0px 6px 12px 0px rgba(47, 53, 64, 0.12);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getConversationsGroup<T>(list: T[]): Record<string, T[]>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
function getConversationsGroup(list) {
|
|
3
|
+
const now = dayjs();
|
|
4
|
+
const startOfWeek = now.startOf('day').subtract(now.day(), 'day');
|
|
5
|
+
const getTime = (it)=>it.updatedAt || it.createdAt || it.time || it.date || void 0;
|
|
6
|
+
const isToday = (t)=>dayjs(t).isSame(now, 'day');
|
|
7
|
+
const isYesterday = (t)=>dayjs(t).isSame(now.clone().subtract(1, 'day'), 'day');
|
|
8
|
+
const inWeek = (t)=>{
|
|
9
|
+
const d = dayjs(t);
|
|
10
|
+
return d.isAfter(startOfWeek) || d.isSame(startOfWeek, 'day');
|
|
11
|
+
};
|
|
12
|
+
const inMonth = (t)=>dayjs(t).isSame(now, 'month');
|
|
13
|
+
const today = [];
|
|
14
|
+
const yesterday = [];
|
|
15
|
+
const week = [];
|
|
16
|
+
const month = [];
|
|
17
|
+
const year = [];
|
|
18
|
+
list.forEach((it)=>{
|
|
19
|
+
const t = getTime(it);
|
|
20
|
+
if (!t) return;
|
|
21
|
+
if (isToday(t)) return void today.push(it);
|
|
22
|
+
if (isYesterday(t)) return void yesterday.push(it);
|
|
23
|
+
if (inWeek(t)) return void week.push(it);
|
|
24
|
+
if (inMonth(t)) return void month.push(it);
|
|
25
|
+
if (dayjs(t).isSame(now, 'year')) return void year.push(it);
|
|
26
|
+
});
|
|
27
|
+
const groups = {
|
|
28
|
+
今天: today,
|
|
29
|
+
昨天: yesterday,
|
|
30
|
+
本周内: week,
|
|
31
|
+
本月内: month,
|
|
32
|
+
今年内: year
|
|
33
|
+
};
|
|
34
|
+
return groups;
|
|
35
|
+
}
|
|
36
|
+
export { getConversationsGroup };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import styled_components from "styled-components";
|
|
4
|
+
import { highlightJson } from "./utils.js";
|
|
5
|
+
const EditorContainer = styled_components.div`
|
|
6
|
+
position: relative;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
10
|
+
'Liberation Mono', 'Courier New', monospace;
|
|
11
|
+
font-size: 0.875rem;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
background: #0d1117;
|
|
14
|
+
`;
|
|
15
|
+
const LineNumbers = styled_components.pre`
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
bottom: 0;
|
|
19
|
+
left: 0;
|
|
20
|
+
width: 56px;
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 1rem 0.75rem;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
white-space: pre;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
color: #6b7280;
|
|
27
|
+
text-align: right;
|
|
28
|
+
user-select: none;
|
|
29
|
+
line-height: 1.625;
|
|
30
|
+
`;
|
|
31
|
+
const HighlightedPre = styled_components.pre`
|
|
32
|
+
position: absolute;
|
|
33
|
+
top: 0;
|
|
34
|
+
right: 0;
|
|
35
|
+
bottom: 0;
|
|
36
|
+
left: 0;
|
|
37
|
+
margin: 0;
|
|
38
|
+
padding: 1rem 1rem 1rem 3.5rem;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
white-space: pre-wrap;
|
|
41
|
+
word-break: break-word;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
line-height: 1.625;
|
|
44
|
+
|
|
45
|
+
span.json-token.key {
|
|
46
|
+
color: #7dd3fc;
|
|
47
|
+
}
|
|
48
|
+
span.json-token.string {
|
|
49
|
+
color: #6ee7b7;
|
|
50
|
+
}
|
|
51
|
+
span.json-token.boolean {
|
|
52
|
+
color: #d8b4fe;
|
|
53
|
+
}
|
|
54
|
+
span.json-token.null {
|
|
55
|
+
color: #fb7185;
|
|
56
|
+
}
|
|
57
|
+
span.json-token.number {
|
|
58
|
+
color: #fdba74;
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
const TextAreaOverlay = styled_components.textarea`
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 0;
|
|
64
|
+
right: 0;
|
|
65
|
+
bottom: 0;
|
|
66
|
+
left: 0;
|
|
67
|
+
width: 100%;
|
|
68
|
+
height: 100%;
|
|
69
|
+
padding: 1rem 1rem 1rem 3.5rem;
|
|
70
|
+
background: transparent;
|
|
71
|
+
color: transparent;
|
|
72
|
+
caret-color: #ffffff;
|
|
73
|
+
outline: none;
|
|
74
|
+
resize: none;
|
|
75
|
+
white-space: pre-wrap;
|
|
76
|
+
overflow: auto;
|
|
77
|
+
z-index: 10;
|
|
78
|
+
transition: color 0.2s ease;
|
|
79
|
+
`;
|
|
80
|
+
const CodeEditor = ({ value, onChange })=>{
|
|
81
|
+
const textareaRef = useRef(null);
|
|
82
|
+
const preRef = useRef(null);
|
|
83
|
+
const lineRef = useRef(null);
|
|
84
|
+
const handleScroll = ()=>{
|
|
85
|
+
if (textareaRef.current) {
|
|
86
|
+
if (preRef.current) {
|
|
87
|
+
preRef.current.scrollTop = textareaRef.current.scrollTop;
|
|
88
|
+
preRef.current.scrollLeft = textareaRef.current.scrollLeft;
|
|
89
|
+
}
|
|
90
|
+
if (lineRef.current) lineRef.current.scrollTop = textareaRef.current.scrollTop;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const lineCount = value ? value.split('\n').length : 1;
|
|
94
|
+
const lineNumbers = Array.from({
|
|
95
|
+
length: lineCount
|
|
96
|
+
}, (_, i)=>`${i + 1}`).join('\n');
|
|
97
|
+
return /*#__PURE__*/ jsxs(EditorContainer, {
|
|
98
|
+
children: [
|
|
99
|
+
/*#__PURE__*/ jsx(LineNumbers, {
|
|
100
|
+
ref: lineRef,
|
|
101
|
+
"aria-hidden": "true",
|
|
102
|
+
children: lineNumbers
|
|
103
|
+
}),
|
|
104
|
+
/*#__PURE__*/ jsx(HighlightedPre, {
|
|
105
|
+
ref: preRef,
|
|
106
|
+
"aria-hidden": "true",
|
|
107
|
+
dangerouslySetInnerHTML: {
|
|
108
|
+
__html: highlightJson(value) + '<br>'
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
/*#__PURE__*/ jsx(TextAreaOverlay, {
|
|
112
|
+
ref: textareaRef,
|
|
113
|
+
value: value,
|
|
114
|
+
onChange: (e)=>onChange(e.target.value),
|
|
115
|
+
onScroll: handleScroll,
|
|
116
|
+
spellCheck: false,
|
|
117
|
+
placeholder: "在这里输入 JSON..."
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
export { CodeEditor };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { WarningOutlined } from "@ant-design/icons";
|
|
3
|
+
import { Tooltip } from "antd";
|
|
4
|
+
import styled_components from "styled-components";
|
|
5
|
+
const ErrorContent = styled_components.div`
|
|
6
|
+
height: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
padding: 2rem;
|
|
12
|
+
text-align: center;
|
|
13
|
+
color: #ef4444;
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
`;
|
|
16
|
+
const ErrorIconSvg = styled_components.svg`
|
|
17
|
+
width: 3rem;
|
|
18
|
+
height: 3rem;
|
|
19
|
+
margin-bottom: 1rem;
|
|
20
|
+
`;
|
|
21
|
+
const ErrorTitle = styled_components.h3`
|
|
22
|
+
font-size: 1.125rem;
|
|
23
|
+
font-weight: 700;
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
`;
|
|
26
|
+
const ErrorText = styled_components.p`
|
|
27
|
+
font-size: 0.875rem;
|
|
28
|
+
word-break: break-all;
|
|
29
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
30
|
+
'Liberation Mono', 'Courier New', monospace;
|
|
31
|
+
`;
|
|
32
|
+
const StyledWarningIcon = styled_components(WarningOutlined)`
|
|
33
|
+
color: red;
|
|
34
|
+
font-size: 14px;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
`;
|
|
37
|
+
const ErrorMessage = (props)=>{
|
|
38
|
+
const { error } = props;
|
|
39
|
+
if (!error) return /*#__PURE__*/ jsx(Fragment, {});
|
|
40
|
+
return /*#__PURE__*/ jsx(Tooltip, {
|
|
41
|
+
color: "white",
|
|
42
|
+
title: /*#__PURE__*/ jsxs(ErrorContent, {
|
|
43
|
+
children: [
|
|
44
|
+
/*#__PURE__*/ jsx(ErrorIconSvg, {
|
|
45
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
46
|
+
fill: "none",
|
|
47
|
+
viewBox: "0 0 24 24",
|
|
48
|
+
stroke: "currentColor",
|
|
49
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
50
|
+
strokeLinecap: "round",
|
|
51
|
+
strokeLinejoin: "round",
|
|
52
|
+
strokeWidth: 2,
|
|
53
|
+
d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
54
|
+
})
|
|
55
|
+
}),
|
|
56
|
+
/*#__PURE__*/ jsx(ErrorTitle, {
|
|
57
|
+
children: "JSON 格式错误"
|
|
58
|
+
}),
|
|
59
|
+
/*#__PURE__*/ jsx(ErrorText, {
|
|
60
|
+
children: error
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
children: /*#__PURE__*/ jsx(StyledWarningIcon, {})
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
export { ErrorMessage };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type JsonValue } from './types';
|
|
2
|
+
export interface JsonEditorProps {
|
|
3
|
+
defaultValue?: JsonValue;
|
|
4
|
+
value?: JsonValue;
|
|
5
|
+
onChange?: (v: JsonValue) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface JsonEditorHandle {
|
|
8
|
+
update: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const JsonEditor: import("react").ForwardRefExoticComponent<JsonEditorProps & import("react").RefAttributes<JsonEditorHandle>>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Flex, message } from "antd";
|
|
3
|
+
import { forwardRef, useImperativeHandle, useState } from "react";
|
|
4
|
+
import { Card } from "../../components/index.js";
|
|
5
|
+
import styled_components from "styled-components";
|
|
6
|
+
import { Toolbar } from "./Toolbar.js";
|
|
7
|
+
import { CodeEditor } from "./CodeEditor.js";
|
|
8
|
+
import { formatJson, minifyJson, validateJson } from "./utils.js";
|
|
9
|
+
import { ErrorMessage } from "./ErrorMessage.js";
|
|
10
|
+
const EditorWrapper = styled_components.div`
|
|
11
|
+
flex: 1;
|
|
12
|
+
display: flex;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
position: relative;
|
|
15
|
+
min-height: 200px;
|
|
16
|
+
height: 100%;
|
|
17
|
+
color: #e5e7eb;
|
|
18
|
+
`;
|
|
19
|
+
const LeftPane = styled_components.div`
|
|
20
|
+
flex: 1;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
border-right: 1px solid #1f2937;
|
|
24
|
+
transition: all 300ms ease;
|
|
25
|
+
width: 100%;
|
|
26
|
+
`;
|
|
27
|
+
const EditorBody = styled_components.div`
|
|
28
|
+
flex: 1;
|
|
29
|
+
position: relative;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
`;
|
|
32
|
+
const InfoBar = styled_components.div`
|
|
33
|
+
position: absolute;
|
|
34
|
+
bottom: 8px;
|
|
35
|
+
right: 16px;
|
|
36
|
+
font-size: 12px;
|
|
37
|
+
color: #6b7280;
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
background: rgba(17, 24, 39, 0.8);
|
|
40
|
+
padding: 4px 8px;
|
|
41
|
+
border-radius: 4px;
|
|
42
|
+
`;
|
|
43
|
+
const JsonEditor = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
44
|
+
const { value, onChange, defaultValue } = props;
|
|
45
|
+
const initialText = void 0 !== value ? JSON.stringify(value, null, 2) : defaultValue ? JSON.stringify(defaultValue, null, 2) : '';
|
|
46
|
+
const [jsonInput, setJsonInput] = useState(initialText);
|
|
47
|
+
const [validation, setValidation] = useState({
|
|
48
|
+
isValid: true,
|
|
49
|
+
data: value
|
|
50
|
+
});
|
|
51
|
+
const updateInput = (next)=>{
|
|
52
|
+
setJsonInput(next);
|
|
53
|
+
const result = validateJson(next);
|
|
54
|
+
setValidation(result);
|
|
55
|
+
if (onChange) if (next.trim()) {
|
|
56
|
+
if (result.isValid) {
|
|
57
|
+
console.log(result);
|
|
58
|
+
onChange(result.data);
|
|
59
|
+
}
|
|
60
|
+
} else onChange(null);
|
|
61
|
+
};
|
|
62
|
+
useImperativeHandle(ref, ()=>({
|
|
63
|
+
update: ()=>{
|
|
64
|
+
updateInput(initialText);
|
|
65
|
+
}
|
|
66
|
+
}), [
|
|
67
|
+
initialText,
|
|
68
|
+
updateInput
|
|
69
|
+
]);
|
|
70
|
+
const handleFormat = ()=>{
|
|
71
|
+
const formatted = formatJson(jsonInput);
|
|
72
|
+
updateInput(formatted);
|
|
73
|
+
};
|
|
74
|
+
const handleMinify = ()=>{
|
|
75
|
+
const minified = minifyJson(jsonInput);
|
|
76
|
+
updateInput(minified);
|
|
77
|
+
};
|
|
78
|
+
const handleCopy = ()=>{
|
|
79
|
+
navigator.clipboard.writeText(jsonInput);
|
|
80
|
+
message.success('复制成功');
|
|
81
|
+
};
|
|
82
|
+
return /*#__PURE__*/ jsx(Card, {
|
|
83
|
+
title: /*#__PURE__*/ jsxs(Flex, {
|
|
84
|
+
align: "center",
|
|
85
|
+
gap: 6,
|
|
86
|
+
children: [
|
|
87
|
+
/*#__PURE__*/ jsx("span", {
|
|
88
|
+
children: "JSON"
|
|
89
|
+
}),
|
|
90
|
+
/*#__PURE__*/ jsx(ErrorMessage, {
|
|
91
|
+
error: validation.error
|
|
92
|
+
})
|
|
93
|
+
]
|
|
94
|
+
}),
|
|
95
|
+
extra: /*#__PURE__*/ jsx(Flex, {
|
|
96
|
+
align: "center",
|
|
97
|
+
gap: 8,
|
|
98
|
+
children: /*#__PURE__*/ jsx(Toolbar, {
|
|
99
|
+
disabled: !validation.isValid,
|
|
100
|
+
onFormat: handleFormat,
|
|
101
|
+
onMinify: handleMinify,
|
|
102
|
+
onCopy: handleCopy
|
|
103
|
+
})
|
|
104
|
+
}),
|
|
105
|
+
children: /*#__PURE__*/ jsx(EditorWrapper, {
|
|
106
|
+
children: /*#__PURE__*/ jsx(LeftPane, {
|
|
107
|
+
children: /*#__PURE__*/ jsxs(EditorBody, {
|
|
108
|
+
children: [
|
|
109
|
+
/*#__PURE__*/ jsx(CodeEditor, {
|
|
110
|
+
value: jsonInput,
|
|
111
|
+
onChange: updateInput
|
|
112
|
+
}),
|
|
113
|
+
/*#__PURE__*/ jsxs(InfoBar, {
|
|
114
|
+
children: [
|
|
115
|
+
"长度: ",
|
|
116
|
+
jsonInput.length,
|
|
117
|
+
" | 行数: ",
|
|
118
|
+
jsonInput.split('\n').length
|
|
119
|
+
]
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
export { JsonEditor };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ToolbarProps {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
onFormat: () => void;
|
|
4
|
+
onMinify: () => void;
|
|
5
|
+
onCopy: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const Toolbar: ({ onFormat, onMinify, onCopy, disabled, }: ToolbarProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Tooltip } from "antd";
|
|
3
|
+
import { Braces, Copy, FileArchive } from "lucide-react";
|
|
4
|
+
import styled_components from "styled-components";
|
|
5
|
+
const ToolbarContainer = styled_components.div`
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
`;
|
|
9
|
+
const ActionButton = styled_components(Button)`
|
|
10
|
+
&:hover {
|
|
11
|
+
color: #ffffff;
|
|
12
|
+
background-color: rgba(31, 41, 55, 0.5);
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
const Toolbar = ({ onFormat, onMinify, onCopy, disabled })=>/*#__PURE__*/ jsxs(ToolbarContainer, {
|
|
16
|
+
children: [
|
|
17
|
+
/*#__PURE__*/ jsx(Tooltip, {
|
|
18
|
+
title: "格式化",
|
|
19
|
+
children: /*#__PURE__*/ jsx(ActionButton, {
|
|
20
|
+
disabled: disabled,
|
|
21
|
+
size: "small",
|
|
22
|
+
type: "text",
|
|
23
|
+
onClick: onFormat,
|
|
24
|
+
icon: /*#__PURE__*/ jsx(Braces, {
|
|
25
|
+
style: {
|
|
26
|
+
fontSize: 16
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
}),
|
|
31
|
+
/*#__PURE__*/ jsx(Tooltip, {
|
|
32
|
+
title: "压缩",
|
|
33
|
+
children: /*#__PURE__*/ jsx(ActionButton, {
|
|
34
|
+
disabled: disabled,
|
|
35
|
+
size: "small",
|
|
36
|
+
type: "text",
|
|
37
|
+
onClick: onMinify,
|
|
38
|
+
icon: /*#__PURE__*/ jsx(FileArchive, {
|
|
39
|
+
style: {
|
|
40
|
+
fontSize: 16
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
}),
|
|
45
|
+
/*#__PURE__*/ jsx(Tooltip, {
|
|
46
|
+
title: "复制",
|
|
47
|
+
children: /*#__PURE__*/ jsx(ActionButton, {
|
|
48
|
+
disabled: disabled,
|
|
49
|
+
size: "small",
|
|
50
|
+
type: "text",
|
|
51
|
+
onClick: onCopy,
|
|
52
|
+
icon: /*#__PURE__*/ jsx(Copy, {
|
|
53
|
+
style: {
|
|
54
|
+
fontSize: 16
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
export { Toolbar };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { JsonEditor, type JsonEditorProps } from './
|
|
1
|
+
export { JsonEditor, type JsonEditorProps } from './JsonEditor';
|