Bin
2025-12-17 2b99d77d73ba568beff0a549534017caaad8a6de
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { inject } from "mobx-react";
import { Button, ButtonGroup } from "@humansignal/ui";
import { Interface } from "../../Common/Interface";
import { useCallback, useEffect, useRef, useState } from "react";
import { IconChevronDown } from "@humansignal/icons";
import { Dropdown } from "@humansignal/ui";
import { Menu } from "../../Common/Menu/Menu";
 
const injector = inject(({ store }) => {
  const { dataStore, currentView } = store;
  const totalTasks = store.project?.task_count ?? store.project?.task_number ?? 0;
  const foundTasks = dataStore?.total ?? 0;
 
  return {
    store,
    canLabel: totalTasks > 0 || foundTasks > 0,
    target: currentView?.target ?? "tasks",
    selectedCount: currentView?.selectedCount,
    allSelected: currentView?.allSelected,
  };
});
 
export const LabelButton = injector(({ store, canLabel, size, target, selectedCount }) => {
  const disabled = target === "annotations";
  const triggerRef = useRef();
  const [isOpen, setIsOpen] = useState(false);
 
  const handleClickOutside = useCallback((e) => {
    const el = triggerRef.current;
 
    if (el && !el.contains(e.target)) {
      setIsOpen(false);
    }
  }, []);
 
  useEffect(() => {
    document.addEventListener("click", handleClickOutside, { capture: true });
 
    return () => {
      document.removeEventListener("click", handleClickOutside, {
        capture: true,
      });
    };
  }, []);
 
  const onLabelAll = () => {
    localStorage.setItem("dm:labelstream:mode", "all");
    store.startLabelStream();
  };
 
  const onLabelVisible = () => {
    localStorage.setItem("dm:labelstream:mode", "filtered");
    store.startLabelStream();
  };
 
  const triggerStyle = {
    width: 24,
    padding: 0,
    borderTopLeftRadius: 0,
    borderBottomLeftRadius: 0,
    borderBottomRightRadius: isOpen ? 0 : undefined,
    boxShadow: "none",
  };
 
  const primaryStyle = {
    width: 160,
    padding: 0,
    borderTopRightRadius: 0,
    borderBottomRightRadius: 0,
    borderBottomLeftRadius: isOpen ? 0 : undefined,
  };
 
  const secondStyle = {
    width: triggerStyle.width + primaryStyle.width,
    padding: 0,
    display: isOpen ? "flex" : "none",
    position: "absolute",
    zIndex: 10,
    borderTopLeftRadius: 0,
    borderTopRightRadius: 0,
  };
 
  selectedCount;
 
  return canLabel ? (
    <Interface name="labelButton">
      <div>
        <ButtonGroup>
          <Button
            size={size ?? "small"}
            variant="primary"
            look="outlined"
            disabled={disabled}
            style={primaryStyle}
            onClick={onLabelAll}
          >
            标注 {selectedCount ? selectedCount : "所有"} 任务
            {/* {!selectedCount || selectedCount > 1 ? "s" : ""} */}
          </Button>
          <Dropdown.Trigger
            align="bottom-right"
            content={
              <Menu size="compact">
                <Menu.Item onClick={onLabelVisible}>标注当前显示的任务</Menu.Item>
              </Menu>
            }
          >
            <Button size={size} look="outlined" variant="primary" aria-label={"Toggle open"}>
              <IconChevronDown />
            </Button>
          </Dropdown.Trigger>
        </ButtonGroup>
      </div>
    </Interface>
  ) : null;
});