mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-08-23 03:26:37 +00:00
refactor(frontend): consolidate inline-edit module and drop the rename framing
Group inline-rename-input.tsx and use-inline-edit-title.ts into a new components/shared/inline-edit/ folder, the same pattern we applied to detail-navigation, overwrite, and unsaved-changes. Rename to drop the misleading "rename" framing — the API has nothing rename-specific (autoFocus, busy, defaultValue, onCancel, onSave) and the same component is used for quick-create and inline-edit flows too: InlineRenameInput → InlineEditInput useInlineEditTitle → useInlineEdit INLINE_RENAME_MAX_LENGTH → inlined as default `maxLength = 200`
This commit is contained in:
@@ -42,7 +42,7 @@ that every list reuses.
|
||||
| File | Role |
|
||||
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| [`detail-navigation/`](detail-navigation/) | Prev / Position / Next toolbar + listbox sheet for detail pages, and the navigation hooks that feed it. |
|
||||
| [`inline-rename-input.tsx`](inline-rename-input.tsx) | `<input>` + Save/Cancel addon with Enter/Escape keybindings. |
|
||||
| [`inline-edit/`](inline-edit/) | Generic inline-edit input (Save/Cancel addons, Enter/Escape) plus the paired `useInlineEdit` state machine. |
|
||||
|
||||
## Hooks
|
||||
|
||||
@@ -53,7 +53,7 @@ that every list reuses.
|
||||
| `usePagination` | `@/hooks/` | URL `?page=` | yes | Canonicalizes `?page=1` away so the URL has one form per view. |
|
||||
| `useNavigation` | `detail-navigation/` (internal) | props | no | Pure computation of Prev/Next around a `currentId`. |
|
||||
| `useDetailNavigation` | `detail-navigation/` | URL + props | no | Bundles the three above into a single hook for detail pages. |
|
||||
| `useInlineEditTitle` | `@/hooks/` | local state | no | Edit-mode toggle + deferred focus (Radix dropdown race fix). |
|
||||
| `useInlineEdit` | `inline-edit/` | local state | no | Edit-mode toggle + deferred focus (Radix dropdown race fix). |
|
||||
| `usePageStorageKeys` | `@/hooks/` | router | no | Resolves the three per-page storage keys reactively. |
|
||||
|
||||
## Library helpers (in `@/lib/`)
|
||||
@@ -146,7 +146,7 @@ and deletes them.
|
||||
|
||||
- `vitest run` covers the pure utilities (`table-filter`, `table-state`,
|
||||
`view-options-storage`, `url-params`, `table-sort`), the hook behaviours
|
||||
(`use-pagination`, `use-table-query-filter`, `use-inline-edit-title`,
|
||||
(`use-pagination`, `use-table-query-filter`, `use-inline-edit`,
|
||||
`use-page-storage-keys`, `use-detail-navigation`), and the components
|
||||
(`detail-navigation/`, `data-table`).
|
||||
- jsdom doesn't ship `Element.prototype.scrollIntoView` or `ResizeObserver`
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export { InlineEditInput } from './inline-edit-input';
|
||||
export { useInlineEdit } from './use-inline-edit';
|
||||
+16
-25
@@ -4,18 +4,7 @@ import { type KeyboardEvent, type Ref } from 'react';
|
||||
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
/**
|
||||
* Default upper bound on title length. Chosen as a balance between letting
|
||||
* users name things expressively and preventing accidental paste-bombs that
|
||||
* would break truncation in tables and breadcrumbs. Backend columns are
|
||||
* unbounded `varchar`, so this is purely a UX guard — the HTML `maxLength`
|
||||
* attribute below silently stops further typing rather than rejecting the
|
||||
* existing value (which lets existing records that exceed this limit still
|
||||
* be edited and re-saved without surprise validation errors).
|
||||
*/
|
||||
export const INLINE_RENAME_MAX_LENGTH = 200;
|
||||
|
||||
interface InlineRenameInputProps {
|
||||
interface InlineEditInputProps {
|
||||
/**
|
||||
* Auto-focus the input on mount. Needed for table-cell call sites that
|
||||
* switch into edit mode in-place: the parent flips a flag (e.g.
|
||||
@@ -29,14 +18,15 @@ interface InlineRenameInputProps {
|
||||
className?: string;
|
||||
/** Initial value rendered inside the uncontrolled input. */
|
||||
defaultValue?: string;
|
||||
/** Ref to the underlying `<input>` element. Pair with `useInlineEditTitle().inputRef`. */
|
||||
/** Ref to the underlying `<input>` element. Pair with `useInlineEdit().inputRef`. */
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
/**
|
||||
* Max length applied via the native HTML `maxLength` attribute. Defaults
|
||||
* to {@link INLINE_RENAME_MAX_LENGTH}. The browser stops typing past the
|
||||
* limit without altering programmatically-set `defaultValue`, so this is
|
||||
* a UX guard, not a hard validation gate. Override only when a specific
|
||||
* surface has a stricter or looser constraint.
|
||||
* to a UX-safe `200` to prevent accidental paste-bombs that would break
|
||||
* truncation in tables and breadcrumbs. Override per call site when a
|
||||
* stricter or looser constraint applies; the browser silently stops
|
||||
* typing past the limit without altering programmatically-set
|
||||
* `defaultValue`, so this is a guard rather than a validation gate.
|
||||
*/
|
||||
maxLength?: number;
|
||||
onCancel: () => void;
|
||||
@@ -50,29 +40,30 @@ interface InlineRenameInputProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline rename input used inside table cells and detail-page breadcrumbs.
|
||||
* Generic inline-edit input used inside table cells and detail-page
|
||||
* breadcrumbs (rename flows, quick-create entries, in-place note edits).
|
||||
*
|
||||
* Pairs with {@link useInlineEditTitle} — the parent owns the open/close
|
||||
* state and supplies a ref via that hook; this component owns the
|
||||
* presentation (input + Save/Cancel addon buttons), keyboard semantics
|
||||
* (`Enter` saves, `Escape` cancels), and the loading spinner during save.
|
||||
* Pairs with {@link useInlineEdit} — the parent owns the open/close state
|
||||
* and supplies a ref via that hook; this component owns the presentation
|
||||
* (input + Save/Cancel addon buttons), keyboard semantics (`Enter` saves,
|
||||
* `Escape` cancels), and the loading spinner during save.
|
||||
*
|
||||
* The input is uncontrolled (`defaultValue`) to match the pattern across
|
||||
* the codebase: callers read the value at submit time from `inputRef.current`,
|
||||
* not from React state, which avoids a re-render per keystroke for a value
|
||||
* that's only relevant once.
|
||||
*/
|
||||
export const InlineRenameInput = ({
|
||||
export const InlineEditInput = ({
|
||||
autoFocus = false,
|
||||
busy = false,
|
||||
className,
|
||||
defaultValue,
|
||||
inputRef,
|
||||
maxLength = INLINE_RENAME_MAX_LENGTH,
|
||||
maxLength = 200,
|
||||
onCancel,
|
||||
onSave,
|
||||
placeholder,
|
||||
}: InlineRenameInputProps) => {
|
||||
}: InlineEditInputProps) => {
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
+12
-12
@@ -1,16 +1,16 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { useInlineEditTitle } from './use-inline-edit-title';
|
||||
import { useInlineEdit } from './use-inline-edit';
|
||||
|
||||
describe('useInlineEditTitle', () => {
|
||||
describe('useInlineEdit', () => {
|
||||
it('starts in non-editing state', () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
expect(result.current.isEditing).toBe(false);
|
||||
});
|
||||
|
||||
it('startEdit flips to editing, stopEdit flips back', () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
|
||||
act(() => result.current.startEdit());
|
||||
expect(result.current.isEditing).toBe(true);
|
||||
@@ -20,7 +20,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('exits edit mode when resetKey changes', () => {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEditTitle({ resetKey }), {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEdit({ resetKey }), {
|
||||
initialProps: { resetKey: 'a' as null | string | undefined },
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('keeps edit mode across rerenders with the same resetKey', () => {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEditTitle({ resetKey }), {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEdit({ resetKey }), {
|
||||
initialProps: { resetKey: 'a' as null | string | undefined },
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('treats `null` and `undefined` as distinct keys (state reset on transition)', () => {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEditTitle({ resetKey }), {
|
||||
const { rerender, result } = renderHook(({ resetKey }) => useInlineEdit({ resetKey }), {
|
||||
initialProps: { resetKey: null as null | string | undefined },
|
||||
});
|
||||
|
||||
@@ -56,12 +56,12 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('returns a ref object whose .current starts at null', () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
expect(result.current.inputRef.current).toBeNull();
|
||||
});
|
||||
|
||||
it('handleDropdownCloseAutoFocus prevents default while editing', () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
|
||||
act(() => result.current.startEdit());
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('handleDropdownCloseAutoFocus is a no-op when not editing', () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
|
||||
const event = new Event('autofocus', { cancelable: true });
|
||||
result.current.handleDropdownCloseAutoFocus(event);
|
||||
@@ -83,7 +83,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('focuses and selects the input on the next animation frame after startEdit', async () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.value = 'hello';
|
||||
@@ -109,7 +109,7 @@ describe('useInlineEditTitle', () => {
|
||||
});
|
||||
|
||||
it('cancels the pending focus when isEditing flips off before the frame fires', async () => {
|
||||
const { result } = renderHook(() => useInlineEditTitle({ resetKey: 'a' }));
|
||||
const { result } = renderHook(() => useInlineEdit({ resetKey: 'a' }));
|
||||
|
||||
const input = document.createElement('input');
|
||||
document.body.appendChild(input);
|
||||
+7
-7
@@ -2,7 +2,7 @@ import type React from 'react';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface UseInlineEditTitleOptions {
|
||||
interface UseInlineEditOptions {
|
||||
/**
|
||||
* When this value changes, the edit session is reset (closes any open
|
||||
* input). Use it for the entity id that owns the editor — navigating
|
||||
@@ -11,7 +11,7 @@ interface UseInlineEditTitleOptions {
|
||||
resetKey?: null | string | undefined;
|
||||
}
|
||||
|
||||
interface UseInlineEditTitleResult<TElement extends HTMLElement = HTMLInputElement> {
|
||||
interface UseInlineEditResult<TElement extends HTMLElement = HTMLInputElement> {
|
||||
/**
|
||||
* Spread onto a Radix `<DropdownMenuContent>` (or any component with the
|
||||
* same `onCloseAutoFocus` semantics) when the dropdown contains a button
|
||||
@@ -29,10 +29,10 @@ interface UseInlineEditTitleResult<TElement extends HTMLElement = HTMLInputEleme
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared state machine for "double-click to rename" headers / table cells.
|
||||
* Shared state machine for inline-edit surfaces (double-click to rename,
|
||||
* quick-add, in-place note edits).
|
||||
*
|
||||
* Combines four micro-responsibilities that every renameable surface in the
|
||||
* app needs:
|
||||
* Combines four micro-responsibilities that every editable surface needs:
|
||||
* - `isEditing` boolean + start/stop helpers,
|
||||
* - a ref for the inline input,
|
||||
* - deferred focus + select-all on the next animation frame, so the focus
|
||||
@@ -44,9 +44,9 @@ interface UseInlineEditTitleResult<TElement extends HTMLElement = HTMLInputEleme
|
||||
* Pass the entity id as `resetKey` so navigation between items closes any
|
||||
* stale editor automatically.
|
||||
*/
|
||||
export const useInlineEditTitle = <TElement extends HTMLElement = HTMLInputElement>({
|
||||
export const useInlineEdit = <TElement extends HTMLElement = HTMLInputElement>({
|
||||
resetKey,
|
||||
}: UseInlineEditTitleOptions = {}): UseInlineEditTitleResult<TElement> => {
|
||||
}: UseInlineEditOptions = {}): UseInlineEditResult<TElement> => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const inputRef = useRef<null | TElement>(null);
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { KnowledgeDocumentFragmentFragment } from '@/graphql/types';
|
||||
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { DetailNavigationToolbar } from '@/components/shared/detail-navigation';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput, useInlineEdit } from '@/components/shared/inline-edit';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { useInlineEditTitle } from '@/hooks/use-inline-edit-title';
|
||||
import { type Knowledge, useKnowledges } from '@/providers/knowledges-provider';
|
||||
|
||||
import { useKnowledgeDetailNavigation } from './use-knowledge-detail-navigation';
|
||||
@@ -65,7 +64,7 @@ export const KnowledgeHeader = ({ isNew, knowledge, onBeforeNavigateAway, saveBu
|
||||
isEditing: isEditingTitle,
|
||||
startEdit: handleRenameStart,
|
||||
stopEdit: handleRenameCancel,
|
||||
} = useInlineEditTitle({ resetKey: knowledgeId });
|
||||
} = useInlineEdit({ resetKey: knowledgeId });
|
||||
|
||||
const handleRenameSave = useCallback(async () => {
|
||||
const newQuestion = editingInputRef.current?.value.trim();
|
||||
@@ -132,7 +131,7 @@ export const KnowledgeHeader = ({ isNew, knowledge, onBeforeNavigateAway, saveBu
|
||||
<BreadcrumbItem className="gap-2">
|
||||
<LibraryBig className="size-4 shrink-0" />
|
||||
{isEditingTitle && canShowActions ? (
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
busy={isRenaming}
|
||||
className="w-64 max-w-full"
|
||||
defaultValue={knowledgeName ?? ''}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ProviderIcon } from '@/components/icons/provider-icon';
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { DetailNavigationToolbar } from '@/components/shared/detail-navigation';
|
||||
import { HeaderButton } from '@/components/shared/header-button';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput, useInlineEdit } from '@/components/shared/inline-edit';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -43,7 +43,6 @@ import { useFlowDetailNavigation } from '@/features/flows/use-flow-detail-naviga
|
||||
import { ResultType, StatusType, useRenameFlowMutation } from '@/graphql/types';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useFlowTabDetection } from '@/hooks/use-flow-tab-detection';
|
||||
import { useInlineEditTitle } from '@/hooks/use-inline-edit-title';
|
||||
import { Log } from '@/lib/log';
|
||||
import { copyToClipboard, downloadTextFile, generateFileName, generateReport } from '@/lib/report';
|
||||
import { formatName } from '@/lib/utils/format';
|
||||
@@ -190,7 +189,7 @@ const Flow = () => {
|
||||
isEditing: isEditingTitle,
|
||||
startEdit: handleFlowRenameStart,
|
||||
stopEdit: handleFlowRenameCancel,
|
||||
} = useInlineEditTitle({ resetKey: flowId });
|
||||
} = useInlineEdit({ resetKey: flowId });
|
||||
|
||||
const [isFinishing, setIsFinishing] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
@@ -308,7 +307,7 @@ const Flow = () => {
|
||||
</>
|
||||
)}
|
||||
{isEditingTitle && flow ? (
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
busy={isRenameLoading}
|
||||
className="w-64 max-w-full"
|
||||
defaultValue={flowTitle}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FlowStatusIcon } from '@/components/icons/flow-status-icon';
|
||||
import { ProviderIcon } from '@/components/icons/provider-icon';
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { HeaderButton } from '@/components/shared/header-button';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput } from '@/components/shared/inline-edit';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -207,7 +207,7 @@ const Flows = () => {
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
autoFocus
|
||||
busy={isRenameLoading}
|
||||
defaultValue={title}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { BadgeVariant } from '@/components/ui/badge';
|
||||
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { HeaderButton } from '@/components/shared/header-button';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput } from '@/components/shared/inline-edit';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -191,7 +191,7 @@ const Knowledges = () => {
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
autoFocus
|
||||
busy={isRenameLoading}
|
||||
defaultValue={question}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { z } from 'zod';
|
||||
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { DetailNavigationToolbar } from '@/components/shared/detail-navigation';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput, useInlineEdit } from '@/components/shared/inline-edit';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -42,7 +42,6 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
|
||||
import { useTemplateDetailNavigation } from '@/features/templates/use-template-detail-navigation';
|
||||
import { useFlowTemplateQuery } from '@/graphql/types';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useInlineEditTitle } from '@/hooks/use-inline-edit-title';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { type Template, useTemplates } from '@/providers/templates-provider';
|
||||
|
||||
@@ -258,7 +257,7 @@ const Template = () => {
|
||||
isEditing: isEditingTitle,
|
||||
startEdit: handleTemplateRenameStart,
|
||||
stopEdit: handleTemplateRenameCancel,
|
||||
} = useInlineEditTitle({ resetKey: templateId });
|
||||
} = useInlineEdit({ resetKey: templateId });
|
||||
|
||||
// Fetch template data when editing
|
||||
const { data: templateData, loading: isLoadingTemplate } = useFlowTemplateQuery({
|
||||
@@ -404,7 +403,7 @@ const Template = () => {
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem className="gap-2">
|
||||
{isEditingTitle && canShowActions ? (
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
busy={isRenaming}
|
||||
className="w-64 max-w-full"
|
||||
defaultValue={templateName ?? ''}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { toast } from 'sonner';
|
||||
|
||||
import ConfirmationDialog from '@/components/shared/confirmation-dialog';
|
||||
import { HeaderButton } from '@/components/shared/header-button';
|
||||
import { InlineRenameInput } from '@/components/shared/inline-rename-input';
|
||||
import { InlineEditInput } from '@/components/shared/inline-edit';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ContextMenuItem, ContextMenuSeparator } from '@/components/ui/context-menu';
|
||||
@@ -124,7 +124,7 @@ const Templates = () => {
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<InlineRenameInput
|
||||
<InlineEditInput
|
||||
autoFocus
|
||||
busy={isRenameLoading}
|
||||
defaultValue={title}
|
||||
|
||||
Reference in New Issue
Block a user