Загрузка данных
import {
ArrowLeft,
ArrowRightLeft,
Ban,
Bell,
Bookmark,
Calculator,
Calendar,
CalendarCheck,
ChartColumnDecreasing,
ChartLine,
ChartNoAxesColumnIncreasing,
ChartNoAxesCombined,
ChartPie,
Check,
ChevronDown,
ChevronLeft,
ChevronRight,
ChevronUp,
CircleAlert,
CircleArrowRight,
CircleCheck,
CircleChevronLeft,
CircleChevronRight,
CircleMinus,
CircleQuestionMark,
CircleX,
Columns2,
Columns3,
Copy,
Copyright,
EllipsisVertical,
Expand,
Eye,
EyeClosed,
File,
FilePlusCorner,
FileUp,
FileX,
FolderPlus,
Forward,
Fullscreen,
Headset,
Image,
ImagePlus,
Inbox,
Info,
LaptopMinimal,
Link,
List,
ListFilter,
ListSortAscending,
ListSortDescending,
LoaderCircle,
Lock,
LockOpen,
LogOut,
Mail,
Megaphone,
Menu,
MessageSquare,
MessageSquareText,
MessagesSquare,
Minus,
MonitorDown,
MoveDown,
MoveUp,
Paperclip,
Pencil,
Phone,
Pin,
Plus,
Printer,
RefreshCw,
Reply,
RotateCcw,
RotateCw,
Save,
Search,
SendHorizontal,
Settings,
Share,
ShieldCheck,
Shrink,
Square,
SquareSplitHorizontal,
Table2,
Trash2,
TriangleAlert,
Underline,
UserPlus,
UserRound,
Users,
X,
Zap,
} from 'lucide-react';
import { ReactComponent as InfoOutlined } from '../../../public/project-icons/info/info-outlined.svg';
import { ReactComponent as BlockRounded } from './icons/migration/block/block-rounded.svg';
import { ReactComponent as Calculate } from './icons/migration/calculate/calculate.svg';
import { ReactComponent as ChevronLeftOutlined } from './icons/migration/chevron-left/chevron-left-outlined.svg';
import { ReactComponent as CompareArrowsRounded } from './icons/migration/compare-arrows/compare-arrows-rounded.svg';
import { ReactComponent as EmailOutLined } from './icons/migration/email/email-outlined.svg';
import { ReactComponent as FileCopyOutlined } from './icons/migration/file-copy/file-copy-outlined.svg';
import { ReactComponent as ForumOutlined } from './icons/migration/forum/forum-outlined.svg';
import { ReactComponent as HeadsetMicOutlined } from './icons/migration/headset-mic/headset-mic-outlined.svg';
import { ReactComponent as LinkRounded } from './icons/migration/link/link-rounded.svg';
import { ReactComponent as MoveToInboxRounded } from './icons/migration/move-to-inbox/move-to-inbox-rounded.svg';
import { ReactComponent as MyMsgStateDlvrd } from './icons/migration/my-message-state/my-message-state-delivered.svg';
import { ReactComponent as MyMessageStateSent } from './icons/migration/my-message-state/my-message-state-sent.svg';
import { ReactComponent as PersonAddOutlined } from './icons/migration/person-add/person-add-outlined.svg';
import { ReactComponent as PhoneOutlined } from './icons/migration/phone/phone-outlined.svg';
import { ReactComponent as PostAddOutlined } from './icons/migration/post-add/post-add-outlined.svg';
import { ReactComponent as PrintOutlined } from './icons/migration/print/print-outlined.svg';
import { ReactComponent as SendRounded } from './icons/migration/send/send-rounded.svg';
import { ReactComponent as UserGroupPlus } from './icons/user-group-plus.svg';
import { IconVariant, IconVariants } from './types';
export const ICON_MAP: Record<IconVariant, React.FC<React.SVGProps<SVGSVGElement>>> = {
'chart-column-decreasing': ChartColumnDecreasing,
bell: Bell,
ban: Ban,
bookmark: Bookmark,
calculator: Calculator,
'calendar-check-2': CalendarCheck,
calendar: Calendar,
'chart-line': ChartLine,
'chart-pie': ChartPie,
check: Check,
'chevron-down': ChevronDown,
'chevron-up': ChevronUp,
'chevron-left': ChevronLeft,
'chevron-right': ChevronRight,
'circle-minus': CircleMinus,
'circle-question-mark': CircleQuestionMark,
'circle-x': CircleX,
'columns-2': Columns2,
'columns-3': Columns3,
copy: Copy,
copyright: Copyright,
'ellipsis-vertical': EllipsisVertical,
expand: Expand,
'eye-closed': EyeClosed,
eye: Eye,
'file-plus-corner': FilePlusCorner,
'file-up': FileUp,
'file-x': FileX,
file: File,
'folder-plus': FolderPlus,
forward: Forward,
fullscreen: Fullscreen,
headset: Headset,
image: Image,
inbox: Inbox,
info: Info,
'laptop-minimal': LaptopMinimal,
'link-2': Link,
list: List,
'lock-open': LockOpen,
lock: Lock,
'log-out': LogOut,
mail: Mail,
megaphone: Megaphone,
'message-square-text': MessageSquareText,
'message-square': MessageSquare,
'messages-square': MessagesSquare,
'monitor-down': MonitorDown,
pencil: Pencil,
phone: Phone,
pin: Pin,
plus: Plus,
printer: Printer,
'refresh-cw': RefreshCw,
reply: Reply,
'rotate-ccw': RotateCcw,
'rotate-cw': RotateCw,
search: Search,
'send-horizontal': SendHorizontal,
settings: Settings,
share: Share,
'shield-check': ShieldCheck,
shrink: Shrink,
'square-split-horizontal': SquareSplitHorizontal,
square: Square,
'table-2': Table2,
'trash-2': Trash2,
'triangle-alert': TriangleAlert,
underline: Underline,
'user-group-plus': UserGroupPlus,
'user-plus': UserPlus,
'user-round': UserRound,
users: Users,
x: X,
zap: Zap,
'arrow-right-left': ArrowRightLeft,
'arrow-left': ArrowLeft,
'chart-no-axes-column-increasing': ChartNoAxesColumnIncreasing,
'chart-no-axes-combined': ChartNoAxesCombined,
'circle-alert': CircleAlert,
'circle-arrow-right': CircleArrowRight,
'circle-check': CircleCheck,
'circle-chevron-left': CircleChevronLeft,
'circle-chevron-right': CircleChevronRight,
'image-plus': ImagePlus,
'list-filter': ListFilter,
'list-sort-ascending': ListSortAscending,
'list-sort-descending': ListSortDescending,
'loader-circle': LoaderCircle,
menu: Menu,
minus: Minus,
'move-down': MoveDown,
'move-up': MoveUp,
paperclip: Paperclip,
save: Save,
};
export const ICON_MAP_LEGACY: Record<IconVariants, React.FC<React.SVGProps<SVGSVGElement>>> = {
[IconVariants.EMAIL_OUTLINED]: EmailOutLined,
[IconVariants.FORUM_OUTLINED]: ForumOutlined,
[IconVariants.FILE_COPY_OUTLINED]: FileCopyOutlined,
[IconVariants.COMPARE_ARROWS_ROUNDED]: CompareArrowsRounded,
[IconVariants.PHONE_OUTLINED]: PhoneOutlined,
[IconVariants.HEADSET_MIC_OUTLINED]: HeadsetMicOutlined,
[IconVariants.POST_ADD_OUTLINED]: PostAddOutlined,
[IconVariants.SEND_ROUNDED]: SendRounded,
[IconVariants.MOVE_TO_INBOX_ROUNDED]: MoveToInboxRounded,
[IconVariants.PRINT_OUTLINED]: PrintOutlined,
[IconVariants.BLOCK_ROUNDED]: BlockRounded,
[IconVariants.LINK_ROUNDED]: LinkRounded,
[IconVariants.PERSON_ADD_OUTLINED]: PersonAddOutlined,
[IconVariants.CALCULATE]: Calculate,
[IconVariants.CHEVRON_LEFT_OUTLINED]: ChevronLeftOutlined,
[IconVariants.MY_MESSAGE_STATE_SENT]: MyMessageStateSent,
[IconVariants.MY_MESSAGE_STATE_DELIVERED]: MyMsgStateDlvrd,
[IconVariants.INFO_OUTLINED]: InfoOutlined,
};
import React from 'react';
import { ICON_MAP } from './const';
import { IconProps } from './types';
export const Icon: React.FC<IconProps> = ({ variant, className = '', style, size = 16, ...restProps }) => {
const IconComponent = ICON_MAP[variant];
const sizeProps = size === 'original'
? {}
: { width: size, height: size }
return (
<IconComponent
className={className}
style={style}
{...sizeProps}
{...restProps}
/>
);
};
import dayjs from 'dayjs';
import React, { useCallback, useMemo, useState } from 'react';
import { CascadeDropdown } from '@components/CascadeDropdown';
import { IconButton } from '@components/IconButton';
import Icons from '@components/Icons';
import { commonDateFormat } from '@configs/standartDateFormat';
import { useInstrumentLists } from '@modules/instrumentLists';
import { useWidgetIdContext } from '@terminal/desktop/workspaces/default/components/Widget/context';
import { Icon } from '@uikit/Icon';
import Tooltip from '@uikit/Tooltip';
import { getComplexKeyForQuotes } from '@widgets/BondScreener/logic/utils';
import { ScreenerInstrumentData } from 'types/BondsScreener';
import styles from './AddToInstrumentsListIcon.module.scss';
type AddToInstrumentsListIconProps = {
screenerInstrumentsData: ScreenerInstrumentData[];
};
const BookmarkIcon = () => <Icons.Bookmark className={styles.icon} />;
export const AddToInstrumentsListIcon: React.FC<AddToInstrumentsListIconProps> = ({ screenerInstrumentsData }) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const widgetId = useWidgetIdContext();
const { createInstrumentList, changeInstrumentList, lists } = useInstrumentLists(widgetId);
const handleAddInstrumentsList = useCallback(() => {
const baseListName = `Скрининг ${dayjs().format(commonDateFormat.dateFormat)}`;
// Подсчет количества уже существующих списков с такой же базовой подстрокой
const existingCount = lists?.filter((list) => list.name.startsWith(baseListName)).length || 0;
// Если существующих списков нет, создаем без скобок, иначе с номером
const listName = existingCount === 0 ? baseListName : `${baseListName} (${existingCount + 1})`;
createInstrumentList({
instruments: screenerInstrumentsData?.map((scrInstr) =>
getComplexKeyForQuotes({ bond: scrInstr, separator: ':' }),
),
name: listName,
actionSource: 'outside',
});
}, [createInstrumentList, screenerInstrumentsData, lists]);
const handleUpdateInstrumentList = useCallback(
(listId: string) => {
const changedList = lists?.find((list) => list.id.toString() === listId);
const currentInstruments = screenerInstrumentsData?.map((instr) =>
getComplexKeyForQuotes({ bond: instr, separator: ':' }),
);
const originalInstruments = changedList?.instruments;
// список уникальных инструментов
const newInstruments = [...new Set([...currentInstruments, ...(originalInstruments || [])])];
changeInstrumentList({ id: Number(listId), instruments: newInstruments }, 'addInstruments', currentInstruments);
},
[lists, screenerInstrumentsData, changeInstrumentList],
);
const items = useMemo(
() => [
{
key: 'title-key',
label: 'Добавьте в список инструментов:',
type: 'group' as const,
},
...(lists?.map((list) => ({
key: list.id,
label: `${list.name} (${list.instruments?.length})`,
type: undefined,
})) || []),
],
[lists],
);
const footer = useMemo(
() => (
<div
className={styles.addNewWrapper}
onClick={handleAddInstrumentsList}
>
<Icon
variant="plus"
className={styles.addNewIcon}
/>
Создать новый список
</div>
),
[handleAddInstrumentsList],
);
const handleChange = useCallback(
(key: string) => {
handleUpdateInstrumentList(key);
},
[handleUpdateInstrumentList],
);
return (
<CascadeDropdown
open={isDropdownOpen}
placement="bottom"
onOpenChange={setIsDropdownOpen}
items={items}
footer={footer}
onChange={handleChange}
renderWrapperClassName={styles.restrictedMenu}
>
<Tooltip title="Добавить в список инструментов">
<IconButton
icon={<BookmarkIcon />}
onClick={() => setIsDropdownOpen(true)}
/>
</Tooltip>
</CascadeDropdown>
);
};
@import 'colors.scss';
.icon {
color: $surface-icon-basis-active-primary !important;
width: 12px !important;
height: 12px !important;
}
.addNewWrapper {
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
cursor: pointer;
padding: 12px;
&:hover {
background-color: $action-surface-hover;
}
}
.addNewIcon {
fill: $text-interface-primary-value;
}
.restrictedMenu {
max-height: 360px;
overflow-y: auto;
overflow-x: hidden;
}