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