deleting questions and answers, selecting answers

This commit is contained in:
Tatiana Nikolaeva 2025-04-08 00:33:20 +05:00
parent 5b2803e67a
commit 8c1bd2e3b8
10 changed files with 174 additions and 47 deletions

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z" fill="#1D1B20"/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z" fill="#EC221F"/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View file

@ -0,0 +1,10 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_9_43)">
<path d="M21.8986 6.46666V21.9681H6.39711V6.46666H21.8986ZM21.8986 4.25217H6.39711C5.17914 4.25217 4.18262 5.24869 4.18262 6.46666V21.9681C4.18262 23.1861 5.17914 24.1826 6.39711 24.1826H21.8986C23.1165 24.1826 24.1131 23.1861 24.1131 21.9681V6.46666C24.1131 5.24869 23.1165 4.25217 21.8986 4.25217Z" fill="#5C5C5C"/>
</g>
<defs>
<clipPath id="clip0_9_43">
<rect width="26.5739" height="26.5739" fill="white" transform="translate(0.86087 0.930435)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 607 B

View file

@ -1,16 +1,26 @@
/*AnswerOption.module.css*/ /*AnswerOption.module.css*/
.answer{ .answer{
width: 100%;
display: flex; display: flex;
gap: 10px; gap: 10px;
margin-bottom: 17px; margin-bottom: 17px;
} }
.textAnswer{ .textAnswer{
text-align: left;
border: none; border: none;
background-color: #ffffff; background-color: #ffffff;
font-size: 18px; font-size: 18px;
font-weight: 500; font-weight: 500;
word-break: break-word;
width: 70%;
}
.buttonMarker{
padding: 0;
border: none;
background-color: transparent;
} }
.answerIcon{ .answerIcon{
@ -29,3 +39,10 @@
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
} }
.deleteButton{
margin-left: auto;
border: none;
background-color: transparent;
padding: 0;
}

View file

@ -3,17 +3,20 @@ import styles from'./AnswerOption.module.css';
const single_selected_response = '../../../public/radio_button_checked.svg'; const single_selected_response = '../../../public/radio_button_checked.svg';
const multiple_selected_response = '../../../public/check_box.svg'; const multiple_selected_response = '../../../public/check_box.svg';
// const single_response = const single_response = '../../../public/radio_button_unchecked.svg';
// const multiple_response = const multiple_response ='../../../public/emptyCheckbox.svg';
interface AnswerOptionProps{ interface AnswerOptionProps{
index: number; index: number;
value: string; value: string;
onChange: (value: string) => void; onChange: (value: string) => void;
onDelete:(index: number) => void;
selectedType: 'single' | 'multiply'; selectedType: 'single' | 'multiply';
isSelected: boolean;
toggleSelect: () => void;
} }
const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, selectedType}) => { const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDelete, selectedType, isSelected, toggleSelect}) => {
const [currentValue, setCurrentValue] = useState(value); const [currentValue, setCurrentValue] = useState(value);
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
@ -33,12 +36,12 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, sele
const handleSave = () => { const handleSave = () => {
setIsEditing(false); setIsEditing(false);
onChange(currentValue); // Отправляем измененное значение родителю onChange(currentValue);
}; };
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => { const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === "Enter") { if (event.key === "Enter") {
event.preventDefault(); // Предотвращаем перенос строки в textarea event.preventDefault();
handleSave(); handleSave();
} }
}; };
@ -53,17 +56,23 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, sele
} }
}, [isEditing]); }, [isEditing]);
const getImage = (typeValue: string): string => { const getImage = () => {
if (typeValue === 'multiply') { if (selectedType === 'multiply') {
return multiple_selected_response; return isSelected ? multiple_selected_response : multiple_response;
} else { } else {
return single_selected_response; return isSelected ? single_selected_response : single_response;
} }
}; };
return ( return (
<div className={styles.answer}> <div className={styles.answer}>
<img className={styles.answerIcon} src={getImage(selectedType)} alt="" /> <button className={styles.buttonMarker} onClick={toggleSelect}>
<img
className={styles.answerIcon}
src={getImage()}
alt=""
/>
</button>
{isEditing ? ( {isEditing ? (
<textarea className={styles.answerInput} <textarea className={styles.answerInput}
ref={textAreaRef} ref={textAreaRef}
@ -78,6 +87,9 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, sele
{currentValue || `Ответ ${index}`} {currentValue || `Ответ ${index}`}
</button> </button>
)} )}
<button className={styles.deleteButton} onClick={() => onDelete(index)}>
<img src='../../../public/delete.svg' alt="Удалить" />
</button>
</div> </div>
); );
}; };

View file

@ -2,6 +2,7 @@
.mainPage{ .mainPage{
width: 100%; width: 100%;
min-height: 85vh;
display: flex; display: flex;
background-color: #F6F6F6; background-color: #F6F6F6;
} }

