@blocklet/labels 1.5.200 → 1.5.202

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/index.es.js CHANGED
@@ -1,82 +1,109 @@
1
- import { jsx as r, jsxs as C, Fragment as L } from "react/jsx-runtime";
2
- import { createContext as V, useRef as D, useState as A, useCallback as k, useEffect as M, useMemo as N, useContext as q } from "react";
3
- import { ExpandMore as W, Add as Z, EditOutlined as G, DeleteOutlineOutlined as U, Edit as Y, LabelOutlined as X } from "@mui/icons-material";
4
- import f from "@mui/material/Box";
5
- import { styled as R } from "@mui/material/styles";
6
- import { Tree as ee } from "react-arborist";
7
- import { Icon as te } from "@iconify/react";
8
- import ne from "lodash/omit";
9
- import re, { components as $ } from "react-select";
10
- import oe from "@mui/material/Typography";
11
- import O from "@mui/material/Button";
12
- import E from "@mui/material/IconButton";
13
- import { TranslationTextField as le, TranslationTag as ae } from "@blocklet/translation-input";
14
- import j from "@mui/material/TextField";
15
- import se from "@mui/material/ClickAwayListener";
16
- import { GithubPicker as ie } from "react-color";
17
- import J from "@arcblock/ux/lib/Dialog";
18
- import ce from "@mui/material/Alert";
19
- import { ClickAwayListener as de, Box as ue, Chip as he, IconButton as pe, Button as fe, getContrastRatio as me, alpha as ge } from "@mui/material";
20
- import { useLocaleContext as ye } from "@arcblock/ux/lib/Locale/context";
21
- const xe = (e) => /* @__PURE__ */ r("svg", { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em", ...e, children: /* @__PURE__ */ r("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" }) }), Ce = (e) => /* @__PURE__ */ r("svg", { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em", ...e, children: /* @__PURE__ */ r("path", { fill: "currentColor", d: "m10 16.4l-4-4L7.4 11l2.6 2.6L16.6 7L18 8.4Z" }) }), _ = V({});
22
- function ve({
23
- selectable: e = !1,
24
- selected: t = [],
25
- onSelect: s,
26
- renderItem: l,
27
- children: o
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 n = (p) => t.indexOf(p) !== -1, a = (p) => {
30
- Array.from(new Set(t).add(p)), s == null || s(p);
31
- }, u = N(
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: e,
34
- selected: t,
35
- isSelected: n,
36
- select: a,
37
- renderItem: l
47
+ selectable,
48
+ selected,
49
+ isSelected,
50
+ select,
51
+ renderItem
38
52
  }),
39
53
  // eslint-disable-next-line react-hooks/exhaustive-deps
40
- [t]
54
+ [selected]
41
55
  );
42
- return /* @__PURE__ */ r(_.Provider, { value: u, children: o });
56
+ return /* @__PURE__ */ jsx(LabelTreeContext.Provider, { value, children });
43
57
  }
