mirror of
https://github.com/mue/mue.git
synced 2026-07-02 12:43:35 +02:00
fix(search): Add preview to options
This commit is contained in:
@@ -2,7 +2,7 @@ import variables from 'config/variables';
|
||||
import { useState, useCallback, memo, useMemo } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { MdRefresh, MdEdit } from 'react-icons/md';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import clsx from 'clsx';
|
||||
|
||||
@@ -15,15 +15,6 @@ const buttonStyles = {
|
||||
transitions: 'transition-colors duration-200',
|
||||
};
|
||||
|
||||
const inputStyles = {
|
||||
base: 'w-20 text-sm px-3 py-1.5 rounded-md font-medium text-center',
|
||||
colors: 'bg-neutral-200 hover:bg-neutral-300 focus:bg-neutral-300',
|
||||
darkMode: 'dark:bg-white/10 dark:hover:bg-white/15 dark:focus:bg-white/15',
|
||||
text: 'text-neutral-800 dark:text-white',
|
||||
focus: 'focus:outline-none focus:ring-2 focus:ring-neutral-400 dark:focus:ring-white/40',
|
||||
transitions: 'transition-colors duration-200',
|
||||
};
|
||||
|
||||
const thumbStyles = {
|
||||
base: [
|
||||
'w-5 h-5 rounded-full cursor-pointer',
|
||||
@@ -76,68 +67,15 @@ const MULTIPLIER_MARKS = {
|
||||
};
|
||||
|
||||
// Memoized value display component
|
||||
const ValueDisplay = memo(({ value, display, onChange }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const ValueDisplay = memo(({ value, display }) => (
|
||||
<span className="text-sm bg-neutral-200 dark:bg-white/15 text-neutral-800 dark:text-white px-3 py-1.5 rounded-md font-medium">
|
||||
{value}
|
||||
{display}
|
||||
</span>
|
||||
));
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsEditing(false);
|
||||
const newValue = Number(inputValue);
|
||||
if (!isNaN(newValue)) {
|
||||
onChange(newValue);
|
||||
} else {
|
||||
setInputValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleBlur();
|
||||
} else if (e.key === 'Escape') {
|
||||
setIsEditing(false);
|
||||
setInputValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={clsx(
|
||||
inputStyles.base,
|
||||
inputStyles.colors,
|
||||
inputStyles.darkMode,
|
||||
inputStyles.text,
|
||||
inputStyles.focus,
|
||||
inputStyles.transitions,
|
||||
)}
|
||||
autoFocus
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm bg-neutral-200 dark:bg-white/15 text-neutral-800 dark:text-white px-3 py-1.5 rounded-md font-medium">
|
||||
{value}
|
||||
{display}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="p-1.5 rounded-md hover:bg-neutral-300 dark:hover:bg-white/15 transition-colors duration-200"
|
||||
aria-label="Edit value"
|
||||
>
|
||||
<MdEdit className="w-4 h-4 text-neutral-600 dark:text-white/70" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
// Memoized reset button component
|
||||
const ResetButton = memo(({ onClick, defaultValue }) => (
|
||||
const ResetButton = memo(({ onClick }) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
@@ -146,11 +84,9 @@ const ResetButton = memo(({ onClick, defaultValue }) => (
|
||||
buttonStyles.darkMode,
|
||||
buttonStyles.text,
|
||||
buttonStyles.transitions,
|
||||
'group focus:outline-none focus:ring-2 focus:ring-neutral-400 dark:focus:ring-white/40',
|
||||
)}
|
||||
aria-label={`Reset to default value: ${defaultValue}`}
|
||||
>
|
||||
<MdRefresh className="w-4 h-4 mr-1.5 group-hover:rotate-180 transition-transform duration-300" />
|
||||
<MdRefresh className="w-4 h-4 mr-1.5" />
|
||||
{variables.getMessage('settings:buttons.reset')}
|
||||
</button>
|
||||
));
|
||||
@@ -258,8 +194,8 @@ function SliderComponent(props) {
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-white">{props.title}</span>
|
||||
<div className="flex items-center gap-2 justify-between w-full">
|
||||
<ValueDisplay value={value} display={props.display} onChange={handleChange} />
|
||||
<ResetButton onClick={resetItem} defaultValue={props.default} />
|
||||
<ValueDisplay value={value} display={props.display} />
|
||||
<ResetButton onClick={resetItem} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import './search.scss';
|
||||
import defaults from './options/default';
|
||||
import searchEngines from './search_engines.json';
|
||||
|
||||
function Search() {
|
||||
function Search({ isPreview }) {
|
||||
const [url, setURL] = useState('');
|
||||
const [query, setQuery] = useState('');
|
||||
const [microphone, setMicrophone] = useState(null);
|
||||
@@ -140,6 +140,29 @@ function Search() {
|
||||
|
||||
const getSuggestionsDebounced = useDebouncedCallback(getSuggestions, 100);
|
||||
|
||||
if (isPreview) {
|
||||
return (
|
||||
<div className="searchComponents">
|
||||
<div className="searchMain">
|
||||
<div className={classList}>
|
||||
<Icon currentSearch={currentSearch} setSearchDropdown={setSearchDropdown} />
|
||||
</div>
|
||||
<form className="searchBar">
|
||||
<div className={classList}>
|
||||
<MdSearch />
|
||||
</div>
|
||||
<input
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
id="searchPreviewText"
|
||||
disabled={true}
|
||||
className="previewInput"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="searchComponents">
|
||||
<div className="searchMain">
|
||||
|
||||
@@ -4,12 +4,14 @@ import { toast } from 'react-toastify';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import { Header, Row, Content, Action, PreferencesWrapper } from 'components/Layout/Settings';
|
||||
import { Hero, Preview, Controls } from 'components/Layout/Settings/Hero';
|
||||
import { Dropdown, Checkbox } from 'components/Form/Settings';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import searchEngines from '../search_engines.json';
|
||||
import defaults from './default';
|
||||
import SearchPreview from './SearchPreview';
|
||||
|
||||
function SearchOptions() {
|
||||
const [customEnabled, setCustomEnabled] = useState(false);
|
||||
@@ -148,6 +150,17 @@ function SearchOptions() {
|
||||
category="widgets"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
<Hero>
|
||||
<Preview>
|
||||
<SearchPreview />
|
||||
</Preview>
|
||||
<Controls
|
||||
title={variables.getMessage(`${SEARCH_SECTION}.title`)}
|
||||
setting="searchBar"
|
||||
category="widgets"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
</Hero>
|
||||
<PreferencesWrapper setting="searchBar" category="widgets" visibilityToggle={true}>
|
||||
<SearchEngineSelection />
|
||||
<AdditionalOptions />
|
||||
|
||||
30
src/features/search/options/SearchPreview.jsx
Normal file
30
src/features/search/options/SearchPreview.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import Search from 'features/search/Search';
|
||||
|
||||
const SearchPreview = ({ zoomLevel = 100 }) => {
|
||||
const searchEngine = localStorage.getItem('searchEngine') || 'duckduckgo';
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="crossfade">
|
||||
<motion.div
|
||||
className="absolute"
|
||||
key={`search-preview-${searchEngine}`}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: zoomLevel / 100,
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{
|
||||
opacity: { duration: 0.2 },
|
||||
scale: { duration: 0.3, ease: 'anticipate' },
|
||||
}}
|
||||
>
|
||||
<Search isPreview={true} />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
export { SearchPreview, SearchPreview as default };
|
||||
Reference in New Issue
Block a user