mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
Symbol Palette: get rid of @reach/tabs
This commit is contained in:
+28
-13
@@ -1,4 +1,3 @@
|
|||||||
import { TabPanels, TabPanel } from '@reach/tabs'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import SymbolPaletteItems from './symbol-palette-items'
|
import SymbolPaletteItems from './symbol-palette-items'
|
||||||
@@ -9,6 +8,7 @@ export default function SymbolPaletteBody({
|
|||||||
filteredSymbols,
|
filteredSymbols,
|
||||||
handleSelect,
|
handleSelect,
|
||||||
focusInput,
|
focusInput,
|
||||||
|
activeCategoryId,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export default function SymbolPaletteBody({
|
|||||||
// note: include empty tab panels so that aria-controls on tabs can still reference the panel ids
|
// note: include empty tab panels so that aria-controls on tabs can still reference the panel ids
|
||||||
if (filteredSymbols) {
|
if (filteredSymbols) {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="symbol-palette-panels">
|
||||||
{filteredSymbols.length ? (
|
{filteredSymbols.length ? (
|
||||||
<SymbolPaletteItems
|
<SymbolPaletteItems
|
||||||
items={filteredSymbols}
|
items={filteredSymbols}
|
||||||
@@ -28,29 +28,43 @@ export default function SymbolPaletteBody({
|
|||||||
<div className="symbol-palette-empty">{t('no_symbols_found')}</div>
|
<div className="symbol-palette-empty">{t('no_symbols_found')}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TabPanels>
|
{categories.map(category => (
|
||||||
{categories.map(category => (
|
<div
|
||||||
<TabPanel key={category.id} tabIndex={-1} />
|
key={category.id}
|
||||||
))}
|
role="tabpanel"
|
||||||
</TabPanels>
|
className="symbol-palette-panel"
|
||||||
</>
|
id={`symbol-palette-panel-${category.id}`}
|
||||||
|
aria-labelledby={`symbol-palette-tab-${category.id}`}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// not searching: show the symbols grouped by category
|
// not searching: show the symbols grouped by category
|
||||||
return (
|
return (
|
||||||
<TabPanels>
|
<div className="symbol-palette-panels">
|
||||||
{categories.map(category => (
|
{categories.map((category) => (
|
||||||
<TabPanel key={category.id} tabIndex={-1}>
|
<div
|
||||||
|
key={category.id}
|
||||||
|
id={`symbol-palette-panel-${category.id}`}
|
||||||
|
className="symbol-palette-panel"
|
||||||
|
role="tabpanel"
|
||||||
|
aria-labelledby={`symbol-palette-tab-${category.id}`}
|
||||||
|
hidden={category.id !== activeCategoryId}
|
||||||
|
>
|
||||||
<SymbolPaletteItems
|
<SymbolPaletteItems
|
||||||
items={categorisedSymbols[category.id]}
|
items={categorisedSymbols[category.id]}
|
||||||
handleSelect={handleSelect}
|
handleSelect={handleSelect}
|
||||||
focusInput={focusInput}
|
focusInput={focusInput}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</div>
|
||||||
))}
|
))}
|
||||||
</TabPanels>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
SymbolPaletteBody.propTypes = {
|
SymbolPaletteBody.propTypes = {
|
||||||
categories: PropTypes.arrayOf(PropTypes.object).isRequired,
|
categories: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
@@ -58,4 +72,5 @@ SymbolPaletteBody.propTypes = {
|
|||||||
filteredSymbols: PropTypes.arrayOf(PropTypes.object),
|
filteredSymbols: PropTypes.arrayOf(PropTypes.object),
|
||||||
handleSelect: PropTypes.func.isRequired,
|
handleSelect: PropTypes.func.isRequired,
|
||||||
focusInput: PropTypes.func.isRequired,
|
focusInput: PropTypes.func.isRequired,
|
||||||
|
activeCategoryId: PropTypes.string.isRequired,
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ export default function SymbolPaletteCloseButton() {
|
|||||||
type="button"
|
type="button"
|
||||||
className="btn-close symbol-palette-close-button"
|
className="btn-close symbol-palette-close-button"
|
||||||
onClick={toggleSymbolPalette}
|
onClick={toggleSymbolPalette}
|
||||||
aria-label={t('clear_search')}
|
aria-label={t('close')}
|
||||||
>
|
>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+12
-8
@@ -1,4 +1,3 @@
|
|||||||
import { Tabs } from '@reach/tabs'
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
@@ -10,8 +9,6 @@ import SymbolPaletteBody from './symbol-palette-body'
|
|||||||
import SymbolPaletteTabs from './symbol-palette-tabs'
|
import SymbolPaletteTabs from './symbol-palette-tabs'
|
||||||
import SymbolPaletteCloseButton from './symbol-palette-close-button'
|
import SymbolPaletteCloseButton from './symbol-palette-close-button'
|
||||||
|
|
||||||
import '@reach/tabs/styles.css'
|
|
||||||
|
|
||||||
export default function SymbolPaletteContent({ handleSelect }) {
|
export default function SymbolPaletteContent({ handleSelect }) {
|
||||||
const [input, setInput] = useState('')
|
const [input, setInput] = useState('')
|
||||||
|
|
||||||
@@ -19,6 +16,7 @@ export default function SymbolPaletteContent({ handleSelect }) {
|
|||||||
|
|
||||||
// build the list of categories with translated labels
|
// build the list of categories with translated labels
|
||||||
const categories = useMemo(() => createCategories(t), [t])
|
const categories = useMemo(() => createCategories(t), [t])
|
||||||
|
const [activeCategoryId, setActiveCategoryId] = useState(categories[0]?.id)
|
||||||
|
|
||||||
// group the symbols by category
|
// group the symbols by category
|
||||||
const categorisedSymbols = useMemo(
|
const categorisedSymbols = useMemo(
|
||||||
@@ -59,18 +57,23 @@ export default function SymbolPaletteContent({ handleSelect }) {
|
|||||||
inputRef.current.focus()
|
inputRef.current.focus()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs className="symbol-palette-container">
|
<div className="symbol-palette-container">
|
||||||
<div className="symbol-palette">
|
<div className="symbol-palette">
|
||||||
<div className="symbol-palette-header-outer">
|
<div className="symbol-palette-header-outer">
|
||||||
<div className="symbol-palette-header">
|
<div className="symbol-palette-header">
|
||||||
<SymbolPaletteTabs categories={categories} />
|
<SymbolPaletteTabs
|
||||||
|
categories={categories}
|
||||||
|
activeCategoryId={activeCategoryId}
|
||||||
|
setActiveCategoryId={setActiveCategoryId}
|
||||||
|
/>
|
||||||
<div className="symbol-palette-header-group">
|
<div className="symbol-palette-header-group">
|
||||||
<SymbolPaletteSearch setInput={setInput} inputRef={inputRef} />
|
<SymbolPaletteSearch setInput={setInput} inputRef={inputRef} />
|
||||||
<SymbolPaletteCloseButton />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="symbol-palette-header-group">
|
||||||
|
<SymbolPaletteCloseButton />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="symbol-palette-body">
|
<div className="symbol-palette-body">
|
||||||
<SymbolPaletteBody
|
<SymbolPaletteBody
|
||||||
@@ -79,10 +82,11 @@ export default function SymbolPaletteContent({ handleSelect }) {
|
|||||||
filteredSymbols={filteredSymbols}
|
filteredSymbols={filteredSymbols}
|
||||||
handleSelect={handleSelect}
|
handleSelect={handleSelect}
|
||||||
focusInput={focusInput}
|
focusInput={focusInput}
|
||||||
|
activeCategoryId={activeCategoryId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SymbolPaletteContent.propTypes = {
|
SymbolPaletteContent.propTypes = {
|
||||||
|
|||||||
+1
-1
@@ -42,4 +42,4 @@ export default function SymbolPaletteSearch({ setInput, inputRef }) {
|
|||||||
SymbolPaletteSearch.propTypes = {
|
SymbolPaletteSearch.propTypes = {
|
||||||
setInput: PropTypes.func.isRequired,
|
setInput: PropTypes.func.isRequired,
|
||||||
inputRef: PropTypes.object.isRequired,
|
inputRef: PropTypes.object.isRequired,
|
||||||
};
|
}
|
||||||
|
|||||||
+69
-15
@@ -1,22 +1,76 @@
|
|||||||
import { TabList, Tab } from '@reach/tabs'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { useState, useRef } from 'react'
|
||||||
|
|
||||||
|
|
||||||
|
export default function SymbolPaletteTabs({
|
||||||
|
categories,
|
||||||
|
activeCategoryId,
|
||||||
|
setActiveCategoryId,
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const buttonRefs = useRef([])
|
||||||
|
const focusTab = (index) => {
|
||||||
|
setActiveCategoryId(categories[index].id)
|
||||||
|
buttonRefs.current[index]?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyDown = (e, index) => {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'ArrowRight':
|
||||||
|
focusTab((index + 1) % categories.length)
|
||||||
|
break
|
||||||
|
case 'ArrowLeft':
|
||||||
|
focusTab((index - 1 + categories.length) % categories.length)
|
||||||
|
break
|
||||||
|
case 'Home':
|
||||||
|
case 'PageUp':
|
||||||
|
focusTab(0)
|
||||||
|
break
|
||||||
|
case 'End':
|
||||||
|
case 'PageDown':
|
||||||
|
focusTab(categories.length - 1)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function SymbolPaletteTabs({ categories }) {
|
|
||||||
return (
|
return (
|
||||||
<TabList aria-label="Symbol Categories" className="symbol-palette-tab-list">
|
<div
|
||||||
{categories.map(category => (
|
role="tablist"
|
||||||
<Tab key={category.id} className="symbol-palette-tab">
|
aria-label="Symbol Categories"
|
||||||
{category.label}
|
className="symbol-palette-tab-list"
|
||||||
</Tab>
|
tabIndex={0}
|
||||||
))}
|
>
|
||||||
</TabList>
|
{categories.map((category, index) => {
|
||||||
|
const selected = activeCategoryId === category.id
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={category.id}
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
className="symbol-palette-tab"
|
||||||
|
id={`symbol-palette-tab-${category.id}`}
|
||||||
|
aria-controls={`symbol-palette-panel-${category.id}`}
|
||||||
|
aria-selected={selected}
|
||||||
|
tabIndex={selected ? 0 : -1}
|
||||||
|
ref={(el) => (buttonRefs.current[index] = el)}
|
||||||
|
onClick={() => setActiveCategoryId(category.id)}
|
||||||
|
onKeyDown={(e) => handleKeyDown(e, index)}
|
||||||
|
>
|
||||||
|
{category.label}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolPaletteTabs.propTypes = {
|
SymbolPaletteTabs.propTypes = {
|
||||||
categories: PropTypes.arrayOf(
|
categories: PropTypes.arrayOf(PropTypes.shape({
|
||||||
PropTypes.shape({
|
id: PropTypes.string.isRequired,
|
||||||
id: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
})).isRequired,
|
||||||
})
|
activeCategoryId: PropTypes.string.isRequired,
|
||||||
).isRequired,
|
setActiveCategoryId: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@
|
|||||||
.symbol-palette-close-button-outer {
|
.symbol-palette-close-button-outer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: var(--spacing-01);
|
margin-right: var(--spacing-05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.symbol-palette-close-button {
|
.symbol-palette-close-button {
|
||||||
|
|||||||
@@ -207,7 +207,6 @@
|
|||||||
"@pollyjs/adapter-node-http": "^6.0.6",
|
"@pollyjs/adapter-node-http": "^6.0.6",
|
||||||
"@pollyjs/core": "^6.0.6",
|
"@pollyjs/core": "^6.0.6",
|
||||||
"@pollyjs/persister-fs": "^6.0.6",
|
"@pollyjs/persister-fs": "^6.0.6",
|
||||||
"@reach/tabs": "0.18.0",
|
|
||||||
"@replit/codemirror-emacs": "overleaf/codemirror-emacs#4394c03858f27053f8768258e9493866e06e938e",
|
"@replit/codemirror-emacs": "overleaf/codemirror-emacs#4394c03858f27053f8768258e9493866e06e938e",
|
||||||
"@replit/codemirror-indentation-markers": "overleaf/codemirror-indentation-markers#78264032eb286bc47871569ae87bff5ca1c6c161",
|
"@replit/codemirror-indentation-markers": "overleaf/codemirror-indentation-markers#78264032eb286bc47871569ae87bff5ca1c6c161",
|
||||||
"@replit/codemirror-vim": "overleaf/codemirror-vim#1bef138382d948018f3f9b8a4d7a70ab61774e4b",
|
"@replit/codemirror-vim": "overleaf/codemirror-vim#1bef138382d948018f3f9b8a4d7a70ab61774e4b",
|
||||||
|
|||||||
Reference in New Issue
Block a user