лучшен визуальный стиль выбранных кнопок: добавлена толстая обводка, эффект приподнятия и сохранение увеличения иконки
This commit is contained in:
parent
12737caa1c
commit
0e586e693a
@ -10,19 +10,20 @@ import StepTitle from './StepTitle';
|
||||
|
||||
interface BlockRendererProps {
|
||||
block: Block;
|
||||
onAction?: (actionType: string, actionValue: string, blockId?: string) => void;
|
||||
onAction?: (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => void;
|
||||
extraProps?: Record<string, any>;
|
||||
selectedButtonId?: string; // Новый prop для передачи ID выбранной кнопки
|
||||
}
|
||||
|
||||
const BlockRenderer: React.FC<BlockRendererProps> = ({ block, onAction, extraProps }) => {
|
||||
const BlockRenderer: React.FC<BlockRendererProps> = ({ block, onAction, extraProps, selectedButtonId }) => {
|
||||
switch (block.type) {
|
||||
case 'scrollableButtons':
|
||||
case 'gridButtons':
|
||||
const buttonBlock = block as ButtonBlock;
|
||||
if (block.type === 'scrollableButtons') {
|
||||
return <ScrollableButtonsBlock block={buttonBlock} onAction={onAction} />;
|
||||
return <ScrollableButtonsBlock block={buttonBlock} onAction={onAction} selectedButtonId={selectedButtonId} />;
|
||||
}
|
||||
return <GridButtonsBlock block={buttonBlock} onAction={onAction} isInputVisible={extraProps?.visible} />;
|
||||
return <GridButtonsBlock block={buttonBlock} onAction={onAction} isInputVisible={extraProps?.visible} selectedButtonId={selectedButtonId} />;
|
||||
case 'uploadPhoto':
|
||||
return <UploadPhotoBlock
|
||||
previewUrl={window.history.state?.usr?.previewUrl || localStorage.getItem('stickerPreviewUrl')}
|
||||
|
||||
@ -7,10 +7,11 @@ interface GridButtonsBlockProps {
|
||||
block: ButtonBlock;
|
||||
onAction?: (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => void;
|
||||
isInputVisible?: boolean;
|
||||
selectedButtonId?: string; // Новый prop для передачи ID выбранной кнопки
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const GridButtonsBlock: React.FC<GridButtonsBlockProps> = ({ block, onAction, isInputVisible }) => {
|
||||
const GridButtonsBlock: React.FC<GridButtonsBlockProps> = ({ block, onAction, isInputVisible, selectedButtonId }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const { buttons, style } = block;
|
||||
const {
|
||||
@ -45,6 +46,7 @@ const GridButtonsBlock: React.FC<GridButtonsBlockProps> = ({ block, onAction, is
|
||||
<SquareButton
|
||||
{...button}
|
||||
size={buttonSize}
|
||||
isSelected={selectedButtonId === button.id}
|
||||
onClick={() => {
|
||||
if (button.action && onAction) {
|
||||
onAction(button.action.type, button.action.value, block.id, button.id);
|
||||
|
||||
@ -6,9 +6,10 @@ import styles from './ScrollableButtonsBlock.module.css';
|
||||
interface ScrollableButtonsBlockProps {
|
||||
block: ButtonBlock;
|
||||
onAction?: (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => void;
|
||||
selectedButtonId?: string; // Новый prop для передачи ID выбранной кнопки
|
||||
}
|
||||
|
||||
const ScrollableButtonsBlock: React.FC<ScrollableButtonsBlockProps> = ({ block, onAction }) => {
|
||||
const ScrollableButtonsBlock: React.FC<ScrollableButtonsBlockProps> = ({ block, onAction, selectedButtonId }) => {
|
||||
const { buttons, style } = block;
|
||||
const { gap = 16, padding = 16, buttonSize = 150 } = style;
|
||||
|
||||
@ -29,6 +30,7 @@ const ScrollableButtonsBlock: React.FC<ScrollableButtonsBlockProps> = ({ block,
|
||||
<SquareButton
|
||||
{...button}
|
||||
size={buttonSize}
|
||||
isSelected={selectedButtonId === button.id}
|
||||
onClick={() => {
|
||||
if (button.action && onAction) {
|
||||
onAction(button.action.type, button.action.value, block.id, button.id);
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
.button {
|
||||
position: relative;
|
||||
border: none;
|
||||
border: 2px solid transparent; /* Добавляем прозрачную границу для всех кнопок */
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-small);
|
||||
cursor: pointer;
|
||||
@ -31,12 +31,41 @@
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/* Стиль для выбранной кнопки */
|
||||
.selected {
|
||||
border-color: var(--color-primary);
|
||||
border-width: 3px; /* Увеличиваем толщину с 2px до 3px */
|
||||
transform: translateY(-2px); /* Добавляем эффект приподнятия */
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* Добавляем тень как при hover */
|
||||
}
|
||||
|
||||
/* Сохраняем эффект увеличения иконки для выбранной кнопки */
|
||||
.selected .icon,
|
||||
.selected .iconImage {
|
||||
transform: scale(1.1); /* Такой же scale как при hover */
|
||||
}
|
||||
|
||||
/* Специальные стили для iOS */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
.selected {
|
||||
border-color: var(--color-primary);
|
||||
border-width: 3px;
|
||||
box-shadow: 0 0 0 3px var(--color-primary), 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* Предотвращаем двойное приподнятие при наведении на выбранную кнопку */
|
||||
.selected:hover {
|
||||
transform: translateY(-2px); /* Оставляем то же приподнятие */
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
@ -115,6 +144,12 @@
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Предотвращаем двойное увеличение иконки при наведении на выбранную кнопку */
|
||||
.selected:hover .icon,
|
||||
.selected:hover .iconImage {
|
||||
transform: scale(1.1); /* Оставляем тот же масштаб */
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -7,6 +7,7 @@ interface SquareButtonProps extends BlockButton {
|
||||
size?: number;
|
||||
onClick?: () => void;
|
||||
disabled?: boolean;
|
||||
isSelected?: boolean; // Новый prop для отображения выбранного состояния
|
||||
}
|
||||
|
||||
const SquareButton: React.FC<SquareButtonProps> = ({
|
||||
@ -19,7 +20,8 @@ const SquareButton: React.FC<SquareButtonProps> = ({
|
||||
action,
|
||||
size = 100,
|
||||
onClick,
|
||||
disabled
|
||||
disabled,
|
||||
isSelected = false // По умолчанию кнопка не выбрана
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -73,7 +75,7 @@ const SquareButton: React.FC<SquareButtonProps> = ({
|
||||
return (
|
||||
<div className={styles.buttonWrapper}>
|
||||
<button
|
||||
className={styles.button}
|
||||
className={`${styles.button} ${isSelected ? styles.selected : ''}`}
|
||||
onClick={handleClick}
|
||||
style={{
|
||||
width: size,
|
||||
@ -82,6 +84,7 @@ const SquareButton: React.FC<SquareButtonProps> = ({
|
||||
color: color || '#FFFFFF'
|
||||
}}
|
||||
disabled={disabled}
|
||||
data-selected={isSelected ? 'true' : 'false'} /* Добавляем data-атрибут для дополнительной стилизации */
|
||||
>
|
||||
{imageUrl ? (
|
||||
<img src={imageUrl} alt={title} className={styles.iconImage} />
|
||||
|
||||
@ -24,7 +24,8 @@ const Home: React.FC = () => {
|
||||
});
|
||||
const [isInputVisible, setIsInputVisible] = useState(false);
|
||||
const [selectedStyle, setSelectedStyle] = useState<string>('chibi'); // По умолчанию выбран первый стиль
|
||||
const [selectedButtonId, setSelectedButtonId] = useState<string | undefined>(undefined); // Для хранения ID выбранной кнопки стиля
|
||||
const [selectedStyleButtonId, setSelectedStyleButtonId] = useState<string | undefined>('chibi'); // Для хранения ID выбранной кнопки стиля
|
||||
const [selectedPresetId, setSelectedPresetId] = useState<string | undefined>(undefined); // Для хранения ID выбранного пресета
|
||||
const [customPrompt, setCustomPrompt] = useState<string>(''); // Для хранения пользовательского промпта
|
||||
|
||||
// Состояния для модального окна уведомления
|
||||
@ -44,7 +45,22 @@ const Home: React.FC = () => {
|
||||
setIsNotificationVisible(false);
|
||||
}, []);
|
||||
|
||||
const handleBlockAction = useCallback(async (actionType: string, actionValue: string, _blockId?: string, buttonId?: string) => {
|
||||
const handleBlockAction = useCallback(async (actionType: string, actionValue: string, blockId?: string, buttonId?: string) => {
|
||||
if (actionType === 'selectStyle') {
|
||||
// Обработка выбора стиля
|
||||
setSelectedStyle(actionValue);
|
||||
setSelectedStyleButtonId(buttonId);
|
||||
console.log('Selected style:', actionValue, 'Button ID:', buttonId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionType === 'selectPreset') {
|
||||
// Обработка выбора пресета
|
||||
setSelectedPresetId(buttonId);
|
||||
console.log('Selected preset:', actionValue, 'Button ID:', buttonId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionType === 'function') {
|
||||
if (actionValue === 'startGeneration') {
|
||||
if (!imageData) {
|
||||
@ -61,10 +77,10 @@ const Home: React.FC = () => {
|
||||
setIsNotificationVisible(true);
|
||||
|
||||
// Если выбран "Свой промпт" и введен текст, используем его
|
||||
const userPrompt = selectedButtonId === 'customPrompt' && customPrompt ? customPrompt : undefined;
|
||||
const userPrompt = selectedPresetId === 'customPrompt' && customPrompt ? customPrompt : undefined;
|
||||
|
||||
// Отправляем запрос на генерацию
|
||||
const response = await apiService.generateImage(imageData, selectedStyle, selectedButtonId, userPrompt);
|
||||
const response = await apiService.generateImage(imageData, selectedStyle, selectedPresetId, userPrompt);
|
||||
console.log('Generation response:', response);
|
||||
|
||||
// Проверяем, была ли ошибка перевода
|
||||
@ -117,8 +133,8 @@ const Home: React.FC = () => {
|
||||
|
||||
if (actionValue === 'toggleInput') {
|
||||
setIsInputVisible(prev => !prev);
|
||||
// Устанавливаем selectedButtonId в 'customPrompt' при нажатии на кнопку "Свой промпт"
|
||||
setSelectedButtonId('customPrompt');
|
||||
// Устанавливаем selectedPresetId в 'customPrompt' при нажатии на кнопку "Свой промпт"
|
||||
setSelectedPresetId('customPrompt');
|
||||
console.log('Выбран свой промпт, установлен ID:', 'customPrompt');
|
||||
return;
|
||||
}
|
||||
@ -137,21 +153,11 @@ const Home: React.FC = () => {
|
||||
// Добавляем обработку для действий типа 'route'
|
||||
navigate(actionValue);
|
||||
return;
|
||||
} else if (actionType === 'selectStyle') {
|
||||
// Обработка выбора стиля
|
||||
setSelectedStyle(actionValue);
|
||||
console.log('Selected style:', actionValue);
|
||||
return;
|
||||
} else if (actionType === 'selectPreset') {
|
||||
// Обработка выбора пресета
|
||||
setSelectedButtonId(buttonId);
|
||||
console.log('Selected preset:', actionValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Если выбрана любая другая кнопка, скрываем поле ввода
|
||||
setIsInputVisible(false);
|
||||
}, [navigate, imageData, selectedStyle, selectedButtonId, customPrompt]);
|
||||
}, [navigate, imageData, selectedStyle, selectedPresetId, customPrompt]);
|
||||
|
||||
// Эффект для обновления window.history.state при загрузке из localStorage
|
||||
useEffect(() => {
|
||||
@ -216,27 +222,38 @@ const Home: React.FC = () => {
|
||||
<div className={styles.content}>
|
||||
{/* Блоки из конфигурации */}
|
||||
<div className={styles.blocks}>
|
||||
{homeScreenConfig.homeScreen.blocks
|
||||
.filter(block => block.type !== 'generateButton')
|
||||
.map((block) => {
|
||||
// Создаем копию блока с модифицированными кнопками
|
||||
const modifiedBlock = {
|
||||
...block,
|
||||
buttons: getBlockButtons(block)
|
||||
};
|
||||
{homeScreenConfig.homeScreen.blocks
|
||||
.filter(block => block.type !== 'generateButton')
|
||||
.map((block) => {
|
||||
// Создаем копию блока с модифицированными кнопками
|
||||
const modifiedBlock = {
|
||||
...block,
|
||||
buttons: getBlockButtons(block)
|
||||
};
|
||||
|
||||
return (
|
||||
<BlockRenderer
|
||||
key={block.id}
|
||||
block={modifiedBlock}
|
||||
onAction={handleBlockAction}
|
||||
extraProps={block.type === 'textInput' ? {
|
||||
visible: isInputVisible,
|
||||
onTextChange: setCustomPrompt
|
||||
} : undefined}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
// Определяем, какой ID выбранной кнопки передавать в зависимости от типа блока
|
||||
let selectedButtonId;
|
||||
if (block.id === 'styleActions') {
|
||||
// Для блока стилей передаем ID кнопки выбранного стиля
|
||||
selectedButtonId = selectedStyleButtonId;
|
||||
} else if (block.id === 'quickActions') {
|
||||
// Для блока пресетов передаем ID выбранного пресета
|
||||
selectedButtonId = selectedPresetId;
|
||||
}
|
||||
|
||||
return (
|
||||
<BlockRenderer
|
||||
key={block.id}
|
||||
block={modifiedBlock}
|
||||
onAction={handleBlockAction}
|
||||
selectedButtonId={selectedButtonId}
|
||||
extraProps={block.type === 'textInput' ? {
|
||||
visible: isInputVisible,
|
||||
onTextChange: setCustomPrompt
|
||||
} : undefined}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{homeScreenConfig.homeScreen.blocks
|
||||
.filter(block => block.type === 'generateButton')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user