Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #5 from NicolasOmar/5-fifth-exercise
Browse files Browse the repository at this point in the history
Typescript | Fifth exercise
  • Loading branch information
NicolasOmar authored Dec 27, 2022
2 parents e884072 + af55abe commit 54d3882
Show file tree
Hide file tree
Showing 23 changed files with 758 additions and 3 deletions.
3 changes: 2 additions & 1 deletion 4-generics-decorators/public/scripts/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// console.log('Logging', constructor)
// }

// THIS IS A DECORATOR FACTORY, YOU CREATE A DECORATOR AS BEFORE, BUT YOU USE YOUR ARGUMENTS TO BE USED IN
// THIS IS A DECORATOR FACTORY, YOU CREATE A DECORATOR AS BEFORE
// BUT YOU USE YOUR ARGUMENTS TO BE USED IN
// DECORATOR'S CONSTRUCTOR
function LoggerWithFactory(
logString: string
Expand Down
1 change: 1 addition & 0 deletions 5-drag-and-drop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
127 changes: 127 additions & 0 deletions 5-drag-and-drop/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
* {
box-sizing: border-box;
}

html {
font-family: sans-serif;
}

body {
margin: 0;
}

label,
input,
textarea {
display: block;
margin: 0.5rem 0;
}

label {
font-weight: bold;
}

input,
textarea {
font: inherit;
padding: 0.2rem 0.4rem;
width: 100%;
max-width: 30rem;
border: 1px solid #ccc;
}

input:focus,
textarea:focus {
outline: none;
background: #fff5f9;
}

button {
font: inherit;
background: #ff0062;
border: 1px solid #ff0062;
cursor: pointer;
color: white;
padding: 0.75rem 1rem;
}

button:focus {
outline: none;
}

button:hover,
button:active {
background: #a80041;
border-color: #a80041;
}

.projects {
margin: 1rem;
border: 1px solid #ff0062;
}

.projects header {
background: #ff0062;
height: 3.5rem;
display: flex;
justify-content: center;
align-items: center;
}

#finished-projects {
border-color: #0044ff;
}

#finished-projects header {
background: #0044ff;
}

.projects h2 {
margin: 0;
color: white;
}

.projects ul {
list-style: none;
margin: 0;
padding: 1rem;
}

.projects li {
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.26);
padding: 1rem;
margin: 1rem;
}

.projects li h2 {
color: #ff0062;
margin: 0.5rem 0;
}

#finished-projects li h2 {
color: #0044ff;
}

.projects li h3 {
color: #575757;
font-size: 1rem;
}

.project li p {
margin: 0;
}

.droppable {
background: #ffe3ee;
}

#finished-projects .droppable {
background: #d6e1ff;
}

#user-input {
margin: 1rem;
padding: 1rem;
border: 1px solid #ff0062;
background: #f7f7f7;
}
2 changes: 2 additions & 0 deletions 5-drag-and-drop/dist/bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions 5-drag-and-drop/dist/bundle.js.map

Large diffs are not rendered by default.

Binary file added 5-drag-and-drop/favicon.ico
Binary file not shown.
47 changes: 47 additions & 0 deletions 5-drag-and-drop/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Typescript Practice | Exercise #5</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="app.css" />
<script type="module" src="dist/bundle.js"></script>
</head>
<body>
<template id="project-input">
<form>
<div class="form-control">
<label for="title">Title</label>
<input type="text" id="title" />
</div>
<div class="form-control">
<label for="description">Description</label>
<textarea id="description" rows="3"></textarea>
</div>
<div class="form-control">
<label for="people">People</label>
<input type="number" id="people" step="1" min="0" max="10" />
</div>
<button type="submit">ADD PROJECT</button>
</form>
</template>
<template id="single-project">
<li draggable="true">
<h2></h2>
<h3></h3>
<p></p>
</li>
</template>
<template id="project-list">
<section class="projects">
<header>
<h2></h2>
</header>
<ul></ul>
</section>
</template>
<div id="app"></div>
</body>
</html>
18 changes: 18 additions & 0 deletions 5-drag-and-drop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "5-drag-and-drop",
"version": "1.0.0",
"scripts": {
"start": "webpack serve",
"build": "webpack",
"build:watch": "tsc -w",
"build:prod": "webpack --config webpack.config.prod.js"
},
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
}
}
12 changes: 12 additions & 0 deletions 5-drag-and-drop/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// COMPONENTS
import { ProjectInput } from "./components/project-input"
import { ProjectList } from "./components/project-list"

// YOU CAN USE REFERENCE AND NAMESPACES TO LINK DEPENDENCIES IN EACH FILE, BUT IS PRONE TO ERRORS WITHOUT
// A PROPER REFERENCE OF WHICH FILE/FEATURE IS MISSING IN WHICH FILE
// /// <reference path="components/project-list.ts" />
// /// <reference path="components/project-input.ts" />

