mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
Reference picker: improve usability
- show tags for nonexistent references - click a badge to find the corresponding reference
This commit is contained in:
+38
-8
@@ -74,6 +74,7 @@ export default function ReferencePickerModal({
|
|||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
const [selectedKeys, setSelectedKeys] = useState<string[]>([])
|
const [selectedKeys, setSelectedKeys] = useState<string[]>([])
|
||||||
const [results, setResults] = useState<{ _source: Bib2JsonEntry }[]>([])
|
const [results, setResults] = useState<{ _source: Bib2JsonEntry }[]>([])
|
||||||
|
const [knownKeys, setKnownKeys] = useState<Set<string>>(() => new Set())
|
||||||
|
|
||||||
const pendingInitialKeysRef = useRef<string[]>([])
|
const pendingInitialKeysRef = useRef<string[]>([])
|
||||||
const requestIdRef = useRef(0)
|
const requestIdRef = useRef(0)
|
||||||
@@ -109,9 +110,9 @@ export default function ReferencePickerModal({
|
|||||||
setResults(r.hits)
|
setResults(r.hits)
|
||||||
|
|
||||||
if (pendingInitialKeysRef.current.length > 0) {
|
if (pendingInitialKeysRef.current.length > 0) {
|
||||||
const knownKeys = new Set(r.hits.map(h => h._source.EntryKey))
|
const known = new Set(r.hits.map(h => h._source.EntryKey))
|
||||||
const matched = pendingInitialKeysRef.current.filter(k => knownKeys.has(k))
|
setKnownKeys(known)
|
||||||
if (matched.length > 0) setSelectedKeys(matched)
|
setSelectedKeys([...pendingInitialKeysRef.current])
|
||||||
pendingInitialKeysRef.current = []
|
pendingInitialKeysRef.current = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +144,26 @@ export default function ReferencePickerModal({
|
|||||||
const cancelButtonRef = useRef<HTMLButtonElement | null>(null)
|
const cancelButtonRef = useRef<HTMLButtonElement | null>(null)
|
||||||
const insertButtonRef = useRef<HTMLButtonElement | null>(null)
|
const insertButtonRef = useRef<HTMLButtonElement | null>(null)
|
||||||
|
|
||||||
|
const scrollToReference = useCallback((key: string) => {
|
||||||
|
const index = results.findIndex(
|
||||||
|
r => r._source.EntryKey === key
|
||||||
|
)
|
||||||
|
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
const el = document.getElementById(
|
||||||
|
`reference-picker-item-${index}`
|
||||||
|
)
|
||||||
|
|
||||||
|
el?.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'nearest',
|
||||||
|
})
|
||||||
|
|
||||||
|
setFocusArea('list')
|
||||||
|
setFocusedIndex(index)
|
||||||
|
}, [results])
|
||||||
|
|
||||||
const removeOne = useCallback((key: string) => {
|
const removeOne = useCallback((key: string) => {
|
||||||
setSelectedKeys(prev => {
|
setSelectedKeys(prev => {
|
||||||
const index = prev.indexOf(key)
|
const index = prev.indexOf(key)
|
||||||
@@ -300,14 +321,23 @@ export default function ReferencePickerModal({
|
|||||||
</OLRow>
|
</OLRow>
|
||||||
|
|
||||||
<OLRow>
|
<OLRow>
|
||||||
<div className="selected-key-tag">
|
<div className="selected-key-tags">
|
||||||
{selectedKeys.map((key, idx) => (
|
{selectedKeys.map((key, idx) => (
|
||||||
<OLTag
|
<span
|
||||||
key={`${key}-${idx}`}
|
key={`${key}-${idx}`}
|
||||||
closeBtnProps={{ onClick: () => removeOne(key) }}
|
className={
|
||||||
|
knownKeys.has(key)
|
||||||
|
? 'existing-key-tag'
|
||||||
|
: 'nonexistent-key-tag'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{key}
|
<OLTag
|
||||||
</OLTag>
|
contentProps={{ onClick: () => scrollToReference(key) }}
|
||||||
|
closeBtnProps={{ onClick: () => removeOne(key) }}
|
||||||
|
>
|
||||||
|
{key}
|
||||||
|
</OLTag>
|
||||||
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</OLRow>
|
</OLRow>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.references-search-modal {
|
.references-search-modal {
|
||||||
.selected-key-tag {
|
.selected-key-tags {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -13,10 +13,22 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
box-shadow: 0 1px 0 var(--ds-color-neutral-100);
|
box-shadow: 0 1px 0 var(--ds-color-neutral-100);
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
.badge-tag {
|
.selected-key-tags .existing-key-tag .badge-tag {
|
||||||
background-color:var(--ds-color-green-50) !important;
|
background-color: var(--ds-color-green-50) !important;
|
||||||
}
|
}
|
||||||
|
.selected-key-tags .nonexistent-key-tag .badge-tag {
|
||||||
|
background-color: var(--ds-color-red-50) !important;
|
||||||
|
}
|
||||||
|
.existing-key-tag .badge-tag-content-btn:hover {
|
||||||
|
background-color: var(--ds-color-green-100);
|
||||||
|
}
|
||||||
|
.nonexistent-key-tag .badge-tag-content-btn:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.badge-close:hover,
|
||||||
|
.badge-close:focus {
|
||||||
|
background-color: var(--ds-color-neutral-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-results {
|
.search-results {
|
||||||
|
|||||||
Reference in New Issue
Block a user