D: Improve TSDocs for frontend components

This commit is contained in:
Ivan 2024-11-21 15:09:31 +03:00
parent c7df031041
commit 4172e387c2
40 changed files with 326 additions and 41 deletions

View File

@ -50,6 +50,7 @@ This readme file is used mostly to document project dependencies and conventions
- @uiw/react-codemirror
- @uiw/codemirror-themes
- @lezer/lr
- @dagrejs/dagre
</pre>
</details>
<details>

View File

@ -24,7 +24,7 @@ function UserMenu() {
<AnimatePresence mode='wait'>
{loading ? (
<AnimateFade key='nav_user_badge_loader'>
<Loader circular size={3} />
<Loader circular scale={1.5} />
</AnimateFade>
) : null}
{!user && !loading ? (

View File

@ -45,6 +45,7 @@ export interface DomIconProps<RequestData> extends IconProps {
value: RequestData;
}
/** Icon for library item type. */
export function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProps<LibraryItemType>) {
switch (value) {
case LibraryItemType.RSFORM:
@ -54,6 +55,7 @@ export function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProp
}
}
/** Icon for access policy. */
export function PolicyIcon({ value, size = '1.25rem', className }: DomIconProps<AccessPolicy>) {
switch (value) {
case AccessPolicy.PRIVATE:
@ -65,6 +67,7 @@ export function PolicyIcon({ value, size = '1.25rem', className }: DomIconProps<
}
}
/** Icon for visibility. */
export function VisibilityIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
if (value) {
return <IconShow size={size} className={className ?? 'clr-text-green'} />;
@ -73,6 +76,7 @@ export function VisibilityIcon({ value, size = '1.25rem', className }: DomIconPr
}
}
/** Icon for subfolders. */
export function SubfoldersIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
if (value) {
return <IconSubfolders size={size} className={className ?? 'clr-text-green'} />;
@ -81,6 +85,7 @@ export function SubfoldersIcon({ value, size = '1.25rem', className }: DomIconPr
}
}
/** Icon for location. */
export function LocationIcon({ value, size = '1.25rem', className }: DomIconProps<string>) {
switch (value.substring(0, 2) as LocationHead) {
case LocationHead.COMMON:
@ -94,6 +99,7 @@ export function LocationIcon({ value, size = '1.25rem', className }: DomIconProp
}
}
/** Icon for term graph dependency mode. */
export function DependencyIcon({ value, size = '1.25rem', className }: DomIconProps<DependencyMode>) {
switch (value) {
case DependencyMode.ALL:
@ -109,6 +115,7 @@ export function DependencyIcon({ value, size = '1.25rem', className }: DomIconPr
}
}
/** Icon for constituenta match mode. */
export function MatchModeIcon({ value, size = '1.25rem', className }: DomIconProps<CstMatchMode>) {
switch (value) {
case CstMatchMode.ALL:
@ -124,6 +131,7 @@ export function MatchModeIcon({ value, size = '1.25rem', className }: DomIconPro
}
}
/** Icon for expression status. */
export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps<ExpressionStatus>) {
switch (value) {
case ExpressionStatus.VERIFIED:
@ -141,6 +149,7 @@ export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps<
}
}
/** Icon for constituenta type. */
export function CstTypeIcon({ value, size = '1.25rem', className }: DomIconProps<CstType>) {
switch (value) {
case CstType.BASE:
@ -162,6 +171,7 @@ export function CstTypeIcon({ value, size = '1.25rem', className }: DomIconProps
}
}
/** Icon for relocation direction. */
export function RelocateUpIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
if (value) {
return <IconMoveUp size={size} className={className ?? 'clr-text-primary'} />;

View File

@ -7,11 +7,19 @@ import { CProps } from '../props';
import TooltipConstituenta from './TooltipConstituenta';
interface BadgeConstituentaProps extends CProps.Styling {
/** Prefix for tooltip ID. */
prefixID?: string;
/** Constituenta to display. */
value: IConstituenta;
/** Color theme to use. */
theme: IColorTheme;
}
/**
* Displays a badge with a constituenta alias and information tooltip.
*/
function BadgeConstituenta({ value, prefixID, className, style, theme }: BadgeConstituentaProps) {
return (
<div

View File

@ -6,9 +6,13 @@ import { colorFgGrammeme } from '@/styling/color';
import { labelGrammeme } from '@/utils/labels';
interface BadgeGrammemeProps {
/** Grammeme to display. */
grammeme: GramData;
}
/**
* Displays a badge with a grammeme tag.
*/
function BadgeGrammeme({ grammeme }: BadgeGrammemeProps) {
const { colors } = useConceptOptions();
return (

View File

@ -8,12 +8,22 @@ import { IconHelp } from '../Icons';
import { CProps } from '../props';
interface BadgeHelpProps extends CProps.Styling {
/** Topic to display in a tooltip. */
topic: HelpTopic;
/** Offset from the cursor to the tooltip. */
offset?: number;
/** Classname for padding. */
padding?: string;
/** Place of the tooltip in relation to the cursor. */
place?: PlacesType;
}
/**
* Display help icon with a manual page tooltip.
*/
function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpProps) {
const { showHelp } = useConceptOptions();

View File

@ -3,9 +3,13 @@ import { globals } from '@/utils/constants';
import { LocationIcon } from '../DomainIcons';
interface BadgeLocationProps {
/** Location to display. */
location: string;
}
/**
* Displays location icon with a full text tooltip.
*/
function BadgeLocation({ location }: BadgeLocationProps) {
return (
<div className='pl-2' data-tooltip-id={globals.tooltip} data-tooltip-content={location}>

View File

@ -3,10 +3,16 @@ import { IWordForm } from '@/models/language';
import BadgeGrammeme from './BadgeGrammeme';
interface BadgeWordFormProps {
keyPrefix?: string;
/** Word form to display. */
form: IWordForm;
/** Prefix for grammemes keys. */
keyPrefix?: string;
}
/**
* Displays a badge with grammemes of a word form.
*/
function BadgeWordForm({ keyPrefix, form }: BadgeWordFormProps) {
return (
<div className='flex flex-wrap justify-start gap-1 select-none w-fit'>

View File

@ -98,8 +98,8 @@ function PickConstituenta({
id={id ? `${id}__search` : undefined}
className='clr-input rounded-t-md'
noBorder
value={filterText}
onChange={newValue => setFilterText(newValue)}
query={filterText}
onChangeQuery={newValue => setFilterText(newValue)}
/>
<DataTable
id={id}

View File

@ -132,8 +132,8 @@ function PickMultiConstituenta({
id='dlg_constituents_search'
noBorder
className='min-w-[6rem] pr-2 flex-grow'
value={filterText}
onChange={setFilterText}
query={filterText}
onChangeQuery={setFilterText}
/>
<ToolbarGraphSelection
graph={foldedGraph}

View File

@ -129,8 +129,8 @@ function PickSchema({
id={id ? `${id}__search` : undefined}
className='clr-input flex-grow rounded-t-md'
noBorder
value={filterText}
onChange={newValue => setFilterText(newValue)}
query={filterText}
onChangeQuery={newValue => setFilterText(newValue)}
/>
<div ref={locationMenu.ref}>
<MiniButton

View File

@ -25,45 +25,84 @@ import TableHeader from './TableHeader';
export { type ColumnSort, createColumnHelper, type RowSelectionState, type VisibilityState };
/** Style to conditionally apply to rows. */
export interface IConditionalStyle<TData> {
/** Callback to determine if the style should be applied. */
when: (rowData: TData) => boolean;
/** Style to apply. */
style: React.CSSProperties;
}
export interface DataTableProps<TData extends RowData>
extends CProps.Styling,
Pick<TableOptions<TData>, 'data' | 'columns' | 'onRowSelectionChange' | 'onColumnVisibilityChange'> {
/** Id of the component. */
id?: string;
/** Indicates that padding should be minimal. */
dense?: boolean;
/** Number of rows to display. */
rows?: number;
/** Height of the content. */
contentHeight?: string;
/** Top position of sticky header (0 if no other sticky elements are present). */
headPosition?: string;
/** Disable header. */
noHeader?: boolean;
/** Disable footer. */
noFooter?: boolean;
/** List of styles to conditionally apply to rows. */
conditionalRowStyles?: IConditionalStyle<TData>[];
/** Component to display when there is no data. */
noDataComponent?: React.ReactNode;
/** Callback to be called when a row is clicked. */
onRowClicked?: (rowData: TData, event: CProps.EventMouse) => void;
/** Callback to be called when a row is double clicked. */
onRowDoubleClicked?: (rowData: TData, event: CProps.EventMouse) => void;
/** Enable row selection. */
enableRowSelection?: boolean;
/** Current row selection. */
rowSelection?: RowSelectionState;
/** Enable hiding of columns. */
enableHiding?: boolean;
/** Current column visibility. */
columnVisibility?: VisibilityState;
/** Enable pagination. */
enablePagination?: boolean;
/** Number of rows per page. */
paginationPerPage?: number;
/** List of options to choose from for pagination. */
paginationOptions?: number[];
/** Callback to be called when the pagination option is changed. */
onChangePaginationOption?: (newValue: number) => void;
/** Enable sorting. */
enableSorting?: boolean;
/** Initial sorting. */
initialSorting?: ColumnSort;
}
/**
* UI element: data representation as a table.
* Dta representation as a table.
*
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
* No sticky header if omitted

View File

@ -7,18 +7,24 @@ import { useConceptOptions } from '@/context/ConceptOptionsContext';
import AnimateFade from '../wrap/AnimateFade';
interface LoaderProps {
size?: number;
/** Scale of the loader from 1 to 10. */
scale?: number;
/** Show a circular loader. */
circular?: boolean;
}
function Loader({ size = 10, circular }: LoaderProps) {
/**
* Displays animated loader.
*/
function Loader({ scale = 5, circular }: LoaderProps) {
const { colors } = useConceptOptions();
return (
<AnimateFade noFadeIn className='flex justify-center'>
{circular ? (
<ThreeCircles color={colors.bgPrimary} height={size * 10} width={size * 10} />
<ThreeCircles color={colors.bgPrimary} height={scale * 20} width={scale * 20} />
) : (
<ThreeDots color={colors.bgPrimary} height={size * 10} width={size * 10} radius={size} />
<ThreeDots color={colors.bgPrimary} height={scale * 20} width={scale * 20} radius={scale * 2} />
)}
</AnimateFade>
);

View File

@ -5,11 +5,19 @@ import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface MiniButtonProps extends CProps.Button {
/** Icon to display in the button. */
icon: React.ReactNode;
/** Disable hover effect. */
noHover?: boolean;
/** Disable padding. */
noPadding?: boolean;
}
/**
* Displays small transparent button with an icon.
*/
function MiniButton({
icon,
noHover,

View File

@ -18,23 +18,46 @@ import MiniButton from './MiniButton';
import Overlay from './Overlay';
export interface ModalProps extends CProps.Styling {
/** Title of the modal window. */
header?: string;
/** Text of the submit button. */
submitText?: string;
/** Tooltip for the submit button when the form is invalid. */
submitInvalidTooltip?: string;
/** Indicates that form is readonly. */
readonly?: boolean;
/** Indicates that submit button is enabled. */
canSubmit?: boolean;
/** Indicates that the modal window should be scrollable. */
overflowVisible?: boolean;
/** Callback to be called when the modal window is closed. */
hideWindow: () => void;
/** Callback to be called before submit. */
beforeSubmit?: () => boolean;
/** Callback to be called after submit. */
onSubmit?: () => void;
/** Callback to be called after cancel. */
onCancel?: () => void;
/** Help topic to be displayed in the modal window. */
helpTopic?: HelpTopic;
/** Callback to determine if help should be displayed. */
hideHelpWhen?: () => boolean;
}
/**
* Displays a customizable modal window.
*/
function Modal({
children,

View File

@ -2,6 +2,9 @@ import clsx from 'clsx';
import { CProps } from '../props';
/**
* Wraps content in a div with a centered text.
*/
function NoData({ className, children, ...restProps }: CProps.Div) {
return (
<div className={clsx('p-3 flex flex-col items-center text-center select-none w-full', className)} {...restProps}>

View File

@ -3,11 +3,19 @@ import clsx from 'clsx';
import { CProps } from '../props';
interface OverlayProps extends CProps.Styling {
/** Id of the overlay. */
id?: string;
/** Classnames for position of the overlay. */
position?: string;
/** Classname for z-index of the overlay. */
layer?: string;
}
/**
* Displays a transparent overlay over the main content.
*/
function Overlay({
children,
className,

View File

@ -5,15 +5,26 @@ import { useMemo } from 'react';
import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize';
/** Maximum width of the viewer. */
const MAXIMUM_WIDTH = 1600;
/** Minimum width of the viewer. */
const MINIMUM_WIDTH = 300;
interface PDFViewerProps {
/** PDF file to display. */
file?: string;
/** Offset from the left side of the window. */
offsetXpx?: number;
/** Minimum width of the viewer. */
minWidth?: number;
}
/**
* Displays a PDF file using an embedded viewer.
*/
function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps) {
const windowSize = useWindowSize();
const { calculateHeight } = useConceptOptions();

View File

@ -2,6 +2,9 @@ interface PrettyJsonProps {
data: unknown;
}
/**
* Displays JSON data in a formatted string.
*/
function PrettyJson({ data }: PrettyJsonProps) {
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

View File

@ -6,15 +6,37 @@ import Overlay from './Overlay';
import TextInput from './TextInput';
interface SearchBarProps extends CProps.Styling {
value: string;
noIcon?: boolean;
/** Id of the search bar. */
id?: string;
/** Search query. */
query: string;
/** Placeholder text. */
placeholder?: string;
onChange?: (newValue: string) => void;
/** Callback to be called when the search query changes. */
onChangeQuery?: (newValue: string) => void;
/** Disable search icon. */
noIcon?: boolean;
/** Disable border. */
noBorder?: boolean;
}
function SearchBar({ id, value, noIcon, onChange, noBorder, placeholder = 'Поиск', ...restProps }: SearchBarProps) {
/**
* Displays a search bar with a search icon and text input.
*/
function SearchBar({
id,
query,
noIcon,
onChangeQuery,
noBorder,
placeholder = 'Поиск',
...restProps
}: SearchBarProps) {
return (
<div {...restProps}>
{!noIcon ? (
@ -29,8 +51,8 @@ function SearchBar({ id, value, noIcon, onChange, noBorder, placeholder = 'По
type='search'
className={clsx('outline-none bg-transparent', !noIcon && 'pl-10')}
noBorder={noBorder}
value={value}
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
value={query}
onChange={event => (onChangeQuery ? onChangeQuery(event.target.value) : undefined)}
/>
</div>
);

View File

@ -45,6 +45,9 @@ export interface SelectMultiProps<Option, Group extends GroupBase<Option> = Grou
noPortal?: boolean;
}
/**
* Displays a multi-select component.
*/
function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>>({
noPortal,
...restProps

View File

@ -46,6 +46,9 @@ export interface SelectSingleProps<Option, Group extends GroupBase<Option> = Gro
noBorder?: boolean;
}
/**
* Displays a single-select component.
*/
function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option>>({
noPortal,
noBorder,

View File

@ -11,22 +11,38 @@ import MiniButton from './MiniButton';
import Overlay from './Overlay';
interface SelectTreeProps<ItemType> extends CProps.Styling {
items: ItemType[];
/** Current value. */
value: ItemType;
setValue: (newItem: ItemType) => void;
getParent: (item: ItemType) => ItemType;
getLabel: (item: ItemType) => string;
getDescription: (item: ItemType) => string;
/** List of available items. */
items: ItemType[];
/** Prefix for the ids of the elements. */
prefix: string;
/** Callback to be called when the value changes. */
onChangeValue: (newItem: ItemType) => void;
/** Callback providing the parent of the item. */
getParent: (item: ItemType) => ItemType;
/** Callback providing the label of the item. */
getLabel: (item: ItemType) => string;
/** Callback providing the description of the item. */
getDescription: (item: ItemType) => string;
}
/**
* Displays a tree of items and allows user to select one.
*/
function SelectTree<ItemType>({
items,
value,
getParent,
getLabel,
getDescription,
setValue,
onChangeValue,
prefix,
...restProps
}: SelectTreeProps<ItemType>) {
@ -71,9 +87,9 @@ function SelectTree<ItemType>({
(event: CProps.EventMouse, target: ItemType) => {
event.preventDefault();
event.stopPropagation();
setValue(target);
onChangeValue(target);
},
[setValue]
[onChangeValue]
);
return (

View File

@ -5,13 +5,22 @@ import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface SelectorButtonProps extends CProps.Button {
/** Text to display in the button. */
text?: string;
/** Icon to display in the button. */
icon?: React.ReactNode;
/** Classnames for the colors of the button. */
colors?: string;
/** Indicates if button background should be transparent. */
transparent?: boolean;
}
/**
* Displays a button with an icon and text that opens a dropdown menu.
*/
function SelectorButton({
text,
icon,

View File

@ -3,11 +3,19 @@ import clsx from 'clsx';
import { CProps } from '../props';
interface SubmitButtonProps extends CProps.Button {
/** Text to display in the button. */
text?: string;
loading?: boolean;
/** Icon to display in the button. */
icon?: React.ReactNode;
/** Indicates that loading is in progress. */
loading?: boolean;
}
/**
* Displays submit type button with icon and text.
*/
function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...restProps }: SubmitButtonProps) {
return (
<button

View File

@ -7,9 +7,13 @@ import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, CProps.Titled {
/** Label to display in the tab. */
label?: string;
}
/**
* Displays a tab header with a label.
*/
function TabLabel({ label, title, titleHtml, hideTitle, className, ...otherProps }: TabLabelProps) {
return (
<TabImpl

View File

@ -4,11 +4,19 @@ import { CProps } from '../props';
import Label from './Label';
export interface TextAreaProps extends CProps.Editor, CProps.Colors, CProps.TextArea {
/** Indicates that padding should be minimal. */
dense?: boolean;
/** Disable resize when content overflows. */
noResize?: boolean;
/** Disable resize to fit content. */
fitContent?: boolean;
}
/**
* Displays a customizable textarea with a label.
*/
function TextArea({
id,
label,

View File

@ -6,11 +6,19 @@ import { truncateToLastWord } from '@/utils/utils';
import { CProps } from '../props';
export interface TextContentProps extends CProps.Styling {
/** Text to display. */
text: string;
/** Maximum number of symbols to display. */
maxLength?: number;
/** Disable full text in a tooltip. */
noTooltip?: boolean;
}
/**
* Displays text limited to a certain number of symbols.
*/
function TextContent({ className, text, maxLength, noTooltip, ...restProps }: TextContentProps) {
const truncated = maxLength ? truncateToLastWord(text, maxLength) : text;
const isTruncated = maxLength && text.length > maxLength;

View File

@ -4,7 +4,10 @@ import { CProps } from '../props';
import Label from './Label';
interface TextInputProps extends CProps.Editor, CProps.Colors, CProps.Input {
/** Indicates that padding should be minimal. */
dense?: boolean;
/** Capture enter key. */
allowEnter?: boolean;
}
@ -14,6 +17,9 @@ function preventEnterCapture(event: React.KeyboardEvent<HTMLInputElement>) {
}
}
/**
* Displays a customizable input with a label.
*/
function TextInput({
id,
label,

View File

@ -1,13 +1,25 @@
import { Link } from 'react-router-dom';
interface TextURLProps {
/** Text to display. */
text: string;
/** Tooltip for the link. */
title?: string;
/** URL to link to. */
href?: string;
/** Color of the link. */
color?: string;
/** Callback to be called when the link is clicked. */
onClick?: () => void;
}
/**
* Displays a text with a clickable link.
*/
function TextURL({ text, href, title, color = 'clr-text-url', onClick }: TextURLProps) {
const design = `cursor-pointer hover:underline ${color}`;
if (href) {

View File

@ -10,10 +10,16 @@ import { useConceptOptions } from '@/context/ConceptOptionsContext';
export type { PlacesType } from 'react-tooltip';
interface TooltipProps extends Omit<ITooltip, 'variant'> {
layer?: string;
/** Text to display in the tooltip. */
text?: string;
/** Classname for z-index */
layer?: string;
}
/**
* Displays content in a tooltip container.
*/
function Tooltip({
text,
children,

View File

@ -7,16 +7,34 @@ import { CProps } from '../props';
import MiniButton from './MiniButton';
interface ValueIconProps extends CProps.Styling, CProps.Titled {
/** Id of the component. */
id?: string;
icon: React.ReactNode;
/** Value to display. */
value: string | number;
/** Icon to display. */
icon: React.ReactNode;
/** Classname for the text. */
textClassName?: string;
/** Callback to be called when the component is clicked. */
onClick?: (event: CProps.EventMouse) => void;
/** Number of symbols to display in a small size. */
smallThreshold?: number;
/** Indicates that padding should be minimal. */
dense?: boolean;
/** Disable interaction. */
disabled?: boolean;
}
/**
* Displays a value with an icon that can be clicked.
*/
function ValueIcon({
id,
dense,

View File

@ -3,12 +3,22 @@ import clsx from 'clsx';
import { CProps } from '../props';
interface ValueLabeledProps extends CProps.Styling {
/** Id of the component. */
id?: string;
/** Label to display. */
label: string;
/** Value to display. */
text: string | number;
/** Tooltip for the component. */
title?: string;
}
/**
* Displays a labeled value.
*/
function ValueLabeled({ id, label, text, title, className, ...restProps }: ValueLabeledProps) {
return (
<div className={clsx('flex justify-between gap-6', className)} {...restProps}>

View File

@ -4,11 +4,19 @@ import { CProps } from '../props';
import ValueIcon from './ValueIcon';
interface ValueStatsProps extends CProps.Styling, CProps.Titled {
/** Id of the component. */
id: string;
/** Icon to display. */
icon: React.ReactNode;
/** Value to display. */
value: string | number;
}
/**
* Displays statistics value with an icon.
*/
function ValueStats(props: ValueStatsProps) {
return <ValueIcon dense smallThreshold={PARAMETER.statSmallThreshold} textClassName='min-w-[1.4rem]' {...props} />;
}

View File

@ -190,8 +190,8 @@ function ToolbarSearch({
placeholder='Поиск'
noBorder
className={clsx('min-w-[7rem] sm:min-w-[10rem] max-w-[20rem]', folderMode && 'flex-grow')}
value={query}
onChange={onChangeQuery}
query={query}
onChangeQuery={onChangeQuery}
/>
{!folderMode ? (
<div ref={headMenu.ref} className='flex items-center h-full py-1 select-none'>
@ -249,8 +249,8 @@ function ToolbarSearch({
noIcon
noBorder
className='w-[4.5rem] sm:w-[5rem] flex-grow'
value={path}
onChange={onChangePath}
query={path}
onChangeQuery={onChangePath}
/>
) : null}
</div>

View File

@ -69,7 +69,7 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
<SelectTree
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
value={activeTopic}
setValue={handleSelectTopic}
onChangeValue={handleSelectTopic}
prefix={prefixes.topic_list}
getParent={item => topicParent.get(item) ?? item}
getLabel={labelHelpTopic}

View File

@ -17,7 +17,7 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
<SelectTree
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
value={activeTopic}
setValue={onChangeTopic}
onChangeValue={onChangeTopic}
prefix={prefixes.topic_list}
getParent={item => topicParent.get(item) ?? item}
getLabel={labelHelpTopic}

View File

@ -54,7 +54,7 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
onClick={onAnalyze}
>
<AnimatePresence mode='wait'>
{processing ? <Loader key='status-loader' size={3} /> : null}
{processing ? <Loader key='status-loader' scale={3} /> : null}
{!processing ? (
<>
<StatusIcon key='status-icon' size='1rem' value={status} />

View File

@ -152,8 +152,8 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
id='constituents_search'
noBorder
className='w-[8rem]'
value={filterText}
onChange={setFilterText}
query={filterText}
onChangeQuery={setFilterText}
/>
</div>
) : null}

View File

@ -74,8 +74,8 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
id='constituents_search'
noBorder
className='min-w-[6rem] pr-2 flex-grow'
value={filterText}
onChange={setFilterText}
query={filterText}
onChangeQuery={setFilterText}
/>
{selectMatchMode}
{selectGraph}