From b7a781351f09777c7968c84e62a72d2b070aee94 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 23 Jun 2024 00:13:03 +0300 Subject: [PATCH 01/24] Small UI fixes --- rsconcept/backend/requirements-dev.txt | 16 +++++----- .../DlgConstituentaTemplate/TemplateTab.tsx | 29 ++++++++++--------- .../pages/ManualsPage/items/HelpPortal.tsx | 8 ++--- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/rsconcept/backend/requirements-dev.txt b/rsconcept/backend/requirements-dev.txt index e3b3cb24..a33d4026 100644 --- a/rsconcept/backend/requirements-dev.txt +++ b/rsconcept/backend/requirements-dev.txt @@ -1,19 +1,19 @@ tzdata -django +Django djangorestframework django-cors-headers django-filter drf-spectacular -drf-spectacular[sidecar] +drf-spectacular-sidecar coreapi +django-rest-passwordreset cctext pyconcept +psycopg2-binary +gunicorn + +djangorestframework-stubs[compatible-mypy] mypy pylint -coverage -djangorestframework-stubs[compatible-mypy] -django-rest-passwordreset - -psycopg2-binary -gunicorn \ No newline at end of file +coverage \ No newline at end of file diff --git a/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TemplateTab.tsx b/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TemplateTab.tsx index e42726cf..e40751bc 100644 --- a/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TemplateTab.tsx +++ b/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TemplateTab.tsx @@ -86,11 +86,24 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) { return ( -
+
item.id == state.templateID)!.title } + : null + } + onChange={data => partialUpdate({ templateID: data ? data.value : undefined })} + /> + - item.id == state.templateID)!.title } - : null - } - onChange={data => partialUpdate({ templateID: data ? data.value : undefined })} - />
, обладающих уникальными обозначениями и формальными определениями

-

