mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #21259 from overleaf/td-bs5-editor-search
[BS5] Migrate Editor search panel GitOrigin-RevId: 37605845d4efc27d6c0c3a11de12387e8e3262f4
This commit is contained in:
parent
2646fefce4
commit
1b2385dfce
6 changed files with 224 additions and 148 deletions
|
@ -15,9 +15,14 @@ import {
|
||||||
getSearchQuery,
|
getSearchQuery,
|
||||||
SearchCursor,
|
SearchCursor,
|
||||||
} from '@codemirror/search'
|
} from '@codemirror/search'
|
||||||
import { Button, ButtonGroup, FormControl, InputGroup } from 'react-bootstrap'
|
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||||
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
import MaterialIcon from '@/shared/components/material-icon'
|
||||||
|
import OLButtonGroup from '@/features/ui/components/ol/ol-button-group'
|
||||||
|
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
|
||||||
|
import OLCloseButton from '@/features/ui/components/ol/ol-close-button'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Tooltip from '../../../shared/components/tooltip'
|
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { useUserSettingsContext } from '@/shared/context/user-settings-context'
|
import { useUserSettingsContext } from '@/shared/context/user-settings-context'
|
||||||
|
@ -226,14 +231,14 @@ const CodeMirrorSearchForm: FC = () => {
|
||||||
role="search"
|
role="search"
|
||||||
>
|
>
|
||||||
<div className="ol-cm-search-controls">
|
<div className="ol-cm-search-controls">
|
||||||
<InputGroup
|
<span
|
||||||
bsSize="small"
|
|
||||||
className={classnames('ol-cm-search-input-group', {
|
className={classnames('ol-cm-search-input-group', {
|
||||||
'ol-cm-search-input-error':
|
'ol-cm-search-input-error':
|
||||||
query.regexp && isInvalidRegExp(query.search),
|
query.regexp && isInvalidRegExp(query.search),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormControl
|
<OLFormControl
|
||||||
|
ref={handleInputRef}
|
||||||
type="text"
|
type="text"
|
||||||
name="search"
|
name="search"
|
||||||
// IMPORTANT: CodeMirror uses this attribute to focus the input
|
// IMPORTANT: CodeMirror uses this attribute to focus the input
|
||||||
|
@ -245,99 +250,90 @@ const CodeMirrorSearchForm: FC = () => {
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleSearchKeyDown}
|
onKeyDown={handleSearchKeyDown}
|
||||||
className="ol-cm-search-form-input"
|
className="ol-cm-search-form-input"
|
||||||
bsSize="small"
|
size="sm"
|
||||||
inputRef={handleInputRef}
|
|
||||||
aria-label={t('search_command_find')}
|
aria-label={t('search_command_find')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InputGroup.Button>
|
<OLTooltip
|
||||||
<Tooltip
|
id="search-match-case"
|
||||||
id="search-match-case"
|
description={t('search_match_case')}
|
||||||
description={t('search_match_case')}
|
>
|
||||||
|
<label
|
||||||
|
className={classnames(
|
||||||
|
'btn btn-sm btn-default ol-cm-search-input-button',
|
||||||
|
{
|
||||||
|
checked: query.caseSensitive,
|
||||||
|
focused: activeSearchOption === 'caseSensitive',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
htmlFor={caseSensitiveId}
|
||||||
|
aria-label={t('search_match_case')}
|
||||||
>
|
>
|
||||||
<label
|
Aa
|
||||||
className={classnames(
|
</label>
|
||||||
'btn btn-sm btn-default ol-cm-search-input-button',
|
</OLTooltip>
|
||||||
{
|
|
||||||
checked: query.caseSensitive,
|
|
||||||
focused: activeSearchOption === 'caseSensitive',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
htmlFor={caseSensitiveId}
|
|
||||||
aria-label={t('search_match_case')}
|
|
||||||
>
|
|
||||||
Aa
|
|
||||||
</label>
|
|
||||||
</Tooltip>
|
|
||||||
</InputGroup.Button>
|
|
||||||
|
|
||||||
<InputGroup.Button>
|
<OLTooltip id="search-regexp" description={t('search_regexp')}>
|
||||||
<Tooltip id="search-regexp" description={t('search_regexp')}>
|
<label
|
||||||
<label
|
className={classnames(
|
||||||
className={classnames(
|
'btn btn-sm btn-default ol-cm-search-input-button',
|
||||||
'btn btn-sm btn-default ol-cm-search-input-button',
|
{
|
||||||
{
|
checked: query.regexp,
|
||||||
checked: query.regexp,
|
focused: activeSearchOption === 'regexp',
|
||||||
focused: activeSearchOption === 'regexp',
|
}
|
||||||
}
|
)}
|
||||||
)}
|
htmlFor={regexpId}
|
||||||
htmlFor={regexpId}
|
aria-label={t('search_regexp')}
|
||||||
aria-label={t('search_regexp')}
|
|
||||||
>
|
|
||||||
[.*]
|
|
||||||
</label>
|
|
||||||
</Tooltip>
|
|
||||||
</InputGroup.Button>
|
|
||||||
|
|
||||||
<InputGroup.Button>
|
|
||||||
<Tooltip
|
|
||||||
id="search-whole-word"
|
|
||||||
description={t('search_whole_word')}
|
|
||||||
>
|
>
|
||||||
<label
|
[.*]
|
||||||
className={classnames(
|
</label>
|
||||||
'btn btn-sm btn-default ol-cm-search-input-button',
|
</OLTooltip>
|
||||||
{
|
|
||||||
checked: query.wholeWord,
|
|
||||||
focused: activeSearchOption === 'wholeWord',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
htmlFor={wholeWordId}
|
|
||||||
aria-label={t('search_whole_word')}
|
|
||||||
>
|
|
||||||
W
|
|
||||||
</label>
|
|
||||||
</Tooltip>
|
|
||||||
</InputGroup.Button>
|
|
||||||
|
|
||||||
<InputGroup.Button>
|
<OLTooltip
|
||||||
<Tooltip
|
id="search-whole-word"
|
||||||
id="search-within-selection"
|
description={t('search_whole_word')}
|
||||||
description={t('search_within_selection')}
|
>
|
||||||
|
<label
|
||||||
|
className={classnames(
|
||||||
|
'btn btn-sm btn-default ol-cm-search-input-button',
|
||||||
|
{
|
||||||
|
checked: query.wholeWord,
|
||||||
|
focused: activeSearchOption === 'wholeWord',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
htmlFor={wholeWordId}
|
||||||
|
aria-label={t('search_whole_word')}
|
||||||
>
|
>
|
||||||
<label
|
W
|
||||||
className={classnames(
|
</label>
|
||||||
'btn btn-sm btn-default ol-cm-search-input-button',
|
</OLTooltip>
|
||||||
{
|
<OLTooltip
|
||||||
checked: !!query.scope,
|
id="search-within-selection"
|
||||||
focused: activeSearchOption === 'withinSelection',
|
description={t('search_within_selection')}
|
||||||
}
|
>
|
||||||
)}
|
<label
|
||||||
htmlFor={withinSelectionId}
|
className={classnames(
|
||||||
aria-label={t('search_within_selection')}
|
'btn btn-sm btn-default ol-cm-search-input-button',
|
||||||
>
|
{
|
||||||
<Icon type="align-left" fw />
|
checked: !!query.scope,
|
||||||
</label>
|
focused: activeSearchOption === 'withinSelection',
|
||||||
</Tooltip>
|
}
|
||||||
</InputGroup.Button>
|
)}
|
||||||
</InputGroup>
|
htmlFor={withinSelectionId}
|
||||||
|
aria-label={t('search_within_selection')}
|
||||||
|
>
|
||||||
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={<Icon type="align-left" fw />}
|
||||||
|
bs5={<MaterialIcon type="format_align_left" />}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</OLTooltip>
|
||||||
|
</span>
|
||||||
|
|
||||||
{showReplace && (
|
{showReplace && (
|
||||||
<InputGroup
|
<span className="ol-cm-search-input-group ol-cm-search-replace-input">
|
||||||
bsSize="small"
|
<OLFormControl
|
||||||
className="ol-cm-search-input-group ol-cm-search-replace-input"
|
ref={handleReplaceRef}
|
||||||
>
|
|
||||||
<FormControl
|
|
||||||
type="text"
|
type="text"
|
||||||
name="replace"
|
name="replace"
|
||||||
placeholder={t('search_replace_with')}
|
placeholder={t('search_replace_with')}
|
||||||
|
@ -346,11 +342,10 @@ const CodeMirrorSearchForm: FC = () => {
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleReplaceKeyDown}
|
onKeyDown={handleReplaceKeyDown}
|
||||||
className="ol-cm-search-form-input"
|
className="ol-cm-search-form-input"
|
||||||
bsSize="small"
|
size="sm"
|
||||||
inputRef={handleReplaceRef}
|
|
||||||
aria-label={t('search_command_replace')}
|
aria-label={t('search_command_replace')}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="ol-cm-search-hidden-inputs">
|
<div className="ol-cm-search-hidden-inputs">
|
||||||
|
@ -404,27 +399,51 @@ const CodeMirrorSearchForm: FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ol-cm-search-form-group ol-cm-search-next-previous">
|
<div className="ol-cm-search-form-group ol-cm-search-next-previous">
|
||||||
<ButtonGroup className="ol-cm-search-form-button-group">
|
<OLButtonGroup className="ol-cm-search-form-button-group">
|
||||||
<Button
|
<OLButton
|
||||||
type="button"
|
variant="secondary"
|
||||||
bsSize="small"
|
size="sm"
|
||||||
onClick={() => findPrevious(view)}
|
onClick={() => findPrevious(view)}
|
||||||
>
|
>
|
||||||
<Icon
|
<BootstrapVersionSwitcher
|
||||||
type="chevron-up"
|
bs3={
|
||||||
fw
|
<Icon
|
||||||
accessibilityLabel={t('search_previous')}
|
type="chevron-up"
|
||||||
|
fw
|
||||||
|
accessibilityLabel={t('search_previous')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
bs5={
|
||||||
|
<MaterialIcon
|
||||||
|
type="keyboard_arrow_up"
|
||||||
|
accessibilityLabel={t('search_previous')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</OLButton>
|
||||||
|
|
||||||
<Button type="button" bsSize="small" onClick={() => findNext(view)}>
|
<OLButton
|
||||||
<Icon
|
variant="secondary"
|
||||||
type="chevron-down"
|
size="sm"
|
||||||
fw
|
onClick={() => findNext(view)}
|
||||||
accessibilityLabel={t('search_next')}
|
>
|
||||||
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={
|
||||||
|
<Icon
|
||||||
|
type="chevron-down"
|
||||||
|
fw
|
||||||
|
accessibilityLabel={t('search_next')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
bs5={
|
||||||
|
<MaterialIcon
|
||||||
|
type="keyboard_arrow_down"
|
||||||
|
accessibilityLabel={t('search_next')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</OLButton>
|
||||||
</ButtonGroup>
|
</OLButtonGroup>
|
||||||
|
|
||||||
{position !== null && (
|
{position !== null && (
|
||||||
<div className="ol-cm-search-form-position">
|
<div className="ol-cm-search-form-position">
|
||||||
|
@ -437,36 +456,29 @@ const CodeMirrorSearchForm: FC = () => {
|
||||||
|
|
||||||
{showReplace && (
|
{showReplace && (
|
||||||
<div className="ol-cm-search-form-group ol-cm-search-replace-buttons">
|
<div className="ol-cm-search-form-group ol-cm-search-replace-buttons">
|
||||||
<Button
|
<OLButton
|
||||||
type="button"
|
variant="secondary"
|
||||||
bsSize="small"
|
size="sm"
|
||||||
onClick={() => replaceNext(view)}
|
onClick={() => replaceNext(view)}
|
||||||
>
|
>
|
||||||
{t('search_replace')}
|
{t('search_replace')}
|
||||||
</Button>
|
</OLButton>
|
||||||
|
|
||||||
<Button
|
<OLButton
|
||||||
type="button"
|
variant="secondary"
|
||||||
bsSize="small"
|
size="sm"
|
||||||
onClick={() => replaceAll(view)}
|
onClick={() => replaceAll(view)}
|
||||||
>
|
>
|
||||||
{t('search_replace_all')}
|
{t('search_replace_all')}
|
||||||
</Button>
|
</OLButton>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ol-cm-search-form-close">
|
<div className="ol-cm-search-form-close">
|
||||||
<Tooltip id="search-close" description={<>{t('close')} (Esc)</>}>
|
<OLTooltip id="search-close" description={<>{t('close')} (Esc)</>}>
|
||||||
<button
|
<OLCloseButton onClick={() => closeSearchPanel(view)} />
|
||||||
className="close"
|
</OLTooltip>
|
||||||
onClick={() => closeSearchPanel(view)}
|
|
||||||
type="button"
|
|
||||||
aria-label={t('close')}
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|
|
@ -257,21 +257,29 @@ export const search = () => {
|
||||||
|
|
||||||
const searchFormTheme = EditorView.theme({
|
const searchFormTheme = EditorView.theme({
|
||||||
'.ol-cm-search-form': {
|
'.ol-cm-search-form': {
|
||||||
padding: '10px',
|
'--ol-cm-search-form-gap': '10px',
|
||||||
|
'--ol-cm-search-form-button-margin': '3px',
|
||||||
|
padding: 'var(--ol-cm-search-form-gap)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: 'var(--ol-cm-search-form-gap)',
|
||||||
background: 'var(--ol-blue-gray-1)',
|
background: 'var(--neutral-20)',
|
||||||
'--ol-cm-search-form-focus-shadow':
|
'--ol-cm-search-form-focus-shadow':
|
||||||
'inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(102 175 233 / 60%)',
|
'inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(102 175 233 / 60%)',
|
||||||
'--ol-cm-search-form-error-shadow':
|
'--ol-cm-search-form-error-shadow':
|
||||||
'inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px var(--input-shadow-danger-color)',
|
'inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px var(--red-50)',
|
||||||
containerType: 'inline-size',
|
containerType: 'inline-size',
|
||||||
},
|
},
|
||||||
|
'&.bootstrap-5 .ol-cm-search-form': {
|
||||||
|
'--ol-cm-search-form-gap': 'var(--spacing-05)',
|
||||||
|
'--ol-cm-search-form-button-margin': 'var(--spacing-02)',
|
||||||
|
'--input-border': 'var(--border-primary)',
|
||||||
|
'--input-border-focus': 'var(--border-active)',
|
||||||
|
},
|
||||||
'.ol-cm-search-controls': {
|
'.ol-cm-search-controls': {
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: 'auto auto',
|
gridTemplateColumns: 'auto auto',
|
||||||
gridTemplateRows: 'auto auto',
|
gridTemplateRows: 'auto auto',
|
||||||
gap: '10px',
|
gap: 'var(--ol-cm-search-form-gap)',
|
||||||
},
|
},
|
||||||
'@container (max-width: 450px)': {
|
'@container (max-width: 450px)': {
|
||||||
'.ol-cm-search-controls': {
|
'.ol-cm-search-controls': {
|
||||||
|
@ -280,12 +288,12 @@ const searchFormTheme = EditorView.theme({
|
||||||
},
|
},
|
||||||
'.ol-cm-search-form-row': {
|
'.ol-cm-search-form-row': {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: 'var(--ol-cm-search-form-gap)',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
'.ol-cm-search-form-group': {
|
'.ol-cm-search-form-group': {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: 'var(--ol-cm-search-form-gap)',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
'.ol-cm-search-input-group': {
|
'.ol-cm-search-input-group': {
|
||||||
|
@ -294,6 +302,8 @@ const searchFormTheme = EditorView.theme({
|
||||||
background: 'white',
|
background: 'white',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: '25em',
|
maxWidth: '25em',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
'& input[type="text"]': {
|
'& input[type="text"]': {
|
||||||
background: 'none',
|
background: 'none',
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
|
@ -303,18 +313,18 @@ const searchFormTheme = EditorView.theme({
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
},
|
},
|
||||||
'& .btn.btn': {
|
'& .btn.btn': {
|
||||||
background: 'var(--ol-blue-gray-0)',
|
background: 'var(--neutral-10)',
|
||||||
color: 'var(--ol-blue-gray-3)',
|
color: 'var(--neutral-60)',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
height: '2em',
|
height: '2em',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: '2em',
|
width: '2em',
|
||||||
marginRight: '3px',
|
marginRight: 'var(--ol-cm-search-form-button-margin)',
|
||||||
'&.checked': {
|
'&.checked': {
|
||||||
color: '#fff',
|
color: 'var(--white)',
|
||||||
backgroundColor: 'var(--ol-blue)',
|
backgroundColor: 'var(--blue-50)',
|
||||||
},
|
},
|
||||||
'&:active': {
|
'&:active': {
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
|
@ -331,7 +341,7 @@ const searchFormTheme = EditorView.theme({
|
||||||
boxShadow: 'var(--ol-cm-search-form-error-shadow)',
|
boxShadow: 'var(--ol-cm-search-form-error-shadow)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'.input-group .ol-cm-search-form-input': {
|
'.ol-cm-search-form-input': {
|
||||||
border: 'none',
|
border: 'none',
|
||||||
},
|
},
|
||||||
'.ol-cm-search-input-button': {
|
'.ol-cm-search-input-button': {
|
||||||
|
@ -355,7 +365,9 @@ const searchFormTheme = EditorView.theme({
|
||||||
left: '-10000px',
|
left: '-10000px',
|
||||||
},
|
},
|
||||||
'.ol-cm-search-form-close': {
|
'.ol-cm-search-form-close': {
|
||||||
flex: 1,
|
marginLeft: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'start',
|
||||||
},
|
},
|
||||||
'.ol-cm-search-replace-input': {
|
'.ol-cm-search-replace-input': {
|
||||||
order: 3,
|
order: 3,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { EditorView } from '@codemirror/view'
|
||||||
import { Annotation, Compartment, TransactionSpec } from '@codemirror/state'
|
import { Annotation, Compartment, TransactionSpec } from '@codemirror/state'
|
||||||
import { syntaxHighlighting } from '@codemirror/language'
|
import { syntaxHighlighting } from '@codemirror/language'
|
||||||
import { classHighlighter } from './class-highlighter'
|
import { classHighlighter } from './class-highlighter'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
const optionsThemeConf = new Compartment()
|
const optionsThemeConf = new Compartment()
|
||||||
const selectedThemeConf = new Compartment()
|
const selectedThemeConf = new Compartment()
|
||||||
|
@ -16,6 +17,7 @@ type Options = {
|
||||||
fontFamily: FontFamily
|
fontFamily: FontFamily
|
||||||
lineHeight: LineHeight
|
lineHeight: LineHeight
|
||||||
overallTheme: OverallTheme
|
overallTheme: OverallTheme
|
||||||
|
bootstrapVersion: 3 | 5
|
||||||
}
|
}
|
||||||
|
|
||||||
export const theme = (options: Options) => [
|
export const theme = (options: Options) => [
|
||||||
|
@ -67,13 +69,17 @@ const createThemeFromOptions = ({
|
||||||
fontFamily = 'monaco',
|
fontFamily = 'monaco',
|
||||||
lineHeight = 'normal',
|
lineHeight = 'normal',
|
||||||
overallTheme = '',
|
overallTheme = '',
|
||||||
|
bootstrapVersion = 3,
|
||||||
}: Options) => {
|
}: Options) => {
|
||||||
/**
|
/**
|
||||||
* Theme styles that depend on settings.
|
* Theme styles that depend on settings.
|
||||||
*/
|
*/
|
||||||
return [
|
return [
|
||||||
EditorView.editorAttributes.of({
|
EditorView.editorAttributes.of({
|
||||||
class: overallTheme === '' ? 'overall-theme-dark' : 'overall-theme-light',
|
class: classNames(
|
||||||
|
overallTheme === '' ? 'overall-theme-dark' : 'overall-theme-light',
|
||||||
|
'bootstrap-' + bootstrapVersion
|
||||||
|
),
|
||||||
style: Object.entries({
|
style: Object.entries({
|
||||||
'--font-size': `${fontSize}px`,
|
'--font-size': `${fontSize}px`,
|
||||||
'--source-font-family': fontFamilies[fontFamily]?.join(', '),
|
'--source-font-family': fontFamilies[fontFamily]?.join(', '),
|
||||||
|
|
|
@ -66,6 +66,7 @@ import { useRangesContext } from '@/features/review-panel-new/context/ranges-con
|
||||||
import { updateRanges } from '@/features/source-editor/extensions/ranges'
|
import { updateRanges } from '@/features/source-editor/extensions/ranges'
|
||||||
import { useThreadsContext } from '@/features/review-panel-new/context/threads-context'
|
import { useThreadsContext } from '@/features/review-panel-new/context/threads-context'
|
||||||
import { useHunspell } from '@/features/source-editor/hooks/use-hunspell'
|
import { useHunspell } from '@/features/source-editor/hooks/use-hunspell'
|
||||||
|
import { isBootstrap5 } from '@/features/utils/bootstrap-5'
|
||||||
|
|
||||||
function useCodeMirrorScope(view: EditorView) {
|
function useCodeMirrorScope(view: EditorView) {
|
||||||
const { fileTreeData } = useFileTreeData()
|
const { fileTreeData } = useFileTreeData()
|
||||||
|
@ -141,6 +142,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||||
lineHeight,
|
lineHeight,
|
||||||
overallTheme,
|
overallTheme,
|
||||||
editorTheme,
|
editorTheme,
|
||||||
|
bootstrapVersion: 3 as 3 | 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -150,6 +152,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||||
lineHeight,
|
lineHeight,
|
||||||
overallTheme,
|
overallTheme,
|
||||||
editorTheme,
|
editorTheme,
|
||||||
|
bootstrapVersion: isBootstrap5() ? 5 : 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
view.dispatch(
|
view.dispatch(
|
||||||
|
@ -158,6 +161,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||||
fontSize,
|
fontSize,
|
||||||
lineHeight,
|
lineHeight,
|
||||||
overallTheme,
|
overallTheme,
|
||||||
|
bootstrapVersion: themeRef.current.bootstrapVersion,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
import { CloseButton, CloseButtonProps } from 'react-bootstrap-5'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { forwardRef } from 'react'
|
||||||
|
|
||||||
|
const OLCloseButton = forwardRef<HTMLButtonElement, CloseButtonProps>(
|
||||||
|
(props, ref) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const bs3CloseButtonProps: React.ButtonHTMLAttributes<HTMLButtonElement> = {
|
||||||
|
className: classNames('close', props.className),
|
||||||
|
onClick: props.onClick,
|
||||||
|
onMouseOver: props.onMouseOver,
|
||||||
|
onMouseOut: props.onMouseOut,
|
||||||
|
|
||||||
|
'aria-label': t('close'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={
|
||||||
|
<button {...bs3CloseButtonProps}>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
bs5={<CloseButton ref={ref} {...props} />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
OLCloseButton.displayName = 'OLCloseButton'
|
||||||
|
|
||||||
|
export default OLCloseButton
|
|
@ -1,4 +1,4 @@
|
||||||
import { forwardRef, ComponentProps } from 'react'
|
import { forwardRef, ComponentProps, useCallback } from 'react'
|
||||||
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
|
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
|
||||||
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
|
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
|
||||||
import BS3FormControl from '@/features/ui/components/bootstrap-3/form/form-control'
|
import BS3FormControl from '@/features/ui/components/bootstrap-3/form/form-control'
|
||||||
|
@ -15,8 +15,22 @@ const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const { bs3Props, ...rest } = props
|
const { bs3Props, ...rest } = props
|
||||||
|
|
||||||
|
// Use a callback so that the ref passed to the BS3 FormControl is stable
|
||||||
|
const bs3InputRef = useCallback(
|
||||||
|
(inputElement: HTMLInputElement) => {
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(inputElement)
|
||||||
|
} else if (ref) {
|
||||||
|
ref.current = inputElement
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ref]
|
||||||
|
)
|
||||||
|
|
||||||
let bs3FormControlProps: BS3FormControlProps = {
|
let bs3FormControlProps: BS3FormControlProps = {
|
||||||
|
inputRef: bs3InputRef,
|
||||||
componentClass: rest.as,
|
componentClass: rest.as,
|
||||||
|
bsSize: rest.size,
|
||||||
id: rest.id,
|
id: rest.id,
|
||||||
name: rest.name,
|
name: rest.name,
|
||||||
className: rest.className,
|
className: rest.className,
|
||||||
|
@ -37,13 +51,6 @@ const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
|
||||||
onFocus: rest.onFocus as BS3FormControlProps['onFocus'],
|
onFocus: rest.onFocus as BS3FormControlProps['onFocus'],
|
||||||
onBlur: rest.onBlur as BS3FormControlProps['onBlur'],
|
onBlur: rest.onBlur as BS3FormControlProps['onBlur'],
|
||||||
onInvalid: rest.onInvalid as BS3FormControlProps['onInvalid'],
|
onInvalid: rest.onInvalid as BS3FormControlProps['onInvalid'],
|
||||||
inputRef: (inputElement: HTMLInputElement) => {
|
|
||||||
if (typeof ref === 'function') {
|
|
||||||
ref(inputElement)
|
|
||||||
} else if (ref) {
|
|
||||||
ref.current = inputElement
|
|
||||||
}
|
|
||||||
},
|
|
||||||
prepend: rest.prepend,
|
prepend: rest.prepend,
|
||||||
append: rest.append,
|
append: rest.append,
|
||||||
...bs3Props,
|
...bs3Props,
|
||||||
|
|
Loading…
Reference in a new issue