-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TagInput basic end, and prevent vite auto polling for restart.
- Loading branch information
Showing
12 changed files
with
401 additions
and
23 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 |
---|---|---|
@@ -0,0 +1,46 @@ | ||
.tag-input-container { | ||
width: 100%; | ||
|
||
.tag-inline-container { | ||
width: 100%; | ||
border-radius: 2px; | ||
|
||
display: flex; | ||
flex-flow: wrap; | ||
|
||
background-color: #f2f2f2; | ||
color: black; | ||
|
||
padding-left: 10px; | ||
padding-right: 5px; | ||
padding-top: 8px; | ||
padding-bottom: 3px; | ||
} | ||
|
||
.tag-input-wrapper { | ||
width: auto; | ||
height: 22px; | ||
|
||
flex: 1 1 60px; | ||
display: flex; | ||
justify-content: left; | ||
align-items: center; | ||
margin-bottom: 7px; | ||
|
||
.tag-input { | ||
border: 0; | ||
width: 100%; | ||
height: 100%; | ||
background-color: inherit; | ||
color: black; | ||
padding-right: 5px; | ||
|
||
&:-ms-input-placeholder, | ||
&:-moz-placeholder, | ||
&::-webkit-input-placeholder, | ||
&::-moz-placeholder { | ||
font-size: 13px; | ||
} | ||
} | ||
} | ||
} |
Empty file.
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 |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import Component from "../../../core/Component/Component"; | ||
import { Range } from "../../../lib/Range/Range"; | ||
import TagBlock from "../../Tag/TagBlock/TagBlock"; | ||
import "./TagInput.scss"; | ||
|
||
interface TagInputOptions { | ||
onFocus?: () => void; | ||
onFocusout?: () => void; | ||
} | ||
|
||
export default class TagInput extends Component { | ||
private readonly onFocus: () => void; | ||
private readonly onFocusout: () => void; | ||
private readonly MAX_TEXT_LENGTH: number = 10; | ||
|
||
private inputElement: HTMLInputElement | null = null; | ||
private isPressingBackspace: boolean = false; | ||
|
||
constructor({ | ||
onFocus = () => {}, | ||
onFocusout = () => {}, | ||
}: TagInputOptions = {}) { | ||
super({ classNames: ["tag-input-container"] }); | ||
|
||
this.onFocus = onFocus; | ||
this.onFocusout = onFocusout; | ||
|
||
this.store.setDefaultState("tags", []); | ||
this.store.setAction("addTag", ({ state, payload }) => { | ||
const { newTag } = payload; | ||
return { tags: [...state.tags, newTag] }; | ||
}); | ||
this.store.setAction("removeTag", ({ state, payload }) => { | ||
const { removeId } = payload; | ||
return { | ||
tags: state.tags.filter((aTagBlock: TagBlock) => { | ||
return aTagBlock.id !== removeId; | ||
}), | ||
}; | ||
}); | ||
|
||
this.render(); | ||
} | ||
|
||
private isValidTextLength(text: string): boolean { | ||
return new Range(text.trim().length) | ||
.moreThan(0) | ||
.equalAndLessThan(this.MAX_TEXT_LENGTH) | ||
.isIn(); | ||
} | ||
|
||
private submitTag(e: Event) { | ||
e.preventDefault(); | ||
|
||
if (!this.inputElement) return; | ||
if (this.inputElement.value === "") return; | ||
if (!this.isValidTextLength(this.inputElement.value)) return; | ||
|
||
this.store.dispatch("addTag", { | ||
newTag: new TagBlock(this.inputElement.value), | ||
}); | ||
|
||
this.inputElement.value = ""; | ||
this.inputElement.focus(); | ||
} | ||
|
||
private renderInputElement(): HTMLInputElement { | ||
this.inputElement = document.createElement("input"); | ||
this.inputElement.classList.add("tag-input"); | ||
this.inputElement.placeholder = "태그 추가..."; | ||
this.inputElement.spellcheck = false; | ||
this.inputElement.maxLength = this.MAX_TEXT_LENGTH; | ||
|
||
this.inputElement.addEventListener("focus", () => { | ||
this.onFocus(); | ||
}); | ||
this.inputElement.addEventListener("focusout", () => { | ||
if (this.store.getState("tags").length > 0) return; | ||
|
||
this.onFocusout(); | ||
}); | ||
|
||
return this.inputElement; | ||
} | ||
|
||
private renderTagInputWrapper(): HTMLElement { | ||
const tagInputWrapper: HTMLFormElement = document.createElement("form"); | ||
tagInputWrapper.classList.add("tag-input-wrapper"); | ||
tagInputWrapper.addEventListener("submit", this.submitTag.bind(this)); | ||
tagInputWrapper.appendChild(this.renderInputElement()); | ||
|
||
return tagInputWrapper; | ||
} | ||
|
||
private handleRemovingTagByClick(e: Event) { | ||
e.stopPropagation(); | ||
|
||
const clickElement = e.target as HTMLElement; | ||
const tagElement: HTMLElement = clickElement.parentElement!; | ||
|
||
if (clickElement.tagName !== "I") return; | ||
|
||
this.store.dispatch("removeTag", { | ||
removeId: tagElement.id, | ||
}); | ||
|
||
tagElement.remove(); | ||
this.inputElement!.focus(); | ||
} | ||
|
||
private handleRemoveingTagByKey(e: KeyboardEvent) { | ||
e.stopPropagation(); | ||
if (e.key !== "Backspace") return; | ||
if (this.inputElement!.value !== "") return; | ||
if (this.isPressingBackspace) return; | ||
|
||
const tags: Array<TagBlock> = this.store.getState("tags"); | ||
const latestTag: TagBlock = tags[tags.length - 1]; | ||
|
||
this.store.dispatch("removeTag", { | ||
removeId: latestTag.id, | ||
}); | ||
|
||
latestTag.container.remove(); | ||
this.inputElement!.focus(); | ||
this.isPressingBackspace = true; | ||
} | ||
|
||
private handleInitializeKeyboardState(e: KeyboardEvent) { | ||
e.stopPropagation(); | ||
if (e.key !== "Backspace") return; | ||
|
||
this.isPressingBackspace = false; | ||
} | ||
|
||
private applyEventsForRemoveTag() { | ||
const tagInlineContainer: HTMLDivElement = this.qs( | ||
".tag-inline-container" | ||
)! as HTMLDivElement; | ||
|
||
tagInlineContainer.addEventListener( | ||
"click", | ||
this.handleRemovingTagByClick.bind(this) | ||
); | ||
tagInlineContainer.addEventListener( | ||
"keydown", | ||
this.handleRemoveingTagByKey.bind(this) | ||
); | ||
tagInlineContainer.addEventListener( | ||
"keyup", | ||
this.handleInitializeKeyboardState.bind(this) | ||
); | ||
} | ||
|
||
render() { | ||
this.container.innerHTML = ` | ||
<div class="tag-inline-container"></div> | ||
`; | ||
|
||
const tags: Array<TagBlock> = this.store.getState("tags"); | ||
this.appendElementsTo( | ||
".tag-inline-container", | ||
...tags.map((aTagBlock) => { | ||
return aTagBlock.container; | ||
}), | ||
this.renderTagInputWrapper() | ||
); | ||
|
||
this.applyEventsForRemoveTag(); | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
.tag-block { | ||
display: inline-flex; | ||
justify-content: center; | ||
align-items: center; | ||
margin-right: 5px; | ||
margin-bottom: 5px; | ||
padding: 3px 10px; | ||
border-radius: 3px; | ||
line-break: anywhere; | ||
font-size: 13px; | ||
height: 22px; | ||
background-color: teal; | ||
|
||
i { | ||
cursor: pointer; | ||
margin-left: 10px; | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// describe("Test TagBlock: Parse hash text.", () => { | ||
// it("Case 1.", () => { | ||
|
||
// }) | ||
// }); |
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 |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import Component from "../../../core/Component/Component"; | ||
import "./TagBlock.scss"; | ||
|
||
interface TagBlockOptions { | ||
hasCloseButton?: boolean; | ||
} | ||
|
||
export default class TagBlock extends Component { | ||
readonly text: string; | ||
private readonly hasCloseButton: boolean; | ||
|
||
constructor(text: string, { hasCloseButton = true }: TagBlockOptions = {}) { | ||
const { hash, salt }: { hash: string; salt: string } = text.getHash(); | ||
super({ id: hash + salt, classNames: ["tag-block"] }); | ||
|
||
this.hasCloseButton = hasCloseButton; | ||
this.text = this.parseHashText(text); | ||
|
||
this.render(); | ||
} | ||
|
||
private parseHashText(text: string): string { | ||
text = text.trim(); | ||
text = text.replaceAll(" ", "_"); | ||
|
||
if (text.startsWith("#")) return text; | ||
return `#${text}`; | ||
} | ||
|
||
setRandomBackgroundColor() { | ||
this.container.style.backgroundColor = `hsl(${Math.floor( | ||
Math.random() * 360 | ||
)}, 60%, 75%)`; | ||
} | ||
|
||
render() { | ||
this.container.innerHTML = ` | ||
<span class="tag-text">${this.text}</span> | ||
${this.hasCloseButton ? "<i class='fa-solid fa-xmark'></i>" : ""} | ||
`; | ||
|
||
this.setRandomBackgroundColor(); | ||
} | ||
} |
Oops, something went wrong.