1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
/*
* Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
*
* This file is part of Sophie.
*
* Sophie is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import FilledInput from '@mui/material/FilledInput';
import { styled } from '@mui/material/styles';
import { Service } from '@sophie/shared';
import { autorun } from 'mobx';
import { observer } from 'mobx-react-lite';
import React, { useCallback, useEffect, useState } from 'react';
import GoAdornment from './GoAdornment';
import LocationOverlayInput from './LocationOverlayInput';
import UrlAdornment from './UrlAdornment';
import UrlOverlay from './UrlOverlay';
import splitUrl from './splitUrl';
const LocationTextFieldRoot = styled(FilledInput, {
name: 'LocationTextField',
slot: 'Root',
})(({ theme }) => ({
paddingLeft: 12,
paddingRight: 12,
borderRadius: 20,
'&.Mui-focused': {
outline: `2px solid ${theme.palette.primary.main}`,
},
}));
function LocationTextField({
service,
}: {
service: Service | undefined;
}): JSX.Element {
const [inputFocused, setInputFocused] = useState(false);
const [changed, setChanged] = useState(false);
const [value, setValue] = useState('');
const resetValue = useCallback(() => {
setValue(service?.currentUrl ?? '');
setChanged(false);
}, [service, setChanged, setValue]);
useEffect(
() =>
autorun(() => {
resetValue();
}),
[resetValue],
);
const inputRefCallback = useCallback(
(input: HTMLInputElement) => {
setInputFocused(
document.activeElement !== null && document.activeElement === input,
);
},
[setInputFocused],
);
const splitResult = splitUrl(service?.currentUrl);
return (
<LocationTextFieldRoot
inputComponent={LocationOverlayInput}
inputProps={{
'aria-label': 'Location',
spellCheck: false,
overlayVisible: !inputFocused && !changed,
overlay: <UrlOverlay splitResult={splitResult} />,
}}
inputRef={inputRefCallback}
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
onChange={({ target: { value: newValue } }) => {
setValue(newValue);
setChanged(true);
}}
onKeyUp={(event) => {
if (event.key === 'Escape') {
resetValue();
event.preventDefault();
}
}}
size="small"
fullWidth
hiddenLabel
disableUnderline
startAdornment={
<UrlAdornment changed={changed} splitResult={splitResult} />
}
endAdornment={changed ? <GoAdornment /> : undefined}
value={value}
/>
);
}
export default observer(LocationTextField);
|