44
- const be = (e) => {
45
- var t;
46
- return e.isLeaf || !((t = e.data.children) != null && t.length);
58
+ const isLeaf = (node) => {
59
+ var _a;
60
+ return node.isLeaf || !((_a = node.data.children) == null ? void 0 : _a.length);
47
61
  };
48
- function Se({ node: e }) {
49
- return be(e) ? /* @__PURE__ */ r("span", {}) : e.isOpen ? /* @__PURE__ */ r(W, { style: { fontSize: 20 } }) : /* @__PURE__ */ r(W, { style: { fontSize: 20, transform: "rotate(-90deg)" } });
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 we({ node: e, style: t, dragHandle: s }) {
52
- const { selectable: l, select: o, isSelected: n, renderItem: a } = q(_), { data: u } = e, p = u.icon ? /* @__PURE__ */ r(te, { icon: u.icon, style: { fontSize: 18 } }) : /* @__PURE__ */ r(xe, { style: { fontSize: 18, color: u.color || "#ddd" } }), y = (x) => {
53
- x.stopPropagation(), e.isInternal && e.toggle();
54
- }, b = (x) => {
55
- x.stopPropagation(), l && o(e.id);
56
- }, m = /* @__PURE__ */ C(L, { children: [
57
- /* @__PURE__ */ C(f, { sx: { display: "flex", alignItems: "center", flex: 1 }, children: [
58
- /* @__PURE__ */ r(f, { sx: { display: "flex", cursor: "pointer" }, onClick: y, children: /* @__PURE__ */ r(Se, { node: e }) }),
59
- /* @__PURE__ */ r(f, { sx: { display: "flex", alignItems: "center", width: 22, height: 22 }, children: p }),
60
- /* @__PURE__ */ r(f, { component: "span", sx: { color: "grey.700" }, children: e.data.name })
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
- l && n(e.id) && /* @__PURE__ */ r(Ce, {})
89
+ selectable && isSelected(node.id) && /* @__PURE__ */ jsx(materialSymbolsCheckSmall, {})
63
90
  ] });
64
- return /* @__PURE__ */ C(
65
- f,
91
+ return /* @__PURE__ */ jsxs(
92
+ Box,
66
93
  {
67
94
  className: "label-tree-item",
68
- style: t,
95
+ style,
69
96
  sx: { display: "flex", alignItems: "center", justifyContent: "space-between" },
70
- ref: s,
71
- onClick: b,
97
+ ref: dragHandle,
98
+ onClick: handleClick,
72
99
  children: [
73
- !a && m,
74
- !!a && a(m, e.data)
100
+ !renderItem && children,
101
+ !!renderItem && renderItem(children, node.data)
75
102
  ]
76
103
  }
77
104
  );
78
105
  }
79
- const Ie = R(f)`
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 F({
94
- data: e,
95
- selectable: t,
96
- selected: s = [],
97
- onSelect: l,
98
- rowHeight: o = 28,
99
- renderItem: n,
100
- sx: a,
101
- ...u
120
+ function LabelTree({
121
+ data,
122
+ selectable,
123
+ selected = [],
124
+ onSelect,
125
+ rowHeight = 28,
126
+ renderItem,
127
+ sx,
128
+ ...rest
102
129
  }) {
103
- const p = D(), y = [...Array.isArray(a) ? a : [a]], [b, m] = A(0), x = k(() => {
104
- var c, i;
105
- m((((i = (c = p.current) == null ? void 0 : c.visibleNodes) == null ? void 0 : i.length) || 0) * o);
106
- }, [o]);
107
- return M(() => {
108
- x();
109
- }, [e, x]), /* @__PURE__ */ r(ve, { selectable: t, selected: s, onSelect: l, renderItem: n, children: /* @__PURE__ */ r(Ie, { ...u, sx: y, children: /* @__PURE__ */ r(ee, { data: e, rowHeight: o, height: b, indent: 32, ref: p, onToggle: () => {
110
- setTimeout(x);
111
- }, children: we }) }) });
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 B = (e) => e.reduce((t, s) => {
114
- var l;
115
- return t.push(s), (l = s.children) != null && l.length && t.push(...B(s.children)), t;
116
- }, []), H = (e) => e.map((t) => {
117
- var s;
118
- return (s = t.children) != null && s.length && (t.children = H(t.children)), t;
119
- }), ze = (e, t = null) => {
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(e);
166
+ return JSON.parse(json);
122
167
  } catch {
123
- return t;
168
+ return defaultValue;
124
169
  }
125
- }, ke = (e) => {
126
- const t = e.reduce((o, n) => (o[n.id] = o[n.id] || {}, n.parentId && (o[n.id].parent = n.parentId, o[n.parentId] = o[n.parentId] || {}, o[n.parentId].children = o[n.parentId].children || [], o[n.parentId].children.push(n.id)), o), {}), s = e.map(({ parentId: o, translation: n, ...a }) => ({
127
- ...a,
128
- translation: ze(n),
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
- })), l = s.reduce((o, n) => ({ ...o, [n.id]: n }), {});
131
- return s.forEach((o) => {
132
- const n = t[o.id];
133
- n.parent && l[n.parent] && (o.parent = l[n.parent]), n.children && (o.children = n.children.map((a) => l[a]));
134
- }), s.filter((o) => !o.parent);
135
- }, Ae = (e) => {
136
- const { options: t, getValue: s, selectProps: l, selectOption: o } = e, n = s(), a = (u) => {
137
- const p = t.find((y) => y.data.id === u);
138
- p && o(p);
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__ */ C(L, { children: [
141
- /* @__PURE__ */ r($.MenuList, { ...ne(e, ["addon"]), children: /* @__PURE__ */ r(f, { sx: { px: 2, py: 1 }, children: /* @__PURE__ */ r(
142
- F,
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: l.data,
145
- selectable: !0,
146
- onSelect: a,
147
- selected: n.map((u) => u.data.id)
212
+ data: selectProps.data,
213
+ selectable: true,
214
+ onSelect: handleSelect,
215
+ selected: value.map((item) => item.data.id)
148
216
  }
149
217
  ) }) }),
150
- e.addon
218
+ props.addon
151
219
  ] });
152
- }, Ne = ({ children: e, ...t }) => {
153
- const [s, l] = e, o = Array.isArray(s) ? s.length : 0;
154
- return /* @__PURE__ */ C($.ValueContainer, { ...t, children: [
155
- /* @__PURE__ */ r(f, { component: "span", sx: { fontSize: 13, fontWeight: "bold" }, children: o ? "Labels" : "Filter by labels" }),
156
- !!o && /* @__PURE__ */ r(
157
- f,
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: o
240
+ children: count
171
241
  }
172
242
  ),
173
- l
243
+ input
174
244
  ] });
175
- }, Te = {
176
- control: (e) => ({
177
- ...e,
245
+ };
246
+ const compactSelectStyles = {
247
+ control: (provided) => ({
248
+ ...provided,
178
249
  minHeight: 31
179
250
  }),
180
- valueContainer: (e) => ({
181
- ...e,
251
+ valueContainer: (provided) => ({
252
+ ...provided,
182
253
  display: "flex"
183
254
  }),
184
- dropdownIndicator: (e) => ({
185
- ...e,
255
+ dropdownIndicator: (provided) => ({
256
+ ...provided,
186
257
  padding: "0 4px"
187
258
  }),
188
- clearIndicator: (e) => ({
189
- ...e,
259
+ clearIndicator: (provided) => ({
260
+ ...provided,
190
261
  padding: "4px"
191
262
  })
192
263
  };
193
- function Oe({
194
- data: e,
195
- value: t = [],
196
- editable: s = !1,
197
- addon: l,
198
- onChange: o,
199
- compact: n,
200
- isMulti: a = !0,
201
- maxHeight: u = 264,
202
- ...p
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 y = D(null), b = N(
205
- () => B(e).map((h) => ({ data: h, label: h.name, value: h.id })),
206
- [e]
207
- ), m = b.reduce(
208
- (h, I) => ({ ...h, [I.data.id]: I }),
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
- ), x = Array.isArray(t) ? t : [t], z = x.map((h) => m[h]), c = D({
211
- prev: x.map((h) => {
212
- var I;
213
- return ((I = m[h]) == null ? void 0 : I.data) || "";
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
- }), i = (h, I) => {
216
- !a || !Array.isArray(h) ? o(h ? [h.data] : []) : c.current.current = h.map((d) => d.data), ["clear", "remove-value"].includes(I.action) && y.current && setTimeout(() => {
217
- var d;
218
- return (d = y == null ? void 0 : y.current) == null ? void 0 : d.blur();
219
- }, 1);
220
- }, S = () => {
221
- c.current.current && JSON.stringify(c.current.prev.map((I) => I.id)) !== JSON.stringify(c.current.current.map((I) => I.id)) && (o(c.current.current), c.current.prev = c.current.current);
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__ */ r(f, { ...p, children: /* @__PURE__ */ r(
224
- re,
314
+ return /* @__PURE__ */ jsx(Box, { ...rest, children: /* @__PURE__ */ jsx(
315
+ Select,
225
316
  {
226
- ref: y,
227
- defaultValue: z,
228
- options: b,
229
- onChange: i,
317
+ ref: selectRef,
318
+ defaultValue: selectedOptions,
319
+ options,
320
+ onChange: handleChange,
230
321
  components: {
231
322
  // @ts-ignore
232
- MenuList: (h) => /* @__PURE__ */ r(Ae, { ...h, addon: l }),
323
+ MenuList: (props) => /* @__PURE__ */ jsx(LabelMenuList, { ...props, addon }),
233
324
  // eslint-disable-line react/no-unstable-nested-components
234
- ...n && { ValueContainer: Ne }
325
+ ...compact && { ValueContainer }
235
326
  },
236
327
  placeholder: "Select labels",
237
328
  styles: {
238
- ...n && Te,
239
- menu: (h) => ({
240
- ...h,
241
- ...s && { paddingBottom: "36px" },
329
+ ...compact && compactSelectStyles,
330
+ menu: (css) => ({
331
+ ...css,
332
+ ...editable && { paddingBottom: "36px" },
242
333
  zIndex: 99
243
334
  }),
244
- menuList: (h) => ({
245
- ...h,
335
+ menuList: (css) => ({
336
+ ...css,
246
337
  overflowY: "auto",
247
- maxHeight: `${u}px`
338
+ maxHeight: `${maxHeight}px`
248
339
  })
249
340
  },
250
- theme: (h) => ({
251
- ...h,
341
+ theme: (_theme) => ({
342
+ ..._theme,
252
343
  colors: {
253
- ...h.colors,
344
+ ..._theme.colors,
254
345
  primary25: "#ddd",
255
346
  primary50: "#ddd",
256
347
  primary: "#ddd"
257
348
  }
258
349
  }),
259
- isSearchable: !1,
260
- isMulti: a,
261
- closeMenuOnSelect: !a,
262
- onMenuClose: S,
263
- isClearable: !0,
264
- data: e
350
+ isSearchable: false,
351
+ isMulti,
352
+ closeMenuOnSelect: !isMulti,
353
+ onMenuClose: handleMenuClose,
354
+ isClearable: true,
355
+ data
265
356
  }
266
357
  ) });
267
358
  }
268
- var P = function() {
269
- return P = Object.assign || function(t) {
270
- for (var s, l = 1, o = arguments.length; l < o; l++) {
271
- s = arguments[l];
272
- for (var n in s)
273
- Object.prototype.hasOwnProperty.call(s, n) && (t[n] = s[n]);
274
- }
275
- return t;
276
- }, P.apply(this, arguments);
277
- };
278
- function Be(e, t) {
279
- var s = typeof Symbol == "function" && e[Symbol.iterator];
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
- return n;
297
- }
298
- var Le = function(e) {
299
- return typeof e == "function";
300
- }, Pe = function(e) {
301
- var t = Be(A(e), 2), s = t[0], l = t[1], o = k(function(n) {
302
- l(function(a) {
303
- var u = Le(n) ? n(a) : n;
304
- return u ? P(P({}, a), u) : a;
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__ */ r(
324
- J,
385
+ return /* @__PURE__ */ jsx(
386
+ Dialog,
325
387
  {
326
- open: e,
327
- showCloseButton: !0,
388
+ open,
389
+ showCloseButton: true,
328
390
  maxWidth: "lg",
329
- title: n ? "Create label" : "Edit label",
330
- actions: /* @__PURE__ */ C(L, { children: [
331
- /* @__PURE__ */ r(O, { color: "inherit", variant: "contained", size: "small", onClick: l, children: "Cancel" }),
332
- /* @__PURE__ */ r(O, { color: "primary", variant: "contained", size: "small", onClick: x, disabled: !y, children: n ? "Create" : "Save Changes" })
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: l,
335
- ...o,
336
- children: /* @__PURE__ */ C(f, { width: 600, minHeight: 320, children: [
337
- /* @__PURE__ */ r(
338
- le,
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: a.name,
403
+ value: state.name,
342
404
  placeholder: "New label",
343
405
  size: "small",
344
- fullWidth: !0,
345
- onChange: (i) => u({ name: i.target.value }),
406
+ fullWidth: true,
407
+ onChange: (e) => setState({ name: e.target.value }),
346
408
  translationInputProps: {
347
- value: a.translation,
348
- onChange: (i) => u({ translation: i })
409
+ value: state.translation,
410
+ onChange: (v) => setState({ translation: v })
349
411
  }
350
412
  }
351
413
  ),
352
- /* @__PURE__ */ r(
353
- j,
414
+ /* @__PURE__ */ jsx(
415
+ TextField,
354
416
  {
355
417
  label: "Slug",
356
- value: a.slug,
418
+ value: state.slug,
357
419
  size: "small",
358
- fullWidth: !0,
359
- disabled: !n,
360
- onChange: (i) => u({ slug: i.target.value }),
420
+ fullWidth: true,
421
+ disabled: !isNew,
422
+ onChange: (e) => setState({ slug: e.target.value }),
361
423
  sx: { mt: 2 }
362
424
  }
363
425
  ),
364
- (t == null ? void 0 : t.parent) && /* @__PURE__ */ r(j, { label: "Parent", value: t.parent.name, size: "small", fullWidth: !0, disabled: !0, sx: { mt: 2 } }),
365
- /* @__PURE__ */ r(se, { onClickAway: () => m(!1), children: /* @__PURE__ */ C(f, { sx: { position: "relative", mt: 2 }, children: [
366
- /* @__PURE__ */ C(f, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
367
- /* @__PURE__ */ r(
368
- f,
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: a.color, borderRadius: 1 },
371
- onClick: () => m(!0)
432
+ sx: { width: 30, height: 30, bgcolor: state.color, borderRadius: 1 },
433
+ onClick: () => setColorPickerVisible(true)
372
434
  }
373
435
  ),
374
- /* @__PURE__ */ r(
375
- j,
436
+ /* @__PURE__ */ jsx(
437
+ TextField,
376
438
  {
377
439
  label: "",
378
- value: a.color,
440
+ value: state.color,
379
441
  size: "small",
380
- onChange: c,
442
+ onChange: handleColorTextChange,
381
443
  inputProps: { maxLength: 7 },
382
444
  sx: { width: 100, ".MuiInputBase-root": { height: 32 } }
383
445
  }
384
446
  )
385
447
  ] }),
386
- b && /* @__PURE__ */ r(f, { sx: { position: "absolute", top: 48, zIndex: 1 }, children: /* @__PURE__ */ r(ie, { color: a.color, onChangeComplete: z }) })
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 De({ open: e, label: t, onSubmit: s, onClose: l, ...o }) {
393
- return /* @__PURE__ */ r(
394
- J,
454
+ function LabelDeleteDialog({ open, label, onSubmit, onClose, ...rest }) {
455
+ return /* @__PURE__ */ jsx(
456
+ Dialog,
395
457
  {
396
- open: e,
397
- showCloseButton: !0,
458
+ open,
459
+ showCloseButton: true,
398
460
  maxWidth: "md",
399
461
  title: "Delete",
400
- actions: /* @__PURE__ */ C(L, { children: [
401
- /* @__PURE__ */ r(O, { color: "inherit", variant: "contained", size: "small", onClick: l, children: "Cancel" }),
402
- /* @__PURE__ */ r(O, { color: "primary", variant: "contained", size: "small", onClick: s, children: "Delete" })
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: l,
405
- ...o,
406
- children: /* @__PURE__ */ C(f, { width: 600, minHeight: 280, children: [
407
- /* @__PURE__ */ r(ce, { severity: "info", sx: { mb: 2 }, children: "Are you sure you want to delete these labels ?" }),
408
- /* @__PURE__ */ r(F, { data: [t] })
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 Me = R(F)`
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 at({ data: e, api: t, ...s }) {
435
- const [l, o] = A(null), [n, a] = A(null), [u, p] = A(H(e)), y = N(() => B(u), [u]), b = N(() => {
436
- const d = (g, w) => g.map((v) => (w && (v.parent = w), v.children && (v.children = d(v.children, v)), v));
437
- return d(u);
438
- }, [u]), m = (d) => {
439
- var g, w;
440
- d.parent ? (d.parent.children = (w = (g = d.parent) == null ? void 0 : g.children) == null ? void 0 : w.map((v) => v.id === d.id ? { ...d } : v), m(d.parent)) : p(u.map((v) => v.id === d.id ? { ...d } : v));
441
- }, x = (d) => {
442
- d.parent ? (d.parent.children = d.parent.children || [], d.parent.children.push(d), p([...u])) : p([...u, d]);
443
- }, z = (d) => {
444
- var g;
445
- d.parent ? d.parent.children = (g = d.parent.children) == null ? void 0 : g.filter((w) => w.id !== d.id) : p(u.filter((w) => w.id !== d.id));
446
- }, c = (d) => {
447
- if (l)
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
- o({ id: "", name: "New label", parent: d, color: "#dddddd" });
450
- }, i = (d) => {
451
- l || o(d);
452
- }, S = async (d) => {
453
- if (!l)
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
- const { parent: g, children: w, translation: v, ...T } = d;
456
- l.id ? (await t.updateLabel({ ...T, translation: JSON.stringify(v), parentId: g == null ? void 0 : g.id }), m(d)) : (await t.createLabel({ ...T, translation: JSON.stringify(v), parentId: g == null ? void 0 : g.id }), x(d)), o(null);
457
- }, h = async () => {
458
- n && (await t.deleteLabel(n.id), z(n), a(null));
459
- }, I = (d, g) => /* @__PURE__ */ C(L, { children: [
460
- /* @__PURE__ */ C(f, { sx: { display: "flex", alignItems: "center", flexWrap: "wrap" }, className: "label-name", children: [
461
- /* @__PURE__ */ r(f, { children: d }),
462
- g.translation && /* @__PURE__ */ r(f, { sx: { display: "flex", alignItems: "center", gap: 0.5, ml: 2 }, className: "label-translation", children: Object.keys(g.translation).map((w) => {
463
- var T;
464
- const v = (T = g.translation) == null ? void 0 : T[w];
465
- return v ? /* @__PURE__ */ r(ae, { locale: w, value: v }, w) : null;
466
- }) })
467
- ] }),
468
- /* @__PURE__ */ C(f, { sx: { display: "flex", gap: 1, flex: "0 0 auto" }, className: "label-action", children: [
469
- /* @__PURE__ */ r(E, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => c(g), children: /* @__PURE__ */ r(Z, { sx: { fontSize: 20 } }) }),
470
- /* @__PURE__ */ r(E, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => i(g), children: /* @__PURE__ */ r(G, { sx: { fontSize: 18 } }) }),
471
- /* @__PURE__ */ r(E, { color: "inherit", size: "small", sx: { color: "grey.500" }, onClick: () => a(g), children: /* @__PURE__ */ r(U, { sx: { fontSize: 20 } }) })
472
- ] })
473
- ] });
474
- return /* @__PURE__ */ C(f, { ...s, className: "label-container", children: [
475
- /* @__PURE__ */ C(f, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
476
- /* @__PURE__ */ r(oe, { component: "h2", variant: "h6", children: "Manage labels" }),
477
- /* @__PURE__ */ r(
478
- O,
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: () => c(),
606
+ onClick: () => handleAdd(),
485
607
  children: "New label"
486
608
  }
487
609
  )
488
610
  ] }),
489
- /* @__PURE__ */ C(f, { sx: { mt: 2, border: 1, borderColor: "grey.300", borderRadius: 1, overflow: "hidden" }, children: [
490
- /* @__PURE__ */ C(f, { sx: { p: 2, fontSize: 14, fontWeight: "bold", bgcolor: "grey.200" }, className: "label-header", children: [
491
- y.length,
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__ */ r(f, { sx: { pt: 0.5 }, children: /* @__PURE__ */ r(Me, { data: b, selected: [], renderItem: I, rowHeight: 64 }) })
616
+ /* @__PURE__ */ jsx(Box, { sx: { pt: 0.5 }, children: /* @__PURE__ */ jsx(StyledLabelTree, { data: treeData, selected: [], renderItem, rowHeight: 64 }) })
495
617
  ] }),
496
- /* @__PURE__ */ r(
497
- je,
618
+ /* @__PURE__ */ jsx(
619
+ LabelFormDialog,
498
620
  {
499
- open: !!l,
500
- onClose: () => o(null),
501
- initialLabel: l,
502
- onSubmit: S
621
+ open: !!editingLabel,
622
+ onClose: () => setEditingLabel(null),
623
+ initialLabel: editingLabel,
624
+ onSubmit: handleSubmit
503
625
  },
504
- l == null ? void 0 : l.id
626
+ editingLabel == null ? void 0 : editingLabel.id
505
627
  ),
506
- /* @__PURE__ */ r(
507
- De,
628
+ /* @__PURE__ */ jsx(
629
+ LabelDeleteDialog,
508
630
  {
509
- open: !!n,
510
- onClose: () => a(null),
511
- label: n,
512
- onSubmit: h
631
+ open: !!deletingLabel,
632
+ onClose: () => setDeletingLabel(null),
633
+ label: deletingLabel,
634
+ onSubmit: handleDelete
513
635
  },
514
- n == null ? void 0 : n.id
636
+ deletingLabel == null ? void 0 : deletingLabel.id
515
637
  )
516
638
  ] });
517
639
  }
518
- const K = V({}), Q = () => q(K), st = () => {
519
- const { updateLabels: e } = Q();
520
- M(() => () => {
521
- e();
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
- function it({ fetchLabels: e, children: t }) {
525
- const { locale: s } = ye(), [l, o] = A({
526
- loading: !0,
527
- labels: [],
528
- updateCounter: 1
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
- M(() => {
531
- (async () => {
532
- try {
533
- o((S) => ({ ...S, loading: !0 }));
534
- const i = await e();
535
- o((S) => ({ ...S, loading: !1, labels: ke(i) }));
536
- } catch (i) {
537
- console.error(i), o((S) => ({ ...S, loading: !1, labels: [] }));
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
- }, [l.updateCounter]);
541
- const n = N(() => {
542
- var i;
543
- return (i = l.labels) != null && i.length ? B(l.labels).reduce((S, h) => ({ ...S, [h.id]: h }), {}) : {};
544
- }, [l.labels]), a = k(
545
- (c) => {
546
- var i;
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
- ...c,
549
- name: ((i = c.translation) == null ? void 0 : i[s]) || c.name
823
+ ...label,
824
+ name: ((_a = label.translation) == null ? void 0 : _a[locale]) || label.name
550
825
  };
551
826
  },
552
- [s]
553
- ), u = k(
554
- (c) => c.filter(Boolean).map((i) => n[i]).map(a),
555
- [n, a]
556
- ), p = k((c) => {
557
- let { parent: i } = c;
558
- const S = [];
559
- for (; i; )
560
- S.unshift(i), i = i.parent;
561
- return S;
562
- }, []), y = k(
563
- (c) => [...p(c), c].map(a).map((S) => S.name).join(" / "),
564
- [a, p]
565
- ), b = k((c) => (c == null ? void 0 : c.split(",")) || [], []), m = k((c = []) => {
566
- if (c != null && c.length)
567
- return c.join(",");
568
- }, []), x = N(() => l.labels.map(a), [l.labels, a]), z = N(() => ({
569
- loading: l.loading,
570
- labels: l.labels || [],
571
- updateLabels: () => o((c) => ({ ...c, updateCounter: ++l.updateCounter })),
572
- getLabelsById: u,
573
- parseLabelIds: b,
574
- stringifyLabelIds: m,
575
- localizedLabels: x,
576
- flattenedLabels: B(x || []),
577
- getFullLabelName: y
578
- }), [l, u, b, m, x, y]);
579
- return /* @__PURE__ */ r(K.Provider, { value: z, children: t });
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 Fe = (e) => {
874
+ const getContrastTextColor = (bgcolor) => {
582
875
  try {
583
- return me(e, "#fff") > 3.5 ? "#fff" : "#111";
876
+ const ratio = getContrastRatio(bgcolor, "#fff");
877
+ return ratio > 3.5 ? "#fff" : "#111";
584
878
  } catch {
585
879
  return "#111";
586
880
  }
587
- }, We = (e, t) => {
881
+ };
882
+ const getAlphaColor = (color, alpha$1) => {
588
883
  try {
589
- if (e)
590
- return ge(e, t);
884
+ if (color) {
885
+ return alpha(color, alpha$1);
886
+ }
591
887
  } catch {
592
- return e;
888
+ return color;
593
889
  }
594
- return e;
890
+ return color;
595
891
  };
596
- function ct({ labels: e, editable: t, onChange: s, sx: l, renderLabel: o }) {
597
- const n = !(e != null && e.length), { labels: a, loading: u, getLabelsById: p, getFullLabelName: y } = Q(), [b, m] = A(!1);
598
- if (u || !t && n)
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
- const x = (i) => {
601
- s == null || s(i), setTimeout(() => {
602
- m(!1);
898
+ }
899
+ const handleOnChange = (v) => {
900
+ onChange == null ? void 0 : onChange(v);
901
+ setTimeout(() => {
902
+ setEditing(false);
603
903
  }, 300);
604
904
  };
605
- if (b)
606
- return /* @__PURE__ */ r(de, { onClickAway: () => b && m(!1), children: /* @__PURE__ */ r("div", { children: /* @__PURE__ */ r(Oe, { data: a, value: e || [], onChange: x }) }) });
607
- const z = p(e || []), c = [
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(l) ? l : [l]
911
+ ...Array.isArray(sx) ? sx : [sx]
610
912
  ];
611
- return /* @__PURE__ */ C(ue, { sx: c, children: [
612
- z.map((i) => i ? o ? o(i) : /* @__PURE__ */ r(
613
- he,
614
- {
615
- label: y(i),
616
- variant: "filled",
617
- size: "small",
618
- sx: {
619
- height: 20,
620
- borderRadius: 1,
621
- fontSize: 12,
622
- bgcolor: We(i.color, 0.8),
623
- color: Fe(i.color)
624
- }
625
- },
626
- i.id
627
- ) : null),
628
- t && !n && /* @__PURE__ */ r(pe, { color: "inherit", size: "small", onClick: () => m(!0), sx: { color: "grey.400" }, children: /* @__PURE__ */ r(Y, { sx: { fontSize: 20 } }) }),
629
- t && n && /* @__PURE__ */ r(fe, { color: "inherit", variant: "outlined", startIcon: /* @__PURE__ */ r(X, {}), onClick: () => m(!0), children: "Edit labels" })
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
- at as LabelManager,
634
- Oe as LabelPicker,
635
- F as LabelTree,
636
- ct as Labels,
637
- it as LabelsProvider,
638
- ke as transformLabels,
639
- Q as useLabelsContext,
640
- st as useLabelsUpdateOnDestroy
943
+ LabelManager,
944
+ LabelPicker,
945
+ LabelTree,
946
+ Labels,
947
+ LabelsContainer,
948
+ LabelsProvider,
949
+ useLabelsContext,
950
+ useLabelsUpdateOnDestroy
641
951
  };