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