Skip to content

Commit 935c1a3

Browse files
committed
2 parents 42f2cad + f7a86b6 commit 935c1a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2704
-5574
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" type="text/css" href="../css/semantic.min.css" />
4+
<link rel="stylesheet" type="text/css" href="../css/store_with_header.css" />
5+
</head>
6+
<body>
7+
<div id="content">
8+
<section class="header">
9+
<h1 class="title">Welcome to Fresh Products Store!</h1>
10+
11+
<div class="ui menu">
12+
<tags-holder class="item"></tags-holder>
13+
<div class="right item">
14+
<search-box></search-box>
15+
</div>
16+
</div>
17+
</section>
18+
19+
<div class="ui items"></div>
20+
</div>
21+
22+
<script src="search_box.js"></script>
23+
<script src="tags_holder.js"></script>
24+
<script src="../data/products.js"></script>
25+
<script src="../sample_003/create_elements.js"></script>
26+
<script src="filter_and_search.js"></script>
27+
</body>
28+
</html>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const filterByTagElement = document.querySelector('tags-holder');
2+
filterByTagElement.addEventListener('tag-clicked', (e) => filterByTagElement.removeTag(e.detail.tag));
3+
filterByTagElement.addEventListener('changed', () => applyFilters());
4+
5+
const searchBoxElement = document.querySelector('search-box');
6+
searchBoxElement.addEventListener('changed', (e) => applyFilters());
7+
8+
function addTagFilter() {
9+
Array.from(document.querySelectorAll('.extra .label'))
10+
.forEach(tagEl => tagEl.addEventListener('click', () => {
11+
filterByTagElement.addTag(tagEl.innerHTML);
12+
applyFilters();
13+
}));
14+
}
15+
16+
function applyFilters() {
17+
createListForProducts(filterByText(filterByTags(products)));
18+
addTagFilter();
19+
}
20+
21+
function filterByTags(products) {
22+
let filtered = products;
23+
filterByTagElement.selectedTags
24+
.forEach((t) => filtered = filtered.filter(p => p.tags.includes(t)));
25+
return filtered;
26+
}
27+
28+
function filterByText(products) {
29+
const txt = searchBoxElement.searchText.toLowerCase();
30+
return products.filter((p) => {
31+
return p.name.toLowerCase().includes(txt)
32+
|| p.description.toLowerCase().includes(txt);
33+
});
34+
}
35+
36+
applyFilters();
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
class SearchBox extends HTMLElement {
2+
constructor() {
3+
super();
4+
this.attachShadow({ mode: 'open' });
5+
this._searchText = '';
6+
7+
this.render();
8+
this.shadowRoot.querySelector('input').addEventListener('keyup', (e) => {
9+
this._searchText = e.target.value;
10+
this.triggerTextChanged(this._searchText);
11+
});
12+
}
13+
14+
get searchText() {
15+
return this._searchText;
16+
}
17+
18+
render() {
19+
this.shadowRoot.innerHTML = `
20+
<link rel="stylesheet" type="text/css" href="../css/semantic.min.css" />
21+
<div class="ui icon input">
22+
<input type="text" placeholder="Search..." />
23+
<i class="search icon"></i>
24+
</div>
25+
`;
26+
}
27+
28+
triggerTextChanged(text) {
29+
const event = new CustomEvent('changed', {
30+
bubbles: true,
31+
detail: { text },
32+
});
33+
this.dispatchEvent(event);
34+
}
35+
}
36+
37+
customElements.define('search-box', SearchBox);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
class TagsHolder extends HTMLElement {
2+
constructor() {
3+
super();
4+
this.attachShadow({ mode: 'open' });
5+
this._selectedTags = [];
6+
this.render();
7+
this.renderTagList();
8+
}
9+
10+
addTag(tag) {
11+
if (!this._selectedTags.includes(tag)) {
12+
this._selectedTags.push(tag);
13+
this._selectedTags.sort();
14+
this.renderTagList();
15+
this.triggerChanged();
16+
}
17+
}
18+
19+
get selectedTags() {
20+
return this._selectedTags.slice(0);
21+
}
22+
23+
removeTag(tag) {
24+
const index = this._selectedTags.indexOf(tag);
25+
if (index >= 0) {
26+
this._selectedTags.splice(index, 1);
27+
this.renderTagList();
28+
this.triggerChanged();
29+
}
30+
}
31+
32+
render() {
33+
this.shadowRoot.innerHTML = `
34+
<link rel="stylesheet" type="text/css" href="../css/semantic.min.css" />
35+
<div>
36+
Filtered by tags:
37+
<span class="tags"></span>
38+
</div>`;
39+
}
40+
41+
renderTagList() {
42+
const tagsHolderElement = this.shadowRoot.querySelector('.tags');
43+
tagsHolderElement.innerHTML = '';
44+
45+
const tags = this._selectedTags;
46+
47+
if (tags.length == 0) {
48+
tagsHolderElement.innerHTML = 'No filters';
49+
return;
50+
}
51+
52+
tags.forEach(tag => {
53+
const tagEl = document.createElement('span');
54+
tagEl.className = "ui label orange";
55+
tagEl.addEventListener('click', () => this.triggerTagClicked(tag));
56+
tagEl.innerHTML = tag;
57+
tagsHolderElement.appendChild(tagEl);
58+
});
59+
}
60+
61+
triggerChanged(tag) {
62+
const event = new CustomEvent('changed', { bubbles: true });
63+
this.dispatchEvent(event);
64+
}
65+
66+
triggerTagClicked(tag) {
67+
const event = new CustomEvent('tag-clicked', {
68+
bubbles: true,
69+
detail: { tag },
70+
});
71+
this.dispatchEvent(event);
72+
}
73+
}
74+
75+
customElements.define('tags-holder', TagsHolder);

