diff --git a/apps/web/src/components/quick-search/Footer.tsx b/apps/web/src/components/quick-search/Footer.tsx
index 832e77d224..7904f19674 100644
--- a/apps/web/src/components/quick-search/Footer.tsx
+++ b/apps/web/src/components/quick-search/Footer.tsx
@@ -6,10 +6,22 @@ import React from 'react';
import { usePageHelper } from '@/hooks/use-page-helper';
import { StyledModalFooterContent } from './style';
-export const Footer = (props: { query: string; onClose: () => void }) => {
+
+const MAX_QUERY_SHOW_LENGTH = 20;
+
+export const Footer = ({
+ query,
+ onClose,
+}: {
+ query: string;
+ onClose: () => void;
+}) => {
const { openPage, createPage } = usePageHelper();
const { t } = useTranslation();
- const { query, onClose } = props;
+ const normalizedQuery =
+ query.length > MAX_QUERY_SHOW_LENGTH
+ ? query.slice(0, MAX_QUERY_SHOW_LENGTH) + '...'
+ : query;
return (
void }) => {
{query ? (
- {t('New Keyword Page', { query: query })}
+ {t('New Keyword Page', { query: normalizedQuery })}
) : (
{t('New Page')}
)}
diff --git a/apps/web/src/components/quick-search/Input.tsx b/apps/web/src/components/quick-search/Input.tsx
deleted file mode 100644
index 45e2627d40..0000000000
--- a/apps/web/src/components/quick-search/Input.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useTranslation } from '@affine/i18n';
-import { SearchIcon } from '@blocksuite/icons';
-import { Command } from 'cmdk';
-import React, {
- Dispatch,
- SetStateAction,
- useEffect,
- useRef,
- useState,
-} from 'react';
-
-import { StyledInputContent, StyledLabel } from './style';
-export const Input = (props: {
- open: boolean;
- query: string;
- setQuery: Dispatch>;
- setLoading: Dispatch>;
- isPublic: boolean;
- publishWorkspaceName: string | undefined;
-}) => {
- const { open, query, setQuery, setLoading, isPublic, publishWorkspaceName } =
- props;
- const [isComposition, setIsComposition] = useState(false);
- const [inputValue, setInputValue] = useState('');
- const inputRef = useRef(null);
- const { t } = useTranslation();
- useEffect(() => {
- if (open) {
- const inputElement = inputRef.current;
- return inputElement?.focus();
- }
- }, [open]);
- useEffect(() => {
- const inputElement = inputRef.current;
- if (!open) {
- return;
- }
- const handleFocus = () => {
- inputElement?.focus();
- };
- inputElement?.addEventListener('blur', handleFocus, true);
- return () => inputElement?.removeEventListener('blur', handleFocus, true);
- }, [inputRef, open]);
- useEffect(() => {
- setInputValue(query);
- }, [query]);
- return (
-
-
-
-
- {
- setIsComposition(true);
- }}
- onCompositionEnd={e => {
- setQuery(e.data);
- setIsComposition(false);
- if (!query) {
- setLoading(true);
- }
- }}
- onValueChange={str => {
- setInputValue(str);
- if (!isComposition) {
- setQuery(str);
- if (!query) {
- setLoading(true);
- }
- }
- }}
- onKeyDown={(e: React.KeyboardEvent) => {
- if (e.key === 'a' && e.metaKey) {
- e.stopPropagation();
- inputRef.current?.select();
- return;
- }
- if (isComposition) {
- if (
- e.key === 'ArrowDown' ||
- e.key === 'ArrowUp' ||
- e.key === 'Enter'
- ) {
- e.stopPropagation();
- }
- }
- }}
- placeholder={
- isPublic
- ? t('Quick search placeholder2', {
- workspace: publishWorkspaceName,
- })
- : t('Quick search placeholder')
- }
- />
-
- );
-};
diff --git a/apps/web/src/components/quick-search/PublishedResults.tsx b/apps/web/src/components/quick-search/PublishedResults.tsx
index 7f2bd6ff01..63ae937128 100644
--- a/apps/web/src/components/quick-search/PublishedResults.tsx
+++ b/apps/web/src/components/quick-search/PublishedResults.tsx
@@ -12,18 +12,17 @@ import { useGlobalState } from '@/store/app';
import { NoResultSVG } from './NoResultSVG';
import { StyledListItem, StyledNotFound } from './style';
-export const PublishedResults = (props: {
+export const PublishedResults = ({
+ query,
+ onClose,
+ setPublishWorkspaceName,
+}: {
query: string;
- loading: boolean;
- setLoading: Dispatch>;
setPublishWorkspaceName: Dispatch>;
onClose: () => void;
}) => {
const [workspace, setWorkspace] = useState();
- const { query, loading, setLoading, onClose, setPublishWorkspaceName } =
- props;
const { search } = usePageHelper();
- const [results, setResults] = useState(new Map());
const dataCenter = useGlobalState(store => store.dataCenter);
const router = useRouter();
const [pageList, setPageList] = useState([]);
@@ -42,57 +41,49 @@ export const PublishedResults = (props: {
});
}, [router, dataCenter, setPublishWorkspaceName]);
const { t } = useTranslation();
- useEffect(() => {
- setResults(search(query, workspace));
- setLoading(false);
- //Save the Map obtained from the search as state
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [query, setResults, setLoading]);
+
+ if (!query) {
+ return <>>;
+ }
+
+ const results = search(query, workspace);
const pageIds = [...results.values()];
const resultsPageMeta = pageList.filter(
page => pageIds.indexOf(page.id) > -1 && !page.trash
);
- return loading ? null : (
- <>
- {query ? (
- resultsPageMeta.length ? (
-
- {resultsPageMeta.map(result => {
- return (
- {
- router.push(
- `/public-workspace/${router.query.workspaceId}/${result.id}`
- );
- onClose();
- }}
- value={result.id}
- >
-
- {result.mode === 'edgeless' ? (
-
- ) : (
-
- )}
- {result.title}
-
-
+ if (!resultsPageMeta.length) {
+ return (
+
+ {t('Find 0 result')}
+
+
+ );
+ }
+
+ return (
+
+ {resultsPageMeta.map(result => {
+ return (
+ {
+ router.push(
+ `/public-workspace/${router.query.workspaceId}/${result.id}`
);
- })}
-
- ) : (
-
- {t('Find 0 result')}
-
-
- )
- ) : (
- <>>
- )}
- >
+ onClose();
+ }}
+ value={result.id}
+ >
+
+ {result.mode === 'edgeless' ? : }
+ {result.title}
+
+
+ );
+ })}
+
);
};
diff --git a/apps/web/src/components/quick-search/Results.tsx b/apps/web/src/components/quick-search/Results.tsx
index b02580e550..cb86e81fe9 100644
--- a/apps/web/src/components/quick-search/Results.tsx
+++ b/apps/web/src/components/quick-search/Results.tsx
@@ -2,13 +2,7 @@ import { useTranslation } from '@affine/i18n';
import { EdgelessIcon, PaperIcon } from '@blocksuite/icons';
import { Command } from 'cmdk';
import { useRouter } from 'next/router';
-import {
- Dispatch,
- SetStateAction,
- useCallback,
- useEffect,
- useState,
-} from 'react';
+import { Dispatch, SetStateAction, useCallback, useEffect } from 'react';
import usePageHelper from '@/hooks/use-page-helper';
import { useGlobalState } from '@/store/app';
@@ -16,14 +10,16 @@ import { useGlobalState } from '@/store/app';
import { useSwitchToConfig } from './config';
import { NoResultSVG } from './NoResultSVG';
import { StyledListItem, StyledNotFound } from './style';
-export const Results = (props: {
+
+export const Results = ({
+ query,
+ setShowCreatePage,
+ onClose,
+}: {
query: string;
- loading: boolean;
onClose: () => void;
- setLoading: Dispatch>;
setShowCreatePage: Dispatch>;
}) => {
- const { query, loading, setLoading, setShowCreatePage, onClose } = props;
const { openPage } = usePageHelper();
const router = useRouter();
const currentWorkspace = useGlobalState(
@@ -34,80 +30,74 @@ export const Results = (props: {
);
const { search } = usePageHelper();
const List = useSwitchToConfig(currentWorkspace?.id);
- const [results, setResults] = useState(new Map());
const { t } = useTranslation();
- useEffect(() => {
- setResults(search(query));
- setLoading(false);
- //Save the Map obtained from the search as state
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [query, setResults, setLoading]);
- const pageIds = [...results.values()];
+ const results = search(query);
+ const pageIds = [...results.values()];
const resultsPageMeta = pageList.filter(
page => pageIds.indexOf(page.id) > -1 && !page.trash
);
+ // TODO lift this state up and remove this effect!
useEffect(() => {
setShowCreatePage(!resultsPageMeta.length);
//Determine whether to display the ‘+ New page’
}, [resultsPageMeta, setShowCreatePage]);
- return loading ? null : (
- <>
- {query ? (
- resultsPageMeta.length ? (
-
+ {List.map(link => {
+ return (
+ {
+ onClose();
+ router.push(link.href);
+ }}
+ >
+
+
+ {link.title}
+
+
+ );
+ })}
+
+ );
+ }
+
+ if (!resultsPageMeta.length) {
+ return (
+
+ {t('Find 0 result')}
+
+
+ );
+ }
+
+ return (
+
+ {resultsPageMeta.map(result => {
+ return (
+ {
+ onClose();
+ openPage(result.id);
+ }}
+ value={result.id}
>
- {resultsPageMeta.map(result => {
- return (
- {
- onClose();
- openPage(result.id);
- }}
- value={result.id}
- >
-
- {result.mode === 'edgeless' ? (
-
- ) : (
-
- )}
- {result.title}
-
-
- );
- })}
-
- ) : (
-
- {t('Find 0 result')}
-
-
- )
- ) : (
-
- {List.map(link => {
- return (
- {
- onClose();
- router.push(link.href);
- }}
- >
-
-
- {link.title}
-
-
- );
- })}
-
- )}
- >
+
+ {result.mode === 'edgeless' ? : }
+ {result.title}
+
+
+ );
+ })}
+
);
};
diff --git a/apps/web/src/components/quick-search/SearchInput.tsx b/apps/web/src/components/quick-search/SearchInput.tsx
new file mode 100644
index 0000000000..d36ee7305a
--- /dev/null
+++ b/apps/web/src/components/quick-search/SearchInput.tsx
@@ -0,0 +1,33 @@
+import { SearchIcon } from '@blocksuite/icons';
+import { Command } from 'cmdk';
+import { forwardRef } from 'react';
+
+import { StyledInputContent, StyledLabel } from './style';
+
+export const SearchInput = forwardRef<
+ HTMLInputElement,
+ Omit<
+ React.InputHTMLAttributes,
+ 'value' | 'onChange' | 'type'
+ > & {
+ /**
+ * Optional controlled state for the value of the search input.
+ */
+ value?: string;
+ /**
+ * Event handler called when the search value changes.
+ */
+ onValueChange?: (search: string) => void;
+ } & React.RefAttributes
+>((props, ref) => {
+ return (
+
+
+
+
+
+
+ );
+});
+
+SearchInput.displayName = 'SearchInput';
diff --git a/apps/web/src/components/quick-search/index.tsx b/apps/web/src/components/quick-search/index.tsx
index d8f9400369..541e98d2cf 100644
--- a/apps/web/src/components/quick-search/index.tsx
+++ b/apps/web/src/components/quick-search/index.tsx
@@ -1,15 +1,16 @@
import { Modal, ModalWrapper } from '@affine/component';
+import { useTranslation } from '@affine/i18n';
import { Command } from 'cmdk';
import { useRouter } from 'next/router';
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import { useModal } from '@/store/globalModal';
import { getUaHelper } from '@/utils';
import { Footer } from './Footer';
-import { Input } from './Input';
import { PublishedResults } from './PublishedResults';
import { Results } from './Results';
+import { SearchInput } from './SearchInput';
import {
StyledContent,
StyledModalDivider,
@@ -17,10 +18,12 @@ import {
StyledModalHeader,
StyledShortcut,
} from './style';
+
type TransitionsModalProps = {
open: boolean;
onClose: () => void;
};
+
const isMac = () => {
return getUaHelper().isMacOs;
};
@@ -28,8 +31,9 @@ const isMac = () => {
// fixme(himself65): support ssr
export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
const router = useRouter();
+ const { t } = useTranslation();
+ const inputRef = useRef(null);
const [query, setQuery] = useState('');
- const [loading, setLoading] = useState(true);
const [isPublic, setIsPublic] = useState(false);
const [publishWorkspaceName, setPublishWorkspaceName] = useState('');
const [showCreatePage, setShowCreatePage] = useState(true);
@@ -38,7 +42,6 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
return isPublic && query.length === 0;
};
const handleClose = () => {
- setQuery('');
onClose();
};
// Add ‘⌘+K’ shortcut keys as switches
@@ -73,10 +76,19 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
}, [router]);
useEffect(() => {
if (router.pathname.startsWith('/404')) {
- return onClose();
+ onClose();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ useEffect(() => {
+ if (open) {
+ // Waiting for DOM rendering
+ requestAnimationFrame(() => {
+ const inputElement = inputRef.current;
+ inputElement?.focus();
+ });
+ }
+ }, [open]);
return (
{
}}
>
- {
+ setQuery(value);
+ }}
+ onKeyDown={e => {
+ // Avoid triggering the cmdk onSelect event when the input method is in use
+ if (e.nativeEvent.isComposing) {
+ e.stopPropagation();
+ return;
+ }
+ }}
+ placeholder={
+ isPublic
+ ? t('Quick search placeholder2', {
+ workspace: publishWorkspaceName,
+ })
+ : t('Quick search placeholder')
+ }
/>
{isMac() ? '⌘ + K' : 'Ctrl + K'}
@@ -128,16 +152,12 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
{!isPublic ? (
) : (