Compare commits

...

4 Commits

Author SHA1 Message Date
IRBorisov
fdfbc35f1a Implementing SelectLocation pt1
Some checks are pending
Frontend CI / build (18.x) (push) Waiting to run
2024-06-21 19:27:51 +03:00
IRBorisov
e1d8c99ebf Minor UI refactorings 2024-06-21 19:16:41 +03:00
IRBorisov
693cf5d391 Remove redundant cursor specifications 2024-06-21 17:13:34 +03:00
IRBorisov
2f33a140b7 Minor UI fixes 2024-06-21 11:16:47 +03:00
29 changed files with 140 additions and 68 deletions

View File

@ -14,8 +14,9 @@ function ToggleNavigationButton() {
tabIndex={-1} tabIndex={-1}
className={clsx( className={clsx(
'absolute top-0 right-0 z-navigation flex items-center justify-center', 'absolute top-0 right-0 z-navigation flex items-center justify-center',
'clr-btn-nav', 'clr-hover',
'select-none' 'select-none',
'min-h-[2rem]'
)} )}
onClick={toggleNoNavigation} onClick={toggleNoNavigation}
initial={false} initial={false}

View File

@ -7,7 +7,7 @@ import { findAliasAt } from '@/utils/codemirror';
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => { const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
return EditorView.domEventHandlers({ return EditorView.domEventHandlers({
click: (event: MouseEvent, view: EditorView) => { click: (event: MouseEvent, view: EditorView) => {
if (!event.ctrlKey) { if (!event.ctrlKey && !event.metaKey) {
return; return;
} }

View File

@ -7,7 +7,7 @@ import { findReferenceAt } from '@/utils/codemirror';
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => { const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
return EditorView.domEventHandlers({ return EditorView.domEventHandlers({
click: (event: MouseEvent, view: EditorView) => { click: (event: MouseEvent, view: EditorView) => {
if (!event.ctrlKey) { if (!event.ctrlKey && !event.metaKey) {
return; return;
} }

View File

@ -12,7 +12,7 @@ import { prefixes } from '@/utils/constants';
import { describeConstituenta } from '@/utils/labels'; import { describeConstituenta } from '@/utils/labels';
import BadgeConstituenta from '../info/BadgeConstituenta'; import BadgeConstituenta from '../info/BadgeConstituenta';
import FlexColumn from '../ui/FlexColumn'; import NoData from '../ui/NoData';
interface PickConstituentaProps { interface PickConstituentaProps {
id?: string; id?: string;
@ -106,10 +106,10 @@ function PickConstituenta({
columns={columns} columns={columns}
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
noDataComponent={ noDataComponent={
<FlexColumn className='p-3 items-center min-h-[6rem]'> <NoData className='min-h-[6rem]'>
<p>Список конституент пуст</p> <p>Список конституент пуст</p>
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</FlexColumn> </NoData>
} }
onRowClicked={onSelectValue} onRowClicked={onSelectValue}
/> />

View File

@ -10,7 +10,7 @@ import { isBasicConcept } from '@/models/rsformAPI';
import { describeConstituenta } from '@/utils/labels'; import { describeConstituenta } from '@/utils/labels';
import BadgeConstituenta from '../info/BadgeConstituenta'; import BadgeConstituenta from '../info/BadgeConstituenta';
import FlexColumn from '../ui/FlexColumn'; import NoData from '../ui/NoData';
import GraphSelectionToolbar from './GraphSelectionToolbar'; import GraphSelectionToolbar from './GraphSelectionToolbar';
interface PickMultiConstituentaProps { interface PickMultiConstituentaProps {
@ -103,9 +103,9 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect
rowSelection={rowSelection} rowSelection={rowSelection}
onRowSelectionChange={handleRowSelection} onRowSelectionChange={handleRowSelection}
noDataComponent={ noDataComponent={
<FlexColumn className='items-center p-3'> <NoData>
<p>Список пуст</p> <p>Список пуст</p>
</FlexColumn> </NoData>
} }
/> />
</div> </div>

View File

@ -104,7 +104,7 @@ function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue }:
columns={columns} columns={columns}
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
noDataComponent={ noDataComponent={
<FlexColumn className='p-3 items-center min-h-[6rem]'> <FlexColumn className='dense p-3 items-center min-h-[6rem]'>
<p>Список схем пуст</p> <p>Список схем пуст</p>
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</FlexColumn> </FlexColumn>

View File

@ -23,6 +23,7 @@ import {
IconRemove, IconRemove,
IconReplace IconReplace
} from '../Icons'; } from '../Icons';
import NoData from '../ui/NoData';
interface PickSubstitutionsProps { interface PickSubstitutionsProps {
prefixID: string; prefixID: string;
@ -259,10 +260,10 @@ function PickSubstitutions({
columns={columns} columns={columns}
headPosition='0' headPosition='0'
noDataComponent={ noDataComponent={
<span className='p-2 text-center min-h-[2rem]'> <NoData className='min-h-[2rem]'>
<p>Список пуст</p> <p>Список пуст</p>
<p>Добавьте отождествление</p> <p>Добавьте отождествление</p>
</span> </NoData>
} }
/> />
</div> </div>

View File

@ -37,7 +37,7 @@ function SelectAccessPolicy({ value, disabled, stretchLeft, onChange }: SelectAc
<MiniButton <MiniButton
title={`Доступ: ${labelAccessPolicy(value)}`} title={`Доступ: ${labelAccessPolicy(value)}`}
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
className='h-full disabled:cursor-auto' className='h-full'
icon={<PolicyIcon value={value} size='1.25rem' />} icon={<PolicyIcon value={value} size='1.25rem' />}
onClick={menu.toggle} onClick={menu.toggle}
disabled={disabled} disabled={disabled}

View File

@ -38,7 +38,7 @@ function SelectItemType({ value, disabled, stretchLeft, onChange }: SelectItemTy
transparent transparent
title={describeLibraryItemType(value)} title={describeLibraryItemType(value)}
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
className='h-full py-1 px-2 disabled:cursor-auto rounded-lg' className='h-full px-2 py-1 rounded-lg'
icon={<ItemTypeIcon value={value} size='1.25rem' />} icon={<ItemTypeIcon value={value} size='1.25rem' />}
text={labelLibraryItemType(value)} text={labelLibraryItemType(value)}
onClick={menu.toggle} onClick={menu.toggle}

View File

@ -0,0 +1,41 @@
'use client';
import { useCallback } from 'react';
import useDropdown from '@/hooks/useDropdown';
import { FolderTree } from '@/models/FolderTree';
import { LocationHead } from '@/models/library';
import { IconFolderTree } from '../Icons';
import MiniButton from '../ui/MiniButton';
interface SelectLocationProps {
value: string;
onChange: (newValue: string) => void;
folderTree: FolderTree;
}
function SelectLocation({ value, onChange, folderTree }: SelectLocationProps) {
const menu = useDropdown();
const handleChange = useCallback(
(newValue: LocationHead) => {
menu.hide();
onChange(newValue);
},
[menu, onChange]
);
return (
<div ref={menu.ref} className='h-full text-right'>
<MiniButton
title='Проводник...'
icon={<IconFolderTree size='1.25rem' className='icon-green' />}
onClick={() => menu.toggle}
/>
</div>
);
}
export default SelectLocation;

View File

@ -14,7 +14,7 @@ import DropdownButton from '../ui/DropdownButton';
interface SelectLocationHeadProps { interface SelectLocationHeadProps {
value: LocationHead; value: LocationHead;
onChange: (value: LocationHead) => void; onChange: (newValue: LocationHead) => void;
excluded?: LocationHead[]; excluded?: LocationHead[];
} }

View File

@ -2,9 +2,7 @@ import clsx from 'clsx';
import { CProps } from '../props'; import { CProps } from '../props';
export interface FlexColumnProps extends CProps.Div {} function FlexColumn({ className, children, ...restProps }: CProps.Div) {
function FlexColumn({ className, children, ...restProps }: FlexColumnProps) {
return ( return (
<div className={clsx('cc-column', className)} {...restProps}> <div className={clsx('cc-column', className)} {...restProps}>
{children} {children}

View File

@ -0,0 +1,13 @@
import clsx from 'clsx';
import { CProps } from '../props';
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}>
{children}
</div>
);
}
export default NoData;

View File

@ -18,9 +18,9 @@ function TextURL({ text, href, title, color = 'clr-text-url', onClick }: TextURL
); );
} else if (onClick) { } else if (onClick) {
return ( return (
<span tabIndex={-1} className={design} onClick={onClick}> <button type='button' tabIndex={-1} className={design} onClick={onClick}>
{text} {text}
</span> </button>
); );
} else { } else {
return null; return null;

View File

@ -101,7 +101,6 @@ function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, tota
/> />
<MiniButton <MiniButton
className='disabled:cursor-auto'
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
icon={<VisibilityIcon value={visible} />} icon={<VisibilityIcon value={visible} />}
onClick={() => setVisible(prev => !prev)} onClick={() => setVisible(prev => !prev)}

View File

@ -9,6 +9,7 @@ import RSInput from '@/components/RSInput';
import PickConstituenta from '@/components/select/PickConstituenta'; import PickConstituenta from '@/components/select/PickConstituenta';
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable'; import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
import NoData from '@/components/ui/NoData';
import AnimateFade from '@/components/wrap/AnimateFade'; import AnimateFade from '@/components/wrap/AnimateFade';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import { IConstituenta, IRSForm } from '@/models/rsform'; import { IConstituenta, IRSForm } from '@/models/rsform';
@ -160,7 +161,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
data={state.arguments} data={state.arguments}
columns={columns} columns={columns}
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
noDataComponent={<p className={clsx('min-h-[3.6rem]', 'p-2', 'text-center')}>Аргументы отсутствуют</p>} noDataComponent={<NoData className='min-h-[3.6rem]'>Аргументы отсутствуют</NoData>}
onRowClicked={handleSelectArgument} onRowClicked={handleSelectArgument}
/> />

View File

@ -7,6 +7,7 @@ import { IconRemove } from '@/components/Icons';
import BadgeWordForm from '@/components/info/BadgeWordForm'; import BadgeWordForm from '@/components/info/BadgeWordForm';
import DataTable, { createColumnHelper } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
import NoData from '@/components/ui/NoData';
import { IWordForm } from '@/models/language'; import { IWordForm } from '@/models/language';
interface WordFormsTableProps { interface WordFormsTableProps {
@ -78,10 +79,10 @@ function WordFormsTable({ forms, setForms, onFormSelect }: WordFormsTableProps)
columns={columns} columns={columns}
headPosition='0' headPosition='0'
noDataComponent={ noDataComponent={
<span className='p-2 text-center min-h-[2rem]'> <NoData className='min-h-[2rem]'>
<p>Список пуст</p> <p>Список пуст</p>
<p>Добавьте словоформу</p> <p>Добавьте словоформу</p>
</span> </NoData>
} }
onRowClicked={onFormSelect} onRowClicked={onFormSelect}
/> />

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
@ -10,6 +10,7 @@ import { IconDownload } from '@/components/Icons';
import InfoError from '@/components/info/InfoError'; import InfoError from '@/components/info/InfoError';
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy'; import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
import SelectItemType from '@/components/select/SelectItemType'; import SelectItemType from '@/components/select/SelectItemType';
import SelectLocation from '@/components/select/SelectLocation';
import SelectLocationHead from '@/components/select/SelectLocationHead'; import SelectLocationHead from '@/components/select/SelectLocationHead';
import Button from '@/components/ui/Button'; import Button from '@/components/ui/Button';
import Label from '@/components/ui/Label'; import Label from '@/components/ui/Label';
@ -30,7 +31,7 @@ import { information } from '@/utils/labels';
function FormCreateItem() { function FormCreateItem() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const { user } = useAuth(); const { user } = useAuth();
const { createItem, processingError, setProcessingError, processing } = useLibrary(); const { createItem, processingError, setProcessingError, processing, folders } = useLibrary();
const [itemType, setItemType] = useState(LibraryItemType.RSFORM); const [itemType, setItemType] = useState(LibraryItemType.RSFORM);
const [title, setTitle] = useState(''); const [title, setTitle] = useState('');
@ -98,6 +99,11 @@ function FormCreateItem() {
} }
} }
const handleSelectLocation = useCallback((newValue: string) => {
setHead(newValue.substring(0, 2) as LocationHead);
setBody(newValue.length > 3 ? newValue.substring(3) : '');
}, []);
return ( return (
<form className={clsx('cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')} onSubmit={handleSubmit}> <form className={clsx('cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')} onSubmit={handleSubmit}>
<h1> <h1>
@ -154,7 +160,6 @@ function FormCreateItem() {
<div className='ml-auto cc-icons'> <div className='ml-auto cc-icons'>
<SelectAccessPolicy value={policy} onChange={setPolicy} /> <SelectAccessPolicy value={policy} onChange={setPolicy} />
<MiniButton <MiniButton
className='disabled:cursor-auto'
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
icon={<VisibilityIcon value={visible} />} icon={<VisibilityIcon value={visible} />}
onClick={() => setVisible(prev => !prev)} onClick={() => setVisible(prev => !prev)}
@ -180,6 +185,11 @@ function FormCreateItem() {
excluded={!user?.is_staff ? [LocationHead.LIBRARY] : []} excluded={!user?.is_staff ? [LocationHead.LIBRARY] : []}
/> />
</div> </div>
{user?.is_staff ? (
<div className='self-start mt-[-0.25rem] ml-[-1.5rem]'>
<SelectLocation folderTree={folders} value={location} onChange={handleSelectLocation} />
</div>
) : null}
<TextArea <TextArea
id='dlg_cst_body' id='dlg_cst_body'
label='Путь' label='Путь'

View File

@ -54,7 +54,7 @@ function LibraryFolders({ folders, currentFolder, setFolder, toggleFolderMode }:
(event: CProps.EventMouse, target: FolderNode) => { (event: CProps.EventMouse, target: FolderNode) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (event.ctrlKey) { if (event.ctrlKey || event.metaKey) {
navigator.clipboard navigator.clipboard
.writeText(target.getPath()) .writeText(target.getPath())
.then(() => toast.success(information.pathReady)) .then(() => toast.success(information.pathReady))

View File

@ -164,7 +164,7 @@ function LibraryTable({ items, resetQuery, folderMode, toggleFolderMode }: Libra
className={clsx('text-xs sm:text-sm cc-scroll-y', { 'border-l border-b': folderMode })} className={clsx('text-xs sm:text-sm cc-scroll-y', { 'border-l border-b': folderMode })}
style={{ maxHeight: tableHeight }} style={{ maxHeight: tableHeight }}
noDataComponent={ noDataComponent={
<FlexColumn className='p-3 items-center min-h-[6rem]'> <FlexColumn className='dense p-3 items-center min-h-[6rem]'>
<p>Список схем пуст</p> <p>Список схем пуст</p>
<p className='flex gap-6'> <p className='flex gap-6'>
<TextURL text='Создать схему' href='/library/create' /> <TextURL text='Создать схему' href='/library/create' />

View File

@ -5,19 +5,16 @@ import { useCallback } from 'react';
import { LocationIcon, SubscribeIcon, VisibilityIcon } from '@/components/DomainIcons'; import { LocationIcon, SubscribeIcon, VisibilityIcon } from '@/components/DomainIcons';
import { IconEditor, IconFilterReset, IconFolder, IconFolderTree, IconOwner } from '@/components/Icons'; import { IconEditor, IconFilterReset, IconFolder, IconFolderTree, IconOwner } from '@/components/Icons';
import BadgeHelp from '@/components/info/BadgeHelp';
import { CProps } from '@/components/props'; import { CProps } from '@/components/props';
import Dropdown from '@/components/ui/Dropdown'; import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton'; import DropdownButton from '@/components/ui/DropdownButton';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import SearchBar from '@/components/ui/SearchBar'; import SearchBar from '@/components/ui/SearchBar';
import SelectorButton from '@/components/ui/SelectorButton'; import SelectorButton from '@/components/ui/SelectorButton';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import useDropdown from '@/hooks/useDropdown'; import useDropdown from '@/hooks/useDropdown';
import { LocationHead } from '@/models/library'; import { LocationHead } from '@/models/library';
import { HelpTopic } from '@/models/miscellaneous'; import { prefixes } from '@/utils/constants';
import { PARAMETER, prefixes } from '@/utils/constants';
import { describeLocationHead, labelLocationHead } from '@/utils/labels'; import { describeLocationHead, labelLocationHead } from '@/utils/labels';
import { tripleToggleColor } from '@/utils/utils'; import { tripleToggleColor } from '@/utils/utils';
@ -90,7 +87,7 @@ function SearchPanel({
const handleFolderClick = useCallback( const handleFolderClick = useCallback(
(event: CProps.EventMouse) => { (event: CProps.EventMouse) => {
if (event.ctrlKey) { if (event.ctrlKey || event.metaKey) {
toggleFolderMode(); toggleFolderMode();
} else { } else {
headMenu.toggle(); headMenu.toggle();
@ -218,14 +215,6 @@ function SearchPanel({
/> />
) : null} ) : null}
</div> </div>
<Overlay position='top-[-0.75rem] right-0'>
<BadgeHelp
topic={HelpTopic.UI_LIBRARY}
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'text-sm')}
offset={5}
place='right-start'
/>
</Overlay>
</div> </div>
); );
} }

View File

@ -221,8 +221,8 @@ function FormConstituenta({
<button <button
key='cst_disable_comment' key='cst_disable_comment'
id='cst_disable_comment' id='cst_disable_comment'
tabIndex={-1}
type='button' type='button'
tabIndex={-1}
className='self-start cc-label clr-text-url hover:underline' className='self-start cc-label clr-text-url hover:underline'
onClick={() => setForceComment(true)} onClick={() => setForceComment(true)}
> >

View File

@ -39,7 +39,6 @@ function AccessToolbar({ visible, toggleVisible, readOnly, toggleReadOnly, contr
/> />
<MiniButton <MiniButton
className='disabled:cursor-auto'
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
icon={<VisibilityIcon value={visible} />} icon={<VisibilityIcon value={visible} />}
onClick={toggleVisible} onClick={toggleVisible}
@ -47,7 +46,6 @@ function AccessToolbar({ visible, toggleVisible, readOnly, toggleReadOnly, contr
/> />
<MiniButton <MiniButton
className='disabled:cursor-auto'
title={readOnly ? 'Изменение: запрещено' : 'Изменение: разрешено'} title={readOnly ? 'Изменение: запрещено' : 'Изменение: разрешено'}
icon={ icon={
readOnly ? ( readOnly ? (

View File

@ -13,6 +13,7 @@ import useDropdown from '@/hooks/useDropdown';
import { ILibraryItemData, ILibraryItemEditor } from '@/models/library'; import { ILibraryItemData, ILibraryItemEditor } from '@/models/library';
import { UserID, UserLevel } from '@/models/user'; import { UserID, UserLevel } from '@/models/user';
import { prefixes } from '@/utils/constants'; import { prefixes } from '@/utils/constants';
import { prompts } from '@/utils/labels';
import LabeledValue from '../../../components/ui/LabeledValue'; import LabeledValue from '../../../components/ui/LabeledValue';
@ -34,11 +35,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
if (newValue === item?.owner) { if (newValue === item?.owner) {
return; return;
} }
if ( if (!window.confirm(prompts.ownerChange)) {
!window.confirm(
'Вы уверены, что хотите изменить владельца? Вы потеряете право управления данной схемой. Данное действие отменить нельзя'
)
) {
return; return;
} }
controller.setOwner(newValue); controller.setOwner(newValue);
@ -59,7 +56,11 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
/> />
</Overlay> </Overlay>
) : null} ) : null}
<LabeledValue className='max-w-[30rem] sm:mb-1 text-ellipsis' label='Путь' text={item?.location ?? ''} /> <LabeledValue
className='max-w-[30rem] sm:mb-1 text-ellipsis' //
label='Путь'
text={item?.location ?? ''}
/>
{accessLevel >= UserLevel.OWNER ? ( {accessLevel >= UserLevel.OWNER ? (
<Overlay position='top-[-0.5rem] left-[5.5rem] cc-icons'> <Overlay position='top-[-0.5rem] left-[5.5rem] cc-icons'>
@ -82,7 +83,11 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
</div> </div>
</Overlay> </Overlay>
) : null} ) : null}
<LabeledValue className='sm:mb-1' label='Владелец' text={getUserLabel(item?.owner ?? null)} /> <LabeledValue
className='sm:mb-1' //
label='Владелец'
text={getUserLabel(item?.owner ?? null)}
/>
{accessLevel >= UserLevel.OWNER ? ( {accessLevel >= UserLevel.OWNER ? (
<Overlay position='top-[-0.5rem] left-[5.5rem] cc-icons'> <Overlay position='top-[-0.5rem] left-[5.5rem] cc-icons'>
@ -97,12 +102,22 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
</div> </div>
</Overlay> </Overlay>
) : null} ) : null}
<LabeledValue id='editor_stats' className='sm:mb-1' label='Редакторы' text={item?.editors.length ?? 0} /> <LabeledValue
id='editor_stats' //
className='sm:mb-1'
label='Редакторы'
text={item?.editors.length ?? 0}
/>
<Tooltip anchorSelect='#editor_stats' layer='z-modalTooltip'> <Tooltip anchorSelect='#editor_stats' layer='z-modalTooltip'>
<InfoUsers items={item?.editors ?? []} prefix={prefixes.user_editors} /> <InfoUsers items={item?.editors ?? []} prefix={prefixes.user_editors} />
</Tooltip> </Tooltip>
<LabeledValue id='sub_stats' className='sm:mb-1' label='Отслеживают' text={item?.subscribers.length ?? 0} /> <LabeledValue
id='sub_stats' //
className='sm:mb-1'
label='Отслеживают'
text={item?.subscribers.length ?? 0}
/>
<Tooltip anchorSelect='#sub_stats' layer='z-modalTooltip'> <Tooltip anchorSelect='#sub_stats' layer='z-modalTooltip'>
<InfoUsers items={item?.subscribers ?? []} prefix={prefixes.user_subs} /> <InfoUsers items={item?.subscribers ?? []} prefix={prefixes.user_subs} />
</Tooltip> </Tooltip>

View File

@ -6,7 +6,8 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import { CProps } from '@/components/props'; import { CProps } from '@/components/props';
import DataTable, { createColumnHelper, RowSelectionState, VisibilityState } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, RowSelectionState, VisibilityState } from '@/components/ui/DataTable';
import FlexColumn from '@/components/ui/FlexColumn'; import NoData from '@/components/ui/NoData';
import TextURL from '@/components/ui/TextURL';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import useWindowSize from '@/hooks/useWindowSize'; import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta } from '@/models/rsform'; import { ConstituentaID, IConstituenta } from '@/models/rsform';
@ -135,12 +136,12 @@ function RSTable({ items, maxHeight, enableSelection, selected, setSelected, onE
rowSelection={selected} rowSelection={selected}
onRowSelectionChange={setSelected} onRowSelectionChange={setSelected}
noDataComponent={ noDataComponent={
<FlexColumn className='items-center p-3'> <NoData>
<p>Список пуст</p> <p>Список пуст</p>
<p className='cursor-pointer clr-text-primary hover:underline' onClick={() => onCreateNew()}> <p>
Создать новую конституенту <TextURL text='Создать конституенту...' onClick={onCreateNew} />
</p> </p>
</FlexColumn> </NoData>
} }
/> />
); );

View File

@ -98,10 +98,11 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
const id = `${prefixes.cst_hidden_list}${cst.alias}`; const id = `${prefixes.cst_hidden_list}${cst.alias}`;
return ( return (
<div key={`wrap-${id}`}> <div key={`wrap-${id}`}>
<div <button
type='button'
key={id} key={id}
id={id} id={id}
className='min-w-[3rem] rounded-md text-center cursor-pointer select-none' className='min-w-[3rem] rounded-md text-center select-none'
style={{ style={{
backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors), backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors),
...(localSelected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {}) ...(localSelected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {})
@ -110,7 +111,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
onDoubleClick={() => onEdit(cstID)} onDoubleClick={() => onEdit(cstID)}
> >
{cst.alias} {cst.alias}
</div> </button>
<ConstituentaTooltip data={cst} anchor={`#${id}`} /> <ConstituentaTooltip data={cst} anchor={`#${id}`} />
</div> </div>
); );

View File

@ -1,10 +1,10 @@
'use client'; 'use client';
import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable';
import NoData from '@/components/ui/NoData';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import useWindowSize from '@/hooks/useWindowSize'; import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta } from '@/models/rsform'; import { ConstituentaID, IConstituenta } from '@/models/rsform';
@ -155,10 +155,10 @@ function ConstituentsTable({ items, activeCst, onOpenEdit, maxHeight, denseThres
columnVisibility={columnVisibility} columnVisibility={columnVisibility}
onColumnVisibilityChange={setColumnVisibility} onColumnVisibilityChange={setColumnVisibility}
noDataComponent={ noDataComponent={
<div className={clsx('min-h-[5rem]', 'p-3', 'text-center', 'select-none')}> <NoData className='min-h-[5rem]'>
<p>Список конституент пуст</p> <p>Список конституент пуст</p>
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</div> </NoData>
} }
onRowClicked={handleRowClicked} onRowClicked={handleRowClicked}
/> />

View File

@ -6,6 +6,7 @@ import { useIntl } from 'react-intl';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import DataTable, { createColumnHelper } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
import NoData from '@/components/ui/NoData';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import { ILibraryItem } from '@/models/library'; import { ILibraryItem } from '@/models/library';
import { animateSideView } from '@/styling/animations'; import { animateSideView } from '@/styling/animations';
@ -74,7 +75,7 @@ function ViewSubscriptions({ items }: ViewSubscriptionsProps) {
id: 'time_update', id: 'time_update',
desc: true desc: true
}} }}
noDataComponent={<div className='h-[10rem]'>Отслеживаемые схемы отсутствуют</div>} noDataComponent={<NoData className='h-[10rem]'>Отслеживаемые схемы отсутствуют</NoData>}
onRowClicked={openRSForm} onRowClicked={openRSForm}
/> />
</motion.div> </motion.div>

View File

@ -942,7 +942,9 @@ export const prompts = {
promptUnsaved: 'Присутствуют несохраненные изменения. Продолжить без их учета?', promptUnsaved: 'Присутствуют несохраненные изменения. Продолжить без их учета?',
deleteLibraryItem: 'Вы уверены, что хотите удалить данную схему?', deleteLibraryItem: 'Вы уверены, что хотите удалить данную схему?',
generateWordforms: 'Данное действие приведет к перезаписи словоформ при совпадении граммем. Продолжить?', generateWordforms: 'Данное действие приведет к перезаписи словоформ при совпадении граммем. Продолжить?',
restoreArchive: 'При восстановлении архивной версии актуальная схему будет заменена. Продолжить?' restoreArchive: 'При восстановлении архивной версии актуальная схему будет заменена. Продолжить?',
ownerChange:
'Вы уверены, что хотите изменить владельца? Вы потеряете право управления данной схемой. Данное действие отменить нельзя'
}; };
// ============== INTERNAL LABELS FOR DEVELOPERS TEXT ================ // ============== INTERNAL LABELS FOR DEVELOPERS TEXT ================