использую библиотеку rc-slider, к сожалению она не предлагает возможности...
const Checkbox = forwardRef(({ label, ...props }, ref) =>
<label>
<input type="checkbox" ref={ref} {...props} />
{label}
</label>
);
function CheckboxGroup({
items,
label = item => item,
selected,
setSelected,
}) {
const onChange = ({ target: { checked, dataset: { index } } }) =>
setSelected(selected => checked
? [ ...selected, items[index] ]
: selected.filter(n => n !== items[index])
);
const allSelectedRef = useRef();
const onAllSelectedChange = ({ target: { checked } }) =>
setSelected(checked ? [...items] : []);
useEffect(() => {
const isAllSelected = items.length === selected.length;
allSelectedRef.current.checked = isAllSelected;
allSelectedRef.current.indeterminate = !isAllSelected && !!selected.length;
}, [ selected ]);
return (
<div className="checkbox-group">
<Checkbox
label="SELECT ALL"
defaultChecked={false}
onChange={onAllSelectedChange}
ref={allSelectedRef}
/>
{items.map((n, i) => (
<Checkbox
label={label(n)}
data-index={i}
checked={selected.includes(n)}
onChange={onChange}
/>
))}
</div>
);
}
кликая на один список открывается и второй
const defaults = {
index: 0,
length: 0,
step: 1,
};
function Typewriter({ strings, delay }) {
const [ state, setState ] = useState(null);
useEffect(() => {
setState(() => ({...defaults}));
}, [ strings ]);
useEffect(() => {
const timeoutId = setTimeout(setState, delay, ({...state}) => {
state.length += state.step;
if (state.length === strings[state.index].length) {
state.step = -1;
} else if (state.length === 0) {
state.step = 1;
state.index = (state.index + 1) % strings.length;
}
return state;
});
return () => clearTimeout(timeoutId);
});
return <div>{strings?.[state?.index]?.slice(0, state?.length)}</div>;
}
li
.const defaultEdit = {
index: -1,
value: '',
};
function App() {
const [ notes, setNotes ] = useState([...'12345']);
const [ edit, setEdit ] = useState(defaultEdit);
const onClick = ({ currentTarget: { dataset: { index } } }) =>
setEdit({ index: +index, value: notes[index] });
const onChange = ({ target: { value } }) =>
setEdit(edit => ({ ...edit, value }));
const onBlur = () => {
setNotes(notes => notes.map((n, i) => i === edit.index ? edit.value : n));
setEdit(defaultEdit);
};
return (
<div>
<ul>{notes.map((n, i) => (
<li data-index={i} onClick={onClick}>
{n}
</li>))}
</ul>
<input value={edit.value} onChange={onChange} onBlur={onBlur} />
</div>
);
}
Объект менять не нужно.
const SERIES = [
{
comment: '1 сезон',
folder: [
{ comment: '1 серия' },
{ comment: '2 серия' },
],
},
{
comment: '2 сезон',
folder: [
{ comment: '1 серия' },
{ comment: '2 серия' },
{ comment: '3 серия' },
],
},
];
const ItemsList = ({ header, items, active, onChange }) => (
<div>
<h3>{header}</h3>
<ul className="items">
{items.map(({ comment }, i) => (
<li
className={`item ${active === i ? 'active' : ''}`}
onClick={() => onChange?.(i !== active ? i : -1)}
>
{comment}
</li>
))}
</ul>
</div>
);
function App() {
const [ iSeason, setActiveSeason ] = useState(-1);
const [ iEpisode, setActiveEpisode ] = useState(-1);
const season = SERIES[iSeason];
const episode = season?.folder[iEpisode];
useEffect(() => {
setActiveEpisode(-1);
}, [ iSeason ]);
return (
<div>
<ItemsList
header="сезоны"
items={SERIES}
active={iSeason}
onChange={setActiveSeason}
/>
{season && <ItemsList
header="серии"
active={iEpisode}
onChange={setActiveEpisode}
items={season.folder}
/>}
{episode && <div>Выбрали: {season.comment}, {episode.comment}</div>}
</div>
);
}
useEffect(() => setValue(props.value), [ props.value ]);
const rowspan = shards => shards.reduce((acc, n) => acc + n.jobs.length, 0);
<table>
<thead>
<tr>{HEADERS.map(n => <th>{n}</th>)}</tr>
</thead>
<tbody>{
DATA.flatMap(({ cloud, shards }) =>
shards.flatMap((shard, iShard) =>
shard.jobs.map((job, iJob) =>
<tr>
{!iShard && !iJob && <td rowSpan={rowspan(shards)}>{cloud}</td>}
{!iJob && <td rowSpan={shard.jobs.length}>{shard.repository}</td>}
{!iJob && <td rowSpan={shard.jobs.length}>{shard.repository_size_gb}</td>}
<td>{job.job}</td>
<td>{job.job_description}</td>
<td>{job.backup_name}</td>
<td>{job.backup_size_gb}</td>
</tr>
)))}
</tbody>
</table>
onClickCapture
: A version ofonClick
that fires in the capture phase.
// это массив ваших "точек"
const locations = [
{ lat: ..., lng: ... },
{ lat: ..., lng: ... },
...
];
const onLoad = map => {
const bounds = new window.google.maps.LatLngBounds();
locations.forEach(n => bounds.extend(n));
map.fitBounds(bounds);
};
<GoogleMap
onLoad={onLoad}
...
>
{locations.map(n => <Marker position={n} />)}
</GoogleMap>
const useChunked = (data, chunkSize) =>
useMemo(
() => Array.prototype.reduce.call(
data,
(acc, n, i) => ((acc[i / chunkSize | 0] ??= []).push(n), acc),
[]
),
[ data, chunkSize ]
);
const ChunkedListItem = ({ item }) =>
<pre>{JSON.stringify(item, null, 2)}</pre>;
const ChunkedList = ({ items, inRow, Item = ChunkedListItem }) => {
const rows = useChunked(items, inRow);
return (
<div className="chunked-list">
{rows.map(row => (
<ul>
{row.map(n => <li><Item item={n} /></li>)}
</ul>
))}
</div>
);
}
const [ items, setItems ] = useState([]);
const [ src, setSrc ] = useState('');
const onChange = e => {
setSrc(e.target.value);
};
const onClick = () => {
setSrc('');
setItems([
...items,
{
id: 1 + Math.max(0, ...items.map(n => n.id)),
src,
},
]);
};
const onSortEnd = (iOld, iNew) => {
setItems(([...items]) => (
items.splice(iNew, 0, items.splice(iOld, 1)[0]),
items
));
};
<div>
<input value={src} onChange={onChange} />
<button onClick={onClick}>add</button>
</div>
<SortableList onSortEnd={onSortEnd}>
{items.map(n => (
<SortableItem key={n.id}>
<img src={n.src} />
</SortableItem>
))}
</SortableList>
const languages = [
{ name: 'Ru', Component: AppRu },
{ name: 'Eng', Component: AppEng },
...
];
const [ language, setLanguage ] = useState(0);
const { name, Component } = languages[language];
const onClick = () => setLanguage((language + 1) % languages.length);
<button onClick={onClick}>{name}</button>
<Component />
<div className={classes.bar1}></div>
bar1
есть, это хорошо. А почему нет bar
? Плохо....Object.fromEntries(Array.from({ length: 12 }, (_, i) => [
`bar${i + 1}`,
{
transform: `rotate(${i * 30}deg) translate(0, -130%)`,
animationDelay: `${i * 0.1 - 1.2}s`,
},
])),
<div className={`${classes.loader} js--loader`}>
{Array.from(
{ length: 12 },
(_, i) => <div className={`${classes.bar} ${classes[`bar${i + 1}`]}`}></div>
)}
</div>
Возникает проблема, что нужно нажать 2 раза на подсказку, чтобы она исчезла.
const onChange = ({ target: { value } }) => {
setVal(value);
setPrintVariables(value.length > 1
? arrayVariables.filter(n => n.includes(value))
: []
);
};
<input value={val} onChange={onChange} />
.
здесь должны быть $
:'&:checked + .todo-item__span::before'
'&:checked + .todo-item__span::after'
'content': '',
'content': '""',
'todo-item--checked': { 'border': '2px solid #2196f3', ' box-shadow': '0 0 10px 3px #2196f3', },
'todo-item--checked .todo-item__description': { 'color': '#6495EDFF', },
'todo-item--checked': {
'border': '2px solid #2196f3',
'box-shadow': '0 0 10px 3px #2196f3',
'& $todo-item__description': {
'color': '#6495EDFF',
},
},
<div className={` ${classes['todo-item__description']} ${classes['todo-item--checked .todo-item__description']} `}>
<div className={classes['todo-item__description']}>