View file

@ -10,11 +10,20 @@
} }
.questionContainer{ .questionContainer{
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.question{
display: flex;
justify-content: space-between;
gap: 40px;
}
.questionTextarea{ .questionTextarea{
width: 70%;
align-items: center;
border: none; border: none;
outline: none; outline: none;
resize: none; resize: none;
@ -24,6 +33,7 @@
} }
.buttonQuestion{ .buttonQuestion{
align-items: center;
border: none; border: none;
outline: none; outline: none;
background-color: #ffffff; background-color: #ffffff;
@ -37,4 +47,28 @@
font-weight: 600; font-weight: 600;
margin-bottom: 35px; margin-bottom: 35px;
text-align: start; text-align: start;
word-break: break-word;
}
.questionActions{
display: flex;
justify-content: space-between;
}
.deleteQuestionButton{
font-size: 18px;
font-weight: 500;
color: #EC221F;
border: none;
padding: 0;
align-items: center;
background-color: transparent;
display: flex;
gap: 3px;
}
.basketImg{
vertical-align: middle;
width: 24px;
color: #EC221F;
} }

View file

@ -10,15 +10,18 @@ interface QuestionItemProps {
initialTextQuestion?: string; initialTextQuestion?: string;
valueQuestion: string; valueQuestion: string;
onChangeQuestion: (valueQuestion: string) => void; onChangeQuestion: (valueQuestion: string) => void;
onDeleteQuestion: (index: number) => void;
} }
const QuestionItem: React.FC<QuestionItemProps> = ({indexQuestion, initialTextQuestion = `Вопрос ${indexQuestion}`, const QuestionItem: React.FC<QuestionItemProps> = ({indexQuestion, initialTextQuestion = `Вопрос ${indexQuestion}`,
valueQuestion, onChangeQuestion}) => { valueQuestion, onChangeQuestion, onDeleteQuestion}) => {
const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single'); const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single');
const [answerOption, setAnswerOption] = useState(['']); const [answerOption, setAnswerOption] = useState(['']);
const [textQuestion, setTextQuestion] = useState(initialTextQuestion); const [textQuestion, setTextQuestion] = useState(initialTextQuestion);
const [isEditingQuestion, setIsEditingQuestion] = useState(false); const [isEditingQuestion, setIsEditingQuestion] = useState(false);
const [selectedAnswers, setSelectedAnswers] = useState<number[]>([]);
const textareaQuestionRef = useRef<HTMLTextAreaElement>(null); const textareaQuestionRef = useRef<HTMLTextAreaElement>(null);
const handleTypeChange = (type: 'single' | 'multiply') => { const handleTypeChange = (type: 'single' | 'multiply') => {
@ -65,43 +68,79 @@ const QuestionItem: React.FC<QuestionItemProps> = ({indexQuestion, initialTextQu
setAnswerOption(newAnswerOption); setAnswerOption(newAnswerOption);
} }
const handleDeleteAnswer = (index: number) => {
const newAnswerOption = answerOption.filter((_, i) => i !== index);
setAnswerOption(newAnswerOption);
setSelectedAnswers(selectedAnswers.filter((i) => i !== index));
};
useEffect(() => { useEffect(() => {
setTextQuestion(valueQuestion); setTextQuestion(valueQuestion);
}, [valueQuestion]); }, [valueQuestion]);
const handleDeleteQuestion = () => {
onDeleteQuestion(indexQuestion);
};
const toggleSelect = (index: number) => {
if (selectedType === 'single') {
setSelectedAnswers([index]);
} else {
setSelectedAnswers((prev) => {
if (prev.includes(index)) {
return prev.filter((i) => i !== index);
} else {
return [...prev, index];
}
});
}
};
return ( return (
<div className={styles.questionCard}> <div className={styles.questionCard}>
<div className={styles.questionContainer}> <div className={styles.questionContainer}>
{isEditingQuestion ? ( <div className={styles.question}>
<textarea {isEditingQuestion ? (
className={styles.questionTextarea} <textarea
ref={textareaQuestionRef} className={styles.questionTextarea}
value={textQuestion === initialTextQuestion ? '' : textQuestion} ref={textareaQuestionRef}
onChange={handleTextareaQuestionChange} value={textQuestion === initialTextQuestion ? '' : textQuestion}
onKeyDown={handleQuestionKeyDown} onChange={handleTextareaQuestionChange}
onBlur={handleQuestionBlur} onKeyDown={handleQuestionKeyDown}
placeholder={initialTextQuestion} onBlur={handleQuestionBlur}
/> placeholder={initialTextQuestion}
) : ( />
<button className={styles.buttonQuestion} onClick={handleQuestionClick}> ) : (
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2> <button className={styles.buttonQuestion} onClick={handleQuestionClick}>
</button> <h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
)} </button>
)}
<TypeDropdown selectedType={selectedType} onTypeChange={handleTypeChange}/>
</div>
{answerOption.map((answerText, index) => ( {answerOption.map((answerText, index) => (
<AnswerOption <AnswerOption
key={index} key={index}
selectedType={selectedType} selectedType={selectedType}
index={index + 1} // Индекс ответа index={index + 1}
value={answerText} value={answerText}
onChange={(value) => handleAnswerChange(index, value)} onChange={(value) => handleAnswerChange(index, value)}
onDelete={() => handleDeleteAnswer(index)}
isSelected={selectedAnswers.includes(index)}
toggleSelect={() => toggleSelect(index)}
/> />
))} ))}
<AddAnswerButton <div className={styles.questionActions}>
onClick={handleAddAnswer} <AddAnswerButton
/> onClick={handleAddAnswer}
/>
<button className={styles.deleteQuestionButton} onClick={handleDeleteQuestion}>
Удалить{/**/}
<img className={styles.basketImg} src='../../../public/deleteQuestion.svg' alt='deleteQuestion' />
</button>
</div>
</div> </div>
<TypeDropdown selectedType={selectedType} onTypeChange={handleTypeChange}/>
</div> </div>
); );
} }

View file

@ -1,4 +1,4 @@
import React, {useState} from "react"; import React, { useState } from "react";
import QuestionItem from "../QuestionItem/QuestionItem.tsx"; import QuestionItem from "../QuestionItem/QuestionItem.tsx";
import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx"; import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx";
@ -6,12 +6,12 @@ interface QuestionsListProps {}
interface Question { interface Question {
id: number; id: number;
text: string text: string;
} }
const QuestionsList: React.FC<QuestionsListProps> = () => { const QuestionsList: React.FC<QuestionsListProps> = () => {
const [questions, setQuestions] = useState<Question[]>([ const [questions, setQuestions] = useState<Question[]>([
{id: 1, text: ''}, { id: 1, text: '' },
]); ]);
const handleAddQuestion = () => { const handleAddQuestion = () => {
@ -23,21 +23,27 @@ const QuestionsList: React.FC<QuestionsListProps> = () => {
setQuestions([...questions, newQuestion]); setQuestions([...questions, newQuestion]);
}; };
const handleQuestionChange = (index: number, value: string) => { const handleQuestionChange = (id: number, value: string) => {
const newQuestions = [...questions]; const newQuestions = questions.map((question) =>
newQuestions[index] = {...newQuestions[index], text: value}; question.id === id ? { ...question, text: value } : question
);
setQuestions(newQuestions);
};
const handleDeleteQuestion = (id: number) => {
const newQuestions = questions.filter((question) => question.id !== id);
setQuestions(newQuestions); setQuestions(newQuestions);
}; };
return ( return (
<> <>
{questions.map((question, index) => ( {questions.map((question) => (
<QuestionItem <QuestionItem
key={question.id} key={question.id}
indexQuestion={index + 1} indexQuestion={question.id}
valueQuestion={''} valueQuestion={question.text}
onChangeQuestion={(value) => handleQuestionChange(index, value)} onDeleteQuestion={() => handleDeleteQuestion(question.id)}
onChangeQuestion={(value) => handleQuestionChange(question.id, value)}
/> />
))} ))}
<AddQuestionButton onClick={handleAddQuestion} /> <AddQuestionButton onClick={handleAddQuestion} />

View file

@ -4,7 +4,8 @@
width: 23%; width: 23%;
position: relative; position: relative;
display: inline-block; display: inline-block;
margin-right: 29px; /*margin-right: 29px;*/
margin-left: auto;
} }
.dropdownButton { .dropdownButton {
@ -17,9 +18,10 @@
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
width: 118%; width: 100%;
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
margin-right: 29px;
} }
.selectedTypeIcon { .selectedTypeIcon {
@ -31,6 +33,7 @@
} }
.dropdownList { .dropdownList {
width: 70%;
margin-top: 11px; margin-top: 11px;
position: absolute; position: absolute;
background-color: #fff; background-color: #fff;
@ -39,8 +42,9 @@
padding: 12px; padding: 12px;
list-style: none; list-style: none;
z-index: 1; z-index: 1;
width: 100%;
box-shadow: 0 0 4.7px 0 #00000040; box-shadow: 0 0 4.7px 0 #00000040;
left: 50%;
transform: translateX(-50%);
} }
.dropdownItem { .dropdownItem {
@ -50,8 +54,6 @@
padding: 0; padding: 0;
border: none; border: none;
background-color: #ffffff; background-color: #ffffff;
/*cursor: pointer;*/
} }
.dropdownItemIcon { .dropdownItemIcon {