-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cc286d7
commit 5f3ac90
Showing
14 changed files
with
489 additions
and
40 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,39 @@ | ||
/** @jsx createVNode */ | ||
import { createVNode } from "../../lib"; | ||
import { globalStore } from "../../stores/index.js"; | ||
|
||
function createContent(event) { | ||
event.preventDefault(); | ||
const formData = new FormData(event.target); | ||
console.log("formData", formData.get("content")); | ||
const data = {}; | ||
|
||
data.id = globalStore.getState().posts.length + 1; | ||
data.author = globalStore.getState().currentUser.username; | ||
data.time = Date.now(); | ||
data.content = formData.get("content"); | ||
data.likeUsers = []; | ||
globalStore.setState({ posts: [...globalStore.getState().posts, data] }); | ||
} | ||
|
||
export const PostForm = () => { | ||
return ( | ||
<div className="mb-4 bg-white rounded-lg shadow p-4"> | ||
<textarea | ||
id="post-content" | ||
placeholder="무슨 생각을 하고 계신가요?" | ||
className="w-full p-2 border rounded" | ||
/> | ||
<button | ||
id="post-submit" | ||
className="mt-2 bg-blue-600 text-white px-4 py-2 rounded" | ||
> | ||
게시 | ||
</button> | ||
<form id={"content-form"} onSubmit={createContent}> | ||
<textarea | ||
id="post-content" | ||
placeholder="무슨 생각을 하고 계신가요?" | ||
className="w-full p-2 border rounded" | ||
name="content" | ||
/> | ||
<button | ||
id="post-submit" | ||
className="mt-2 bg-blue-600 text-white px-4 py-2 rounded" | ||
type={"submit"} | ||
> | ||
게시 | ||
</button> | ||
</form> | ||
</div> | ||
); | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,47 @@ | ||
import { addEvent } from "./eventManager"; | ||
|
||
export function createElement(vNode) {} | ||
export function createElement(vNode) { | ||
if (vNode === undefined || vNode === null || typeof vNode === "boolean") { | ||
return document.createTextNode(""); | ||
} else if (typeof vNode === "number" || typeof vNode === "string") { | ||
return document.createTextNode(vNode.toString()); | ||
} else if (Array.isArray(vNode)) { | ||
const parentComponent = document.createDocumentFragment(); | ||
vNode.forEach((vN) => parentComponent.appendChild(createElement(vN))); | ||
return parentComponent; | ||
} | ||
|
||
function updateAttributes($el, props) {} | ||
/* vNode를 컴포넌트로 바꾸는 과정입니다 */ | ||
// 일반 Object 형태의 컴포넌트인 경우 | ||
// console.log("vNode", vNode); | ||
// vNode.type에 맞게 해당 타입으로 컴포넌트 하나 생성 | ||
const component = document.createElement(vNode.type); | ||
|
||
// vNode에 있는 props로 component | ||
updateAttributes(component, vNode.props); | ||
// component의 자식들을 creatElement재귀를 돌려서 appendChild시키기 | ||
vNode.children.forEach((child) => { | ||
component.appendChild(createElement(child)); | ||
}); | ||
// 최종 컴포넌트 리턴 | ||
return component; | ||
} | ||
/* create할 떄 attribute값을 업데이트 해주는 함수 */ | ||
function updateAttributes($el, props) { | ||
// props 값이 존재해야만 할 수 있음 | ||
if (props) { | ||
Object.entries(props).forEach(([key, value]) => { | ||
// 이벤트 함수인 경우 | ||
if (key.startsWith("on") && typeof value === "function") { | ||
const eventType = key.toLowerCase().slice(2); // on 글자 제외 | ||
addEvent($el, eventType, value); // 이벤트를 등록하기. 등록하고자 하는 컴포넌트, 이벤트 타입, 이벤트시 실행될 함수 | ||
} else if (key === "className") { | ||
// 스타일 적용하기 | ||
$el.setAttribute("class", value); | ||
} else { | ||
// 나머지 id 값 등 | ||
$el.setAttribute(key, value); | ||
} | ||
}); | ||
} | ||
} |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,14 @@ | ||
export function createVNode(type, props, ...children) { | ||
return {}; | ||
// depth 없이 펼치기 | ||
const flattenChildren = (children) => { | ||
return children.flat(Infinity).filter((child) => { | ||
return child || child === 0 || child === ""; | ||
}); | ||
}; | ||
|
||
return { | ||
type: type, | ||
props: props, | ||
children: flattenChildren(children), | ||
}; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,202 @@ | ||
export function setupEventListeners(root) {} | ||
// 사용 중인 이벤트를 담을 객체 | ||
// const eventMap = new Map(); | ||
// let rootElement = null; | ||
// | ||
// // eventMap = { | ||
// // element : { | ||
// // eventType : handler | ||
// // } | ||
// // } | ||
// | ||
// // 컴포넌트에 이벤트를 추가해주는 함수 | ||
// export function addEvent(element, eventType, handler) { | ||
// let thisElement = element; | ||
// while (thisElement && thisElement !== rootElement) { | ||
// if (eventMap.get(thisElement)) { | ||
// eventMap.get(thisElement).set(eventType, handler); | ||
// } else { | ||
// const newEventMap = new Map(); | ||
// newEventMap.set(eventType, handler); | ||
// eventMap.set(thisElement, newEventMap); | ||
// } | ||
// console.log("부모", thisElement.parentElement) | ||
// thisElement = thisElement.parentElement; | ||
// } | ||
// } | ||
// | ||
// export function setupEventListeners(root) { | ||
// rootElement = root; | ||
// Object.entries(eventMap).forEach(([element, eventTypeMap]) => { | ||
// Object.entries(eventTypeMap).forEach(([eventType, handler]) => { | ||
// rootElement.removeEventListener(eventType, (event) => handleEvent(element, event)); | ||
// rootElement.addEventListener(eventType, (event) => handleEvent(element, event)); | ||
// }) | ||
// }) | ||
// } | ||
// | ||
// export function handleEvent(element, event) { | ||
// let target = event.target; | ||
// // 이벤트 버블 현상으로 Root까지 찾아나가기 | ||
// while (target && target !== rootElement) { | ||
// const eventHandlerMap = eventMap.get(element); | ||
// if (eventHandlerMap) { | ||
// Object.entries(eventHandlerMap).forEach(([eventType, handler]) => { | ||
// handler(event); | ||
// }); | ||
// } | ||
// target = target.parentElement; | ||
// } | ||
// } | ||
// | ||
// export function removeEvent(element, eventType, handler) { | ||
// let thisElement = element; | ||
// while (thisElement && thisElement !== rootElement) { | ||
// const eventTypeMap = eventMap.get(thisElement); | ||
// if (eventTypeMap) { | ||
// if (eventTypeMap.get(eventType)) { | ||
// eventTypeMap.delete(eventType) | ||
// } | ||
// if(Object.entries(eventTypeMap).length === 0) { | ||
// eventMap.delete(thisElement); | ||
// } | ||
// } else { | ||
// return; | ||
// } | ||
// | ||
// if (thisElement.size === 0) { | ||
// eventMap.delete(thisElement); | ||
// const eventTypeMap = eventMap.get(thisElement); | ||
// Object.entries(([key, value]) => { | ||
// if (rootElement && rootElement._listener?.has(key)) { | ||
// rootElement.removeEventListener(key, (event) => handleEvent(thisElement, event), true); | ||
// rootElement._listener.delete(key); | ||
// } | ||
// }) | ||
// | ||
// } | ||
// thisElement = element.parentElement; | ||
// } | ||
// } | ||
|
||
export function addEvent(element, eventType, handler) {} | ||
const eventMap = new Map(); | ||
let rootElement = null; | ||
|
||
export function removeEvent(element, eventType, handler) {} | ||
export function addEvent(element, eventType, handler) { | ||
if (!eventMap.has(element)) { | ||
eventMap.set(element, new Map()); | ||
} | ||
eventMap.get(element).set(eventType, handler); | ||
} | ||
|
||
export function setupEventListeners(root) { | ||
// 기존 리스너 제거 | ||
if (rootElement) { | ||
const handlers = rootElement._eventHandlers || new Map(); | ||
for (const [type, handler] of handlers) { | ||
rootElement.removeEventListener(type, handler); | ||
} | ||
} | ||
|
||
rootElement = root; | ||
rootElement._eventHandlers = new Map(); | ||
|
||
for (const eventData of eventMap) { | ||
const eventTypeMap = eventData[1]; | ||
for (const eventTypeData of eventTypeMap) { | ||
const eventType = eventTypeData[0]; | ||
if (!rootElement._eventHandlers.has(eventType)) { | ||
const boundHandler = (event) => handleEvent(event); | ||
rootElement._eventHandlers.set(eventType, boundHandler); | ||
rootElement.addEventListener(eventType, boundHandler); | ||
} | ||
} | ||
} | ||
} | ||
|
||
export function handleEvent(event) { | ||
let target = event.target; | ||
const eventType = event.type; | ||
|
||
while (target && target !== rootElement) { | ||
if (eventMap.has(target)) { | ||
const handler = eventMap.get(target).get(eventType); | ||
if (handler) { | ||
// console.log("여기 에러나나", handler, event) | ||
handler?.(event); | ||
} | ||
} | ||
target = target.parentElement; | ||
} | ||
} | ||
|
||
export function removeEvent(element, eventType) { | ||
if (!eventMap.has(element)) return; | ||
|
||
const elementEvents = eventMap.get(element); | ||
elementEvents.delete(eventType); | ||
|
||
if (elementEvents.size === 0) { | ||
eventMap.delete(element); | ||
} | ||
|
||
// 해당 이벤트 타입에 대한 리스너가 더 이상 필요없는 경우 | ||
if (![...eventMap.values()].some((map) => map.has(eventType))) { | ||
const handler = rootElement._eventHandlers?.get(eventType); | ||
if (handler) { | ||
rootElement.removeEventListener(eventType, handler, true); | ||
rootElement._eventHandlers.delete(eventType); | ||
} | ||
} | ||
} | ||
|
||
// function handleEvent(event) { | ||
// let target = event.target; | ||
// while (target && target !== rootElement) { | ||
// const elementHandlers = eventMap.get(event.type)?.get(target); | ||
// if (elementHandlers) { | ||
// elementHandlers.forEach((handler) => handler(event)); | ||
// } | ||
// target = target.parentNode; | ||
// } | ||
// } | ||
// | ||
// export function setupEventListeners(root) { | ||
// rootElement = root; | ||
// eventMap.forEach((handlers, eventType) => { | ||
// rootElement.removeEventListener(eventType, handleEvent); | ||
// rootElement.addEventListener(eventType, handleEvent); | ||
// }); | ||
// } | ||
// | ||
// // 컴포넌트이 이벤트를 추가해주는 함수 | ||
// export function addEvent(element, eventType, handler) { | ||
// if (!eventMap.has(eventType)) { | ||
// eventMap.set(eventType, new WeakMap()); | ||
// } | ||
// const elementMap = eventMap.get(eventType); | ||
// if (!elementMap.has(element)) { | ||
// elementMap.set(element, new Set()); | ||
// } | ||
// elementMap.get(element).add(handler); | ||
// } | ||
// | ||
// export function removeEvent(element, eventType, handler) { | ||
// const elementMap = eventMap.get(eventType); | ||
// if (!elementMap) return; | ||
// | ||
// const handlers = elementMap.get(element); | ||
// if (handlers) { | ||
// handlers.delete(handler); | ||
// if (handlers.size === 0) { | ||
// elementMap.delete(element); | ||
// } | ||
// } | ||
// | ||
// if (element.size === 0) { | ||
// eventMap.delete(eventType); | ||
// if (rootElement && rootElement._listener?.has(eventType)) { | ||
// rootElement.removeEventListener(eventType, handleEvent, true); | ||
// rootElement._listener.delete(eventType); | ||
// } | ||
// } | ||
// } |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,28 @@ | ||
export function normalizeVNode(vNode) { | ||
return vNode; | ||
// 값이 없는 경우는 빈 스트링으로 | ||
if (vNode === null || vNode === undefined || typeof vNode === "boolean") { | ||
return ""; | ||
} | ||
|
||
// 스프링이거나 숫자인 경우는 string으로 | ||
if (typeof vNode === "string" || typeof vNode === "number") { | ||
return String(vNode); | ||
} | ||
|
||
// 함수형으로 들어온다면. HomePage.jsx 같은 값은 함수이다. 함수인 경우는 type에 함수가 있음 | ||
// 함수를 실행하는데, 내부 값에는 props와 children을 전달한다. | ||
if (typeof vNode.type === "function") { | ||
// console.log("함수로 들어온다는게 뭐야?", vNode) | ||
return normalizeVNode( | ||
vNode.type({ ...vNode.props, children: vNode.children }), | ||
); | ||
} | ||
|
||
// 일반 Object 타입인 경우 | ||
return { | ||
...vNode, | ||
children: vNode.children | ||
?.map((child) => normalizeVNode(child)) | ||
.filter(Boolean), | ||
}; | ||
} |
Oops, something went wrong.