Commit 792d58b7 authored by Johnny's avatar Johnny

refactor: consolidate and update type definitions across MemoEditor components

parent 40585607
import type { EditorRefActions } from "."; import type { SlashCommandsProps } from "../types";
import type { Command } from "./commands";
import { SuggestionsPopup } from "./SuggestionsPopup"; import { SuggestionsPopup } from "./SuggestionsPopup";
import { useSuggestions } from "./useSuggestions"; import { useSuggestions } from "./useSuggestions";
interface SlashCommandsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
commands: Command[];
}
const SlashCommands = ({ editorRef, editorActions, commands }: SlashCommandsProps) => { const SlashCommands = ({ editorRef, editorActions, commands }: SlashCommandsProps) => {
const { position, suggestions, selectedIndex, isVisible, handleItemSelect } = useSuggestions({ const { position, suggestions, selectedIndex, isVisible, handleItemSelect } = useSuggestions({
editorRef, editorRef,
......
...@@ -3,15 +3,10 @@ import { matchPath } from "react-router-dom"; ...@@ -3,15 +3,10 @@ import { matchPath } from "react-router-dom";
import OverflowTip from "@/components/kit/OverflowTip"; import OverflowTip from "@/components/kit/OverflowTip";
import { useTagCounts } from "@/hooks/useUserQueries"; import { useTagCounts } from "@/hooks/useUserQueries";
import { Routes } from "@/router"; import { Routes } from "@/router";
import type { EditorRefActions } from "."; import type { TagSuggestionsProps } from "../types";
import { SuggestionsPopup } from "./SuggestionsPopup"; import { SuggestionsPopup } from "./SuggestionsPopup";
import { useSuggestions } from "./useSuggestions"; import { useSuggestions } from "./useSuggestions";
interface TagSuggestionsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
}
export default function TagSuggestions({ editorRef, editorActions }: TagSuggestionsProps) { export default function TagSuggestions({ editorRef, editorActions }: TagSuggestionsProps) {
// On explore page, show all users' tags; otherwise show current user's tags // On explore page, show all users' tags; otherwise show current user's tags
const isExplorePage = Boolean(matchPath(Routes.EXPLORE, window.location.pathname)); const isExplorePage = Boolean(matchPath(Routes.EXPLORE, window.location.pathname));
......
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react"; import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { EDITOR_HEIGHT } from "../constants"; import { EDITOR_HEIGHT } from "../constants";
import type { EditorProps } from "../types";
import { editorCommands } from "./commands"; import { editorCommands } from "./commands";
import SlashCommands from "./SlashCommands"; import SlashCommands from "./SlashCommands";
import TagSuggestions from "./TagSuggestions"; import TagSuggestions from "./TagSuggestions";
...@@ -22,19 +23,7 @@ export interface EditorRefActions { ...@@ -22,19 +23,7 @@ export interface EditorRefActions {
setLine: (lineNumber: number, text: string) => void; setLine: (lineNumber: number, text: string) => void;
} }
interface Props { const Editor = forwardRef(function Editor(props: EditorProps, ref: React.ForwardedRef<EditorRefActions>) {
className: string;
initialContent: string;
placeholder: string;
onContentChange: (content: string) => void;
onPaste: (event: React.ClipboardEvent) => void;
isFocusMode?: boolean;
isInIME?: boolean;
onCompositionStart?: () => void;
onCompositionEnd?: () => void;
}
const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<EditorRefActions>) {
const { const {
className, className,
initialContent, initialContent,
......
import { LatLng } from "leaflet"; import { LatLng } from "leaflet";
import { uniqBy } from "lodash-es"; import { uniqBy } from "lodash-es";
import { FileIcon, LinkIcon, LoaderIcon, MapPinIcon, Maximize2Icon, MoreHorizontalIcon, PlusIcon } from "lucide-react"; import { FileIcon, LinkIcon, LoaderIcon, MapPinIcon, Maximize2Icon, MoreHorizontalIcon, PlusIcon } from "lucide-react";
import { useContext, useState } from "react"; import { useState } from "react";
import type { LocalFile } from "@/components/memo-metadata"; import type { LocalFile } from "@/components/memo-metadata";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
...@@ -14,24 +14,17 @@ import { ...@@ -14,24 +14,17 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
useDropdownMenuSubHoverDelay, useDropdownMenuSubHoverDelay,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service_pb"; import type { MemoRelation } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { LinkMemoDialog, LocationDialog } from "../components"; import { LinkMemoDialog, LocationDialog } from "../components";
import { GEOCODING } from "../constants"; import { GEOCODING } from "../constants";
import { useFileUpload, useLinkMemo, useLocation } from "../hooks"; import { useAbortController, useFileUpload, useLinkMemo, useLocation } from "../hooks";
import { useAbortController } from "../hooks/useAbortController"; import { useEditorContext } from "../state";
import { MemoEditorContext } from "../types"; import type { InsertMenuProps } from "../types";
interface Props { const InsertMenu = (props: InsertMenuProps) => {
isUploading?: boolean;
location?: Location;
onLocationChange: (location?: Location) => void;
onToggleFocusMode?: () => void;
}
const InsertMenu = (props: Props) => {
const t = useTranslate(); const t = useTranslate();
const context = useContext(MemoEditorContext); const { state, actions, dispatch } = useEditorContext();
const [linkDialogOpen, setLinkDialogOpen] = useState(false); const [linkDialogOpen, setLinkDialogOpen] = useState(false);
const [locationDialogOpen, setLocationDialogOpen] = useState(false); const [locationDialogOpen, setLocationDialogOpen] = useState(false);
...@@ -46,17 +39,15 @@ const InsertMenu = (props: Props) => { ...@@ -46,17 +39,15 @@ const InsertMenu = (props: Props) => {
); );
const { fileInputRef, selectingFlag, handleFileInputChange, handleUploadClick } = useFileUpload((newFiles: LocalFile[]) => { const { fileInputRef, selectingFlag, handleFileInputChange, handleUploadClick } = useFileUpload((newFiles: LocalFile[]) => {
if (context.addLocalFiles) { newFiles.forEach((file) => dispatch(actions.addLocalFile(file)));
context.addLocalFiles(newFiles);
}
}); });
const linkMemo = useLinkMemo({ const linkMemo = useLinkMemo({
isOpen: linkDialogOpen, isOpen: linkDialogOpen,
currentMemoName: context.memoName, currentMemoName: props.memoName,
existingRelations: context.relationList, existingRelations: state.metadata.relations,
onAddRelation: (relation: MemoRelation) => { onAddRelation: (relation: MemoRelation) => {
context.setRelationList(uniqBy([...context.relationList, relation], (r) => r.relatedMemo?.name)); dispatch(actions.setMetadata({ relations: uniqBy([...state.metadata.relations, relation], (r) => r.relatedMemo?.name) }));
setLinkDialogOpen(false); setLinkDialogOpen(false);
}, },
}); });
......
...@@ -3,14 +3,9 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge ...@@ -3,14 +3,9 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
import VisibilityIcon from "@/components/VisibilityIcon"; import VisibilityIcon from "@/components/VisibilityIcon";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb"; import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import type { VisibilitySelectorProps } from "../types";
interface Props { const VisibilitySelector = (props: VisibilitySelectorProps) => {
value: Visibility;
onChange: (visibility: Visibility) => void;
onOpenChange?: (open: boolean) => void;
}
const VisibilitySelector = (props: Props) => {
const { value, onChange } = props; const { value, onChange } = props;
const t = useTranslate(); const t = useTranslate();
......
...@@ -3,11 +3,7 @@ import type { LocalFile } from "@/components/memo-metadata"; ...@@ -3,11 +3,7 @@ import type { LocalFile } from "@/components/memo-metadata";
import Editor, { type EditorRefActions } from "../Editor"; import Editor, { type EditorRefActions } from "../Editor";
import { useBlobUrls, useDragAndDrop } from "../hooks"; import { useBlobUrls, useDragAndDrop } from "../hooks";
import { useEditorContext } from "../state"; import { useEditorContext } from "../state";
import type { EditorContentProps } from "../types";
interface EditorContentProps {
placeholder?: string;
autoFocus?: boolean;
}
export const EditorContent = forwardRef<EditorRefActions, EditorContentProps>(({ placeholder }, ref) => { export const EditorContent = forwardRef<EditorRefActions, EditorContentProps>(({ placeholder }, ref) => {
const { state, actions, dispatch } = useEditorContext(); const { state, actions, dispatch } = useEditorContext();
......
import type { FC } from "react"; import type { FC } from "react";
import { AttachmentList, LocationDisplay, RelationList } from "@/components/memo-metadata"; import { AttachmentList, LocationDisplay, RelationList } from "@/components/memo-metadata";
import { useEditorContext } from "../state"; import { useEditorContext } from "../state";
import type { EditorMetadataProps } from "../types";
export const EditorMetadata: FC = () => { export const EditorMetadata: FC<EditorMetadataProps> = () => {
const { state, actions, dispatch } = useEditorContext(); const { state, actions, dispatch } = useEditorContext();
return ( return (
......
...@@ -4,13 +4,9 @@ import { validationService } from "../services"; ...@@ -4,13 +4,9 @@ import { validationService } from "../services";
import { useEditorContext } from "../state"; import { useEditorContext } from "../state";
import InsertMenu from "../Toolbar/InsertMenu"; import InsertMenu from "../Toolbar/InsertMenu";
import VisibilitySelector from "../Toolbar/VisibilitySelector"; import VisibilitySelector from "../Toolbar/VisibilitySelector";
import type { EditorToolbarProps } from "../types";
interface EditorToolbarProps { export const EditorToolbar: FC<EditorToolbarProps> = ({ onSave, onCancel, memoName }) => {
onSave: () => void;
onCancel?: () => void;
}
export const EditorToolbar: FC<EditorToolbarProps> = ({ onSave, onCancel }) => {
const { state, actions, dispatch } = useEditorContext(); const { state, actions, dispatch } = useEditorContext();
const { valid } = validationService.canSave(state); const { valid } = validationService.canSave(state);
...@@ -36,6 +32,7 @@ export const EditorToolbar: FC<EditorToolbarProps> = ({ onSave, onCancel }) => { ...@@ -36,6 +32,7 @@ export const EditorToolbar: FC<EditorToolbarProps> = ({ onSave, onCancel }) => {
location={state.metadata.location} location={state.metadata.location}
onLocationChange={handleLocationChange} onLocationChange={handleLocationChange}
onToggleFocusMode={handleToggleFocusMode} onToggleFocusMode={handleToggleFocusMode}
memoName={memoName}
/> />
</div> </div>
......
import { Minimize2Icon } from "lucide-react"; import { Minimize2Icon } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { FOCUS_MODE_STYLES } from "../constants"; import { FOCUS_MODE_STYLES } from "../constants";
import type { FocusModeExitButtonProps, FocusModeOverlayProps } from "../types";
interface FocusModeOverlayProps {
isActive: boolean;
onToggle: () => void;
}
export function FocusModeOverlay({ isActive, onToggle }: FocusModeOverlayProps) { export function FocusModeOverlay({ isActive, onToggle }: FocusModeOverlayProps) {
if (!isActive) return null; if (!isActive) return null;
...@@ -21,12 +17,6 @@ export function FocusModeOverlay({ isActive, onToggle }: FocusModeOverlayProps) ...@@ -21,12 +17,6 @@ export function FocusModeOverlay({ isActive, onToggle }: FocusModeOverlayProps)
); );
} }
interface FocusModeExitButtonProps {
isActive: boolean;
onToggle: () => void;
title: string;
}
export function FocusModeExitButton({ isActive, onToggle, title }: FocusModeExitButtonProps) { export function FocusModeExitButton({ isActive, onToggle, title }: FocusModeExitButtonProps) {
if (!isActive) return null; if (!isActive) return null;
......
import { timestampDate } from "@bufbuild/protobuf/wkt"; import { timestampDate } from "@bufbuild/protobuf/wkt";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import type { LinkMemoDialogProps } from "../types";
function highlightSearchText(content: string, searchText: string): React.ReactNode { function highlightSearchText(content: string, searchText: string): React.ReactNode {
if (!searchText) return content; if (!searchText) return content;
...@@ -29,16 +29,6 @@ function highlightSearchText(content: string, searchText: string): React.ReactNo ...@@ -29,16 +29,6 @@ function highlightSearchText(content: string, searchText: string): React.ReactNo
); );
} }
interface LinkMemoDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
searchText: string;
onSearchChange: (text: string) => void;
filteredMemos: Memo[];
isFetching: boolean;
onSelectMemo: (memo: Memo) => void;
}
export const LinkMemoDialog = ({ export const LinkMemoDialog = ({
open, open,
onOpenChange, onOpenChange,
......
import { LatLng } from "leaflet";
import LeafletMap from "@/components/LeafletMap"; import LeafletMap from "@/components/LeafletMap";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog";
...@@ -7,19 +6,7 @@ import { Label } from "@/components/ui/label"; ...@@ -7,19 +6,7 @@ import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { VisuallyHidden } from "@/components/ui/visually-hidden"; import { VisuallyHidden } from "@/components/ui/visually-hidden";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { LocationState } from "../types/insert-menu"; import type { LocationDialogProps } from "../types";
interface LocationDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
state: LocationState;
locationInitialized: boolean;
onPositionChange: (position: LatLng) => void;
onUpdateCoordinate: (type: "lat" | "lng", value: string) => void;
onPlaceholderChange: (value: string) => void;
onCancel: () => void;
onConfirm: () => void;
}
export const LocationDialog = ({ export const LocationDialog = ({
open, open,
......
...@@ -7,6 +7,5 @@ export { useFileUpload } from "./useFileUpload"; ...@@ -7,6 +7,5 @@ export { useFileUpload } from "./useFileUpload";
export { useFocusMode } from "./useFocusMode"; export { useFocusMode } from "./useFocusMode";
export { useKeyboard } from "./useKeyboard"; export { useKeyboard } from "./useKeyboard";
export { useLinkMemo } from "./useLinkMemo"; export { useLinkMemo } from "./useLinkMemo";
export { useLocalFileManager } from "./useLocalFileManager";
export { useLocation } from "./useLocation"; export { useLocation } from "./useLocation";
export { useMemoInit } from "./useMemoInit"; export { useMemoInit } from "./useMemoInit";
import { useState } from "react";
export function useDragAndDrop(onDrop: (files: FileList) => void) { export function useDragAndDrop(onDrop: (files: FileList) => void) {
const [isDragging, setIsDragging] = useState(false);
return { return {
isDragging,
dragHandlers: { dragHandlers: {
onDragOver: (e: React.DragEvent) => { onDragOver: (e: React.DragEvent) => {
if (e.dataTransfer?.types.includes("Files")) { if (e.dataTransfer?.types.includes("Files")) {
e.preventDefault(); e.preventDefault();
e.dataTransfer.dropEffect = "copy"; e.dataTransfer.dropEffect = "copy";
setIsDragging(true);
} }
}, },
onDragLeave: (e: React.DragEvent) => { onDragLeave: (e: React.DragEvent) => {
e.preventDefault(); e.preventDefault();
setIsDragging(false);
}, },
onDrop: (e: React.DragEvent) => { onDrop: (e: React.DragEvent) => {
if (e.dataTransfer?.files.length) { if (e.dataTransfer?.files.length) {
e.preventDefault(); e.preventDefault();
setIsDragging(false);
onDrop(e.dataTransfer.files); onDrop(e.dataTransfer.files);
} }
}, },
......
import { useState } from "react";
import type { LocalFile } from "@/components/memo-metadata";
import { useBlobUrls } from "./useBlobUrls";
export function useLocalFileManager() {
const [localFiles, setLocalFiles] = useState<LocalFile[]>([]);
const { createBlobUrl, revokeBlobUrl } = useBlobUrls();
const addFiles = (files: FileList | File[]): void => {
const fileArray = Array.from(files);
const newLocalFiles: LocalFile[] = fileArray.map((file) => ({
file,
previewUrl: createBlobUrl(file),
}));
setLocalFiles((prev) => [...prev, ...newLocalFiles]);
};
const removeFile = (previewUrl: string): void => {
setLocalFiles((prev) => {
const toRemove = prev.find((f) => f.previewUrl === previewUrl);
if (toRemove) {
revokeBlobUrl(toRemove.previewUrl);
}
return prev.filter((f) => f.previewUrl !== previewUrl);
});
};
const clearFiles = (): void => {
localFiles.forEach(({ previewUrl }) => revokeBlobUrl(previewUrl));
setLocalFiles([]);
};
return {
localFiles,
addFiles,
removeFile,
clearFiles,
};
}
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useMemo, useRef } from "react"; import { useRef } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { memoKeys } from "@/hooks/useMemoQueries"; import { memoKeys } from "@/hooks/useMemoQueries";
...@@ -13,18 +13,7 @@ import type { EditorRefActions } from "./Editor"; ...@@ -13,18 +13,7 @@ import type { EditorRefActions } from "./Editor";
import { useAutoSave, useFocusMode, useKeyboard, useMemoInit } from "./hooks"; import { useAutoSave, useFocusMode, useKeyboard, useMemoInit } from "./hooks";
import { cacheService, errorService, memoService, validationService } from "./services"; import { cacheService, errorService, memoService, validationService } from "./services";
import { EditorProvider, useEditorContext } from "./state"; import { EditorProvider, useEditorContext } from "./state";
import { MemoEditorContext } from "./types"; import type { MemoEditorProps } from "./types";
export interface MemoEditorProps {
className?: string;
cacheKey?: string;
placeholder?: string;
memoName?: string;
parentMemoName?: string;
autoFocus?: boolean;
onConfirm?: (memoName: string) => void;
onCancel?: () => void;
}
const MemoEditor = (props: MemoEditorProps) => { const MemoEditor = (props: MemoEditorProps) => {
const { className, cacheKey, memoName, parentMemoName, autoFocus, placeholder, onConfirm, onCancel } = props; const { className, cacheKey, memoName, parentMemoName, autoFocus, placeholder, onConfirm, onCancel } = props;
...@@ -61,26 +50,6 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({ ...@@ -61,26 +50,6 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({
const editorRef = useRef<EditorRefActions>(null); const editorRef = useRef<EditorRefActions>(null);
const { state, actions, dispatch } = useEditorContext(); const { state, actions, dispatch } = useEditorContext();
// Bridge for old MemoEditorContext (used by InsertMenu and other components)
const legacyContextValue = useMemo(
() => ({
attachmentList: state.metadata.attachments,
relationList: state.metadata.relations,
setAttachmentList: (attachments: typeof state.metadata.attachments) => dispatch(actions.setMetadata({ attachments })),
setRelationList: (relations: typeof state.metadata.relations) => dispatch(actions.setMetadata({ relations })),
memoName,
addLocalFiles: (files: typeof state.localFiles) => {
files.forEach((file) => {
dispatch(actions.addLocalFile(file));
});
},
removeLocalFile: (previewUrl: string) => dispatch(actions.removeLocalFile(previewUrl)),
localFiles: state.localFiles,
}),
[state.metadata.attachments, state.metadata.relations, state.localFiles, memoName, actions, dispatch],
);
// Initialize editor (load memo or cache)
useMemoInit(editorRef, memoName, cacheKey, currentUser?.name ?? "", autoFocus); useMemoInit(editorRef, memoName, cacheKey, currentUser?.name ?? "", autoFocus);
// Auto-save content to localStorage // Auto-save content to localStorage
...@@ -149,7 +118,7 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({ ...@@ -149,7 +118,7 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({
} }
return ( return (
<MemoEditorContext.Provider value={legacyContextValue}> <>
<FocusModeOverlay isActive={state.ui.isFocusMode} onToggle={handleToggleFocusMode} /> <FocusModeOverlay isActive={state.ui.isFocusMode} onToggle={handleToggleFocusMode} />
{/* {/*
...@@ -175,10 +144,10 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({ ...@@ -175,10 +144,10 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({
{/* Metadata and toolbar grouped together at bottom */} {/* Metadata and toolbar grouped together at bottom */}
<div className="w-full flex flex-col gap-2"> <div className="w-full flex flex-col gap-2">
<EditorMetadata /> <EditorMetadata />
<EditorToolbar onSave={handleSave} onCancel={onCancel} /> <EditorToolbar onSave={handleSave} onCancel={onCancel} memoName={memoName} />
</div> </div>
</div> </div>
</MemoEditorContext.Provider> </>
); );
}; };
......
import type { LatLng } from "leaflet";
import type { Location, Memo, Visibility } from "@/types/proto/api/v1/memo_service_pb";
import type { EditorRefActions } from "../Editor";
import type { Command } from "../Editor/commands";
import type { LocationState } from "./insert-menu";
export interface MemoEditorProps {
className?: string;
cacheKey?: string;
placeholder?: string;
memoName?: string;
parentMemoName?: string;
autoFocus?: boolean;
onConfirm?: (memoName: string) => void;
onCancel?: () => void;
}
export interface EditorContentProps {
placeholder?: string;
autoFocus?: boolean;
}
export interface EditorToolbarProps {
onSave: () => void;
onCancel?: () => void;
memoName?: string;
}
export interface EditorMetadataProps {}
export interface FocusModeOverlayProps {
isActive: boolean;
onToggle: () => void;
}
export interface FocusModeExitButtonProps {
isActive: boolean;
onToggle: () => void;
title: string;
}
export interface LinkMemoDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
searchText: string;
onSearchChange: (text: string) => void;
filteredMemos: Memo[];
isFetching: boolean;
onSelectMemo: (memo: Memo) => void;
}
export interface LocationDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
state: LocationState;
locationInitialized: boolean;
onPositionChange: (position: LatLng) => void;
onUpdateCoordinate: (type: "lat" | "lng", value: string) => void;
onPlaceholderChange: (placeholder: string) => void;
onCancel: () => void;
onConfirm: () => void;
}
export interface InsertMenuProps {
isUploading?: boolean;
location?: Location;
onLocationChange: (location?: Location) => void;
onToggleFocusMode?: () => void;
memoName?: string;
}
export interface TagSuggestionsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
}
export interface SlashCommandsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
commands: Command[];
}
export interface EditorProps {
className: string;
initialContent: string;
placeholder: string;
onContentChange: (content: string) => void;
onPaste: (event: React.ClipboardEvent) => void;
isFocusMode?: boolean;
isInIME?: boolean;
onCompositionStart?: () => void;
onCompositionEnd?: () => void;
}
export interface VisibilitySelectorProps {
value: Visibility;
onChange: (visibility: Visibility) => void;
onOpenChange?: (open: boolean) => void;
}
// MemoEditor type exports // MemoEditor type exports
export type {
EditorContentProps,
EditorMetadataProps,
EditorProps,
EditorToolbarProps,
FocusModeExitButtonProps,
FocusModeOverlayProps,
InsertMenuProps,
LinkMemoDialogProps,
LocationDialogProps,
MemoEditorProps,
SlashCommandsProps,
TagSuggestionsProps,
VisibilitySelectorProps,
} from "./components";
export { MemoEditorContext, type MemoEditorContextValue } from "./context"; export { MemoEditorContext, type MemoEditorContextValue } from "./context";
export type { LocationState } from "./insert-menu"; export type { LocationState } from "./insert-menu";
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment