@blocklet/labels 2.4.36 → 2.4.38

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 DELETED
@@ -1,1463 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { createContext, useRef, useState, useCallback, useEffect, useMemo, useContext, Fragment as Fragment$1, createElement } from "react";
3
- import { ExpandMore, Edit, LabelOutlined } from "@mui/icons-material";
4
- import Box from "@mui/material/Box";
5
- import { styled } from "@mui/material/styles";
6
- import { Tree } from "react-arborist";
7
- import { Icon } from "@iconify/react";
8
- import omit from "lodash/omit";
9
- import Select, { components } from "react-select";
10
- import { ClickAwayListener, Box as Box$1, Chip, IconButton, Button, getContrastRatio, alpha, useTheme, ButtonBase, Popper, Autocomplete, InputBase, autocompleteClasses, TextField } from "@mui/material";
11
- import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
12
- import { useReactive, useLocalStorageState, useRequest, useSize, useDeepCompareLayoutEffect } from "ahooks";
13
- import { createContainer } from "unstated-next";
14
- import { arrayToTree } from "performant-array-to-tree";
15
- import { SessionContext } from "@arcblock/did-connect-react/lib/Session";
16
- import chroma from "chroma-js";
17
- import CloseOutlineIcon from "@mui/icons-material/Close";
18
- 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: "M4 20q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h11q.475 0 .9.213t.7.587l4.5 6q.4.525.4 1.2t-.4 1.2l-4.5 6q-.275.375-.7.588T15 20z" }) });
19
- 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" }) });
20
- const LabelTreeContext = createContext({});
21
- function LabelTreeProvider({
22
- selectable = false,
23
- selected = [],
24
- onSelect,
25
- renderItem,
26
- children
27
- }) {
28
- const isSelected = (id) => selected.indexOf(id) !== -1;
29
- const select = (id) => {
30
- Array.from(new Set(selected).add(id));
31
- onSelect?.(id);
32
- };
33
- const value = useMemo(
34
- () => ({
35
- selectable,
36
- selected,
37
- isSelected,
38
- select,
39
- renderItem
40
- }),
41
- // eslint-disable-next-line react-hooks/exhaustive-deps
42
- [selected]
43
- );
44
- return /* @__PURE__ */ jsx(LabelTreeContext.Provider, { value, children });
45
- }
46
- const isLeaf = (node) => {
47
- return node.isLeaf || !node.data.children?.length;
48
- };
49
- function FolderArrow({ node }) {
50
- if (isLeaf(node)) return /* @__PURE__ */ jsx("span", {});
51
- return node.isOpen ? /* @__PURE__ */ jsx(ExpandMore, { style: { fontSize: 20 } }) : /* @__PURE__ */ jsx(ExpandMore, { style: { fontSize: 20, transform: "rotate(-90deg)" } });
52
- }
53
- function Node({ node, style, dragHandle }) {
54
- const { selectable, select, isSelected, renderItem } = useContext(LabelTreeContext);
55
- const { data } = node;
56
- const icon = data.icon ? /* @__PURE__ */ jsx(Icon, { icon: data.icon, style: { fontSize: 18 } }) : /* @__PURE__ */ jsx(materialSymbolsLabelRounded, { style: { fontSize: 18, color: data.color || "#ddd" } });
57
- const handleToggle = (e) => {
58
- e.stopPropagation();
59
- if (node.isInternal) {
60
- node.toggle();
61
- }
62
- };
63
- const handleClick = (e) => {
64
- e.stopPropagation();
65
- if (selectable) {
66
- select(node.id);
67
- }
68
- };
69
- const children = /* @__PURE__ */ jsxs(Fragment, { children: [
70
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", flex: 1 }, children: [
71
- /* @__PURE__ */ jsx(Box, { sx: { display: "flex", cursor: "pointer" }, onClick: handleToggle, children: /* @__PURE__ */ jsx(FolderArrow, { node }) }),
72
- /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", width: 22, height: 22 }, children: icon }),
73
- /* @__PURE__ */ jsx(Box, { component: "span", sx: { color: "grey.700" }, children: node.data.name })
74
- ] }),
75
- selectable && isSelected(node.id) && /* @__PURE__ */ jsx(materialSymbolsCheckSmall, {})
76
- ] });
77
- return /* @__PURE__ */ jsxs(
78
- Box,
79
- {
80
- className: "label-tree-item",
81
- style,
82
- sx: {
83
- display: "flex",
84
- alignItems: "center",
85
- justifyContent: "space-between",
86
- color: (theme) => theme.palette.mode === "dark" ? "grey.800" : "text.primary"
87
- },
88
- ref: dragHandle,
89
- onClick: handleClick,
90
- children: [
91
- !renderItem && children,
92
- !!renderItem && renderItem(children, node.data)
93
- ]
94
- }
95
- );
96
- }
97
- const Root = styled(Box)`
98
- > div,
99
- > div > div,
100
- > div > div > div {
101
- width: auto !important;
102
- }
103
- [role='treeitem'] {
104
- display: flex;
105
- align-items: stretch;
106
- }
107
- .label-tree-item {
108
- width: 100%;
109
- }
110
- `;
111
- function LabelTree({
112
- data,
113
- selectable,
114
- selected = [],
115
- onSelect,
116
- rowHeight = 28,
117
- renderItem,
118
- sx,
119
- ...rest
120
- }) {
121
- const tree = useRef(void 0);
122
- const mergedSx = [...Array.isArray(sx) ? sx : [sx]];
123
- const [height, setHeight] = useState(0);
124
- const updateHeight = useCallback(() => {
125
- setHeight((tree.current?.visibleNodes?.length || 0) * rowHeight);
126
- }, [rowHeight]);
127
- useEffect(() => {
128
- updateHeight();
129
- }, [data, updateHeight]);
130
- const handleToggle = () => {
131
- setTimeout(updateHeight);
132
- };
133
- 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 }) }) });
134
- }
135
- const flatten = (data) => {
136
- return data.reduce((acc, cur) => {
137
- acc.push(cur);
138
- if (cur.children?.length) {
139
- acc.push(...flatten(cur.children));
140
- }
141
- return acc;
142
- }, []);
143
- };
144
- const safeParseJSON = (json, defaultValue = null) => {
145
- try {
146
- return JSON.parse(json);
147
- } catch {
148
- return defaultValue;
149
- }
150
- };
151
- const transformLabels = (labelDtos) => {
152
- const relations = labelDtos.reduce((acc, cur) => {
153
- acc[cur.id] = acc[cur.id] || {};
154
- if (cur.parentId) {
155
- acc[cur.id].parent = cur.parentId;
156
- acc[cur.parentId] = acc[cur.parentId] || {};
157
- acc[cur.parentId].children = acc[cur.parentId].children || [];
158
- acc[cur.parentId].children.push(cur.id);
159
- }
160
- return acc;
161
- }, {});
162
- const labels = labelDtos.map(({ parentId, translation, ...rest }) => ({
163
- ...rest,
164
- translation: safeParseJSON(translation),
165
- children: []
166
- }));
167
- const labelsKeyById = labels.reduce((acc, cur) => ({ ...acc, [cur.id]: cur }), {});
168
- labels.forEach((item) => {
169
- const relation = relations[item.id];
170
- if (relation.parent && labelsKeyById[relation.parent]) {
171
- item.parent = labelsKeyById[relation.parent];
172
- }
173
- if (relation.children) {
174
- item.children = relation.children.map((childId) => labelsKeyById[childId]);
175
- }
176
- });
177
- return labels.filter((item) => !item.parent);
178
- };
179
- const LabelMenuList = (props) => {
180
- const { options, getValue, selectProps, selectOption } = props;
181
- const value = getValue();
182
- const handleSelect = (id) => {
183
- const option = options.find((item) => item.data.id === id);
184
- if (option) {
185
- selectOption(option);
186
- }
187
- };
188
- return /* @__PURE__ */ jsxs(Fragment, { children: [
189
- /* @__PURE__ */ jsx(components.MenuList, { ...omit(props, ["addon"]), children: /* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1 }, children: /* @__PURE__ */ jsx(
190
- LabelTree,
191
- {
192
- data: selectProps.data,
193
- selectable: true,
194
- onSelect: handleSelect,
195
- selected: value.map((item) => item.data.id)
196
- }
197
- ) }) }),
198
- props.addon
199
- ] });
200
- };
201
- const ValueContainer = ({ children, ...props }) => {
202
- const [values, input] = children;
203
- const count = Array.isArray(values) ? values.length : 0;
204
- return /* @__PURE__ */ jsxs(components.ValueContainer, { ...props, children: [
205
- /* @__PURE__ */ jsx(
206
- Box,
207
- {
208
- component: "span",
209
- sx: {
210
- fontSize: 13,
211
- fontWeight: "bold",
212
- color: (theme) => theme.palette.mode === "dark" ? "grey.800" : "text.primary"
213
- },
214
- children: count ? "Labels" : "Filter by labels"
215
- }
216
- ),
217
- !!count && /* @__PURE__ */ jsx(
218
- Box,
219
- {
220
- sx: {
221
- display: "inline-flex",
222
- justifyContent: "center",
223
- alignItems: "center",
224
- width: 22,
225
- height: 22,
226
- ml: 1,
227
- borderRadius: "100%",
228
- fontSize: 12,
229
- color: (theme) => theme.palette.mode === "dark" ? "grey.800" : "text.primary",
230
- bgcolor: "grey.200"
231
- },
232
- children: count
233
- }
234
- ),
235
- input
236
- ] });
237
- };
238
- const compactSelectStyles = {
239
- control: (provided) => ({
240
- ...provided,
241
- minHeight: 31
242
- }),
243
- valueContainer: (provided) => ({
244
- ...provided,
245
- display: "flex"
246
- }),
247
- dropdownIndicator: (provided) => ({
248
- ...provided,
249
- padding: "0 4px"
250
- }),
251
- clearIndicator: (provided) => ({
252
- ...provided,
253
- padding: "4px"
254
- })
255
- };
256
- function LabelPicker({
257
- data,
258
- value = [],
259
- editable = false,
260
- addon,
261
- onChange,
262
- compact,
263
- isMulti = true,
264
- maxHeight = 264,
265
- ...rest
266
- }) {
267
- const selectRef = useRef(null);
268
- const options = useMemo(
269
- () => flatten(data).map((item) => ({ data: item, label: item.name, value: item.id })),
270
- [data]
271
- );
272
- const optionsKeyById = options.reduce(
273
- (acc, cur) => ({ ...acc, [cur.data.id]: cur }),
274
- {}
275
- );
276
- const valueArr = Array.isArray(value) ? value : [value];
277
- const selectedOptions = valueArr.map((item) => optionsKeyById[item]);
278
- const changes = useRef({
279
- prev: valueArr.map((item) => optionsKeyById[item]?.data || "")
280
- });
281
- const handleChange = (v, actionMeta) => {
282
- if (!isMulti || !Array.isArray(v)) {
283
- onChange(v ? [v.data] : []);
284
- } else {
285
- changes.current.current = v.map((item) => item.data);
286
- }
287
- if (["clear", "remove-value"].includes(actionMeta.action) && selectRef.current) {
288
- setTimeout(() => selectRef?.current?.blur(), 1);
289
- }
290
- };
291
- const handleMenuClose = () => {
292
- if (changes.current.current) {
293
- const changed = JSON.stringify(changes.current.prev.map((item) => item.id)) !== JSON.stringify(changes.current.current.map((item) => item.id));
294
- if (changed) {
295
- onChange(changes.current.current);
296
- changes.current.prev = changes.current.current;
297
- }
298
- }
299
- };
300
- return /* @__PURE__ */ jsx(Box, { ...rest, children: /* @__PURE__ */ jsx(
301
- Select,
302
- {
303
- ref: selectRef,
304
- defaultValue: selectedOptions,
305
- options,
306
- onChange: handleChange,
307
- components: {
308
- // @ts-ignore
309
- MenuList: (props) => /* @__PURE__ */ jsx(LabelMenuList, { ...props, addon }),
310
- // eslint-disable-line react/no-unstable-nested-components
311
- ...compact && { ValueContainer }
312
- },
313
- placeholder: "Select labels",
314
- styles: {
315
- ...compact && compactSelectStyles,
316
- menu: (css) => ({
317
- ...css,
318
- ...editable && { paddingBottom: "36px" },
319
- zIndex: 99
320
- }),
321
- menuList: (css) => ({
322
- ...css,
323
- overflowY: "auto",
324
- maxHeight: `${maxHeight}px`
325
- })
326
- },
327
- theme: (_theme) => ({
328
- ..._theme,
329
- colors: {
330
- ..._theme.colors,
331
- primary25: "#ddd",
332
- primary50: "#ddd",
333
- primary: "#ddd"
334
- }
335
- }),
336
- isSearchable: false,
337
- isMulti,
338
- closeMenuOnSelect: !isMulti,
339
- onMenuClose: handleMenuClose,
340
- isClearable: true,
341
- data
342
- }
343
- ) });
344
- }
345
- class TreeNode {
346
- data;
347
- parent;
348
- children;
349
- constructor({ data, parent, children }) {
350
- this.data = data;
351
- this.parent = parent;
352
- this.children = children || [];
353
- }
354
- setParent(node) {
355
- this.parent = node;
356
- }
357
- add(...nodes) {
358
- this.children.push(...nodes);
359
- nodes.forEach((node) => node.setParent(this));
360
- }
361
- removeChild(node) {
362
- this.children = this.children.filter((x) => x !== node);
363
- node.setParent(void 0);
364
- }
365
- isLeaf(node) {
366
- return !node.children?.length;
367
- }
368
- getAllParents(includeSelf = true) {
369
- const parents = [];
370
- if (includeSelf) {
371
- parents.unshift(this);
372
- }
373
- let { parent } = this;
374
- while (parent) {
375
- parents.unshift(parent);
376
- parent = parent.parent;
377
- }
378
- return parents;
379
- }
380
- getAllSiblings() {
381
- const siblings = this.parent?.children.filter((node) => node !== this) || [];
382
- return siblings;
383
- }
384
- flatten(includeRoot) {
385
- const nodes = [];
386
- const traverse = (node) => {
387
- nodes.push(node);
388
- node.children.forEach(traverse);
389
- };
390
- traverse(this);
391
- if (!includeRoot) {
392
- nodes.shift();
393
- }
394
- return nodes;
395
- }
396
- clone() {
397
- const cloned = new TreeNode({ data: this.data });
398
- cloned.add(...this.children);
399
- return cloned;
400
- }
401
- }
402
- class Label {
403
- id;
404
- name;
405
- desc;
406
- image;
407
- icon;
408
- type;
409
- color;
410
- passports;
411
- translation;
412
- constructor(data) {
413
- this.id = data.id;
414
- this.name = data.name;
415
- this.desc = data.desc;
416
- this.image = data.image;
417
- this.icon = data.icon;
418
- this.type = data.type;
419
- this.color = data.color || "#ddd";
420
- this.passports = data.passports || [];
421
- this.translation = this._normalizeTranslation(data.translation);
422
- }
423
- _normalizeTranslation(translation) {
424
- if (typeof translation === "object") {
425
- return translation;
426
- }
427
- try {
428
- return JSON.parse(translation);
429
- } catch {
430
- return {};
431
- }
432
- }
433
- getName(locale) {
434
- if (!locale) {
435
- return this.name;
436
- }
437
- return this?.translation?.[locale] || this?.name;
438
- }
439
- }
440
- class LabelTreeNode extends TreeNode {
441
- static Label = Label;
442
- getName(locale) {
443
- return this.data.getName(locale);
444
- }
445
- isSystemLabel() {
446
- return this.data.type === "system";
447
- }
448
- getFullName(locale) {
449
- const parents = this.getAllParents();
450
- parents.shift();
451
- if (locale) {
452
- return parents.map((item) => item.getName(locale)).join(" / ");
453
- }
454
- return parents.map((item) => item.data.name).join(" / ");
455
- }
456
- }
457
- const mapToTree = (items, parent) => {
458
- return items.map((item) => {
459
- const node = new LabelTreeNode({ data: new Label(item.data), parent });
460
- const children = mapToTree(item.children || [], node);
461
- node.add(...children);
462
- return node;
463
- });
464
- };
465
- const initLabelTree = (data) => {
466
- const treeData = arrayToTree(data);
467
- const root = new LabelTreeNode({ data: {} });
468
- root.add(...mapToTree(treeData));
469
- return root;
470
- };
471
- const createEmptyLabelTree = () => {
472
- return new LabelTreeNode({ data: {}, children: [] });
473
- };
474
- const useLabels = ({ loading, data, createLabel: createLabelAPI }) => {
475
- const { locale } = useLocaleContext();
476
- const state = useReactive({
477
- loading: true,
478
- tree: createEmptyLabelTree(),
479
- stats: []
480
- });
481
- useEffect(() => {
482
- try {
483
- if (data && !loading) {
484
- const { labels, stats } = data;
485
- const tree = initLabelTree(labels || []);
486
- state.loading = false;
487
- state.tree = tree;
488
- state.stats = stats.sort((a, b) => b.count - a.count);
489
- }
490
- } catch (e) {
491
- console.error(e);
492
- state.loading = false;
493
- }
494
- }, [data, loading]);
495
- const flattened = useMemo(() => state.tree.flatten(), [state.tree]);
496
- const counts = useMemo(
497
- () => state.stats.reduce((acc, cur) => ({ ...acc, [cur.id]: cur.count }), {}),
498
- [state.stats]
499
- );
500
- const nodesKeyById = useMemo(() => {
501
- return flattened.reduce((acc, cur) => ({ ...acc, [cur.data.id]: cur }), {});
502
- }, [flattened]);
503
- const popularLabels = useMemo(
504
- () => state.stats.slice(0, 12).map((x) => nodesKeyById[x.id]),
505
- [nodesKeyById, state.stats]
506
- );
507
- const getLabelsByIds = useCallback(
508
- (ids) => {
509
- return ids.map((id) => nodesKeyById[id]).filter(Boolean);
510
- },
511
- [nodesKeyById]
512
- );
513
- const labelExists = useCallback((id) => !!nodesKeyById[id], [nodesKeyById]);
514
- const getLabelName = (id) => {
515
- return nodesKeyById[id]?.data.getName(locale);
516
- };
517
- const getRelatedLabels = (id) => {
518
- const node = nodesKeyById[id];
519
- if (!node || node.parent === state.tree) {
520
- return [];
521
- }
522
- const siblings = node.getAllSiblings();
523
- return siblings;
524
- };
525
- const getRecommended = (id) => {
526
- const related = id ? getRelatedLabels(id) : [];
527
- const set = new Set([...related.map((x) => x.data.id), id].filter(Boolean));
528
- return [...related, ...popularLabels.filter((x) => !set.has(x.data.id))].filter((x) => !!counts[x.data.id]);
529
- };
530
- const createLabel = async (name) => {
531
- const saved = await createLabelAPI({ name });
532
- const node = new LabelTreeNode({ data: new LabelTreeNode.Label(saved) });
533
- state.tree.add(node);
534
- state.tree = state.tree.clone();
535
- return node.data;
536
- };
537
- const addLabelNode = (payload) => {
538
- const parent = payload.parentId ? nodesKeyById[payload.parentId] : state.tree;
539
- const node = new LabelTreeNode({ data: new LabelTreeNode.Label(payload) });
540
- parent.add(node);
541
- state.tree = state.tree.clone();
542
- };
543
- const updateLabelNode = (id, payload) => {
544
- const existing = nodesKeyById[id];
545
- if (existing && existing.parent) {
546
- const node = new LabelTreeNode({ data: new LabelTreeNode.Label(payload) });
547
- node.add(...existing.children);
548
- if (existing.parent.data.id == payload.parentId) {
549
- existing.parent.children = existing.parent.children.map((x) => {
550
- if (x.data.id === id) {
551
- node.setParent(existing.parent);
552
- return node;
553
- }
554
- return x;
555
- });
556
- } else {
557
- const parent = nodesKeyById[payload.parentId];
558
- if (parent) {
559
- existing.parent.children = existing.parent.children.filter((x) => x.data.id !== id);
560
- parent.add(node);
561
- }
562
- }
563
- state.tree = state.tree.clone();
564
- }
565
- };
566
- const removeLabelNode = (id) => {
567
- const node = nodesKeyById[id];
568
- if (node?.parent) {
569
- node.parent.removeChild(node);
570
- state.tree = state.tree.clone();
571
- }
572
- };
573
- const getFullLabelName = (id) => {
574
- return nodesKeyById[id]?.getFullName(locale) ?? id;
575
- };
576
- const parseLabelsStr = useCallback((labelIdsStr) => {
577
- return labelIdsStr?.split(",").filter(Boolean) || [];
578
- }, []);
579
- const [recentLabels = [], setRecentLabels] = useLocalStorageState("recent-labels", { defaultValue: [] });
580
- const addRecentLabel = (label) => {
581
- if (!label || labelExists(label)) return;
582
- setRecentLabels([label, ...recentLabels].slice(0, 10));
583
- };
584
- return {
585
- ...state,
586
- flattenedLabels: flattened,
587
- popularLabels,
588
- counts,
589
- getLabelsByIds,
590
- labelExists,
591
- getLabelName,
592
- getRelatedLabels,
593
- getRecommended,
594
- createLabel,
595
- addLabelNode,
596
- updateLabelNode,
597
- removeLabelNode,
598
- getFullLabelName,
599
- parseLabelsStr,
600
- recentLabels,
601
- setRecentLabels,
602
- addRecentLabel
603
- };
604
- };
605
- const LabelsContainer = createContainer(useLabels);
606
- const createCacheKey = (args) => {
607
- return args.map((x) => typeof x === "string" ? x : JSON.stringify(x)).join("-");
608
- };
609
- const CACHE_PREFIX = "request-cache-";
610
- const getStorageKey = (key) => `${CACHE_PREFIX}${key}`;
611
- const serialize = (data) => {
612
- try {
613
- return JSON.stringify(data);
614
- } catch (error) {
615
- console.warn("Failed to serialize cache data:", error);
616
- return "";
617
- }
618
- };
619
- const deserialize = (data) => {
620
- try {
621
- return JSON.parse(data);
622
- } catch (error) {
623
- console.warn("Failed to deserialize cache data:", error);
624
- return null;
625
- }
626
- };
627
- const setCache = (key, cachedData, options = {}) => {
628
- const storageKey = getStorageKey(key);
629
- const expiresAt = options.cacheTime ? Date.now() + options.cacheTime : Number.MAX_SAFE_INTEGER;
630
- const recordData = {
631
- ...cachedData,
632
- expiresAt
633
- };
634
- localStorage.setItem(storageKey, serialize(recordData));
635
- };
636
- const getCache = (key) => {
637
- const storageKey = getStorageKey(key);
638
- const cachedData = localStorage.getItem(storageKey);
639
- if (!cachedData) {
640
- return null;
641
- }
642
- const recordData = deserialize(cachedData);
643
- if (!recordData) {
644
- return null;
645
- }
646
- if (Date.now() > recordData.expiresAt) {
647
- localStorage.removeItem(storageKey);
648
- return null;
649
- }
650
- return recordData;
651
- };
652
- const cleanupExpiredCache = () => {
653
- const keysToRemove = [];
654
- for (let i = 0; i < localStorage.length; i++) {
655
- const key = localStorage.key(i);
656
- if (key && key.startsWith(CACHE_PREFIX)) {
657
- const cachedData = localStorage.getItem(key);
658
- if (cachedData) {
659
- const recordData = deserialize(cachedData);
660
- if (recordData?.expiresAt && Date.now() > recordData.expiresAt) {
661
- keysToRemove.push(key);
662
- }
663
- }
664
- }
665
- }
666
- keysToRemove.forEach((key) => localStorage.removeItem(key));
667
- };
668
- cleanupExpiredCache();
669
- const LabelsContext = createContext({});
670
- const useLabelsContext = () => useContext(LabelsContext);
671
- const useLabelsUpdateOnDestroy = () => {
672
- const { updateLabels } = useLabelsContext();
673
- useEffect(() => {
674
- return () => {
675
- updateLabels();
676
- };
677
- }, []);
678
- };
679
- function LabelsProvider({ fetchLabels, createLabel, children }) {
680
- const { locale } = useLocaleContext();
681
- const [updateCounter, setUpdateCounter] = useState(1);
682
- const cacheKey = createCacheKey(["labels"]);
683
- const { loading, data } = useRequest(fetchLabels, {
684
- cacheKey,
685
- getCache: () => getCache(cacheKey),
686
- setCache: (d) => setCache(cacheKey, d),
687
- refreshDeps: [updateCounter]
688
- });
689
- const labels = useMemo(() => transformLabels(data?.labels || []), [data]);
690
- const labelsKeyById = useMemo(() => {
691
- if (!labels?.length) {
692
- return {};
693
- }
694
- const flattened = flatten(labels);
695
- return flattened.reduce((acc, cur) => ({ ...acc, [cur.id]: cur }), {});
696
- }, [labels]);
697
- const localeTransformer = useCallback(
698
- (label) => ({
699
- ...label,
700
- name: label.translation?.[locale] || label.name
701
- }),
702
- [locale]
703
- );
704
- const getLabelsById = useCallback(
705
- (ids) => ids.filter(Boolean).map((item) => labelsKeyById[item]).map(localeTransformer),
706
- [labelsKeyById, localeTransformer]
707
- );
708
- const getParents = useCallback((label) => {
709
- let { parent } = label;
710
- const parents = [];
711
- while (parent) {
712
- parents.unshift(parent);
713
- parent = parent.parent;
714
- }
715
- return parents;
716
- }, []);
717
- const getFullLabelName = useCallback(
718
- (label) => {
719
- const parents = [...getParents(label), label].map(localeTransformer);
720
- return parents.map((item) => item.name).join(" / ");
721
- },
722
- [localeTransformer, getParents]
723
- );
724
- const parseLabelIds = useCallback((labelIdsStr) => {
725
- return labelIdsStr?.split(",").filter(Boolean) || [];
726
- }, []);
727
- const stringifyLabelIds = useCallback((labelIds = []) => {
728
- if (!labelIds?.length) {
729
- return void 0;
730
- }
731
- return labelIds.join(",");
732
- }, []);
733
- const localizedLabels = useMemo(() => labels.map(localeTransformer), [labels, localeTransformer]);
734
- const value = useMemo(() => {
735
- return {
736
- loading,
737
- labels: labels || [],
738
- updateLabels: () => setUpdateCounter((prev) => prev + 1),
739
- getLabelsById,
740
- parseLabelIds,
741
- stringifyLabelIds,
742
- localizedLabels,
743
- flattenedLabels: flatten(localizedLabels || []),
744
- getFullLabelName
745
- };
746
- }, [loading, labels, getLabelsById, parseLabelIds, stringifyLabelIds, localizedLabels, getFullLabelName]);
747
- return /* @__PURE__ */ jsx(LabelsContext.Provider, { value, children: /* @__PURE__ */ jsx(LabelsContainer.Provider, { initialState: { loading, data, createLabel }, children }) });
748
- }
749
- const getContrastTextColor = (bgcolor) => {
750
- try {
751
- const ratio = getContrastRatio(bgcolor, "#fff");
752
- return ratio > 2.7 ? "#fff" : "#111";
753
- } catch {
754
- return "#111";
755
- }
756
- };
757
- const getAlphaColor = (color, alpha$1) => {
758
- try {
759
- if (color) {
760
- return alpha(color, alpha$1);
761
- }
762
- } catch {
763
- return color;
764
- }
765
- return color;
766
- };
767
- function Labels({ labels, editable, onChange, sx, renderLabel }) {
768
- const isEmpty = !labels?.length;
769
- const { labels: allLabels, loading, getLabelsById, getFullLabelName } = useLabelsContext();
770
- const [editing, setEditing] = useState(false);
771
- if (loading || !editable && isEmpty) {
772
- return null;
773
- }
774
- const handleOnChange = (v) => {
775
- onChange?.(v);
776
- setTimeout(() => {
777
- setEditing(false);
778
- }, 300);
779
- };
780
- if (editing) {
781
- return /* @__PURE__ */ jsx(ClickAwayListener, { onClickAway: () => editing && setEditing(false), children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(LabelPicker, { data: allLabels, value: labels || [], onChange: handleOnChange }) }) });
782
- }
783
- const labelObjects = getLabelsById(labels || []).filter((x) => x.type !== "system");
784
- const mergedSx = [
785
- { display: "flex", gap: 1, alignItems: "center", flexWrap: "wrap" },
786
- ...Array.isArray(sx) ? sx : [sx]
787
- ];
788
- return /* @__PURE__ */ jsxs(Box$1, { sx: mergedSx, children: [
789
- labelObjects.map((item) => {
790
- if (!item) {
791
- return null;
792
- }
793
- if (renderLabel) {
794
- return renderLabel(item);
795
- }
796
- return /* @__PURE__ */ jsx(
797
- Chip,
798
- {
799
- label: getFullLabelName(item),
800
- variant: "filled",
801
- size: "small",
802
- sx: {
803
- height: 20,
804
- borderRadius: 1,
805
- fontSize: 12,
806
- bgcolor: getAlphaColor(item.color || "#ddd", 0.8),
807
- color: getContrastTextColor(item.color || "#ddd")
808
- }
809
- },
810
- item.id
811
- );
812
- }),
813
- editable && !isEmpty && /* @__PURE__ */ jsx(IconButton, { color: "inherit", size: "small", onClick: () => setEditing(true), sx: { color: "grey.400" }, children: /* @__PURE__ */ jsx(Edit, { sx: { fontSize: 20 } }) }),
814
- editable && isEmpty && /* @__PURE__ */ jsx(Button, { color: "inherit", variant: "outlined", startIcon: /* @__PURE__ */ jsx(LabelOutlined, {}), onClick: () => setEditing(true), children: "Edit labels" })
815
- ] });
816
- }
817
- const mdiTagPlusOutline = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em", ...props, children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M6.5 5A1.5 1.5 0 1 0 8 6.5A1.5 1.5 0 0 0 6.5 5m0 0A1.5 1.5 0 1 0 8 6.5A1.5 1.5 0 0 0 6.5 5m14.91 6.58l-9-9A2 2 0 0 0 11 2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 .59 1.42l.41.4a5.6 5.6 0 0 1 2.08-.74L4 11V4h7l9 9l-7 7l-1.08-1.08a5.6 5.6 0 0 1-.74 2.08l.41.41A2 2 0 0 0 13 22a2 2 0 0 0 1.41-.59l7-7A2 2 0 0 0 22 13a2 2 0 0 0-.59-1.42M6.5 5A1.5 1.5 0 1 0 8 6.5A1.5 1.5 0 0 0 6.5 5M10 19H7v3H5v-3H2v-2h3v-3h2v3h3Z" }) });
818
- const useSessionContext = () => {
819
- const ctx = useContext(SessionContext) || {};
820
- const loginRole = ctx?.session?.user?.role;
821
- return {
822
- ...ctx,
823
- isAdmin: ["admin", "owner"].includes(loginRole),
824
- hasAnyPassport: (passports) => {
825
- if (!passports || passports.length === 0) {
826
- return true;
827
- }
828
- const passportsArr = Array.isArray(passports) ? passports : [passports];
829
- return passportsArr.includes(loginRole);
830
- }
831
- };
832
- };
833
- const getTextColor = (color, mode) => {
834
- const baseColor = chroma(color);
835
- if (mode === "dark") {
836
- return baseColor.brighten(4).hex();
837
- }
838
- return baseColor.hex();
839
- };
840
- const getBorderColor = (color, mode) => {
841
- const baseColor = chroma(color);
842
- if (mode === "dark") {
843
- return baseColor.brighten(2).alpha(0.4).hex();
844
- }
845
- if (baseColor.luminance() > 0.5) {
846
- return baseColor.darken(3).hex();
847
- }
848
- return baseColor.brighten(0.5).alpha(0.25).hex();
849
- };
850
- const getBackgroundColor = (color, mode) => {
851
- const baseColor = chroma(color);
852
- if (mode === "dark") {
853
- if (baseColor.luminance() > 0.5) {
854
- return baseColor.darken(2).alpha(0.5).hex();
855
- }
856
- return baseColor.brighten(1.5).alpha(0.35).hex();
857
- }
858
- if (baseColor.luminance() > 0.5) {
859
- return baseColor.darken(2.5).hex();
860
- }
861
- return baseColor.brighten(2.5).alpha(0.25).hex();
862
- };
863
- const getFilterStyle = (color, mode) => {
864
- const baseColor = chroma(color);
865
- if (mode === "dark") {
866
- return "brightness(1.3) contrast(1.2)";
867
- }
868
- if (baseColor.luminance() > 0.5) {
869
- return "brightness(0.85) contrast(1.2)";
870
- }
871
- return "brightness(0.85) contrast(1.2)";
872
- };
873
- function LabelChip({
874
- label,
875
- onDelete,
876
- sx,
877
- fullName = true,
878
- onClick,
879
- disabled = false,
880
- renderName = (name) => name
881
- }) {
882
- const { getFullLabelName, getLabelName } = LabelsContainer.useContainer();
883
- const theme = useTheme();
884
- const { mode } = theme.palette;
885
- const color = getTextColor(label.data.color, mode);
886
- const mergedSx = [
887
- {
888
- height: 20,
889
- borderRadius: 0.5,
890
- fontSize: 12,
891
- fontWeight: 500,
892
- color: `${color} !important`,
893
- bgcolor: `${getBackgroundColor(label.data.color, mode)} !important`,
894
- transition: "filter 0.2s",
895
- borderColor: `${getBorderColor(label.data.color, mode)} !important`,
896
- WebkitTextFillColor: `${color} !important`,
897
- ".MuiChip-deleteIcon": {
898
- color: `${color} !important`,
899
- cursor: "pointer",
900
- transition: "transform 0.3s",
901
- "&:hover": {
902
- transform: "rotate(90deg)"
903
- }
904
- },
905
- ".MuiChip-label": {
906
- px: 0.5,
907
- maxHeight: 20
908
- },
909
- ...onClick && {
910
- cursor: "pointer",
911
- "&:hover": {
912
- filter: getFilterStyle(label.data.color, mode)
913
- }
914
- }
915
- },
916
- ...Array.isArray(sx) ? sx : [sx]
917
- ];
918
- const hasDelete = !disabled && !!onDelete;
919
- const hasOnClick = !disabled && !!onClick;
920
- return (
921
- // @ts-ignore
922
- /* @__PURE__ */ jsx(
923
- Chip,
924
- {
925
- label: renderName(fullName ? getFullLabelName(label.data.id) : getLabelName(label.data.id)),
926
- variant: "outlined",
927
- size: "small",
928
- ...hasDelete && {
929
- onDelete: (e) => {
930
- e.stopPropagation();
931
- e.preventDefault();
932
- onDelete(label);
933
- },
934
- deleteIcon: /* @__PURE__ */ jsx(CloseOutlineIcon, {})
935
- },
936
- ...hasOnClick && {
937
- onClick: (e) => {
938
- e.stopPropagation();
939
- e.preventDefault();
940
- onClick(label);
941
- }
942
- },
943
- sx: mergedSx
944
- },
945
- label.data.id
946
- )
947
- );
948
- }
949
- function Labels2({
950
- compact = true,
951
- labels,
952
- sx,
953
- renderLabel,
954
- renderWhenEmpty,
955
- prepend,
956
- displaySystemLabels = true,
957
- onItemClick
958
- }) {
959
- const { getLabelsByIds, loading } = LabelsContainer.useContainer();
960
- const labelsWrapperRef = useRef(null);
961
- const [overflowIndex, setOverflowIndex] = useState(99999);
962
- let labelNodes = getLabelsByIds(labels || []);
963
- const size = useSize(labelsWrapperRef);
964
- useDeepCompareLayoutEffect(() => {
965
- if (compact) {
966
- if (labelsWrapperRef.current) {
967
- const { right } = labelsWrapperRef.current.getBoundingClientRect();
968
- const labelElements = labelsWrapperRef.current.querySelectorAll(".MuiChip-root");
969
- if (labelElements?.length > 0) {
970
- Array.from(labelElements).some((x, i) => {
971
- const rect = x.getBoundingClientRect();
972
- if (rect.right > right - 36) {
973
- setOverflowIndex(i);
974
- return true;
975
- }
976
- return false;
977
- });
978
- }
979
- }
980
- } else {
981
- setOverflowIndex(99999);
982
- }
983
- }, [size, compact]);
984
- if (loading) {
985
- return null;
986
- }
987
- if (!displaySystemLabels) {
988
- labelNodes = labelNodes.filter((x) => x.data.type !== "system");
989
- }
990
- const mergedSx = [
991
- {
992
- display: "flex",
993
- gap: 1,
994
- width: "100%",
995
- alignItems: "center",
996
- overflow: "hidden",
997
- flexWrap: compact ? "no-wrap" : "wrap"
998
- },
999
- ...Array.isArray(sx) ? sx : [sx]
1000
- ];
1001
- return /* @__PURE__ */ jsxs(Box$1, { sx: mergedSx, ref: labelsWrapperRef, children: [
1002
- prepend,
1003
- labelNodes.map((item, index) => {
1004
- if (!item) {
1005
- return null;
1006
- }
1007
- const visible = index < overflowIndex;
1008
- let extra = null;
1009
- if (index === overflowIndex) {
1010
- extra = /* @__PURE__ */ jsx(
1011
- Box$1,
1012
- {
1013
- sx: {
1014
- display: "inline-flex",
1015
- ...compact && { height: 20, lineHeight: "20px" },
1016
- color: "text.secondary",
1017
- fontWeight: "bold",
1018
- fontSize: 14
1019
- },
1020
- children: `+${labelNodes.length - overflowIndex}`
1021
- },
1022
- "overflow-label"
1023
- );
1024
- }
1025
- let labelChip = /* @__PURE__ */ jsx(
1026
- LabelChip,
1027
- {
1028
- label: item,
1029
- onClick: onItemClick,
1030
- sx: {
1031
- visibility: visible ? "visible" : "hidden",
1032
- pointerEvents: visible ? "auto" : "none"
1033
- }
1034
- },
1035
- item.data.id
1036
- );
1037
- if (renderLabel) {
1038
- labelChip = renderLabel(item, labelChip);
1039
- }
1040
- if (extra) {
1041
- return [extra, labelChip];
1042
- }
1043
- return labelChip;
1044
- }),
1045
- labelNodes.length === 0 && renderWhenEmpty
1046
- ] });
1047
- }
1048
- function PopperComponent({
1049
- disablePortal,
1050
- anchorEl,
1051
- open,
1052
- ...other
1053
- }) {
1054
- return /* @__PURE__ */ jsx(
1055
- Box$1,
1056
- {
1057
- ...other,
1058
- sx: (theme) => ({
1059
- [`& .${autocompleteClasses.paper}`]: {
1060
- width: 360,
1061
- boxShadow: "none",
1062
- margin: 0,
1063
- color: "inherit",
1064
- fontSize: 13
1065
- },
1066
- [`& .${autocompleteClasses.listbox}`]: {
1067
- bgcolor: theme.palette.background.paper,
1068
- padding: 1,
1069
- [`& .${autocompleteClasses.option}`]: {
1070
- minHeight: "auto"
1071
- }
1072
- },
1073
- [`&.${autocompleteClasses.popperDisablePortal}`]: {
1074
- position: "relative"
1075
- }
1076
- })
1077
- }
1078
- );
1079
- }
1080
- function GithubLabelPicker({
1081
- value = [],
1082
- onChange,
1083
- noLabelsText,
1084
- buttonSx,
1085
- actions,
1086
- trigger,
1087
- multiple = true,
1088
- updateImmediately = true,
1089
- excludes = [],
1090
- disabled = false,
1091
- enableAccessControl = true,
1092
- disablePortal
1093
- }) {
1094
- const { t, locale } = useLocaleContext();
1095
- const { isAdmin, hasAnyPassport } = useSessionContext();
1096
- const {
1097
- flattenedLabels: labels,
1098
- getLabelsByIds,
1099
- loading,
1100
- createLabel,
1101
- recentLabels,
1102
- addRecentLabel
1103
- } = LabelsContainer.useContainer();
1104
- const [anchorEl, setAnchorEl] = useState(null);
1105
- const selectedLabelIds = value;
1106
- const selectedLabels = useMemo(() => getLabelsByIds(selectedLabelIds), [getLabelsByIds, selectedLabelIds]);
1107
- const [inputValue, setInputValue] = useState("");
1108
- const exists = useMemo(
1109
- () => labels.some((x) => x.data.getName(locale).toLowerCase() === inputValue.toLowerCase()),
1110
- [inputValue, labels, locale]
1111
- );
1112
- const filteredLabels = useMemo(() => {
1113
- const excludesSet = new Set(excludes);
1114
- return labels.filter((x) => !excludesSet.has(x.data.id));
1115
- }, [labels]);
1116
- const initialRecentLabels = useMemo(() => recentLabels, [anchorEl]);
1117
- const handleClick = (event) => {
1118
- if (disabled) {
1119
- return;
1120
- }
1121
- event.preventDefault();
1122
- setAnchorEl(event.currentTarget);
1123
- };
1124
- const handleClose = () => {
1125
- if (!updateImmediately) {
1126
- onChange?.(selectedLabelIds);
1127
- }
1128
- setInputValue("");
1129
- if (anchorEl) {
1130
- anchorEl.focus();
1131
- }
1132
- setAnchorEl(null);
1133
- };
1134
- const handleCreate = async () => {
1135
- const { id: id2 } = await createLabel(inputValue);
1136
- if (!multiple) {
1137
- onChange?.([id2]);
1138
- } else if (updateImmediately) {
1139
- onChange([...selectedLabelIds, id2]);
1140
- }
1141
- };
1142
- const handleLabelSelectionChange = (newValue) => {
1143
- if (!multiple) {
1144
- onChange?.(newValue.length ? [newValue[newValue.length - 1]] : []);
1145
- setAnchorEl(null);
1146
- } else if (updateImmediately) {
1147
- onChange(newValue);
1148
- }
1149
- };
1150
- const open = Boolean(anchorEl);
1151
- const id = open ? "github-label-picker" : void 0;
1152
- const getSelectedSx = (selected) => {
1153
- if (selected) {
1154
- return {
1155
- bgcolor: (theme) => `${theme.palette.primary.main} !important`,
1156
- color: (theme) => `${theme.palette.primary.contrastText} !important`,
1157
- WebkitTextFillColor: (theme) => `${theme.palette.primary.contrastText} !important`
1158
- };
1159
- }
1160
- return void 0;
1161
- };
1162
- const isLabelPermitted = (node) => {
1163
- const label = node.data;
1164
- const passportsToCheck = [...label.passports || [], ...label.type === "system" ? ["admin", "owner"] : []];
1165
- const isPermitted = !enableAccessControl || isAdmin || hasAnyPassport(passportsToCheck);
1166
- return isPermitted;
1167
- };
1168
- if (loading) {
1169
- return null;
1170
- }
1171
- function renderRecentLabels() {
1172
- const recentLabelNodes = getLabelsByIds(initialRecentLabels).filter(isLabelPermitted);
1173
- if (!recentLabelNodes?.length) {
1174
- return null;
1175
- }
1176
- return /* @__PURE__ */ jsxs("li", { children: [
1177
- /* @__PURE__ */ jsx(Box$1, { sx: { mb: 0.5, fontSize: 12, fontWeight: "medium" }, children: t("label.recently") }),
1178
- /* @__PURE__ */ jsx(Box$1, { sx: { display: "flex", flexWrap: "wrap", gap: 0.75, mb: 2 }, children: recentLabelNodes.map((x) => {
1179
- const labelId = x.data.id;
1180
- const selected = value?.includes(labelId);
1181
- return /* @__PURE__ */ jsx(
1182
- LabelChip,
1183
- {
1184
- label: x,
1185
- fullName: false,
1186
- onClick: () => {
1187
- handleLabelSelectionChange(
1188
- selected ? selectedLabelIds.filter((y) => y !== labelId) : [...selectedLabelIds, labelId]
1189
- );
1190
- },
1191
- sx: {
1192
- height: 24,
1193
- lineHeight: "24px",
1194
- ".MuiChip-label": { maxHeight: 24 },
1195
- ...getSelectedSx(selected)
1196
- }
1197
- },
1198
- labelId
1199
- );
1200
- }) })
1201
- ] });
1202
- }
1203
- function renderGroup(params) {
1204
- return /* @__PURE__ */ jsxs("li", { children: [
1205
- /* @__PURE__ */ jsx(Box$1, { sx: { mb: 0.5, fontSize: 12, fontWeight: "medium" }, children: t("label.title") }),
1206
- /* @__PURE__ */ jsx(
1207
- Box$1,
1208
- {
1209
- component: "ul",
1210
- sx: {
1211
- display: "flex",
1212
- justifyContent: "flex-start",
1213
- flexWrap: "wrap",
1214
- gap: 0.75,
1215
- p: 0
1216
- },
1217
- children: params.children
1218
- }
1219
- )
1220
- ] });
1221
- }
1222
- function renderAutocomplete() {
1223
- return /* @__PURE__ */ jsx(
1224
- Autocomplete,
1225
- {
1226
- open: true,
1227
- multiple: true,
1228
- inputValue,
1229
- onInputChange: (event, newInputValue, reason) => {
1230
- if (reason === "reset") {
1231
- return;
1232
- }
1233
- setInputValue(newInputValue);
1234
- },
1235
- onClose: (event, reason) => {
1236
- if (reason === "escape") {
1237
- handleClose();
1238
- }
1239
- },
1240
- value: selectedLabels,
1241
- onChange: (event, newValue, reason) => {
1242
- event.preventDefault();
1243
- if (event.type === "keydown" && event.key === "Backspace" && reason === "removeOption") {
1244
- return;
1245
- }
1246
- if (reason === "selectOption") {
1247
- addRecentLabel(newValue[newValue.length - 1].data.id);
1248
- }
1249
- handleLabelSelectionChange(newValue.map((x) => x.data.id));
1250
- },
1251
- disableCloseOnSelect: true,
1252
- renderTags: () => null,
1253
- noOptionsText: noLabelsText || "No labels",
1254
- renderOption: (props, option, { selected }) => {
1255
- const isPermitted = isLabelPermitted(option);
1256
- if (!isPermitted) {
1257
- return null;
1258
- }
1259
- const label = option.data;
1260
- return /* @__PURE__ */ createElement(Box$1, { component: "li", ...props, key: label.id, sx: { "&.MuiAutocomplete-option": { p: "0 !important" } } }, /* @__PURE__ */ jsx(
1261
- LabelChip,
1262
- {
1263
- label: option,
1264
- sx: {
1265
- height: 24,
1266
- lineHeight: "24px",
1267
- ".MuiChip-label": { maxHeight: 24 },
1268
- ...getSelectedSx(selected)
1269
- },
1270
- fullName: false
1271
- }
1272
- ));
1273
- },
1274
- groupBy: () => "x",
1275
- renderGroup: (params) => {
1276
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1277
- renderRecentLabels(),
1278
- renderGroup(params)
1279
- ] }, params.key);
1280
- },
1281
- options: filteredLabels,
1282
- getOptionLabel: (option) => option.data.getName(locale),
1283
- renderInput: (params) => /* @__PURE__ */ jsx(
1284
- Box$1,
1285
- {
1286
- component: InputBase,
1287
- ref: params.InputProps.ref,
1288
- inputProps: params.inputProps,
1289
- autoFocus: true,
1290
- placeholder: "Filter labels",
1291
- sx: (theme) => ({
1292
- padding: 1,
1293
- width: "100%",
1294
- bgcolor: "background.paper",
1295
- "& input": {
1296
- borderRadius: 1,
1297
- backgroundColor: "background.paper",
1298
- padding: 1,
1299
- transition: theme.transitions.create(["border-color", "box-shadow"]),
1300
- border: `1px solid ${theme.palette.divider}`,
1301
- fontSize: 14,
1302
- "&:focus": {
1303
- boxShadow: `0px 0px 0px 3px ${alpha(theme.palette.primary.light, 0.4)}`,
1304
- borderColor: theme.palette.primary.light
1305
- }
1306
- }
1307
- })
1308
- }
1309
- ),
1310
- slots: {
1311
- popper: PopperComponent
1312
- }
1313
- }
1314
- );
1315
- }
1316
- function renderActions() {
1317
- return /* @__PURE__ */ jsxs(Box$1, { children: [
1318
- isAdmin && inputValue && !exists && /* @__PURE__ */ jsx(
1319
- Box$1,
1320
- {
1321
- sx: {
1322
- display: "flex",
1323
- alignItems: "center",
1324
- gap: 1,
1325
- width: "100%",
1326
- height: 36,
1327
- px: 2,
1328
- borderTop: "1px solid",
1329
- borderColor: "divider",
1330
- fontSize: 14,
1331
- color: "grey.600",
1332
- cursor: "pointer",
1333
- textDecoration: "none"
1334
- },
1335
- onClick: handleCreate,
1336
- children: /* @__PURE__ */ jsxs("span", { children: [
1337
- 'Create new label "',
1338
- inputValue,
1339
- '"'
1340
- ] })
1341
- }
1342
- ),
1343
- actions
1344
- ] });
1345
- }
1346
- const labelInput2 = trigger;
1347
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1348
- /* @__PURE__ */ jsx(Box$1, { onClick: handleClick, sx: { display: "flex", alignItems: "center" }, children: labelInput2 || /* @__PURE__ */ jsx(
1349
- Box$1,
1350
- {
1351
- component: ButtonBase,
1352
- disableRipple: true,
1353
- "aria-describedby": id,
1354
- className: "label-picker-trigger",
1355
- sx: [
1356
- (theme) => ({
1357
- width: "100%",
1358
- fontSize: 13,
1359
- color: theme.palette.mode === "light" ? "#586069" : "#8b949e",
1360
- fontWeight: 600,
1361
- "&:hover": {
1362
- color: theme.palette.mode === "light" ? "primary.main" : "#58a6ff"
1363
- },
1364
- "& svg": {
1365
- width: 22,
1366
- height: 22
1367
- }
1368
- }),
1369
- ...Array.isArray(buttonSx) ? buttonSx : [buttonSx]
1370
- ],
1371
- children: /* @__PURE__ */ jsx(mdiTagPlusOutline, {})
1372
- }
1373
- ) }),
1374
- /* @__PURE__ */ jsx(
1375
- Box$1,
1376
- {
1377
- component: Popper,
1378
- id,
1379
- open,
1380
- anchorEl,
1381
- placement: "bottom-start",
1382
- disablePortal,
1383
- sx: (theme) => ({
1384
- border: `1px solid ${theme.palette.divider}`,
1385
- boxShadow: `0 8px 24px ${theme.palette.mode === "light" ? "rgba(149, 157, 165, 0.2)" : "rgb(1, 4, 9)"}`,
1386
- borderRadius: 1,
1387
- width: 360,
1388
- overflow: "hidden",
1389
- zIndex: theme.zIndex.modal,
1390
- fontSize: 13,
1391
- color: "text.secondary",
1392
- backgroundColor: "background.paper"
1393
- }),
1394
- children: /* @__PURE__ */ jsx(ClickAwayListener, { onClickAway: handleClose, children: /* @__PURE__ */ jsxs(Box$1, { onClick: (e) => e.preventDefault(), children: [
1395
- renderAutocomplete(),
1396
- renderActions()
1397
- ] }) })
1398
- }
1399
- )
1400
- ] });
1401
- }
1402
- function LabelsInput({
1403
- value = [],
1404
- onChange,
1405
- LabelPickerProps,
1406
- sx,
1407
- ...rest
1408
- }) {
1409
- const { getLabelsByIds, loading } = LabelsContainer.useContainer();
1410
- const valueOptions = useMemo(() => getLabelsByIds(value), [getLabelsByIds, value]);
1411
- const mergedSx = [
1412
- {
1413
- "& .MuiInputBase-root": { flexWrap: "wrap", gap: 0.5, py: 0.75 },
1414
- "& .MuiInputBase-input": { width: !value?.length ? "initial" : "20px", p: 0 }
1415
- },
1416
- ...Array.isArray(sx) ? sx : [sx]
1417
- ];
1418
- const placeholder = !value?.length ? rest.placeholder || "Select labels" : "";
1419
- if (loading) {
1420
- return null;
1421
- }
1422
- return /* @__PURE__ */ jsx(
1423
- GithubLabelPicker,
1424
- {
1425
- value,
1426
- onChange,
1427
- trigger: /* @__PURE__ */ jsx(
1428
- TextField,
1429
- {
1430
- ...rest,
1431
- placeholder,
1432
- fullWidth: true,
1433
- sx: mergedSx,
1434
- slotProps: {
1435
- input: {
1436
- autoComplete: "off",
1437
- ...rest.InputProps,
1438
- startAdornment: [
1439
- rest.InputProps?.startAdornment,
1440
- ...valueOptions.map((x, index) => /* @__PURE__ */ jsx(LabelChip, { label: x }, index))
1441
- ].filter(Boolean)
1442
- }
1443
- }
1444
- }
1445
- ),
1446
- ...LabelPickerProps
1447
- }
1448
- );
1449
- }
1450
- export {
1451
- GithubLabelPicker,
1452
- LabelChip,
1453
- LabelPicker,
1454
- LabelTree,
1455
- LabelTreeNode,
1456
- Labels,
1457
- Labels2,
1458
- LabelsContainer,
1459
- LabelsInput,
1460
- LabelsProvider,
1461
- useLabelsContext,
1462
- useLabelsUpdateOnDestroy
1463
- };