Last active
February 19, 2023 10:07
-
-
Save kaenova/203bd4d34014764f6dbcf17b3bf235f0 to your computer and use it in GitHub Desktop.
Recursion Components with Callback function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// These are custom component, you can change it to your liking | |
import MainButton from '@/components/button/MainButton'; | |
import InputText from '@/components/input/InputText'; | |
import React, { useEffect, useState } from 'react'; | |
function Test() { | |
return ( | |
<div className="flex flex-col w-screen h-screen items-center justify-center"> | |
<div className="w-5/12 rounded-md p-3 border border-neutral-500/20 shadow-md bg-white"> | |
<Headers onChange={(data) => console.log(data)} /> | |
</div> | |
</div> | |
); | |
} | |
interface HeaderData { | |
title: string; | |
headers: HeaderData[]; | |
} | |
function Headers({ onChange }: { onChange: (data: HeaderData) => void }) { | |
const [Data, setData] = useState<HeaderData>({ | |
title: 'Main title', | |
headers: [], | |
}); | |
// Callback on Data changes | |
useEffect(() => { | |
onChange(Data); | |
}, [Data]); | |
function handleDeleteSubheader(idx: number) { | |
const newData = { ...Data }; | |
newData.headers.splice(idx, 1); | |
setData(newData); | |
} | |
function handleChangesSubheader(idx: number, newSubheader: HeaderData) { | |
const newData = { ...Data }; | |
newData.headers[idx] = newSubheader; | |
setData(newData); | |
} | |
return ( | |
<div className="flex flex-col gap-2"> | |
<div className="flex flex-row gap-2"> | |
<InputText | |
placeholder="Title" | |
value={Data.title} | |
onChange={(e) => { | |
setData({ ...Data, title: e.target.value }); | |
}} | |
/> | |
<MainButton | |
buttonText="Tambah" | |
onClick={() => { | |
const newData = { ...Data }; | |
newData.headers = [...Data.headers, { title: '', headers: [] }]; | |
setData(newData); | |
}} | |
/> | |
</div> | |
<div className="flex flex-col items-start"> | |
{Data.headers.map((v, idx) => ( | |
<SubHeader | |
header={v} | |
parentIdxs={[]} | |
currentIdx={idx} | |
onChange={(subHeaderIdx, newData) => { | |
handleChangesSubheader(subHeaderIdx, newData); | |
}} | |
onDelete={(idxReq) => handleDeleteSubheader(idxReq)} | |
/> | |
))} | |
</div> | |
</div> | |
); | |
} | |
function SubHeader({ | |
parentIdxs, | |
currentIdx, | |
header, | |
onChange, | |
onDelete, | |
}: { | |
header: HeaderData; | |
parentIdxs: number[]; | |
currentIdx: number; | |
// eslint-disable-next-line no-unused-vars | |
onChange: (changesIndex: number, newSubheaderData: HeaderData) => void; | |
// eslint-disable-next-line no-unused-vars | |
onDelete: (deleteIndex: number) => void; | |
}) { | |
const allIdx = [...parentIdxs, currentIdx]; | |
// Create subHeadeing Numbering | |
let subHeaderNumber = ''; | |
for (let i = 0; i < parentIdxs.length; i += 1) { | |
subHeaderNumber += `${parentIdxs[i] + 1}.`; | |
} | |
subHeaderNumber += `${currentIdx + 1}`; | |
// Handle changes from self | |
function handleAddButton() { | |
const newData = { ...header }; | |
newData.headers.push({ title: '', headers: [] }); | |
onChange(currentIdx, newData); | |
} | |
function handleDeleteButton() { | |
onDelete(currentIdx); | |
} | |
function handleChangesText(newText: string) { | |
const newData = { ...header }; | |
newData.title = newText; | |
onChange(currentIdx, newData); | |
} | |
// Handle changes from child subheader | |
function handleChangeSubheader(idx: number, newSubheader: HeaderData) { | |
const newData = { ...header }; | |
newData.headers[idx] = newSubheader; | |
onChange(currentIdx, newData); | |
} | |
function handleDeleteSubheader(idx: number) { | |
const newData = { ...header }; | |
newData.headers.splice(idx, 1); | |
onChange(currentIdx, newData); | |
} | |
return ( | |
<div className="flex flex-col gap-2 items-left w-full"> | |
<div className="flex flex-row gap-2 items-center justify-between"> | |
<p className="bg-lime-300 rounded-full p-1 px-5 font-bold"> | |
{subHeaderNumber} | |
</p> | |
<InputText | |
placeholder="Subheading title" | |
value={header.title} | |
onChange={(v) => { | |
handleChangesText(v.target.value); | |
}} | |
/> | |
<div className="flex flex-row gap-3"> | |
<MainButton | |
className="text-xs" | |
buttonText="+" | |
onClick={handleAddButton} | |
/> | |
<MainButton | |
className="text-xs bg-red-600" | |
buttonText="-" | |
onClick={handleDeleteButton} | |
/> | |
</div> | |
</div> | |
<div className="ml-9"> | |
{header.headers.map((v, idx) => ( | |
<SubHeader | |
onChange={(subheaderIdx, newSubheader) => { | |
handleChangeSubheader(subheaderIdx, newSubheader); | |
}} | |
onDelete={(deletedIndex) => { | |
handleDeleteSubheader(deletedIndex); | |
}} | |
currentIdx={idx} | |
parentIdxs={allIdx} | |
header={v} | |
/> | |
))} | |
</div> | |
</div> | |
); | |
} | |
export default Test; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment