first commit

This commit is contained in:
2024-04-17 12:11:48 -04:00
commit 5015e79d85
7 changed files with 2781 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
build/

16
Makefile Normal file
View File

@@ -0,0 +1,16 @@
build/MyCard.js: ./node_modules ./build
npx swc ./src/MyCard.ts -o ./build/MyCard.js
./build:
mkdir -p ./build
./node_modules:
npm install
serve: ./build/MyCard.js
npx serve
clean:
rm -rf ./build ./node_modules
.PHONY: clean serve

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# MEDTRACE: Case - Frontend developer
## build and run
From the repo root, run `make` to build the project. From there you can use your own server to serve from the repo root, or run `make serve` to run a development server. `make clean` will remove the dependencies and build directory.

90
index.html Normal file
View File

@@ -0,0 +1,90 @@
<!doctype html>
<html>
<head>
<title>MEDTRACE: Case - Frontend Developer</title>
<script type="module" src="./build/MyCard.js"></script>
<!-- "light" DOM styles -->
<style>
html, body {
margin: 0;
padding: 0;
}
:root {
/* init the CSS var from the "light" DOM */
--my-card-header-color: red;
}
#cards {
padding: 20px;
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
grid-gap: 1rem;
justify-content: space-between;
}
</style>
</head>
<body>
<!-- the template for the custom component -->
<template id="my-card-template">
<!-- shadow DOM styles -->
<style>
:host {
box-shadow: 0px 0px 5px #ccc;
display: block;
padding: 10px;
}
header {
/* using the CSS var assigned from the "light" DOM; default
green is never displayed because the var is initialized
above. */
color: var(--my-card-header-color, green);
}
#message {
margin: 5px;
padding: 10px;
background-color: #eee;
}
</style>
<header class="">
<h1>
<slot name="headline">
Default headline
</slot>
</h1>
</header>
<!-- this could have been a slot named "main" like
`<slot name="main"></slot>`
but that makes it harder to use in the HTML below. -->
<section id="main">
<slot></slot>
</section>
<div id="message"></div>
</template>
<!-- begin content -->
<section id="cards">
<my-card message="initial message parsed from HTML">
<span slot="headline">Custom headline</span>
<button id="my-button">click me</button>
</my-card>
<my-card>hello world</my-card>
</section>
<script>
const cards = document.getElementsByTagName("my-card");
const $myButton = document.getElementById("my-button");
$myButton.addEventListener("click", () => {
const msg = "button clicked";
for (const $card of cards) {
$card.setAttribute(
"message",
$card.getAttribute("message") === msg
? ""
: msg,
);
}
});
</script>
</body>
</html>

2612
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

7
package.json Normal file
View File

@@ -0,0 +1,7 @@
{
"devDependencies": {
"@swc/cli": "^0.3.12",
"@swc/core": "^1.4.15",
"serve": "^14.2.1"
}
}

49
src/MyCard.ts Normal file
View File

@@ -0,0 +1,49 @@
/**
* This could also be imported as a string or written here and attached to the
* `document.body` as a `template` element, like
*
* const template = document.createElement("template");
* template.innerHTML = `<style>...</style><div>...</div>`;
* document.body.appendChild(template);
*
* which would only be run here once, similar in performance to parsing it in
* index.html file.
*/
const template = document.getElementById("my-card-template") as
HTMLTemplateElement;
enum Attributes {
MESSAGE = "message",
}
window.customElements.define("my-card", class extends HTMLElement {
static observedAttributes = [Attributes.MESSAGE];
private _content: DocumentFragment;
$message: HTMLElement;
constructor() {
super();
this.attachShadow({mode: "open"});
this._content = template.content.cloneNode(true) as DocumentFragment;
this.$message = this._content.getElementById("message")!;
}
connectedCallback() {
if (this.shadowRoot) {
this.shadowRoot.appendChild(this._content);
} else {
console.warn("No shadowRoot detected.");
}
}
attributeChangedCallback(name: string, _prev: string, curr: string) {
if (Attributes.MESSAGE === name) {
while (this.$message.firstChild) {
this.$message.removeChild(this.$message.lastChild!);
}
this.$message.appendChild(document.createTextNode(curr));
}
}
});