Lesson01/data/products.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ const products = [
174174
{
175175
"price": 2.79,
176176
"unit": "lb",
177-
"name": "Smilling Cookies",
177+
"name": "Smiling Cookies",
178178
"description": "Delicious sandwich cookies with creamy chocolate filling that always smile back to you, even knowing that you will eat them.",
179179
"image": "../images/products/smiley_cookies.jpg",
180180
"tags": [
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" type="text/css" href="../css/semantic.min.css" />
4+
<link rel="stylesheet" type="text/css" href="../css/store_with_header.css" />
5+
</head>
6+
<body>
7+
<div id="content">
8+
<section class="header">
9+
<h1 class="title">Welcome to Fresh Products Store!</h1>
10+
11+
<div class="ui menu">
12+
<div class="item">
13+
Filtered by tags: <span class="tags"></span>
14+
</div>
15+
<div class="right item">
16+
<search-box></search-box>
17+
</div>
18+
</div>
19+
</section>
20+
21+
<div class="ui items"></div>
22+
</div>
23+
24+
<script src="search_box.js"></script>
25+
<script src="../data/products.js"></script>
26+
<script src="../sample_003/create_elements.js"></script>
27+
<script src="filter_and_search.js"></script>
28+
</body>
29+
</html>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const tagsToFilterBy = [];
2+
3+
const searchBoxElement = document.querySelector('search-box');
4+
searchBoxElement.addEventListener('changed', (e) => applyFilters());
5+
6+
function addTagFilter() {
7+
Array.from(document.querySelectorAll('.extra .label'))
8+
.forEach(tagEl => {
9+
tagEl.addEventListener('click', () => {
10+
if (!tagsToFilterBy.includes(tagEl.innerHTML)) {
11+
tagsToFilterBy.push(tagEl.innerHTML);
12+
applyFilters();
13+
}
14+
});
15+
});
16+
}
17+
18+
function applyFilters() {
19+
createListForProducts(filterByText(filterByTags(products)));
20+
addTagFilter();
21+
updateTagFilterList();
22+
}
23+
24+
function createTagFilterLabel(tag) {
25+
const el = document.createElement('span');
26+
el.className = 'ui label orange';
27+
el.innerText = tag;
28+
29+
el.addEventListener('click', () => {
30+
const index = tagsToFilterBy.indexOf(tag);
31+
tagsToFilterBy.splice(index, 1);
32+
applyFilters();
33+
});
34+
return el;
35+
}
36+
37+
function filterByTags(products) {
38+
let filtered = products;
39+
tagsToFilterBy.forEach((t) => filtered = filtered.filter(p => p.tags.includes(t)));
40+
return filtered;
41+
}
42+
43+
function filterByText(products) {
44+
const txt = searchBoxElement.searchText.toLowerCase();
45+
return products.filter((p) => {
46+
return p.name.toLowerCase().includes(txt)
47+
|| p.description.toLowerCase().includes(txt);
48+
});
49+
}
50+
51+
function updateTagFilterList() {
52+
const tagHolder = document.querySelector('.item span.tags');
53+
54+
if (tagsToFilterBy.length == 0) {
55+
tagHolder.innerHTML = 'No filters';
56+
} else {
57+
tagHolder.innerHTML = '';
58+
tagsToFilterBy.sort();
59+
tagsToFilterBy.map(createTagFilterLabel)
60+
.forEach((tEl) => tagHolder.appendChild(tEl));
61+
}
62+
}
63+
64+
applyFilters();
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
class SearchBox extends HTMLElement {
2+
constructor() {
3+
super();
4+
this.attachShadow({ mode: 'open' });
5+
this._searchText = '';
6+
7+
this.render();
8+
this.shadowRoot.querySelector('input').addEventListener('keyup', (e) => {
9+
this._searchText = e.target.value;
10+
this.triggerTextChanged(this._searchText);
11+
});
12+
}
13+
14+
get searchText() {
15+
return this._searchText;
16+
}
17+
18+
render() {
19+
this.shadowRoot.innerHTML = `
20+
<link rel="stylesheet" type="text/css" href="../css/semantic.min.css" />
21+
<div class="ui icon input">
22+
<input type="text" placeholder="Search..." />
23+
<i class="search icon"></i>
24+
</div>
25+
`;
26+
}
27+
28+
triggerTextChanged(text) {
29+
const event = new CustomEvent('changed', {
30+
bubbles: true,
31+
detail: { text },
32+
});
33+
this.dispatchEvent(event);
34+
}
35+
}
36+
37+
customElements.define('search-box', SearchBox);

Lesson01/sample_002/sample-store-front.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ <h1 class="title">Welcome to Fresh Products Store!</h1>
248248
<div class="item">
249249
<div class="image"><img src="../images/products/smiley_cookies.jpg" /></div>
250250
<div class="content">
251-
<a class="header">Smilling Cookies</a>
251+
<a class="header">Smiling Cookies</a>
252252
<div class="meta">
253253
<span>$2.79 / lb</span>
254254
</div>

Lesson03/activity_001/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
products.csv

0 commit comments

Comments
 (0)