Разделы Портала

  • @@ -35,7 +34,6 @@ function HelpPortal() { – данные пользователя и смена пароля
  • -

    Разделы Справки

    {[ @@ -50,7 +48,6 @@ function HelpPortal() { ].map(topic => ( ))} -

    Лицензирование и раскрытие информации

  • Пользователи Портала сохраняют авторские права на создаваемый ими контент
  • @@ -65,12 +62,15 @@ function HelpPortal() { Данный сайт использует доменное имя и серверные мощности{' '} -

    Поддержка

    Портал разрабатывается

    +

    + Портал поддерживает актуальные версии браузеров Chrome, Firefox, Safari. Убедитесь, что используете последнюю + версию браузера в случае возникновения визуальных ошибок или проблем с производительностью. +

    Ваши пожелания по доработке, найденные ошибки и иные предложения можно направлять по email:{' '} From b303b2550205e97c6e705d95f637d1bbbe29185b Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:45:41 +0300 Subject: [PATCH 02/24] Improve help page --- .../frontend/src/pages/ManualsPage/items/HelpContributors.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx index c50615f4..60f4e6ed 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx @@ -74,6 +74,10 @@ function HelpInfo() { 1994 Кучкаров З.А., Ким В.Л. Разработка родоструктурных конструктов для библиотеки моделей и исследование возможностей их развития. +

  • + 1994 Коваль А.Г., Воробей П.Н. Редактор Программного комплекса Экстеор 1.5,{' '} + упростивший механизм печати экспликаций и улучшивший синтаксический анализ формального выражения. +
  • 1996 Коваль А.Г., Кучкаров З.А., Костюк А.В., Кононенко А.А., Син Ю.Е., Маклаков Ю.И. Программа родоструктурного синтеза операционализированных терминальных концептуальных моделей Экстеор 2,{' '} From c430a0132c9a4f3f2eb8848ab74c41d167894023 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:20:52 +0300 Subject: [PATCH 03/24] Small fixes --- .vscode/settings.json | 4 ++++ rsconcept/backend/apps/rsform/graph.py | 2 +- rsconcept/backend/apps/rsform/models/Editor.py | 4 ++-- .../frontend/src/app/Navigation/Navigation.tsx | 16 +++------------- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b666ff7a..eeb1c625 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -38,6 +38,10 @@ { "name": "django", "depth": 5 + }, + { + "name": "djangorestframework", + "depth": 2 } ], "colorize.include": [".tsx", ".jsx", ".ts", ".js"], diff --git a/rsconcept/backend/apps/rsform/graph.py b/rsconcept/backend/apps/rsform/graph.py index 4177840d..b6a749ad 100644 --- a/rsconcept/backend/apps/rsform/graph.py +++ b/rsconcept/backend/apps/rsform/graph.py @@ -91,7 +91,7 @@ class Graph(Generic[ItemType]): if len(self.inputs[node_id]) == 0: continue for parent in self.inputs[node_id]: - result[parent] = result[parent] + [id for id in result[node_id] if not id in result[parent]] + result[parent] = result[parent] + [id for id in result[node_id] if id not in result[parent]] return result def topological_order(self) -> list[ItemType]: diff --git a/rsconcept/backend/apps/rsform/models/Editor.py b/rsconcept/backend/apps/rsform/models/Editor.py index 2f9ce6a0..d43ae17c 100644 --- a/rsconcept/backend/apps/rsform/models/Editor.py +++ b/rsconcept/backend/apps/rsform/models/Editor.py @@ -60,12 +60,12 @@ class Editor(Model): ''' Set editors for item. ''' processed: list[User] = [] for editor_item in Editor.objects.filter(item=item): - if not editor_item.editor in users: + if editor_item.editor not in users: editor_item.delete() else: processed.append(editor_item.editor) for user in users: - if not user in processed: + if user not in processed: processed.append(user) Editor.objects.create(item=item, editor=user) diff --git a/rsconcept/frontend/src/app/Navigation/Navigation.tsx b/rsconcept/frontend/src/app/Navigation/Navigation.tsx index 207dccae..447efeb2 100644 --- a/rsconcept/frontend/src/app/Navigation/Navigation.tsx +++ b/rsconcept/frontend/src/app/Navigation/Navigation.tsx @@ -48,19 +48,9 @@ function Navigation() {
  • - } - onClick={navigateCreateNew} - /> - } - onClick={navigateLibrary} - /> - } onClick={navigateHelp} /> + } onClick={navigateCreateNew} /> + } onClick={navigateLibrary} /> + } onClick={navigateHelp} />
    From 122bf4fcc15306c0803611255512fd5cafcc0d8e Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 23 Jun 2024 19:43:22 +0300 Subject: [PATCH 04/24] Fix context loading semantics Loading is a flag for data loading on context create Processing is a flag for waiting for response after function call --- rsconcept/frontend/src/context/LibraryContext.tsx | 2 +- rsconcept/frontend/src/context/UserProfileContext.tsx | 2 +- .../src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx | 10 +++++----- rsconcept/frontend/src/hooks/useCheckExpression.ts | 6 +++--- rsconcept/frontend/src/hooks/useConceptText.ts | 10 +++++----- rsconcept/frontend/src/hooks/useOssDetails.ts | 2 +- rsconcept/frontend/src/hooks/useRSFormDetails.ts | 2 +- rsconcept/frontend/src/hooks/useResolveText.ts | 6 +++--- .../EditorRSExpression/EditorRSExpression.tsx | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/rsconcept/frontend/src/context/LibraryContext.tsx b/rsconcept/frontend/src/context/LibraryContext.tsx index 16e2c11d..ebb84e78 100644 --- a/rsconcept/frontend/src/context/LibraryContext.tsx +++ b/rsconcept/frontend/src/context/LibraryContext.tsx @@ -70,7 +70,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => { const [items, setItems] = useState([]); const [templates, setTemplates] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(false); const [loadingError, setLoadingError] = useState(undefined); const [processingError, setProcessingError] = useState(undefined); diff --git a/rsconcept/frontend/src/context/UserProfileContext.tsx b/rsconcept/frontend/src/context/UserProfileContext.tsx index 7185a553..7c39352e 100644 --- a/rsconcept/frontend/src/context/UserProfileContext.tsx +++ b/rsconcept/frontend/src/context/UserProfileContext.tsx @@ -36,7 +36,7 @@ interface UserProfileStateProps { export const UserProfileState = ({ children }: UserProfileStateProps) => { const { users } = useUsers(); const [user, setUser] = useState(undefined); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(false); const [error, setError] = useState(undefined); const [errorProcessing, setErrorProcessing] = useState(undefined); diff --git a/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx b/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx index e1557d99..e0b182b4 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx @@ -165,14 +165,14 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) noHover title='Определить граммемы' icon={} - disabled={textProcessor.loading || !inputText} + disabled={textProcessor.processing || !inputText} onClick={handleParse} /> } - disabled={textProcessor.loading || inputGrams.length == 0} + disabled={textProcessor.processing || inputGrams.length == 0} onClick={handleInflect} /> @@ -190,14 +190,14 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) noHover title='Внести словоформу' icon={} - disabled={textProcessor.loading || !inputText || inputGrams.length == 0} + disabled={textProcessor.processing || !inputText || inputGrams.length == 0} onClick={handleAddForm} /> } - disabled={textProcessor.loading || !inputText} + disabled={textProcessor.processing || !inputText} onClick={handleGenerateLexeme} /> @@ -210,7 +210,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) title='Сбросить все словоформы' className='py-0' icon={} - disabled={textProcessor.loading || forms.length === 0} + disabled={textProcessor.processing || forms.length === 0} onClick={handleResetAll} /> diff --git a/rsconcept/frontend/src/hooks/useCheckExpression.ts b/rsconcept/frontend/src/hooks/useCheckExpression.ts index 89ebfc5e..7d711b20 100644 --- a/rsconcept/frontend/src/hooks/useCheckExpression.ts +++ b/rsconcept/frontend/src/hooks/useCheckExpression.ts @@ -11,7 +11,7 @@ import { RSErrorType } from '@/models/rslang'; import { PARAMETER } from '@/utils/constants'; function useCheckExpression({ schema }: { schema?: IRSForm }) { - const [loading, setLoading] = useState(false); + const [processing, setProcessing] = useState(false); const [error, setError] = useState(undefined); const [parseData, setParseData] = useState(undefined); @@ -22,7 +22,7 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) { postCheckExpression(String(schema!.id), { data: { expression: expression }, showError: true, - setLoading, + setLoading: setProcessing, onError: setError, onSuccess: parse => { if (activeCst) { @@ -34,7 +34,7 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) { }); } - return { parseData, checkExpression, resetParse, error, setError, loading }; + return { parseData, checkExpression, resetParse, error, setError, processing }; } export default useCheckExpression; diff --git a/rsconcept/frontend/src/hooks/useConceptText.ts b/rsconcept/frontend/src/hooks/useConceptText.ts index 5ea48753..661d6ee7 100644 --- a/rsconcept/frontend/src/hooks/useConceptText.ts +++ b/rsconcept/frontend/src/hooks/useConceptText.ts @@ -7,7 +7,7 @@ import { ErrorData } from '@/components/info/InfoError'; import { ILexemeData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language'; function useConceptText() { - const [loading, setLoading] = useState(false); + const [processing, setProcessing] = useState(false); const [error, setError] = useState(undefined); const inflect = useCallback((data: IWordFormPlain, onSuccess: DataCallback) => { @@ -15,7 +15,7 @@ function useConceptText() { postInflectText({ data: data, showError: true, - setLoading, + setLoading: setProcessing, onError: setError, onSuccess: data => { if (onSuccess) onSuccess(data); @@ -28,7 +28,7 @@ function useConceptText() { postParseText({ data: data, showError: true, - setLoading, + setLoading: setProcessing, onError: setError, onSuccess: data => { if (onSuccess) onSuccess(data); @@ -41,7 +41,7 @@ function useConceptText() { postGenerateLexeme({ data: data, showError: true, - setLoading, + setLoading: setProcessing, onError: setError, onSuccess: data => { if (onSuccess) onSuccess(data); @@ -49,7 +49,7 @@ function useConceptText() { }); }, []); - return { inflect, parse, generateLexeme, error, setError, loading }; + return { inflect, parse, generateLexeme, error, setError, processing }; } export default useConceptText; diff --git a/rsconcept/frontend/src/hooks/useOssDetails.ts b/rsconcept/frontend/src/hooks/useOssDetails.ts index ae2184f8..ef202f99 100644 --- a/rsconcept/frontend/src/hooks/useOssDetails.ts +++ b/rsconcept/frontend/src/hooks/useOssDetails.ts @@ -9,7 +9,7 @@ import { OssLoader } from '@/models/OssLoader'; function useOssDetails({ target }: { target?: string }) { const [schema, setInner] = useState(undefined); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [error, setError] = useState(undefined); function setSchema(data?: IOperationSchemaData) { diff --git a/rsconcept/frontend/src/hooks/useRSFormDetails.ts b/rsconcept/frontend/src/hooks/useRSFormDetails.ts index dd2c532b..0d2d5144 100644 --- a/rsconcept/frontend/src/hooks/useRSFormDetails.ts +++ b/rsconcept/frontend/src/hooks/useRSFormDetails.ts @@ -9,7 +9,7 @@ import { RSFormLoader } from '@/models/RSFormLoader'; function useRSFormDetails({ target, version }: { target?: string; version?: string }) { const [schema, setInnerSchema] = useState(undefined); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [error, setError] = useState(undefined); function setSchema(data?: IRSFormData) { diff --git a/rsconcept/frontend/src/hooks/useResolveText.ts b/rsconcept/frontend/src/hooks/useResolveText.ts index 2926d330..7d578398 100644 --- a/rsconcept/frontend/src/hooks/useResolveText.ts +++ b/rsconcept/frontend/src/hooks/useResolveText.ts @@ -8,7 +8,7 @@ import { IResolutionData } from '@/models/language'; import { IRSForm } from '@/models/rsform'; function useResolveText({ schema }: { schema?: IRSForm }) { - const [loading, setLoading] = useState(false); + const [processing, setProcessing] = useState(false); const [error, setError] = useState(undefined); const [refsData, setRefsData] = useState(undefined); @@ -19,7 +19,7 @@ function useResolveText({ schema }: { schema?: IRSForm }) { postResolveText(String(schema!.id), { data: { text: text }, showError: true, - setLoading, + setLoading: setProcessing, onError: setError, onSuccess: data => { setRefsData(data); @@ -28,7 +28,7 @@ function useResolveText({ schema }: { schema?: IRSForm }) { }); } - return { refsData, resolveText, resetData, error, setError, loading }; + return { refsData, resolveText, resetData, error, setError, processing }; } export default useResolveText; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx index c61ac3f8..86cfbdc2 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx @@ -170,7 +170,7 @@ function EditorRSExpression({ Date: Wed, 26 Jun 2024 10:11:59 +0300 Subject: [PATCH 05/24] Update HelpContributors.tsx --- .../src/pages/ManualsPage/items/HelpContributors.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx index 60f4e6ed..eb06ad0d 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpContributors.tsx @@ -161,6 +161,10 @@ function HelpInfo() { 2004 Кононенко А.А. Генерация кода на языке программирования C++ по тексту концептуальной схемы, эксплицированной в родах структур. +
  • + 2004 Кононенко А.А., Кучкаров З.А., Никаноров С.П., Никитина Н.К. Технология концептуального проектирования, + — монография, собравшая исторический обзор и перспективы развития технологического направления. +
  • 2006 Кучкаров З.А., Никаноров С.П. Библиотека моделей.
  • 2006 Кучкаров З.А., Лавров В.А. Полные системы простых теоретико-множественных операций.
  • @@ -176,10 +180,6 @@ function HelpInfo() {
  • 2008 Пономарев И.Н. Об эквивалентной представимости рода структуры с помощью заданной типовой характеристики.
  • -
  • - 2008 Кононенко А.А., Кучкаров З.А., Никаноров С.П., Никитина Н.К. Технология концептуального проектирования, - — монография, собравшая исторический обзор и перспективы развития технологического направления. -
  • 2010 Кононенко А.А., Грязнов А.Д. Исследование и построение транслятора концептуальной схемы в концептуальную модель. From edb3bb4f10ff380cc9149375c30b52290d6b65f7 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:00:29 +0300 Subject: [PATCH 06/24] Implement location selector button --- .../src/components/select/SelectLocation.tsx | 113 ++++++++++++++---- .../select/SelectLocationContext.tsx | 62 ++++++++++ .../frontend/src/components/ui/Dropdown.tsx | 6 +- .../src/dialogs/DlgChangeLocation.tsx | 19 ++- .../src/dialogs/DlgCloneLibraryItem.tsx | 11 +- .../pages/CreateItemPage/FormCreateItem.tsx | 8 +- .../src/pages/LibraryPage/LibraryFolders.tsx | 107 ++--------------- 7 files changed, 196 insertions(+), 130 deletions(-) create mode 100644 rsconcept/frontend/src/components/select/SelectLocationContext.tsx diff --git a/rsconcept/frontend/src/components/select/SelectLocation.tsx b/rsconcept/frontend/src/components/select/SelectLocation.tsx index 43cbcd66..cded1871 100644 --- a/rsconcept/frontend/src/components/select/SelectLocation.tsx +++ b/rsconcept/frontend/src/components/select/SelectLocation.tsx @@ -1,40 +1,109 @@ 'use client'; -import { useCallback } from 'react'; +import clsx from 'clsx'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; -import useDropdown from '@/hooks/useDropdown'; -import { FolderTree } from '@/models/FolderTree'; +import { FolderNode, FolderTree } from '@/models/FolderTree'; +import { labelFolderNode } from '@/utils/labels'; -import { IconFolderTree } from '../Icons'; +import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '../Icons'; +import { CProps } from '../props'; import MiniButton from '../ui/MiniButton'; -interface SelectLocationProps { +interface SelectLocationProps extends CProps.Styling { value: string; - onChange: (newValue: string) => void; - folderTree: FolderTree; + prefix: string; + dense?: boolean; + onClick: (event: CProps.EventMouse, target: FolderNode) => void; } -function SelectLocation({ value, onChange, folderTree }: SelectLocationProps) { - const menu = useDropdown(); +function SelectLocation({ value, folderTree, dense, prefix, onClick, className, style }: SelectLocationProps) { + const activeNode = useMemo(() => folderTree.at(value), [folderTree, value]); - const handleChange = useCallback( - (newValue: string) => { - console.log(folderTree.roots.size); - console.log(value); - menu.hide(); - onChange(newValue); + const items = useMemo(() => folderTree.getTree(), [folderTree]); + const [folded, setFolded] = useState(items); + + useLayoutEffect(() => { + setFolded(items.filter(item => item !== activeNode && (!activeNode || !activeNode.hasPredecessor(item)))); + }, [items, activeNode]); + + const onFoldItem = useCallback( + (target: FolderNode, showChildren: boolean) => { + setFolded(prev => + items.filter(item => { + if (item === target) { + return !showChildren; + } + if (!showChildren && item.hasPredecessor(target)) { + return true; + } else { + return prev.includes(item); + } + }) + ); }, - [menu, onChange, value, folderTree] + [items] + ); + + const handleClickFold = useCallback( + (event: CProps.EventMouse, target: FolderNode, showChildren: boolean) => { + event.preventDefault(); + event.stopPropagation(); + onFoldItem(target, showChildren); + }, + [onFoldItem] ); return ( -
    - } - onClick={() => handleChange('/U/test')} - /> +
    + {items.map((item, index) => + !item.parent || !folded.includes(item.parent) ? ( +
    5 ? 5 : item.rank) * 0.5 + 0.5}rem` }} + onClick={event => onClick(event, item)} + > + {item.children.size > 0 ? ( + + ) : ( + + ) + ) : ( + + ) + } + onClick={event => handleClickFold(event, item, folded.includes(item))} + /> + ) : ( +
    + {item.filesInside ? ( + + ) : ( + + )} +
    + )} +
    {labelFolderNode(item)}
    +
    + ) : null + )}
    ); } diff --git a/rsconcept/frontend/src/components/select/SelectLocationContext.tsx b/rsconcept/frontend/src/components/select/SelectLocationContext.tsx new file mode 100644 index 00000000..91e260a8 --- /dev/null +++ b/rsconcept/frontend/src/components/select/SelectLocationContext.tsx @@ -0,0 +1,62 @@ +'use client'; + +import clsx from 'clsx'; +import { useCallback } from 'react'; + +import useDropdown from '@/hooks/useDropdown'; +import { FolderTree } from '@/models/FolderTree'; +import { prefixes } from '@/utils/constants'; + +import { IconFolderTree } from '../Icons'; +import { CProps } from '../props'; +import Dropdown from '../ui/Dropdown'; +import MiniButton from '../ui/MiniButton'; +import SelectLocation from './SelectLocation'; + +interface SelectLocationContextProps extends CProps.Styling { + value: string; + folderTree: FolderTree; + stretchTop?: boolean; + + onChange: (newValue: string) => void; +} + +function SelectLocationContext({ value, folderTree, onChange, className, style }: SelectLocationContextProps) { + const menu = useDropdown(); + + const handleClick = useCallback( + (event: CProps.EventMouse, newValue: string) => { + event.preventDefault(); + event.stopPropagation(); + menu.hide(); + onChange(newValue); + }, + [menu, onChange] + ); + + return ( +
    + } + onClick={() => menu.toggle()} + /> + + handleClick(event, target.getPath())} + /> + +
    + ); +} + +export default SelectLocationContext; diff --git a/rsconcept/frontend/src/components/ui/Dropdown.tsx b/rsconcept/frontend/src/components/ui/Dropdown.tsx index c92f33b7..be089f30 100644 --- a/rsconcept/frontend/src/components/ui/Dropdown.tsx +++ b/rsconcept/frontend/src/components/ui/Dropdown.tsx @@ -7,11 +7,12 @@ import { CProps } from '../props'; interface DropdownProps extends CProps.Styling { stretchLeft?: boolean; + stretchTop?: boolean; isOpen: boolean; children: React.ReactNode; } -function Dropdown({ isOpen, stretchLeft, className, children, ...restProps }: DropdownProps) { +function Dropdown({ isOpen, stretchLeft, stretchTop, className, children, ...restProps }: DropdownProps) { return (
    (initial.substring(0, 2) as LocationHead); const [body, setBody] = useState(initial.substring(3)); + const { folders } = useLibrary(); + const location = useMemo(() => combineLocation(head, body), [head, body]); const isValid = useMemo(() => initial !== location && validateLocation(location), [initial, location]); + const handleSelectLocation = useCallback((newValue: string) => { + setHead(newValue.substring(0, 2) as LocationHead); + setBody(newValue.length > 3 ? newValue.substring(3) : ''); + }, []); + function handleSubmit() { onChangeLocation(location); } @@ -41,13 +50,19 @@ function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeL className={clsx('w-[35rem]', 'pb-3 px-6 flex gap-3')} >
    -
    +