/
effective_wiki
/
web-client
Обзор
Документация
Войти
/
effective_wiki
/
web-client
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
src/components/database/database-title.tsx
132 строки
4 KB
Andrey Lila
feat: sync database name with the page name
19 дек 2025, 14:50
19 дек 2025, 14:50
5ed0ca0
Код
Авторство
О чём код?
import React, { useState, useRef, useEffect } from "react"; import { Info } from "lucide-react"; import { useAction, useAtom } from "@reatom/npm-react"; import { databaseAtom, datasourcesAtom, updateDatabaseFieldAction } from "@/store/database"; import { Button } from "@/components/ui/button"; import { DEFAULT_DEBOUNCE_TIME } from "@/helpers/database"; import { activePageIdAtom, updatePageTitleAction } from "@/store/pages"; export const DatabaseTitle: React.FC = () => { const [showDescription, setShowDescription] = useState(false); const [database] = useAtom(databaseAtom); const [datasources] = useAtom(datasourcesAtom); const [activePageId] = useAtom(activePageIdAtom); const updateDatabaseField = useAction(updateDatabaseFieldAction); const updatePageTitle = useAction(updatePageTitleAction); const [title, setTitle] = useState(database?.title || ""); const [description, setDescription] = useState(database?.description || ""); const titleDebounceRef = useRef<NodeJS.Timeout | null>(null); const descriptionDebounceRef = useRef<NodeJS.Timeout | null>(null); useEffect(() => { if (!database) return; // eslint-disable-next-line react-hooks/set-state-in-effect setTitle(database.title ?? ""); }, [database?.title, database]); useEffect(() => { if (!database) return; // eslint-disable-next-line react-hooks/set-state-in-effect setDescription(database.description ?? ""); }, [database?.description, database]); const updateTitle = (value: string) => { updateDatabaseField({ field: "title", value }); if (!activePageId) return; updatePageTitle({ pageId: activePageId, title: value }); }; const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; setTitle(value); if (titleDebounceRef.current) clearTimeout(titleDebounceRef.current); titleDebounceRef.current = setTimeout(() => { if (database) { updateTitle(value); } }, DEFAULT_DEBOUNCE_TIME); }; const handleTitleBlur = () => { if (titleDebounceRef.current) { clearTimeout(titleDebounceRef.current); titleDebounceRef.current = null; } updateTitle(title); }; const handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; setDescription(value); if (descriptionDebounceRef.current) clearTimeout(descriptionDebounceRef.current); descriptionDebounceRef.current = setTimeout(() => { if (database) { updateDatabaseField({ field: "description", value }); } }, DEFAULT_DEBOUNCE_TIME); }; const handleDescriptionBlur = () => { if (descriptionDebounceRef.current) { clearTimeout(descriptionDebounceRef.current); descriptionDebounceRef.current = null; } if (database) { updateDatabaseField({ field: "description", value: description }); } }; const handleDescriptionBtnToggle = () => setShowDescription((prev) => !prev); if (!database || !datasources) return null; return ( <div className="relative group pl-24 sticky start-0"> <div className="flex pt-4 pb-1"> <Button variant="ghost" size="sm" className="invisible group-hover:visible transition-[visibility] gap-1" onClick={handleDescriptionBtnToggle} > <Info size={14} /> <span className="text-[14px]"> {showDescription && "Спрятать описание"} {!showDescription && database.description ? "Показать описание" : null} {!showDescription && !database.description ? "Добавить описание" : null} </span> </Button> </div> <input type="text" value={title} onChange={handleTitleChange} onBlur={handleTitleBlur} placeholder="Новая база данных..." className="peer font-bold text-[2rem] outline-none min-h-[40px] bg-white w-full placeholder:text-gray-400" /> {showDescription && ( <input type="text" value={description} onChange={handleDescriptionChange} onBlur={handleDescriptionBlur} placeholder="Добавить описание..." className="peer pt-[3px] pb-[4px] pl-[2px] text-[14px] outline-none min-h-[28px] bg-white w-full placeholder:text-gray-400" /> )} </div> ); };