Skip to content

Commit 0e6dc38

Browse files
committed
Extract PickerInput component
1 parent 00a5947 commit 0e6dc38

File tree

5 files changed

+142
-85
lines changed

5 files changed

+142
-85
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@import (reference) '../colours.less';
2+
3+
.picker-input {
4+
background: #FFFFFF;
5+
overflow-y: auto;
6+
7+
> label {
8+
display: block;
9+
position: relative;
10+
border-bottom: 1px solid @beige-shadow;
11+
12+
&:last-child {
13+
border-bottom: none;
14+
}
15+
16+
> span {
17+
display: block;
18+
cursor: pointer;
19+
20+
&:hover {
21+
background: @beige-back;
22+
}
23+
}
24+
25+
/* class name and type included to ensure high specificity */
26+
> input.picker-hidden[type='radio'] {
27+
margin: 0;
28+
position: absolute;
29+
left: -10px;
30+
width: 1px;
31+
height: 100%;
32+
box-sizing: border-box;
33+
34+
&:focus-visible {
35+
outline: none;
36+
37+
+ span {
38+
box-shadow: inset 0 0 0 2px #005FCC;
39+
}
40+
}
41+
42+
&:checked + span {
43+
background: hsl(200, 30%, 84%);
44+
}
45+
}
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React, { useCallback, memo } from 'react';
2+
import classNames from 'classnames';
3+
import './PickerInput.less';
4+
5+
interface OptionT {
6+
value: string;
7+
label: React.ReactNode;
8+
}
9+
10+
interface PropsT {
11+
className?: string;
12+
name: string;
13+
value: string;
14+
onChange: (value: string) => void;
15+
options: OptionT[];
16+
}
17+
18+
export default memo(({
19+
className,
20+
name,
21+
value,
22+
onChange,
23+
options,
24+
}: PropsT) => {
25+
const changeHandler = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
26+
if (e.target.checked) {
27+
onChange?.(e.target.value);
28+
}
29+
}, [onChange]);
30+
31+
return (
32+
<div className={classNames('picker-input', className)}>
33+
{ options.map((o) => (
34+
<label key={o.value}>
35+
<input
36+
className="picker-hidden"
37+
name={name}
38+
type="radio"
39+
value={o.value}
40+
checked={value === o.value}
41+
onChange={changeHandler}
42+
autoComplete="off"
43+
/>
44+
<span>
45+
{ o.label }
46+
</span>
47+
</label>
48+
)) }
49+
</div>
50+
);
51+
});

src/frontend/src/components/form.less

+3-64
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
display: block;
1111
}
1212

13-
input, .prefixed-input {
13+
input, .prefixed-input, .picker-input {
1414
display: block;
1515
margin: 4px 0 16px;
1616
padding: 8px;
@@ -67,69 +67,8 @@
6767
}
6868
}
6969

70-
.picker {
71-
margin: 4px 0 16px;
72-
background: #FFFFFF;
73-
border: 1px solid @beige-shadow;
74-
overflow-y: auto;
75-
width: 100%;
76-
max-height: 18rem;
77-
box-sizing: border-box;
78-
79-
label {
80-
display: block;
81-
position: relative;
82-
border-bottom: 1px solid @beige-shadow;
83-
84-
&:last-child {
85-
border-bottom: none;
86-
}
87-
}
88-
89-
input {
90-
margin: 0;
91-
padding: 0;
92-
position: absolute;
93-
left: -100px;
94-
height: 100%;
95-
box-sizing: border-box;
96-
}
97-
98-
.row {
99-
display: block;
100-
cursor: pointer;
101-
}
102-
103-
.row:hover {
104-
background: @beige-back;
105-
}
106-
107-
input:focus-visible {
108-
outline: none;
109-
}
110-
111-
input:focus-visible + .row {
112-
outline: solid blue;
113-
outline: auto 5px -webkit-focus-ring-color;
114-
}
115-
116-
input:checked + .row {
117-
background: hsl(200, 30%, 84%);
118-
}
119-
}
120-
121-
.theme-row {
122-
padding: 4px 8px 4px 24px;
123-
display: grid;
124-
grid-template-columns: auto 3.5rem 3.5rem 3.5rem;
125-
align-items: center;
126-
127-
.preview {
128-
font-size: 2.5rem;
129-
vertical-align: middle;
130-
display: inline-block;
131-
text-align: center;
132-
}
70+
.picker-input {
71+
padding: 0;
13372
}
13473

13574
.prefixed-input {

src/frontend/src/components/retro-settings/SettingsForm.less

+23
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,27 @@
22

33
form.retro-settings {
44
.mixin-form-styling;
5+
6+
.theme {
7+
max-height: 18rem;
8+
}
9+
10+
.theme-row {
11+
padding: 4px 8px;
12+
display: grid;
13+
grid-template-columns: auto 3.5rem 3.5rem 3.5rem;
14+
align-items: center;
15+
16+
.name {
17+
padding-left: 16px;
18+
font-size: 1rem;
19+
}
20+
21+
.preview {
22+
font-size: 2.5rem;
23+
vertical-align: middle;
24+
display: inline-block;
25+
text-align: center;
26+
}
27+
}
528
}

src/frontend/src/components/retro-settings/SettingsForm.tsx

+18-21
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Retro } from 'refacto-entities';
33
import { actionsSyncedCallback } from 'shared-reducer-frontend';
44
import type { RetroDispatch } from '../../api/RetroTracker';
55
import Input from '../common/Input';
6+
import PickerInput from '../common/PickerInput';
67
import SlugEntry from '../retro-create/SlugEntry';
78
import Alert from '../common/Alert';
89
import useSubmissionCallback from '../../hooks/useSubmissionCallback';
@@ -49,25 +50,17 @@ export default memo(({
4950
]);
5051
}, [name, slug, alwaysShowAddAction, theme, dispatch, onSave]);
5152

52-
const themeChoices = getThemes().map(([value, detail]) => (
53-
<label key={value}>
54-
<Input
55-
name="theme"
56-
type="radio"
57-
value={value}
58-
selected={theme}
59-
onChange={setTheme}
60-
/>
61-
<span className="row">
62-
<span className="theme-row">
63-
<span className="name">{ detail.name }</span>
64-
<span className="preview">{ detail.icons.happy }</span>
65-
<span className="preview">{ detail.icons.meh }</span>
66-
<span className="preview">{ detail.icons.sad }</span>
67-
</span>
53+
const themeChoices = getThemes().map(([value, detail]) => ({
54+
value,
55+
label: (
56+
<span className="theme-row">
57+
<span className="name">{ detail.name }</span>
58+
<span className="preview">{ detail.icons.happy }</span>
59+
<span className="preview">{ detail.icons.meh }</span>
60+
<span className="preview">{ detail.icons.sad }</span>
6861
</span>
69-
</label>
70-
));
62+
),
63+
}));
7164

7265
return (
7366
<form onSubmit={handleSubmit} className="retro-settings">
@@ -104,9 +97,13 @@ export default memo(({
10497
</label>
10598
<fieldset>
10699
<legend>Theme</legend>
107-
<div className="picker">
108-
{ themeChoices }
109-
</div>
100+
<PickerInput
101+
className="theme"
102+
name="theme"
103+
value={theme}
104+
onChange={setTheme}
105+
options={themeChoices}
106+
/>
110107
</fieldset>
111108
{ sending ? (<div className="sending">&hellip;</div>) : (
112109
<button type="submit" title="Save Changes">

0 commit comments

Comments
 (0)