@blocklet/labels 1.5.201 → 1.5.203
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/label/context.d.ts +3 -2
- package/dist/components/label/index.d.ts +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +751 -441
- package/dist/index.umd.js +901 -3
- package/dist/label2/labels-context.d.ts +21 -0
- package/dist/label2/tree.d.ts +34 -0
- package/dist/types.d.ts +15 -0
- package/package.json +8 -4
package/dist/index.es.js
CHANGED
|
@@ -1,82 +1,109 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => {
|
|
4
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
8
|
+
import { createContext, useRef, useState, useCallback, useEffect, useMemo, useContext } from "react";
|
|
9
|
+
import { ExpandMore, Add, EditOutlined, DeleteOutlineOutlined, Edit, LabelOutlined } from "@mui/icons-material";
|
|
10
|
+
import Box from "@mui/material/Box";
|
|
11
|
+
import { styled } from "@mui/material/styles";
|
|
12
|
+
import { Tree } from "react-arborist";
|
|
13
|
+
import { Icon } from "@iconify/react";
|
|
14
|
+
import omit from "lodash/omit";
|
|
15
|
+
import Select, { components } from "react-select";
|
|
16
|
+
import Typography from "@mui/material/Typography";
|
|
17
|
+
import Button from "@mui/material/Button";
|
|
18
|
+
import IconButton from "@mui/material/IconButton";
|
|
19
|
+
import { TranslationTextField, TranslationTag } from "@blocklet/translation-input";
|
|
20
|
+
import { useSetState, useReactive, useRequest } from "ahooks";
|
|
21
|
+
import TextField from "@mui/material/TextField";
|
|
22
|
+
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
|
23
|
+
import { GithubPicker } from "react-color";
|
|
24
|
+
import Dialog from "@arcblock/ux/lib/Dialog";
|
|
25
|
+
import Alert from "@mui/material/Alert";
|
|
26
|
+
import { ClickAwayListener as ClickAwayListener$1, Box as Box$1, Chip, IconButton as IconButton$1, Button as Button$1, getContrastRatio, alpha } from "@mui/material";
|
|
27
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
28
|
+
import { createContainer } from "unstated-next";
|
|
29
|
+
import { arrayToTree } from "performant-array-to-tree";
|
|
30
|
+
const materialSymbolsLabelRounded = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em", ...props, children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M5 19q-.825 0-1.413-.587Q3 17.825 3 17V7q0-.825.587-1.412Q4.175 5 5 5h10q.5 0 .938.225q.437.225.712.625l3.525 5q.375.525.375 1.15q0 .625-.375 1.15l-3.525 5q-.275.4-.712.625Q15.5 19 15 19Z" }) });
|
|
31
|
+
const materialSymbolsCheckSmall = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em", ...props, children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "m10 16.4l-4-4L7.4 11l2.6 2.6L16.6 7L18 8.4Z" }) });
|
|
32
|
+
const LabelTreeContext = createContext({});
|
|
33
|
+
function LabelTreeProvider({
|
|
34
|
+
selectable = false,
|
|
35
|
+
selected = [],
|
|
36
|
+
onSelect,
|
|
37
|
+
renderItem,
|
|
38
|
+
children
|
|
28
39
|
}) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
const isSelected = (id) => selected.indexOf(id) !== -1;
|
|
41
|
+
const select = (id) => {
|
|
42
|
+
Array.from(new Set(selected).add(id));
|
|
43
|
+
onSelect == null ? void 0 : onSelect(id);
|
|
44
|
+
};
|
|
45
|
+
const value = useMemo(
|
|
32
46
|
() => ({
|
|
33
|
-
selectable
|
|
34
|
-
selected
|
|
35
|
-
isSelected
|
|
36
|
-
select
|
|
37
|
-
renderItem
|
|
47
|
+
selectable,
|
|
48
|
+
selected,
|
|
49
|
+
isSelected,
|
|
50
|
+
select,
|
|
51
|
+
renderItem
|
|
38
52
|
}),
|
|
39
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
-
[
|
|
54
|
+
[selected]
|
|
41
55
|
);
|
|
42
|
-
return /* @__PURE__ */
|
|
56
|
+
return /* @__PURE__ */ jsx(LabelTreeContext.Provider, { value, children });
|
|
43
57
|
}
|
|
44
|
-
const
|
|
45
|
-
var
|
|
46
|
-
return
|
|
58
|
+
const isLeaf = (node) => {
|
|
59
|
+
var _a;
|
|
60
|
+
return node.isLeaf || !((_a = node.data.children) == null ? void 0 : _a.length);
|
|
47
61
|
};
|
|
48
|
-
function
|
|
49
|
-
|
|
62
|
+
function FolderArrow({ node }) {
|
|
63
|
+
if (isLeaf(node))
|
|
64
|
+
return /* @__PURE__ */ jsx("span", {});
|
|
65
|
+
return node.isOpen ? /* @__PURE__ */ jsx(ExpandMore, { style: { fontSize: 20 } }) : /* @__PURE__ */ jsx(ExpandMore, { style: { fontSize: 20, transform: "rotate(-90deg)" } });
|
|
50
66
|
}
|
|
51
|
-
function
|
|
52
|
-
const { selectable
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
function Node({ node, style, dragHandle }) {
|
|
68
|
+
const { selectable, select, isSelected, renderItem } = useContext(LabelTreeContext);
|
|
69
|
+
const { data } = node;
|
|
70
|
+
const icon = data.icon ? /* @__PURE__ */ jsx(Icon, { icon: data.icon, style: { fontSize: 18 } }) : /* @__PURE__ */ jsx(materialSymbolsLabelRounded, { style: { fontSize: 18, color: data.color || "#ddd" } });
|
|
71
|
+
const handleToggle = (e) => {
|
|
72
|
+
e.stopPropagation();
|
|
73
|
+
if (node.isInternal) {
|
|
74
|
+
node.toggle();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const handleClick = (e) => {
|
|
78
|
+
e.stopPropagation();
|
|
79
|
+
if (selectable) {
|
|
80
|
+
select(node.id);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const children = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
84
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", flex: 1 }, children: [
|
|
85
|
+
/* @__PURE__ */ jsx(Box, { sx: { display: "flex", cursor: "pointer" }, onClick: handleToggle, children: /* @__PURE__ */ jsx(FolderArrow, { node }) }),
|
|
86
|
+
/* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", width: 22, height: 22 }, children: icon }),
|
|
87
|
+
/* @__PURE__ */ jsx(Box, { component: "span", sx: { color: "grey.700" }, children: node.data.name })
|
|
61
88
|
] }),
|
|
62
|
-
|
|
89
|
+
selectable && isSelected(node.id) && /* @__PURE__ */ jsx(materialSymbolsCheckSmall, {})
|
|
63
90
|
] });
|
|
64
|
-
return /* @__PURE__ */
|
|
65
|
-
|
|
91
|
+
return /* @__PURE__ */ jsxs(
|
|
92
|
+
Box,
|
|
66
93
|
{
|
|
67
94
|
className: "label-tree-item",
|
|
68
|
-
style
|
|
95
|
+
style,
|
|
69
96
|
sx: { display: "flex", alignItems: "center", justifyContent: "space-between" },
|
|
70
|
-
ref:
|
|
71
|
-
onClick:
|
|
97
|
+
ref: dragHandle,
|
|
98
|
+
onClick: handleClick,
|
|
72
99
|
children: [
|
|
73
|
-
!
|
|
74
|
-
!!
|
|
100
|
+
!renderItem && children,
|
|
101
|
+
!!renderItem && renderItem(children, node.data)
|
|
75
102
|
]
|
|
76
103
|
}
|
|
77
104
|
);
|
|
78
105
|
}
|
|
79
|
-
const
|
|
106
|
+
const Root = styled(Box)`
|
|
80
107
|
> div,
|
|
81
108
|
> div > div,
|
|
82
109
|
> div > div > div {
|
|
@@ -90,71 +117,114 @@ const Ie = R(f)`
|
|
|
90
117
|
width: 100%;
|
|
91
118
|
}
|
|
92
119
|
`;
|
|
93
|
-
function
|
|
94
|
-
data
|
|
95
|
-
selectable
|
|
96
|
-
selected
|
|
97
|
-
onSelect
|
|
98
|
-
rowHeight
|
|
99
|
-
renderItem
|
|
100
|
-
sx
|
|
101
|
-
...
|
|
120
|
+
function LabelTree({
|
|
121
|
+
data,
|
|
122
|
+
selectable,
|
|
123
|
+
selected = [],
|
|
124
|
+
onSelect,
|
|
125
|
+
rowHeight = 28,
|
|
126
|
+
renderItem,
|
|
127
|
+
sx,
|
|
128
|
+
...rest
|
|
102
129
|
}) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}, [
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
const tree = useRef();
|
|
131
|
+
const mergedSx = [...Array.isArray(sx) ? sx : [sx]];
|
|
132
|
+
const [height, setHeight] = useState(0);
|
|
133
|
+
const updateHeight = useCallback(() => {
|
|
134
|
+
var _a, _b;
|
|
135
|
+
setHeight((((_b = (_a = tree.current) == null ? void 0 : _a.visibleNodes) == null ? void 0 : _b.length) || 0) * rowHeight);
|
|
136
|
+
}, [rowHeight]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
updateHeight();
|
|
139
|
+
}, [data, updateHeight]);
|
|
140
|
+
const handleToggle = () => {
|
|
141
|
+
setTimeout(updateHeight);
|
|
142
|
+
};
|
|
143
|
+
return /* @__PURE__ */ jsx(LabelTreeProvider, { selectable, selected, onSelect, renderItem, children: /* @__PURE__ */ jsx(Root, { ...rest, sx: mergedSx, children: /* @__PURE__ */ jsx(Tree, { data, rowHeight, height, indent: 32, ref: tree, onToggle: handleToggle, children: Node }) }) });
|
|
112
144
|
}
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
145
|
+
const flatten = (data) => {
|
|
146
|
+
return data.reduce((acc, cur) => {
|
|
147
|
+
var _a;
|
|
148
|
+
acc.push(cur);
|
|
149
|
+
if ((_a = cur.children) == null ? void 0 : _a.length) {
|
|
150
|
+
acc.push(...flatten(cur.children));
|
|
151
|
+
}
|
|
152
|
+
return acc;
|
|
153
|
+
}, []);
|
|
154
|
+
};
|
|
155
|
+
const copyTree = (data) => {
|
|
156
|
+
return data.map((item) => {
|
|
157
|
+
var _a;
|
|
158
|
+
if ((_a = item.children) == null ? void 0 : _a.length) {
|
|
159
|
+
item.children = copyTree(item.children);
|
|
160
|
+
}
|
|
161
|
+
return item;
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
const safeParseJSON = (json, defaultValue = null) => {
|
|
120
165
|
try {
|
|
121
|
-
return JSON.parse(
|
|
166
|
+
return JSON.parse(json);
|
|
122
167
|
} catch {
|
|
123
|
-
return
|
|
168
|
+
return defaultValue;
|
|
124
169
|
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
170
|
+
};
|
|
171
|
+
const transformLabels = (labelDtos) => {
|
|
172
|
+
const relations = labelDtos.reduce((acc, cur) => {
|
|
173
|
+
acc[cur.id] = acc[cur.id] || {};
|
|
174
|
+
if (cur.parentId) {
|
|
175
|
+
acc[cur.id].parent = cur.parentId;
|
|
176
|
+
acc[cur.parentId] = acc[cur.parentId] || {};
|
|
177
|
+
acc[cur.parentId].children = acc[cur.parentId].children || [];
|
|
178
|
+
acc[cur.parentId].children.push(cur.id);
|
|
179
|
+
}
|
|
180
|
+
return acc;
|
|
181
|
+
}, {});
|
|
182
|
+
const labels = labelDtos.map(({ parentId, translation, ...rest }) => ({
|
|
183
|
+
...rest,
|
|
184
|
+
translation: safeParseJSON(translation),
|
|
129
185
|
children: []
|
|
130
|
-
}))
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
186
|
+
}));
|
|
187
|
+
const labelsKeyById = labels.reduce((acc, cur) => ({ ...acc, [cur.id]: cur }), {});
|
|
188
|
+
labels.forEach((item) => {
|
|
189
|
+
const relation = relations[item.id];
|
|
190
|
+
if (relation.parent && labelsKeyById[relation.parent]) {
|
|
191
|
+
item.parent = labelsKeyById[relation.parent];
|
|
192
|
+
}
|
|
193
|
+
if (relation.children) {
|
|
194
|
+
item.children = relation.children.map((childId) => labelsKeyById[childId]);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
return labels.filter((item) => !item.parent);
|
|
198
|
+
};
|
|
199
|
+
const LabelMenuList = (props) => {
|
|
200
|
+
const { options, getValue, selectProps, selectOption } = props;
|
|
201
|
+
const value = getValue();
|
|
202
|
+
const handleSelect = (id) => {
|
|
203
|
+
const option = options.find((item) => item.data.id === id);
|
|
204
|
+
if (option) {
|
|
205
|
+
selectOption(option);
|
|
206
|
+
}
|
|
139
207
|
};
|
|
140
|
-
return /* @__PURE__ */
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
|
|
208
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
209
|
+
/* @__PURE__ */ jsx(components.MenuList, { ...omit(props, ["addon"]), children: /* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1 }, children: /* @__PURE__ */ jsx(
|
|
210
|
+
LabelTree,
|
|
143
211
|
{
|
|
144
|
-
data:
|
|
145
|
-
selectable:
|
|
146
|
-
onSelect:
|
|
147
|
-
selected:
|
|
212
|
+
data: selectProps.data,
|
|
213
|
+
selectable: true,
|
|
214
|
+
onSelect: handleSelect,
|
|
215
|
+
selected: value.map((item) => item.data.id)
|
|
148
216
|
}
|
|
149
217
|
) }) }),
|
|
150
|
-
|
|
218
|
+
props.addon
|
|
151
219
|
] });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
220
|
+
};
|
|
221
|
+
const ValueContainer = ({ children, ...props }) => {
|
|
222
|
+
const [values, input] = children;
|
|
223
|
+
const count = Array.isArray(values) ? values.length : 0;
|
|
224
|
+
return /* @__PURE__ */ jsxs(components.ValueContainer, { ...props, children: [
|
|
225
|
+
/* @__PURE__ */ jsx(Box, { component: "span", sx: { fontSize: 13, fontWeight: "bold" }, children: count ? "Labels" : "Filter by labels" }),
|
|
226
|
+
!!count && /* @__PURE__ */ jsx(
|
|
227
|
+
Box,
|
|
158
228
|
{
|
|
159
229
|
sx: {
|
|
160
230
|
display: "inline-flex",
|
|
@@ -167,250 +237,242 @@ const B = (e) => e.reduce((t, s) => {
|
|
|
167
237
|
fontSize: 12,
|
|
168
238
|
bgcolor: "grey.200"
|
|
169
239
|
},
|
|
170
|
-
children:
|
|
240
|
+
children: count
|
|
171
241
|
}
|
|
172
242
|
),
|
|
173
|
-
|
|
243
|
+
input
|
|
174
244
|
] });
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
245
|
+
};
|
|
246
|
+
const compactSelectStyles = {
|
|
247
|
+
control: (provided) => ({
|
|
248
|
+
...provided,
|
|
178
249
|
minHeight: 31
|
|
179
250
|
}),
|
|
180
|
-
valueContainer: (
|
|
181
|
-
...
|
|
251
|
+
valueContainer: (provided) => ({
|
|
252
|
+
...provided,
|
|
182
253
|
display: "flex"
|
|
183
254
|
}),
|
|
184
|
-
dropdownIndicator: (
|
|
185
|
-
...
|
|
255
|
+
dropdownIndicator: (provided) => ({
|
|
256
|
+
...provided,
|
|
186
257
|
padding: "0 4px"
|
|
187
258
|
}),
|
|
188
|
-
clearIndicator: (
|
|
189
|
-
...
|
|
259
|
+
clearIndicator: (provided) => ({
|
|
260
|
+
...provided,
|
|
190
261
|
padding: "4px"
|
|
191
262
|
})
|
|
192
263
|
};
|
|
193
|
-
function
|
|
194
|
-
data
|
|
195
|
-
value
|
|
196
|
-
editable
|
|
197
|
-
addon
|
|
198
|
-
onChange
|
|
199
|
-
compact
|
|
200
|
-
isMulti
|
|
201
|
-
maxHeight
|
|
202
|
-
...
|
|
264
|
+
function LabelPicker({
|
|
265
|
+
data,
|
|
266
|
+
value = [],
|
|
267
|
+
editable = false,
|
|
268
|
+
addon,
|
|
269
|
+
onChange,
|
|
270
|
+
compact,
|
|
271
|
+
isMulti = true,
|
|
272
|
+
maxHeight = 264,
|
|
273
|
+
...rest
|
|
203
274
|
}) {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
275
|
+
const selectRef = useRef(null);
|
|
276
|
+
const options = useMemo(
|
|
277
|
+
() => flatten(data).map((item) => ({ data: item, label: item.name, value: item.id })),
|
|
278
|
+
[data]
|
|
279
|
+
);
|
|
280
|
+
const optionsKeyById = options.reduce(
|
|
281
|
+
(acc, cur) => ({ ...acc, [cur.data.id]: cur }),
|
|
209
282
|
{}
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
283
|
+
);
|
|
284
|
+
const valueArr = Array.isArray(value) ? value : [value];
|
|
285
|
+
const selectedOptions = valueArr.map((item) => optionsKeyById[item]);
|
|
286
|
+
const changes = useRef({
|
|
287
|
+
prev: valueArr.map((item) => {
|
|
288
|
+
var _a;
|
|
289
|
+
return ((_a = optionsKeyById[item]) == null ? void 0 : _a.data) || "";
|
|
214
290
|
})
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
291
|
+
});
|
|
292
|
+
const handleChange = (v, actionMeta) => {
|
|
293
|
+
if (!isMulti || !Array.isArray(v)) {
|
|
294
|
+
onChange(v ? [v.data] : []);
|
|
295
|
+
} else {
|
|
296
|
+
changes.current.current = v.map((item) => item.data);
|
|
297
|
+
}
|
|
298
|
+
if (["clear", "remove-value"].includes(actionMeta.action) && selectRef.current) {
|
|
299
|
+
setTimeout(() => {
|
|
300
|
+
var _a;
|
|
301
|
+
return (_a = selectRef == null ? void 0 : selectRef.current) == null ? void 0 : _a.blur();
|
|
302
|
+
}, 1);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
const handleMenuClose = () => {
|
|
306
|
+
if (changes.current.current) {
|
|
307
|
+
const changed = JSON.stringify(changes.current.prev.map((item) => item.id)) !== JSON.stringify(changes.current.current.map((item) => item.id));
|
|
308
|
+
if (changed) {
|
|
309
|
+
onChange(changes.current.current);
|
|
310
|
+
changes.current.prev = changes.current.current;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
222
313
|
};
|
|
223
|
-
return /* @__PURE__ */
|
|
224
|
-
|
|
314
|
+
return /* @__PURE__ */ jsx(Box, { ...rest, children: /* @__PURE__ */ jsx(
|
|
315
|
+
Select,
|
|
225
316
|
{
|
|
226
|
-
ref:
|
|
227
|
-
defaultValue:
|
|
228
|
-
options
|
|
229
|
-
onChange:
|
|
317
|
+
ref: selectRef,
|
|
318
|
+
defaultValue: selectedOptions,
|
|
319
|
+
options,
|
|
320
|
+
onChange: handleChange,
|
|
230
321
|
components: {
|
|
231
322
|
// @ts-ignore
|
|
232
|
-
MenuList: (
|
|
323
|
+
MenuList: (props) => /* @__PURE__ */ jsx(LabelMenuList, { ...props, addon }),
|
|
233
324
|
// eslint-disable-line react/no-unstable-nested-components
|
|
234
|
-
...
|
|
325
|
+
...compact && { ValueContainer }
|
|
235
326
|
},
|
|
236
327
|
placeholder: "Select labels",
|
|
237
328
|
styles: {
|
|
238
|
-
...
|
|
239
|
-
menu: (
|
|
240
|
-
...
|
|
241
|
-
...
|
|
329
|
+
...compact && compactSelectStyles,
|
|
330
|
+
menu: (css) => ({
|
|
331
|
+
...css,
|
|
332
|
+
...editable && { paddingBottom: "36px" },
|
|
242
333
|
zIndex: 99
|
|
243
334
|
}),
|
|
244
|
-
menuList: (
|
|
245
|
-
...
|
|
335
|
+
menuList: (css) => ({
|
|
336
|
+
...css,
|
|
246
337
|
overflowY: "auto",
|
|
247
|
-
maxHeight: `${
|
|
338
|
+
maxHeight: `${maxHeight}px`
|
|
248
339
|
})
|
|
249
340
|
},
|
|
250
|
-
theme: (
|
|
251
|
-
...
|
|
341
|
+
theme: (_theme) => ({
|
|
342
|
+
..._theme,
|
|
252
343
|
colors: {
|
|
253
|
-
...
|
|
344
|
+
..._theme.colors,
|
|
254
345
|
primary25: "#ddd",
|
|
255
346
|
primary50: "#ddd",
|
|
256
347
|
primary: "#ddd"
|
|
257
348
|
}
|
|
258
349
|
}),
|
|
259
|
-
isSearchable:
|
|
260
|
-
isMulti
|
|
261
|
-
closeMenuOnSelect: !
|
|
262
|
-
onMenuClose:
|
|
263
|
-
isClearable:
|
|
264
|
-
data
|
|
350
|
+
isSearchable: false,
|
|
351
|
+
isMulti,
|
|
352
|
+
closeMenuOnSelect: !isMulti,
|
|
353
|
+
onMenuClose: handleMenuClose,
|
|
354
|
+
isClearable: true,
|
|
355
|
+
data
|
|
265
356
|
}
|
|
266
357
|
) });
|
|
267
358
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
},
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (!s)
|
|
281
|
-
return e;
|
|
282
|
-
var l = s.call(e), o, n = [], a;
|
|
283
|
-
try {
|
|
284
|
-
for (; (t === void 0 || t-- > 0) && !(o = l.next()).done; )
|
|
285
|
-
n.push(o.value);
|
|
286
|
-
} catch (u) {
|
|
287
|
-
a = { error: u };
|
|
288
|
-
} finally {
|
|
289
|
-
try {
|
|
290
|
-
o && !o.done && (s = l.return) && s.call(l);
|
|
291
|
-
} finally {
|
|
292
|
-
if (a)
|
|
293
|
-
throw a.error;
|
|
359
|
+
function LabelFormDialog({ open, initialLabel, onSubmit, onClose, ...rest }) {
|
|
360
|
+
const isNew = !(initialLabel == null ? void 0 : initialLabel.id);
|
|
361
|
+
const [state, setState] = useSetState({
|
|
362
|
+
name: (initialLabel == null ? void 0 : initialLabel.name) || "",
|
|
363
|
+
color: (initialLabel == null ? void 0 : initialLabel.color) || "#ddd",
|
|
364
|
+
slug: (initialLabel == null ? void 0 : initialLabel.id) || "",
|
|
365
|
+
translation: (initialLabel == null ? void 0 : initialLabel.translation) || {}
|
|
366
|
+
});
|
|
367
|
+
const isValidColor = (color) => /^#([0-9A-F]{3}){1,2}$/i.test(color);
|
|
368
|
+
const canSubmit = useMemo(() => {
|
|
369
|
+
if (isNew) {
|
|
370
|
+
return state.name && state.color && state.slug && isValidColor(state.color);
|
|
294
371
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return [s, o];
|
|
308
|
-
};
|
|
309
|
-
const Ee = Pe;
|
|
310
|
-
function je({ open: e, initialLabel: t, onSubmit: s, onClose: l, ...o }) {
|
|
311
|
-
const n = !(t != null && t.id), [a, u] = Ee({
|
|
312
|
-
name: (t == null ? void 0 : t.name) || "",
|
|
313
|
-
color: (t == null ? void 0 : t.color) || "#ddd",
|
|
314
|
-
slug: (t == null ? void 0 : t.id) || "",
|
|
315
|
-
translation: (t == null ? void 0 : t.translation) || {}
|
|
316
|
-
}), p = (i) => /^#([0-9A-F]{3}){1,2}$/i.test(i), y = N(() => n ? a.name && a.color && a.slug && p(a.color) : a.name && a.color && p(a.color), [a, n]), [b, m] = A(!1), x = () => {
|
|
317
|
-
s({ ...t, name: a.name, color: a.color, id: a.slug, translation: a.translation });
|
|
318
|
-
}, z = (i) => {
|
|
319
|
-
u({ color: i.hex }), setTimeout(() => m(!1));
|
|
320
|
-
}, c = (i) => {
|
|
321
|
-
u({ color: i.target.value });
|
|
372
|
+
return state.name && state.color && isValidColor(state.color);
|
|
373
|
+
}, [state, isNew]);
|
|
374
|
+
const [colorPickerVisible, setColorPickerVisible] = useState(false);
|
|
375
|
+
const handleSubmit = () => {
|
|
376
|
+
onSubmit({ ...initialLabel, name: state.name, color: state.color, id: state.slug, translation: state.translation });
|
|
377
|
+
};
|
|
378
|
+
const handleColorChange = (color) => {
|
|
379
|
+
setState({ color: color.hex });
|
|
380
|
+
setTimeout(() => setColorPickerVisible(false));
|
|
381
|
+
};
|
|
382
|
+
const handleColorTextChange = (e) => {
|
|
383
|
+
setState({ color: e.target.value });
|
|
322
384
|
};
|
|
323
|
-
return /* @__PURE__ */
|
|
324
|
-
|
|
385
|
+
return /* @__PURE__ */ jsx(
|
|
386
|
+
Dialog,
|
|
325
387
|
{
|
|
326
|
-
open
|
|
327
|
-
showCloseButton:
|
|
388
|
+
open,
|
|
389
|
+
showCloseButton: true,
|
|
328
390
|
maxWidth: "lg",
|
|
329
|
-
title:
|
|
330
|
-
actions: /* @__PURE__ */
|
|
331
|
-
/* @__PURE__ */
|
|
332
|
-
/* @__PURE__ */
|
|
391
|
+
title: isNew ? "Create label" : "Edit label",
|
|
392
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
393
|
+
/* @__PURE__ */ jsx(Button, { color: "inherit", variant: "contained", size: "small", onClick: onClose, children: "Cancel" }),
|
|
394
|
+
/* @__PURE__ */ jsx(Button, { color: "primary", variant: "contained", size: "small", onClick: handleSubmit, disabled: !canSubmit, children: isNew ? "Create" : "Save Changes" })
|
|
333
395
|
] }),
|
|
334
|
-
onClose
|
|
335
|
-
...
|
|
336
|
-
children: /* @__PURE__ */
|
|
337
|
-
/* @__PURE__ */
|
|
338
|
-
|
|
396
|
+
onClose,
|
|
397
|
+
...rest,
|
|
398
|
+
children: /* @__PURE__ */ jsxs(Box, { width: 600, minHeight: 320, children: [
|
|
399
|
+
/* @__PURE__ */ jsx(
|
|
400
|
+
TranslationTextField,
|
|
339
401
|
{
|
|
340
402
|
label: "Name",
|
|
341
|
-
value:
|
|
403
|
+
value: state.name,
|
|
342
404
|
placeholder: "New label",
|
|
343
405
|
size: "small",
|
|
344
|
-
fullWidth:
|
|
345
|
-
onChange: (
|
|
406
|
+
fullWidth: true,
|
|
407
|
+
onChange: (e) => setState({ name: e.target.value }),
|
|
346
408
|
translationInputProps: {
|
|
347
|
-
value:
|
|
348
|
-
onChange: (
|
|
409
|
+
value: state.translation,
|
|
410
|
+
onChange: (v) => setState({ translation: v })
|
|
349
411
|
}
|
|
350
412
|
}
|
|
351
413
|
),
|
|
352
|
-
/* @__PURE__ */
|
|
353
|
-
|
|
414
|
+
/* @__PURE__ */ jsx(
|
|
415
|
+
TextField,
|
|
354
416
|
{
|
|
355
417
|
label: "Slug",
|
|
356
|
-
value:
|
|
418
|
+
value: state.slug,
|
|
357
419
|
size: "small",
|
|
358
|
-
fullWidth:
|
|
359
|
-
disabled: !
|
|
360
|
-
onChange: (
|
|
420
|
+
fullWidth: true,
|
|
421
|
+
disabled: !isNew,
|
|
422
|
+
onChange: (e) => setState({ slug: e.target.value }),
|
|
361
423
|
sx: { mt: 2 }
|
|
362
424
|
}
|
|
363
425
|
),
|
|
364
|
-
(
|
|
365
|
-
/* @__PURE__ */
|
|
366
|
-
/* @__PURE__ */
|
|
367
|
-
/* @__PURE__ */
|
|
368
|
-
|
|
426
|
+
(initialLabel == null ? void 0 : initialLabel.parent) && /* @__PURE__ */ jsx(TextField, { label: "Parent", value: initialLabel.parent.name, size: "small", fullWidth: true, disabled: true, sx: { mt: 2 } }),
|
|
427
|
+
/* @__PURE__ */ jsx(ClickAwayListener, { onClickAway: () => setColorPickerVisible(false), children: /* @__PURE__ */ jsxs(Box, { sx: { position: "relative", mt: 2 }, children: [
|
|
428
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
429
|
+
/* @__PURE__ */ jsx(
|
|
430
|
+
Box,
|
|
369
431
|
{
|
|
370
|
-
sx: { width: 30, height: 30, bgcolor:
|
|
371
|
-
onClick: () =>
|
|
432
|
+
sx: { width: 30, height: 30, bgcolor: state.color, borderRadius: 1 },
|
|
433
|
+
onClick: () => setColorPickerVisible(true)
|
|
372
434
|
}
|
|
373
435
|
),
|
|
374
|
-
/* @__PURE__ */
|
|
375
|
-
|
|
436
|
+
/* @__PURE__ */ jsx(
|
|
437
|
+
TextField,
|
|
376
438
|
{
|
|
377
439
|
label: "",
|
|
378
|
-
value:
|
|
440
|
+
value: state.color,
|
|
379
441
|
size: "small",
|
|
380
|
-
onChange:
|
|
442
|
+
onChange: handleColorTextChange,
|
|
381
443
|
inputProps: { maxLength: 7 },
|
|
382
444
|
sx: { width: 100, ".MuiInputBase-root": { height: 32 } }
|
|
383
445
|
}
|
|
384
446
|
)
|
|
385
447
|
] }),
|
|
386
|
-
|
|
448
|
+
colorPickerVisible && /* @__PURE__ */ jsx(Box, { sx: { position: "absolute", top: 48, zIndex: 1 }, children: /* @__PURE__ */ jsx(GithubPicker, { color: state.color, onChangeComplete: handleColorChange }) })
|
|
387
449
|
] }) })
|
|
388
450
|
] })
|
|
389
451
|
}
|
|
390
452
|
);
|
|
391
453
|
}
|
|
392
|
-
function
|
|
393
|
-
return /* @__PURE__ */
|
|
394
|
-
|
|
454
|
+
function LabelDeleteDialog({ open, label, onSubmit, onClose, ...rest }) {
|
|
455
|
+
return /* @__PURE__ */ jsx(
|
|
456
|
+
Dialog,
|
|
395
457
|
{
|
|
396
|
-
open
|
|
397
|
-
showCloseButton:
|
|
458
|
+
open,
|
|
459
|
+
showCloseButton: true,
|
|
398
460
|
maxWidth: "md",
|
|
399
461
|
title: "Delete",
|
|
400
|
-
actions: /* @__PURE__ */
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
462
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
463
|
+
/* @__PURE__ */ jsx(Button, { color: "inherit", variant: "contained", size: "small", onClick: onClose, children: "Cancel" }),
|
|
464
|
+
/* @__PURE__ */ jsx(Button, { color: "primary", variant: "contained", size: "small", onClick: onSubmit, children: "Delete" })
|
|
403
465
|
] }),
|
|
404
|
-
onClose
|
|
405
|
-
...
|
|
406
|
-
children: /* @__PURE__ */
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
466
|
+
onClose,
|
|
467
|
+
...rest,
|
|
468
|
+
children: /* @__PURE__ */ jsxs(Box, { width: 600, minHeight: 280, children: [
|
|
469
|
+
/* @__PURE__ */ jsx(Alert, { severity: "info", sx: { mb: 2 }, children: "Are you sure you want to delete these labels ?" }),
|
|
470
|
+
/* @__PURE__ */ jsx(LabelTree, { data: [label] })
|
|
409
471
|
] })
|
|
410
472
|
}
|
|
411
473
|
);
|
|
412
474
|
}
|
|
413
|
-
const
|
|
475
|
+
const StyledLabelTree = styled(LabelTree)`
|
|
414
476
|
.label-tree-item {
|
|
415
477
|
&:before {
|
|
416
478
|
content: '';
|
|
@@ -431,211 +493,459 @@ const Me = R(F)`
|
|
|
431
493
|
padding: 0 16px;
|
|
432
494
|
}
|
|
433
495
|
`;
|
|
434
|
-
function
|
|
435
|
-
const [
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
496
|
+
function LabelManager({ data, api, ...rest }) {
|
|
497
|
+
const [editingLabel, setEditingLabel] = useState(null);
|
|
498
|
+
const [deletingLabel, setDeletingLabel] = useState(null);
|
|
499
|
+
const [copy, setCopy] = useState(copyTree(data));
|
|
500
|
+
const flattened = useMemo(() => flatten(copy), [copy]);
|
|
501
|
+
const treeData = useMemo(() => {
|
|
502
|
+
const format = (nodes, parent) => {
|
|
503
|
+
return nodes.map((node) => {
|
|
504
|
+
if (parent) {
|
|
505
|
+
node.parent = parent;
|
|
506
|
+
}
|
|
507
|
+
if (node.children) {
|
|
508
|
+
node.children = format(node.children, node);
|
|
509
|
+
}
|
|
510
|
+
return node;
|
|
511
|
+
});
|
|
512
|
+
};
|
|
513
|
+
return format(copy);
|
|
514
|
+
}, [copy]);
|
|
515
|
+
const updateNode = (label) => {
|
|
516
|
+
var _a, _b;
|
|
517
|
+
if (!label.parent) {
|
|
518
|
+
setCopy(copy.map((item) => item.id === label.id ? { ...label } : item));
|
|
519
|
+
} else {
|
|
520
|
+
label.parent.children = (_b = (_a = label.parent) == null ? void 0 : _a.children) == null ? void 0 : _b.map((item) => item.id === label.id ? { ...label } : item);
|
|
521
|
+
updateNode(label.parent);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
const addNode = (label) => {
|
|
525
|
+
if (label.parent) {
|
|
526
|
+
label.parent.children = label.parent.children || [];
|
|
527
|
+
label.parent.children.push(label);
|
|
528
|
+
setCopy([...copy]);
|
|
529
|
+
} else {
|
|
530
|
+
setCopy([...copy, label]);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const deleteNode = (label) => {
|
|
534
|
+
var _a;
|
|
535
|
+
if (label.parent) {
|
|
536
|
+
label.parent.children = (_a = label.parent.children) == null ? void 0 : _a.filter((item) => item.id !== label.id);
|
|
537
|
+
} else {
|
|
538
|
+
setCopy(copy.filter((item) => item.id !== label.id));
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const handleAdd = (parent) => {
|
|
542
|
+
if (editingLabel) {
|
|
448
543
|
return;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
|
|
544
|
+
}
|
|
545
|
+
const newLabel = { id: "", name: "New label", parent, color: "#dddddd" };
|
|
546
|
+
setEditingLabel(newLabel);
|
|
547
|
+
};
|
|
548
|
+
const handleEdit = (label) => {
|
|
549
|
+
if (editingLabel) {
|
|
454
550
|
return;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
551
|
+
}
|
|
552
|
+
setEditingLabel(label);
|
|
553
|
+
};
|
|
554
|
+
const handleSubmit = async (payload) => {
|
|
555
|
+
if (!editingLabel) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const { parent, children, translation, ...submitData } = payload;
|
|
559
|
+
if (!editingLabel.id) {
|
|
560
|
+
await api.createLabel({ ...submitData, translation: JSON.stringify(translation), parentId: parent == null ? void 0 : parent.id });
|
|
561
|
+
addNode(payload);
|
|
562
|
+
} else {
|
|
563
|
+
await api.updateLabel({ ...submitData, translation: JSON.stringify(translation), parentId: parent == null ? void 0 : parent.id });
|
|
564
|
+
updateNode(payload);
|
|
565
|
+
}
|
|
566
|
+
setEditingLabel(null);
|
|
567
|
+
};
|
|
568
|
+
const handleDelete = async () => {
|
|
569
|
+
if (!deletingLabel) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
await api.deleteLabel(deletingLabel.id);
|
|
573
|
+
deleteNode(deletingLabel);
|
|
574
|
+
setDeletingLabel(null);
|
|
575
|
+
};
|
|
576
|
+
const renderItem = (children, label) => {
|
|
577
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
578
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", flexWrap: "wrap" }, className: "label-name", children: [
|
|
579
|
+
/* @__PURE__ */ jsx(Box, { children }),
|
|
580
|
+
label.translation && /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, ml: 2 }, className: "label-translation", children: Object.keys(label.translation).map((key) => {
|
|
581
|
+
var _a;
|
|
582
|
+
const value = (_a = label.translation) == null ? void 0 : _a[key];
|
|
583
|
+
if (value) {
|
|
584
|
+
return /* @__PURE__ */ jsx(TranslationTag, { locale: key, value }, key);
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}) })
|
|
588
|
+
] }),
|
|
589
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, flex: "0 0 auto" }, className: "label-action", children: [
|
|
590
|
+
/* @__PURE__ */ jsx(IconButton, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => handleAdd(label), children: /* @__PURE__ */ jsx(Add, { sx: { fontSize: 20 } }) }),
|
|
591
|
+
/* @__PURE__ */ jsx(IconButton, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => handleEdit(label), children: /* @__PURE__ */ jsx(EditOutlined, { sx: { fontSize: 18 } }) }),
|
|
592
|
+
/* @__PURE__ */ jsx(IconButton, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => setDeletingLabel(label), children: /* @__PURE__ */ jsx(DeleteOutlineOutlined, { sx: { fontSize: 20 } }) })
|
|
593
|
+
] })
|
|
594
|
+
] });
|
|
595
|
+
};
|
|
596
|
+
return /* @__PURE__ */ jsxs(Box, { ...rest, className: "label-container", children: [
|
|
597
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
598
|
+
/* @__PURE__ */ jsx(Typography, { component: "h2", variant: "h6", children: "Manage labels" }),
|
|
599
|
+
/* @__PURE__ */ jsx(
|
|
600
|
+
Button,
|
|
479
601
|
{
|
|
480
602
|
color: "primary",
|
|
481
603
|
variant: "contained",
|
|
482
604
|
size: "small",
|
|
483
605
|
sx: { textTransform: "none" },
|
|
484
|
-
onClick: () =>
|
|
606
|
+
onClick: () => handleAdd(),
|
|
485
607
|
children: "New label"
|
|
486
608
|
}
|
|
487
609
|
)
|
|
488
610
|
] }),
|
|
489
|
-
/* @__PURE__ */
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
|
|
611
|
+
/* @__PURE__ */ jsxs(Box, { sx: { mt: 2, border: 1, borderColor: "grey.300", borderRadius: 1, overflow: "hidden" }, children: [
|
|
612
|
+
/* @__PURE__ */ jsxs(Box, { sx: { p: 2, fontSize: 14, fontWeight: "bold", bgcolor: "grey.200" }, className: "label-header", children: [
|
|
613
|
+
flattened.length,
|
|
492
614
|
" labels"
|
|
493
615
|
] }),
|
|
494
|
-
/* @__PURE__ */
|
|
616
|
+
/* @__PURE__ */ jsx(Box, { sx: { pt: 0.5 }, children: /* @__PURE__ */ jsx(StyledLabelTree, { data: treeData, selected: [], renderItem, rowHeight: 64 }) })
|
|
495
617
|
] }),
|
|
496
|
-
/* @__PURE__ */
|
|
497
|
-
|
|
618
|
+
/* @__PURE__ */ jsx(
|
|
619
|
+
LabelFormDialog,
|
|
498
620
|
{
|
|
499
|
-
open: !!
|
|
500
|
-
onClose: () =>
|
|
501
|
-
initialLabel:
|
|
502
|
-
onSubmit:
|
|
621
|
+
open: !!editingLabel,
|
|
622
|
+
onClose: () => setEditingLabel(null),
|
|
623
|
+
initialLabel: editingLabel,
|
|
624
|
+
onSubmit: handleSubmit
|
|
503
625
|
},
|
|
504
|
-
|
|
626
|
+
editingLabel == null ? void 0 : editingLabel.id
|
|
505
627
|
),
|
|
506
|
-
/* @__PURE__ */
|
|
507
|
-
|
|
628
|
+
/* @__PURE__ */ jsx(
|
|
629
|
+
LabelDeleteDialog,
|
|
508
630
|
{
|
|
509
|
-
open: !!
|
|
510
|
-
onClose: () =>
|
|
511
|
-
label:
|
|
512
|
-
onSubmit:
|
|
631
|
+
open: !!deletingLabel,
|
|
632
|
+
onClose: () => setDeletingLabel(null),
|
|
633
|
+
label: deletingLabel,
|
|
634
|
+
onSubmit: handleDelete
|
|
513
635
|
},
|
|
514
|
-
|
|
636
|
+
deletingLabel == null ? void 0 : deletingLabel.id
|
|
515
637
|
)
|
|
516
638
|
] });
|
|
517
639
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
640
|
+
class TreeNode {
|
|
641
|
+
constructor({ data, parent, children }) {
|
|
642
|
+
__publicField(this, "data");
|
|
643
|
+
__publicField(this, "parent");
|
|
644
|
+
__publicField(this, "children");
|
|
645
|
+
this.data = data;
|
|
646
|
+
this.parent = parent;
|
|
647
|
+
this.children = children || [];
|
|
648
|
+
}
|
|
649
|
+
setParent(node) {
|
|
650
|
+
this.parent = node;
|
|
651
|
+
}
|
|
652
|
+
add(...nodes) {
|
|
653
|
+
this.children.push(...nodes);
|
|
654
|
+
nodes.forEach((node) => node.setParent(this));
|
|
655
|
+
}
|
|
656
|
+
isLeaf(node) {
|
|
657
|
+
var _a;
|
|
658
|
+
return !((_a = node.children) == null ? void 0 : _a.length);
|
|
659
|
+
}
|
|
660
|
+
getAllParents(includeSelf = true) {
|
|
661
|
+
const parents = [];
|
|
662
|
+
if (includeSelf) {
|
|
663
|
+
parents.unshift(this);
|
|
664
|
+
}
|
|
665
|
+
let { parent } = this;
|
|
666
|
+
while (parent) {
|
|
667
|
+
parents.unshift(parent);
|
|
668
|
+
parent = parent.parent;
|
|
669
|
+
}
|
|
670
|
+
return parents;
|
|
671
|
+
}
|
|
672
|
+
getAllSiblings() {
|
|
673
|
+
var _a;
|
|
674
|
+
const siblings = ((_a = this.parent) == null ? void 0 : _a.children.filter((node) => node !== this)) || [];
|
|
675
|
+
return siblings;
|
|
676
|
+
}
|
|
677
|
+
flatten(includeRoot) {
|
|
678
|
+
const nodes = [];
|
|
679
|
+
const traverse = (node) => {
|
|
680
|
+
nodes.push(node);
|
|
681
|
+
node.children.forEach(traverse);
|
|
682
|
+
};
|
|
683
|
+
traverse(this);
|
|
684
|
+
if (!includeRoot) {
|
|
685
|
+
nodes.shift();
|
|
686
|
+
}
|
|
687
|
+
return nodes;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
class Label {
|
|
691
|
+
constructor(data) {
|
|
692
|
+
__publicField(this, "id");
|
|
693
|
+
__publicField(this, "name");
|
|
694
|
+
__publicField(this, "icon");
|
|
695
|
+
__publicField(this, "color");
|
|
696
|
+
__publicField(this, "translation");
|
|
697
|
+
this.id = data.id;
|
|
698
|
+
this.name = data.name;
|
|
699
|
+
this.icon = data.icon;
|
|
700
|
+
this.color = data.color;
|
|
701
|
+
this.translation = this._normalizeTranslation(data.translation);
|
|
702
|
+
}
|
|
703
|
+
_normalizeTranslation(translation) {
|
|
704
|
+
if (typeof translation === "object") {
|
|
705
|
+
return translation;
|
|
706
|
+
}
|
|
707
|
+
try {
|
|
708
|
+
return JSON.parse(translation);
|
|
709
|
+
} catch {
|
|
710
|
+
return {};
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
getName(locale) {
|
|
714
|
+
var _a;
|
|
715
|
+
if (!locale) {
|
|
716
|
+
return this.name;
|
|
717
|
+
}
|
|
718
|
+
return ((_a = this == null ? void 0 : this.translation) == null ? void 0 : _a[locale]) || (this == null ? void 0 : this.name);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
class LabelTreeNode extends TreeNode {
|
|
722
|
+
}
|
|
723
|
+
const mapToTree = (items, parent) => {
|
|
724
|
+
return items.map((item) => {
|
|
725
|
+
const node = new LabelTreeNode({ data: new Label(item.data), parent });
|
|
726
|
+
const children = mapToTree(item.children || [], node);
|
|
727
|
+
node.add(...children);
|
|
728
|
+
return node;
|
|
729
|
+
});
|
|
730
|
+
};
|
|
731
|
+
const initLabelTree = (data) => {
|
|
732
|
+
const treeData = arrayToTree(data);
|
|
733
|
+
const root = new LabelTreeNode({ data: {}, children: mapToTree(treeData) });
|
|
734
|
+
return root;
|
|
523
735
|
};
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
736
|
+
const createEmptyLabelTree = () => {
|
|
737
|
+
return new LabelTreeNode({ data: {}, children: [] });
|
|
738
|
+
};
|
|
739
|
+
const useLabels = ({ loading, data }) => {
|
|
740
|
+
const { locale } = useLocaleContext();
|
|
741
|
+
const state = useReactive({
|
|
742
|
+
loading: true,
|
|
743
|
+
tree: createEmptyLabelTree(),
|
|
744
|
+
stats: []
|
|
529
745
|
});
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
746
|
+
useEffect(() => {
|
|
747
|
+
try {
|
|
748
|
+
if (data && !loading) {
|
|
749
|
+
const { labels, stats } = data;
|
|
750
|
+
const tree = initLabelTree(labels || []);
|
|
751
|
+
state.loading = false;
|
|
752
|
+
state.tree = tree;
|
|
753
|
+
state.stats = stats.sort((a, b) => b.count - a.count);
|
|
538
754
|
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
755
|
+
} catch (e) {
|
|
756
|
+
console.error(e);
|
|
757
|
+
state.loading = false;
|
|
758
|
+
}
|
|
759
|
+
}, [data, loading]);
|
|
760
|
+
const flattened = useMemo(() => state.tree.flatten(), [state.tree]);
|
|
761
|
+
const counts = useMemo(
|
|
762
|
+
() => state.stats.reduce((acc, cur) => ({ ...acc, [cur.id]: cur.count }), {}),
|
|
763
|
+
[state.stats]
|
|
764
|
+
);
|
|
765
|
+
const nodesKeyById = useMemo(() => {
|
|
766
|
+
return flattened.reduce((acc, cur) => ({ ...acc, [cur.data.id]: cur }), {});
|
|
767
|
+
}, [flattened]);
|
|
768
|
+
const popularLabels = useMemo(
|
|
769
|
+
() => state.stats.slice(0, 12).map((x) => nodesKeyById[x.id]),
|
|
770
|
+
[nodesKeyById, state.stats]
|
|
771
|
+
);
|
|
772
|
+
const getLabelsByIds = (ids) => {
|
|
773
|
+
return ids.map((id) => nodesKeyById[id]).filter(Boolean);
|
|
774
|
+
};
|
|
775
|
+
const getLabelName = (id) => {
|
|
776
|
+
var _a;
|
|
777
|
+
return (_a = nodesKeyById[id]) == null ? void 0 : _a.data.getName(locale);
|
|
778
|
+
};
|
|
779
|
+
const getRelatedLabels = (id) => {
|
|
780
|
+
const node = nodesKeyById[id];
|
|
781
|
+
if (!node) {
|
|
782
|
+
return [];
|
|
783
|
+
}
|
|
784
|
+
const siblings = node.getAllSiblings();
|
|
785
|
+
return siblings;
|
|
786
|
+
};
|
|
787
|
+
const getRecommended = (id) => {
|
|
788
|
+
const related = getRelatedLabels(id);
|
|
789
|
+
const set = /* @__PURE__ */ new Set([...related.map((x) => x.data.id), id]);
|
|
790
|
+
return [...related, ...popularLabels.filter((x) => !set.has(x.data.id))];
|
|
791
|
+
};
|
|
792
|
+
return { ...state, popularLabels, counts, getLabelsByIds, getLabelName, getRelatedLabels, getRecommended };
|
|
793
|
+
};
|
|
794
|
+
const LabelsContainer = createContainer(useLabels);
|
|
795
|
+
const LabelsContext = createContext({});
|
|
796
|
+
const useLabelsContext = () => useContext(LabelsContext);
|
|
797
|
+
const useLabelsUpdateOnDestroy = () => {
|
|
798
|
+
const { updateLabels } = useLabelsContext();
|
|
799
|
+
useEffect(() => {
|
|
800
|
+
return () => {
|
|
801
|
+
updateLabels();
|
|
802
|
+
};
|
|
803
|
+
}, []);
|
|
804
|
+
};
|
|
805
|
+
function LabelsProvider({ fetchLabels, children }) {
|
|
806
|
+
const { locale } = useLocaleContext();
|
|
807
|
+
const [updateCounter, setUpdateCounter] = useState(1);
|
|
808
|
+
const { loading, data } = useRequest(fetchLabels, {
|
|
809
|
+
refreshDeps: [updateCounter]
|
|
810
|
+
});
|
|
811
|
+
const labels = useMemo(() => transformLabels((data == null ? void 0 : data.labels) || []), [data]);
|
|
812
|
+
const labelsKeyById = useMemo(() => {
|
|
813
|
+
if (!(labels == null ? void 0 : labels.length)) {
|
|
814
|
+
return {};
|
|
815
|
+
}
|
|
816
|
+
const flattened = flatten(labels);
|
|
817
|
+
return flattened.reduce((acc, cur) => ({ ...acc, [cur.id]: cur }), {});
|
|
818
|
+
}, [labels]);
|
|
819
|
+
const localeTransformer = useCallback(
|
|
820
|
+
(label) => {
|
|
821
|
+
var _a;
|
|
547
822
|
return {
|
|
548
|
-
...
|
|
549
|
-
name: ((
|
|
823
|
+
...label,
|
|
824
|
+
name: ((_a = label.translation) == null ? void 0 : _a[locale]) || label.name
|
|
550
825
|
};
|
|
551
826
|
},
|
|
552
|
-
[
|
|
553
|
-
)
|
|
554
|
-
|
|
555
|
-
[
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
827
|
+
[locale]
|
|
828
|
+
);
|
|
829
|
+
const getLabelsById = useCallback(
|
|
830
|
+
(ids) => ids.filter(Boolean).map((item) => labelsKeyById[item]).map(localeTransformer),
|
|
831
|
+
[labelsKeyById, localeTransformer]
|
|
832
|
+
);
|
|
833
|
+
const getParents = useCallback((label) => {
|
|
834
|
+
let { parent } = label;
|
|
835
|
+
const parents = [];
|
|
836
|
+
while (parent) {
|
|
837
|
+
parents.unshift(parent);
|
|
838
|
+
parent = parent.parent;
|
|
839
|
+
}
|
|
840
|
+
return parents;
|
|
841
|
+
}, []);
|
|
842
|
+
const getFullLabelName = useCallback(
|
|
843
|
+
(label) => {
|
|
844
|
+
const parents = [...getParents(label), label].map(localeTransformer);
|
|
845
|
+
return parents.map((item) => item.name).join(" / ");
|
|
846
|
+
},
|
|
847
|
+
[localeTransformer, getParents]
|
|
848
|
+
);
|
|
849
|
+
const parseLabelIds = useCallback((labelIdsStr) => {
|
|
850
|
+
return (labelIdsStr == null ? void 0 : labelIdsStr.split(",")) || [];
|
|
851
|
+
}, []);
|
|
852
|
+
const stringifyLabelIds = useCallback((labelIds = []) => {
|
|
853
|
+
if (!(labelIds == null ? void 0 : labelIds.length)) {
|
|
854
|
+
return void 0;
|
|
855
|
+
}
|
|
856
|
+
return labelIds.join(",");
|
|
857
|
+
}, []);
|
|
858
|
+
const localizedLabels = useMemo(() => labels.map(localeTransformer), [labels, localeTransformer]);
|
|
859
|
+
const value = useMemo(() => {
|
|
860
|
+
return {
|
|
861
|
+
loading,
|
|
862
|
+
labels: labels || [],
|
|
863
|
+
updateLabels: () => setUpdateCounter((prev) => prev + 1),
|
|
864
|
+
getLabelsById,
|
|
865
|
+
parseLabelIds,
|
|
866
|
+
stringifyLabelIds,
|
|
867
|
+
localizedLabels,
|
|
868
|
+
flattenedLabels: flatten(localizedLabels || []),
|
|
869
|
+
getFullLabelName
|
|
870
|
+
};
|
|
871
|
+
}, [loading, labels, getLabelsById, parseLabelIds, stringifyLabelIds, localizedLabels, getFullLabelName]);
|
|
872
|
+
return /* @__PURE__ */ jsx(LabelsContext.Provider, { value, children: /* @__PURE__ */ jsx(LabelsContainer.Provider, { initialState: { loading, data }, children }) });
|
|
580
873
|
}
|
|
581
|
-
const
|
|
874
|
+
const getContrastTextColor = (bgcolor) => {
|
|
582
875
|
try {
|
|
583
|
-
|
|
876
|
+
const ratio = getContrastRatio(bgcolor, "#fff");
|
|
877
|
+
return ratio > 3.5 ? "#fff" : "#111";
|
|
584
878
|
} catch {
|
|
585
879
|
return "#111";
|
|
586
880
|
}
|
|
587
|
-
}
|
|
881
|
+
};
|
|
882
|
+
const getAlphaColor = (color, alpha$1) => {
|
|
588
883
|
try {
|
|
589
|
-
if (
|
|
590
|
-
return
|
|
884
|
+
if (color) {
|
|
885
|
+
return alpha(color, alpha$1);
|
|
886
|
+
}
|
|
591
887
|
} catch {
|
|
592
|
-
return
|
|
888
|
+
return color;
|
|
593
889
|
}
|
|
594
|
-
return
|
|
890
|
+
return color;
|
|
595
891
|
};
|
|
596
|
-
function
|
|
597
|
-
const
|
|
598
|
-
|
|
892
|
+
function Labels({ labels, editable, onChange, sx, renderLabel }) {
|
|
893
|
+
const isEmpty = !(labels == null ? void 0 : labels.length);
|
|
894
|
+
const { labels: allLabels, loading, getLabelsById, getFullLabelName } = useLabelsContext();
|
|
895
|
+
const [editing, setEditing] = useState(false);
|
|
896
|
+
if (loading || !editable && isEmpty) {
|
|
599
897
|
return null;
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
898
|
+
}
|
|
899
|
+
const handleOnChange = (v) => {
|
|
900
|
+
onChange == null ? void 0 : onChange(v);
|
|
901
|
+
setTimeout(() => {
|
|
902
|
+
setEditing(false);
|
|
603
903
|
}, 300);
|
|
604
904
|
};
|
|
605
|
-
if (
|
|
606
|
-
return /* @__PURE__ */
|
|
607
|
-
|
|
905
|
+
if (editing) {
|
|
906
|
+
return /* @__PURE__ */ jsx(ClickAwayListener$1, { onClickAway: () => editing && setEditing(false), children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(LabelPicker, { data: allLabels, value: labels || [], onChange: handleOnChange }) }) });
|
|
907
|
+
}
|
|
908
|
+
const labelObjects = getLabelsById(labels || []).filter((x) => !x.id.startsWith("system:"));
|
|
909
|
+
const mergedSx = [
|
|
608
910
|
{ display: "flex", gap: 1, alignItems: "center", flexWrap: "wrap" },
|
|
609
|
-
...Array.isArray(
|
|
911
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
610
912
|
];
|
|
611
|
-
return /* @__PURE__ */
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
913
|
+
return /* @__PURE__ */ jsxs(Box$1, { sx: mergedSx, children: [
|
|
914
|
+
labelObjects.map((item) => {
|
|
915
|
+
if (!item) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
if (renderLabel) {
|
|
919
|
+
return renderLabel(item);
|
|
920
|
+
}
|
|
921
|
+
return /* @__PURE__ */ jsx(
|
|
922
|
+
Chip,
|
|
923
|
+
{
|
|
924
|
+
label: getFullLabelName(item),
|
|
925
|
+
variant: "filled",
|
|
926
|
+
size: "small",
|
|
927
|
+
sx: {
|
|
928
|
+
height: 20,
|
|
929
|
+
borderRadius: 1,
|
|
930
|
+
fontSize: 12,
|
|
931
|
+
bgcolor: getAlphaColor(item.color, 0.8),
|
|
932
|
+
color: getContrastTextColor(item.color)
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
item.id
|
|
936
|
+
);
|
|
937
|
+
}),
|
|
938
|
+
editable && !isEmpty && /* @__PURE__ */ jsx(IconButton$1, { color: "inherit", size: "small", onClick: () => setEditing(true), sx: { color: "grey.400" }, children: /* @__PURE__ */ jsx(Edit, { sx: { fontSize: 20 } }) }),
|
|
939
|
+
editable && isEmpty && /* @__PURE__ */ jsx(Button$1, { color: "inherit", variant: "outlined", startIcon: /* @__PURE__ */ jsx(LabelOutlined, {}), onClick: () => setEditing(true), children: "Edit labels" })
|
|
630
940
|
] });
|
|
631
941
|
}
|
|
632
942
|
export {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
943
|
+
LabelManager,
|
|
944
|
+
LabelPicker,
|
|
945
|
+
LabelTree,
|
|
946
|
+
Labels,
|
|
947
|
+
LabelsContainer,
|
|
948
|
+
LabelsProvider,
|
|
949
|
+
useLabelsContext,
|
|
950
|
+
useLabelsUpdateOnDestroy
|
|
641
951
|
};
|