mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
improve font
This commit is contained in:
@@ -24,7 +24,13 @@ import {
|
||||
useLiveData,
|
||||
useService,
|
||||
} from '@toeverything/infra';
|
||||
import { type ChangeEvent, useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
type ChangeEvent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { menu, menuTrigger, searchInput, settingWrapper } from './style.css';
|
||||
|
||||
@@ -92,29 +98,32 @@ const getFontFamily = (font: string) => `${font}, ${fontStyleOptions[0].value}`;
|
||||
|
||||
const FontMenuItems = ({ onSelect }: { onSelect: (font: string) => void }) => {
|
||||
const systemFontFamily = useService(SystemFontFamilyService).systemFontFamily;
|
||||
const systemFontList = useLiveData(systemFontFamily.fontList$);
|
||||
useEffect(() => {
|
||||
systemFontFamily.loadFontList();
|
||||
systemFontFamily.clearSearch();
|
||||
}, [systemFontFamily]);
|
||||
|
||||
const isLoading = useLiveData(systemFontFamily.isLoading$);
|
||||
const result = useLiveData(systemFontFamily.result$);
|
||||
const searchText = useLiveData(systemFontFamily.searchText$);
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const onInputChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
}, []);
|
||||
const onInputChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
systemFontFamily.search(e.target.value);
|
||||
},
|
||||
[systemFontFamily]
|
||||
);
|
||||
const onInputKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
systemFontFamily.search(inputValue);
|
||||
} else if (e.key === 'Backspace' || e.key === 'Escape') {
|
||||
systemFontFamily.clearSearch();
|
||||
}
|
||||
e.stopPropagation(); // avoid typeahead search built-in in the menu
|
||||
},
|
||||
[inputValue, systemFontFamily]
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={inputValue}
|
||||
value={searchText ?? ''}
|
||||
onChange={onInputChange}
|
||||
onKeyDown={onInputKeyDown}
|
||||
autoFocus
|
||||
@@ -131,12 +140,8 @@ const FontMenuItems = ({ onSelect }: { onSelect: (font: string) => void }) => {
|
||||
result.map(font => (
|
||||
<FontMenuItem key={font} font={font} onSelect={onSelect} />
|
||||
))
|
||||
) : searchText && searchText.length > 0 ? (
|
||||
<div>not found</div>
|
||||
) : (
|
||||
systemFontList.map(font => (
|
||||
<FontMenuItem key={font} font={font} onSelect={onSelect} />
|
||||
))
|
||||
<div>not found</div>
|
||||
)}
|
||||
</Scrollable.Viewport>
|
||||
<Scrollable.Scrollbar />
|
||||
|
||||
@@ -1,79 +1,61 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { apis } from '@affine/electron-api';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
import {
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
of,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
tap,
|
||||
} from 'rxjs';
|
||||
|
||||
const logger = new DebugLogger('affine:system-font-family');
|
||||
effect,
|
||||
Entity,
|
||||
fromPromise,
|
||||
LiveData,
|
||||
mapInto,
|
||||
onComplete,
|
||||
onStart,
|
||||
} from '@toeverything/infra';
|
||||
import { exhaustMap } from 'rxjs';
|
||||
|
||||
export class SystemFontFamily extends Entity {
|
||||
constructor() {
|
||||
super();
|
||||
this.loadFontList().catch(error => {
|
||||
logger.error('Failed to load system font list', error);
|
||||
});
|
||||
}
|
||||
|
||||
readonly searchText$ = new LiveData<string | null>(null);
|
||||
readonly isLoading$ = new LiveData<boolean>(false);
|
||||
readonly fontList$ = new LiveData<string[]>([]);
|
||||
readonly result$ = LiveData.from(
|
||||
this.searchText$.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(500),
|
||||
switchMap(searchText => {
|
||||
if (!searchText) {
|
||||
return of([]);
|
||||
readonly result$ = LiveData.computed(get => {
|
||||
const fontList = get(this.fontList$);
|
||||
const searchText = get(this.searchText$);
|
||||
if (!searchText) {
|
||||
return fontList;
|
||||
}
|
||||
|
||||
const filteredFonts = fontList.filter(font =>
|
||||
font.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
return filteredFonts;
|
||||
}).throttleTime(500);
|
||||
|
||||
loadFontList = effect(
|
||||
exhaustMap(() => {
|
||||
return fromPromise(async () => {
|
||||
if (!apis?.fontList) {
|
||||
return [];
|
||||
}
|
||||
return this.fontList$.pipe(
|
||||
tap(() => {
|
||||
this.isLoading$.next(true);
|
||||
}),
|
||||
switchMap(fontList => {
|
||||
const filteredFonts = fontList.filter(font =>
|
||||
font.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
this.isLoading$.next(false);
|
||||
return of(filteredFonts);
|
||||
})
|
||||
);
|
||||
}),
|
||||
shareReplay({
|
||||
bufferSize: 1,
|
||||
refCount: true,
|
||||
})
|
||||
),
|
||||
[]
|
||||
return apis.fontList.getSystemFonts();
|
||||
}).pipe(
|
||||
mapInto(this.fontList$),
|
||||
// TODO: catchErrorInto(this.error$),
|
||||
onStart(() => {
|
||||
this.isLoading$.next(true);
|
||||
}),
|
||||
onComplete(() => {
|
||||
this.isLoading$.next(false);
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
async loadFontList() {
|
||||
if (!apis?.fontList) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.isLoading$.next(true);
|
||||
const fontList = await apis.fontList.getSystemFonts();
|
||||
this.fontList$.next(fontList);
|
||||
} catch (error) {
|
||||
logger.error('Failed to load system font list', error);
|
||||
} finally {
|
||||
this.isLoading$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
search(searchText: string) {
|
||||
if (!this.searchText$.value) return;
|
||||
this.searchText$.next(searchText);
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
if (!this.searchText$.value) return;
|
||||
this.searchText$.next(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user