PluginFieldsSelect
Выпадающий список для выбора полей Трекера внутри плагина. Принимает плоский список полей с метаданными категорий и автоматически группирует их в сворачиваемые секции. Поддерживает поиск, мультивыбор, кнопки применения/сброса и состояние загрузки.
Базовое использование
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField} from '@weavix/tracker-components';
const fields: PluginFieldsSelectField[] = [
{id: 'summary', name: 'Summary', category: {id: '1', display: 'Системные поля'}},
{id: 'description', name: 'Description', category: {id: '1', display: 'Системные поля'}},
{id: 'sprint', name: 'Sprint', category: {id: '2', display: 'Пользовательские поля'}},
];
<PluginFieldsSelect
open={true}
fields={fields}
onOpenChange={(open) => console.log(open)}
onItemSelect={(item) => console.log('selected', item)}
onItemDeselect={(item) => console.log('deselected', item)}
/>;
Пример 1 — Только начальная загрузка глобальных полей
Загружает все глобальные поля один раз при монтировании. Поиск работает на стороне клиента по уже загруженному списку.
import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';
interface Props {
trackerApi: TrackerApi;
onSubmit: (selected: TrackerSelectItem[]) => void;
}
export const GlobalFieldsSelect: React.FC<Props> = ({trackerApi, onSubmit}) => {
const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
const [loading, setLoading] = useState(true);
const [open, setOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);
useEffect(() => {
trackerApi.v3
.get['/fields']()
.then((result) => setFields(result.data))
.finally(() => setLoading(false));
}, [trackerApi]);
return (
<PluginFieldsSelect
open={open}
fields={fields}
loading={loading}
selectedItems={selectedItems}
onOpenChange={setOpen}
onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
onItemDeselect={(item) =>
setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
}
onSubmit={() => onSubmit(selectedItems)}
expandSystemGroup
/>
);
};
Пример 2 — Глобальный саджест при вводе пользователя
Использует начальную загрузку для первоначального списка и переключается на саджест-эндпоинт при вводе текста.
import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';
export const GlobalFieldsSelectWithSuggest: React.FC<Props> = ({trackerApi, onSubmit}) => {
const [initialFields, setInitialFields] = useState<PluginFieldsSelectField[]>([]);
const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
const [loading, setLoading] = useState(true);
const [open, setOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);
useEffect(() => {
trackerApi.v3
.get['/fields']()
.then((result) => {
setInitialFields(result.data);
setFields(result.data);
})
.finally(() => setLoading(false));
}, [trackerApi]);
const handleSearchChange = async (text: string) => {
// Саджест возвращает пустой ответ при пустом input —
// возвращаемся к начальному списку при очистке поиска
if (!text) {
setFields(initialFields);
return;
}
const result = await trackerApi.v3.post['/fields/_suggest']({
bodyParams: {input: text, readOnly: true, localFields: false, extended: true},
});
setFields(result.data);
};
return (
<PluginFieldsSelect
open={open}
fields={fields}
loading={loading}
selectedItems={selectedItems}
onOpenChange={setOpen}
onSearchChange={handleSearchChange}
onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
onItemDeselect={(item) =>
setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
}
onSubmit={() => onSubmit(selectedItems)}
expandGroupsOnSearch
/>
);
};
Пример 3 — Глобальные + локальные поля для конкретных очередей
Расширяет пример 2, добавляя поля, специфичные для очередей. Если queueKeys пуст — используется только глобальный саджест, так как localFields: true с пустым массивом queues приводит к ошибке запроса.
import React, {useEffect, useState} from 'react';
import {PluginFieldsSelect} from '@weavix/tracker-components';
import type {PluginFieldsSelectField, TrackerSelectItem} from '@weavix/tracker-components';
interface Props {
trackerApi: TrackerApi;
/** Необходимо передать хотя бы один ключ очереди при использовании локальных полей */
queueKeys: string[];
onSubmit: (selected: TrackerSelectItem[]) => void;
}
export const LocalFieldsSelectWithSuggest: React.FC<Props> = ({
trackerApi,
queueKeys,
onSubmit,
}) => {
const [initialFields, setInitialFields] = useState<PluginFieldsSelectField[]>([]);
const [fields, setFields] = useState<PluginFieldsSelectField[]>([]);
const [loading, setLoading] = useState(true);
const [open, setOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState<TrackerSelectItem[]>([]);
useEffect(() => {
trackerApi.v3
.get['/fields']()
.then((result) => {
setInitialFields(result.data);
setFields(result.data);
})
.finally(() => setLoading(false));
}, [trackerApi]);
const handleSearchChange = async (text: string) => {
if (!text) {
setFields(initialFields);
return;
}
const hasQueues = queueKeys.length > 0;
const result = await trackerApi.v3.post['/fields/_suggest']({
bodyParams: {
input: text,
readOnly: true,
localFields: hasQueues,
extended: true,
...(hasQueues && {queues: queueKeys}),
},
});
setFields(result.data);
};
return (
<PluginFieldsSelect
open={open}
fields={fields}
loading={loading}
selectedItems={selectedItems}
onOpenChange={setOpen}
onSearchChange={handleSearchChange}
onItemSelect={(item) => setSelectedItems((prev) => [...prev, item])}
onItemDeselect={(item) =>
setSelectedItems((prev) => prev.filter((s) => s.id !== item.id))
}
onSubmit={() => onSubmit(selectedItems)}
expandGroupsOnSearch
/>
);
};
Основные свойства
|
Свойство |
Тип |
По умолчанию |
Описание |
|
|
|
— |
Плоский список полей для отображения |
|
|
|
— |
Открыт ли выпадающий список |
|
|
|
— |
Колбэк при изменении состояния открытия |
|
|
|
— |
Текущие выбранные элементы |
|
|
|
|
Состояние загрузки — показывает спиннер |
|
|
|
— |
Колбэк при выборе элемента |
|
|
|
— |
Колбэк при снятии выбора с элемента |
|
|
|
— |
Колбэк при нажатии кнопки применения |
|
|
|
— |
Колбэк при нажатии кнопки сброса |
|
|
|
— |
Колбэк при изменении текста поиска |
|
|
|
|
Показывать поле поиска |
|
|
|
|
Показывать кнопку применения |
|
|
|
|
Показывать кнопку сброса |
|
|
|
— |
Текст кнопки применения |
|
|
|
— |
Текст кнопки сброса |
|
|
|
|
Раскрывать группы при вводе в поле поиска |
|
|
|
|
Раскрывать группу системных полей (id= |
|
|
|
|
Режим одиночного выбора — закрывает список после выбора |
|
|
|
|
Скрыть чекбоксы рядом с элементами |
|
|
|
|
Показывать мета-строку под названием элемента |
|
|
|
— |
ID элементов, которые нужно отобразить как недоступные |
|
|
|
— |
Дополнительный контент под списком элементов |
|
|
|
— |
Дополнительный CSS-класс корневого элемента |