new ProjectInput()
new ProjectList('active')
new ProjectList('finished')
35 changes: 35 additions & 0 deletions 5-drag-and-drop/src/components/base-component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// COMPONENT BASE CLASS
export abstract class Component<T extends HTMLElement, U extends HTMLElement> {
// THIS PROP IS FOR HTML TEMPLATE
templateElement: HTMLTemplateElement
// THIS PROP IS FOR THE PLACE WHERE WE ARE GOING TO PUT THE HTML WE WANT
hostElement: T
// THIS PROP IS FOR SELECT THE HTML ELEMENT WE WANT TO MOVE FROM THE TEMPLATE TO THE HOST
element: U

constructor(
templateId: string,
hostElementId: string,
insertAtStart: boolean,
newElementId?: string // YOU CAN USE THE '?' OR ADD A '| undefined' TO ASSIGN POSSIBLE NULL AS A VALUE
) {
this.templateElement = document.getElementById(templateId)! as HTMLTemplateElement
this.hostElement = document.getElementById(hostElementId)! as T

// HERE YOU ARE GETTING THE TEMPLATE HTML, IMPORTING A DEEP COPY OF THAT NODE/HTMLFRAGMENT
const importedNode = document.importNode(this.templateElement.content, true)
// YOU INSTANTIATE THE FIRST CHILD OF THAT IMPORTED NODE HAS A FORM ELEMENT (YOU KNOW IT WILL BE)
this.element = importedNode.firstElementChild as U
newElementId && (this.element.id = newElementId)
this.attach(insertAtStart)
}

private attach(insertAtBeginning: boolean) {
this.hostElement.insertAdjacentElement(insertAtBeginning ? 'beforebegin' : 'afterbegin', this.element)
}

// NO IMPLEMENTATION HERE, BUT IT WILL BE NECESSARY ON CLASSES EXTENDED FROM THIS ONE
// AN ABSTRACT CLASS CAN ONLY BE PUBLIC
abstract configure(): void
abstract renderContent(): void
}
91 changes: 91 additions & 0 deletions 5-drag-and-drop/src/components/project-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// COMPONENTS
import { Component } from "./base-component"
// MODELS & INTERFACES
import { globalProjectState } from "../state/project-state"
// DECORATORS
import { AutoBind } from "../decorators/autobind"
import { Validatable, validate } from "../utils/validation"

// YOU CAN USE REFERENCE AND NAMESPACES TO LINK DEPENDENCIES IN EACH FILE, BUT IS PRONE TO ERRORS WITHOUT
// A PROPER REFERENCE OF WHICH FILE/FEATURE IS MISSING IN WHICH FILE
// /// <reference path="base-component.ts" />
// /// <reference path="../decorators/autobind.ts" />

export class ProjectInput extends Component<HTMLDivElement, HTMLFormElement> {
titleInputElement: HTMLInputElement
descriptionInputElement: HTMLInputElement
peopleInputElement: HTMLInputElement

constructor() {
super('project-input', 'app', true, 'user-input')
this.templateElement = document.getElementById('project-input')! as HTMLTemplateElement
this.hostElement = document.getElementById('app')! as HTMLDivElement

// YOU GET THE FORM INPUTS
this.titleInputElement = this.element.querySelector('#title') as HTMLInputElement
this.descriptionInputElement = this.element.querySelector('#description') as HTMLInputElement
this.peopleInputElement = this.element.querySelector('#people') as HTMLInputElement
// HERE YOU ARE SETTING THE FORM EVENT LISTENER WITH THE SUBMIT HANDLER METHOD
this.configure()
}

// THE FORM HANDLER METHOD TO BE BINDED WITH FORM'S EVENT LISTENER
@AutoBind
private submitHandler(event: Event) {
event.preventDefault()
const userInputs = this.gatherUserInput()

if (Array.isArray(userInputs)) {
// USING A SPREAD OPERATOR, YOU ARE SENDING THE INPUTS ARRAY AS A LIST OF PARAMETERS
// INSIDE THE ADDPROJECT METHOD
globalProjectState.addProject(...userInputs)
this.clearInputs()
}
}

// THOSE FUNCTIONS ARE HERE TO MANTAIN CONCERN SEPARATION
// ANY ABSTRACT METHODS SHOULD BE PUBLIC IN IMPLEMENTED CLASSES AS WELL
configure() {
// YOU ARE BINDING THE SUBMITHANDLER IN ORDER TO FIX THE LEXICAL ENVIROMENT/SCOPE INSIDE THE METHOD
this.element.addEventListener('submit', this.submitHandler)
}

renderContent(): void { }

// USE A TUPLE RETURN TYPE AND A UNION TYPE TO RETURN A SPECIFIC ARRAY OF VALUES OR NOTHING
private gatherUserInput(): [string, string, number] | void {
const title = this.titleInputElement.value
const description = this.descriptionInputElement.value
const people = this.peopleInputElement.value

const titleValidator: Validatable = {
value: title,
required: true
}
const descriptionValidator: Validatable = {
value: description,
required: true
}
const peopleValidator: Validatable = {
value: people,
required: true,
min: 0
}

if (
validate(titleValidator) &&
validate(descriptionValidator) &&
validate(peopleValidator)
) {
return [title, description, +people]
} else {
alert('Invalid inputs, please try again')
}
}

private clearInputs() {
this.titleInputElement.value = ''
this.descriptionInputElement.value = ''
this.peopleInputElement.value = ''
}
}
Loading

0 comments on commit 54d3882

Please sign in to comment.