This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,54 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// Helper
|
||||
function parents(el,selector){const parents=[];while((el=el.parentNode)&&el!==document){if(!selector||el.matches(selector))parents.push(el);}return parents;}
|
||||
|
||||
const ProductManager=(el=null,options={})=>{// === DOM & VARS =======
|
||||
const module=el||document.querySelector('.product-manager')||console.warn('Product Manager Element not found');if(!module)return;const DOM={module,table:module.querySelector('.table-products'),tbody:module.querySelector('tbody'),inputName:module.querySelector('.input-product-name'),inputPrice:module.querySelector('.input-product-price'),btnAdd:module.querySelector('.button-product-add'),templateRow:module.querySelector('.template-row')};// console.log(DOM);
|
||||
// === INIT =============
|
||||
const init=()=>{// Funktionsaufrufe zu beginn der Anwendung
|
||||
initProducts();DOM.btnAdd.disabled=true;// Event-Lauscher zu beginn der Anwendung
|
||||
DOM.btnAdd.addEventListener('click',onClickAdd);window.addEventListener('keyup',onKeyUp);};// === EVENTHANDLER =====
|
||||
const onKeyUp=e=>{DOM.btnAdd.disabled=!(DOM.inputName.value!==''&&DOM.inputPrice.value!=='');// if (DOM.inputName.value !== '' && DOM.inputPrice.value !== '') {
|
||||
// DOM.btnAdd.disabled = false;
|
||||
// } else {
|
||||
// DOM.btnAdd.disabled = true;
|
||||
// }
|
||||
};const onClickAdd=e=>{const product={name:DOM.inputName.value,//
|
||||
price:Number(DOM.inputPrice.valueAsNumber)};addProduct(product);DOM.inputName.value=DOM.inputPrice.value='';DOM.btnAdd.disabled=true;disableNonFunctionalButtons();};const onClickRemove=e=>{const btn=e.currentTarget;const currentRow=parents(btn,'tr')[0];// btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
currentRow.remove();// currentRow.parentNode.removeChild(currentRow); // IE11 support
|
||||
disableNonFunctionalButtons();};const onClickMoveUp=e=>{const btn=e.currentTarget;const currentRow=parents(btn,'tr')[0];currentRow.previousElementSibling.before(currentRow);// DOM.tbody.insertBefore(currentRow, currentRow.previousElementSibling);
|
||||
disableNonFunctionalButtons();};const onClickMoveDown=e=>{const btn=e.currentTarget;const currentRow=parents(btn,'tr')[0];currentRow.nextElementSibling.after(currentRow);// DOM.tbody.insertBefore(currentRow, currentRow.nextElementSibling.nextElementSibling);
|
||||
disableNonFunctionalButtons();};const onMouseDownDrag=e=>{const btn=e.currentTarget;const currentRow=parents(btn,'tr')[0];// btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
currentRow.draggable=true;// => <tr draggable="true" >...</tr>
|
||||
};const onMouseUpDrag=e=>{const btn=e.currentTarget;const currentRow=parents(btn,'tr')[0];// btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
currentRow.draggable=false;// => <tr draggable="false" >...</tr>
|
||||
};const onDragStart=e=>{// console.log('DragEvent: ', e);
|
||||
const currentRow=e.currentTarget;const idx=[...currentRow.parentNode.children].indexOf(currentRow);e.dataTransfer.setData('text/plain',String(idx));// console.log('tr reference index: ', idx);
|
||||
};const onDragOver=e=>{e.preventDefault();// WICHTIG: sonst funktioniet drop-EventHandler nicht
|
||||
};const onDrop=e=>{const currentRow=e.currentTarget;// <- Zielzeile, aud die gedroppt wird
|
||||
const targetIdx=[...currentRow.parentNode.children].indexOf(currentRow);const sourceIdx=Number(e.dataTransfer.getData('text/plain'));console.log({targetIdx,sourceIdx});//const draggedRow = DOM.tbody.querySelector(`tr:nth-child(${sourceIdx + 1})`);
|
||||
const draggedRow=DOM.tbody.children[sourceIdx];// Guard wenn Element auf sich selbst losgelassen wurde, abbruch
|
||||
if(sourceIdx===targetIdx)return;// Verschiebe das Element im DOM
|
||||
if(sourceIdx>targetIdx){currentRow.before(draggedRow);}else {currentRow.after(draggedRow);}disableNonFunctionalButtons();};// === XHR/FETCH ========
|
||||
const fetchProducts=async()=>{try{const response=await fetch('/data/products.json');// HTTP-Request URL
|
||||
if(!response.ok){throw new Error('Fetch went wrong.');}const data=await response.json();// HTTP-Response-Body in JS-Objekt geparsed -> Promise
|
||||
return data;}catch(error){console.error(error);throw error;}};// === FUNCTIONS ========
|
||||
const initProducts=()=>{fetchProducts().then(products=>{products.forEach(addProduct);disableNonFunctionalButtons();});};const addProduct=product=>{const{name,price}=product;// https://developer.mozilla.org/de/docs/Web/HTML/Reference/Elements/template#anwendungshinweise
|
||||
const trEl=DOM.templateRow.content.firstElementChild.cloneNode(true);const tdNameEl=trEl.querySelector('.td-name');const tdPriceEl=trEl.querySelector('.td-price');const btnDragEl=trEl.querySelector('.button-product-drag');const btnRemoveEl=trEl.querySelector('.button-product-remove');const btnMoveUpEl=trEl.querySelector('.button-product-move-up');const btnMoveDownEl=trEl.querySelector('.button-product-move-down');// Drag 'n Drop hinzufügen.
|
||||
btnDragEl.addEventListener('mousedown',onMouseDownDrag);btnDragEl.addEventListener('mouseup',onMouseUpDrag);trEl.addEventListener('dragstart',onDragStart);trEl.addEventListener('dragover',onDragOver);trEl.addEventListener('drop',onDrop);tdNameEl.textContent=name;tdPriceEl.textContent=Number(price).toFixed(2);btnRemoveEl.addEventListener('click',onClickRemove);btnMoveUpEl.addEventListener('click',onClickMoveUp);btnMoveDownEl.addEventListener('click',onClickMoveDown);DOM.tbody.appendChild(trEl);};const disableNonFunctionalButtons=()=>{DOM.tbody.querySelectorAll('tr').forEach(tr=>{tr.querySelector('.button-product-move-up').disabled=!tr.previousElementSibling;tr.querySelector('.button-product-move-down').disabled=!tr.nextElementSibling;});};// Helper
|
||||
// function parents(el, selector) {
|
||||
// const parents = [];
|
||||
// while ((el = el.parentNode) && el !== document) {
|
||||
// if (!selector || el.matches(selector)) parents.push(el);
|
||||
// }
|
||||
// return parents;
|
||||
// }
|
||||
init();return {element:DOM.module,init};};
|
||||
|
||||
(()=>{// === DOM & VARS =======
|
||||
const init=()=>{console.log('init!');};// === EVENTHANDLER =====
|
||||
const init=()=>{console.log('init!');ProductManager();};// === EVENTHANDLER =====
|
||||
// === XHR/FETCH ========
|
||||
// === FUNCTIONS ========
|
||||
init();})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -38,7 +38,7 @@
|
||||
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
|
||||
<ul class="sublist">
|
||||
<li><a href="./projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
|
||||
<li><a href="#">Project 2</a></li>
|
||||
<li><a href="/projects/02_product-manager.html">02 Product Manager</a></li>
|
||||
<li><a href="#">Project 3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
|
||||
<ul class="sublist">
|
||||
<li><a href="@@webRoot/projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
|
||||
<li><a href="#">Project 2</a></li>
|
||||
<li><a href="/projects/02_product-manager.html">02 Product Manager</a></li>
|
||||
<li><a href="#">Project 3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Chuck Norris Joke (API)</title>
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="JS Course" />
|
||||
<link rel="manifest" href="/favicon/site.webmanifest" />
|
||||
|
||||
<link rel="stylesheet" href="/assets/css/main.css" />
|
||||
<script src="/assets/js/build.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- header.header-main>.content-box>figure.logo-main>img+figc -->
|
||||
<!-- <header class="header-main">
|
||||
<div class="content-box">
|
||||
<figure class="logo-main">
|
||||
<img src="/assets/img/logo-js.min.svg" alt="Logo - JS Course" />
|
||||
<figcaption>JS course</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</header> -->
|
||||
<!-- nav.menu-main>ul.list>li*4>a[href="#"]{Link $} -->
|
||||
<nav class="menu-main">
|
||||
<ul class="list">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/about.html">About</a></li>
|
||||
<li>
|
||||
<a href="#dropdown">Projects</a>
|
||||
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
|
||||
<ul class="sublist">
|
||||
<li><a href="/projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
|
||||
<li><a href="#">Project 2</a></li>
|
||||
<li><a href="#">Project 3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/contact.html">Contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<section class="section-joke">
|
||||
<!-- .chuck-norris-joke>blockquote.joke-text+button.btn.btn-dark.button-get-joke{Get Joke} -->
|
||||
<div class="chuck-norris-joke">
|
||||
<blockquote class="joke-text"></blockquote>
|
||||
<button class="btn btn-dark button-get-joke">Get Joke</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
@@include('head.html')
|
||||
<body>
|
||||
@@include('menu-main.html')
|
||||
<main>
|
||||
<!-- ▼ Product Manager ▼ -->
|
||||
<div class="product-manager">
|
||||
<div class="container py-5">
|
||||
<h1>Productmanager</h1>
|
||||
<!-- table.table.table-striped.table-products>thead.table-dark>tr>th*2 -->
|
||||
<table class="table table-striped table-products">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th class="th-dragger">#</th>
|
||||
<th class="th-name">Name</th>
|
||||
<th class="th-price">Price in €</th>
|
||||
<th class="th-actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-lg-6">
|
||||
<input
|
||||
type="text"
|
||||
name="product-name"
|
||||
class="form-control input-product-name"
|
||||
placeholder="Insert product name"
|
||||
aria-label="Product name" />
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">€</span>
|
||||
<input
|
||||
type="number"
|
||||
name="prodcut-price"
|
||||
class="form-control input-product-price"
|
||||
placeholder="00.00"
|
||||
aria-label="Product price"
|
||||
min="0"
|
||||
step="0.01" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg">
|
||||
<button class="btn btn-dark button-product-add">
|
||||
<i class="fas fa-plus"></i>
|
||||
Add Product
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<template class="template-row">
|
||||
<tr>
|
||||
<td class="td-dragger">
|
||||
<button class="btn btn-default button-product-drag">
|
||||
<i class="fas fa-grip-vertical"></i>
|
||||
<span class="visually-hidden">Drag Product</span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="td-name">{PRODUCT_NAME}</td>
|
||||
<td class="td-price">{PRODUCT_PRICE}</td>
|
||||
<td class="td-actions">
|
||||
<button class="btn btn-danger button-product-remove">
|
||||
<i class="fas fa-trash-can"></i>
|
||||
<span class="visually-hidden">Remove Product</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary button-product-move-up">
|
||||
<i class="fas fa-chevron-up"></i>
|
||||
<span class="visually-hidden">Move up</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary button-product-move-down">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
<span class="visually-hidden">Move down</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</div>
|
||||
<!-- ▲ /Product Manager ▲ -->
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
import ProductManager from './modules/ProductManager'; // Dateiendung nicht notwendig ?
|
||||
|
||||
(() => {
|
||||
// === DOM & VARS =======
|
||||
@@ -7,6 +7,7 @@
|
||||
// === INIT =============
|
||||
const init = () => {
|
||||
console.log('init!');
|
||||
ProductManager();
|
||||
};
|
||||
|
||||
// === EVENTHANDLER =====
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
const CreateBtn = (options = {}) => {
|
||||
const {
|
||||
classNames = ['btn-primary'],
|
||||
iconName = 'circle',
|
||||
label,
|
||||
clickHandler = (e) => {
|
||||
console.log('no click action');
|
||||
},
|
||||
} = options;
|
||||
const btn = document.createElement('button');
|
||||
const icon = document.createElement('i');
|
||||
const span = document.createElement('span');
|
||||
|
||||
btn.classList.add('btn', ...classNames);
|
||||
icon.classList.add('fas', `fa-${iconName}`);
|
||||
span.classList.add('visually-hidden');
|
||||
span.textContent = label;
|
||||
|
||||
btn.addEventListener('click', clickHandler);
|
||||
|
||||
btn.appendChild(icon);
|
||||
btn.appendChild(span);
|
||||
|
||||
return btn;
|
||||
};
|
||||
|
||||
export default CreateBtn;
|
||||
@@ -0,0 +1,204 @@
|
||||
import { parents } from '../utils/helpers.js';
|
||||
|
||||
// Factory Function
|
||||
const ProductManager = (el = null, options = {}) => {
|
||||
// === DOM & VARS =======
|
||||
|
||||
const module = el || document.querySelector('.product-manager') || console.warn('Product Manager Element not found');
|
||||
|
||||
if (!module) return;
|
||||
|
||||
const DOM = {
|
||||
module,
|
||||
table: module.querySelector('.table-products'),
|
||||
tbody: module.querySelector('tbody'),
|
||||
inputName: module.querySelector('.input-product-name'),
|
||||
inputPrice: module.querySelector('.input-product-price'),
|
||||
btnAdd: module.querySelector('.button-product-add'),
|
||||
templateRow: module.querySelector('.template-row'),
|
||||
};
|
||||
|
||||
// console.log(DOM);
|
||||
|
||||
// === INIT =============
|
||||
const init = () => {
|
||||
// Funktionsaufrufe zu beginn der Anwendung
|
||||
initProducts();
|
||||
DOM.btnAdd.disabled = true;
|
||||
// Event-Lauscher zu beginn der Anwendung
|
||||
DOM.btnAdd.addEventListener('click', onClickAdd);
|
||||
window.addEventListener('keyup', onKeyUp);
|
||||
};
|
||||
|
||||
// === EVENTHANDLER =====
|
||||
const onKeyUp = (e) => {
|
||||
DOM.btnAdd.disabled = !(DOM.inputName.value !== '' && DOM.inputPrice.value !== '');
|
||||
// if (DOM.inputName.value !== '' && DOM.inputPrice.value !== '') {
|
||||
// DOM.btnAdd.disabled = false;
|
||||
// } else {
|
||||
// DOM.btnAdd.disabled = true;
|
||||
// }
|
||||
};
|
||||
|
||||
const onClickAdd = (e) => {
|
||||
const product = {
|
||||
name: DOM.inputName.value, //
|
||||
price: Number(DOM.inputPrice.valueAsNumber),
|
||||
};
|
||||
|
||||
addProduct(product);
|
||||
DOM.inputName.value = DOM.inputPrice.value = '';
|
||||
DOM.btnAdd.disabled = true;
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
const onClickRemove = (e) => {
|
||||
const btn = e.currentTarget;
|
||||
const currentRow = parents(btn, 'tr')[0]; // btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
|
||||
currentRow.remove();
|
||||
// currentRow.parentNode.removeChild(currentRow); // IE11 support
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
const onClickMoveUp = (e) => {
|
||||
const btn = e.currentTarget;
|
||||
const currentRow = parents(btn, 'tr')[0];
|
||||
|
||||
currentRow.previousElementSibling.before(currentRow);
|
||||
// DOM.tbody.insertBefore(currentRow, currentRow.previousElementSibling);
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
const onClickMoveDown = (e) => {
|
||||
const btn = e.currentTarget;
|
||||
const currentRow = parents(btn, 'tr')[0];
|
||||
currentRow.nextElementSibling.after(currentRow);
|
||||
// DOM.tbody.insertBefore(currentRow, currentRow.nextElementSibling.nextElementSibling);
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
const onMouseDownDrag = (e) => {
|
||||
const btn = e.currentTarget;
|
||||
const currentRow = parents(btn, 'tr')[0]; // btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
currentRow.draggable = true; // => <tr draggable="true" >...</tr>
|
||||
};
|
||||
const onMouseUpDrag = (e) => {
|
||||
const btn = e.currentTarget;
|
||||
const currentRow = parents(btn, 'tr')[0]; // btn.closest('tr'); // btn.parentNode.parentNode;
|
||||
currentRow.draggable = false; // => <tr draggable="false" >...</tr>
|
||||
};
|
||||
const onDragStart = (e) => {
|
||||
// console.log('DragEvent: ', e);
|
||||
const currentRow = e.currentTarget;
|
||||
const idx = [...currentRow.parentNode.children].indexOf(currentRow);
|
||||
|
||||
e.dataTransfer.setData('text/plain', String(idx));
|
||||
|
||||
// console.log('tr reference index: ', idx);
|
||||
};
|
||||
const onDragOver = (e) => {
|
||||
e.preventDefault(); // WICHTIG: sonst funktioniet drop-EventHandler nicht
|
||||
};
|
||||
const onDrop = (e) => {
|
||||
const currentRow = e.currentTarget; // <- Zielzeile, aud die gedroppt wird
|
||||
const targetIdx = [...currentRow.parentNode.children].indexOf(currentRow);
|
||||
const sourceIdx = Number(e.dataTransfer.getData('text/plain'));
|
||||
|
||||
console.log({ targetIdx, sourceIdx });
|
||||
|
||||
//const draggedRow = DOM.tbody.querySelector(`tr:nth-child(${sourceIdx + 1})`);
|
||||
const draggedRow = DOM.tbody.children[sourceIdx];
|
||||
|
||||
// Guard wenn Element auf sich selbst losgelassen wurde, abbruch
|
||||
if (sourceIdx === targetIdx) return;
|
||||
|
||||
// Verschiebe das Element im DOM
|
||||
if (sourceIdx > targetIdx) {
|
||||
currentRow.before(draggedRow);
|
||||
} else {
|
||||
currentRow.after(draggedRow);
|
||||
}
|
||||
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
// === XHR/FETCH ========
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/products.json'); // HTTP-Request URL
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Fetch went wrong.');
|
||||
}
|
||||
const data = await response.json(); // HTTP-Response-Body in JS-Objekt geparsed -> Promise
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
// === FUNCTIONS ========
|
||||
const initProducts = () => {
|
||||
fetchProducts().then((products) => {
|
||||
products.forEach(addProduct);
|
||||
disableNonFunctionalButtons();
|
||||
});
|
||||
};
|
||||
|
||||
const addProduct = (product) => {
|
||||
const { name, price } = product;
|
||||
|
||||
// https://developer.mozilla.org/de/docs/Web/HTML/Reference/Elements/template#anwendungshinweise
|
||||
const trEl = DOM.templateRow.content.firstElementChild.cloneNode(true);
|
||||
|
||||
const tdNameEl = trEl.querySelector('.td-name');
|
||||
const tdPriceEl = trEl.querySelector('.td-price');
|
||||
const btnDragEl = trEl.querySelector('.button-product-drag');
|
||||
const btnRemoveEl = trEl.querySelector('.button-product-remove');
|
||||
const btnMoveUpEl = trEl.querySelector('.button-product-move-up');
|
||||
const btnMoveDownEl = trEl.querySelector('.button-product-move-down');
|
||||
|
||||
// Drag 'n Drop hinzufügen.
|
||||
btnDragEl.addEventListener('mousedown', onMouseDownDrag);
|
||||
btnDragEl.addEventListener('mouseup', onMouseUpDrag);
|
||||
|
||||
trEl.addEventListener('dragstart', onDragStart);
|
||||
trEl.addEventListener('dragover', onDragOver);
|
||||
trEl.addEventListener('drop', onDrop);
|
||||
|
||||
tdNameEl.textContent = name;
|
||||
tdPriceEl.textContent = Number(price).toFixed(2);
|
||||
btnRemoveEl.addEventListener('click', onClickRemove);
|
||||
btnMoveUpEl.addEventListener('click', onClickMoveUp);
|
||||
btnMoveDownEl.addEventListener('click', onClickMoveDown);
|
||||
|
||||
DOM.tbody.appendChild(trEl);
|
||||
};
|
||||
|
||||
const disableNonFunctionalButtons = () => {
|
||||
DOM.tbody.querySelectorAll('tr').forEach((tr) => {
|
||||
tr.querySelector('.button-product-move-up').disabled = !tr.previousElementSibling;
|
||||
tr.querySelector('.button-product-move-down').disabled = !tr.nextElementSibling;
|
||||
});
|
||||
};
|
||||
|
||||
// Helper
|
||||
// function parents(el, selector) {
|
||||
// const parents = [];
|
||||
// while ((el = el.parentNode) && el !== document) {
|
||||
// if (!selector || el.matches(selector)) parents.push(el);
|
||||
// }
|
||||
// return parents;
|
||||
// }
|
||||
|
||||
init();
|
||||
|
||||
return {
|
||||
element: DOM.module,
|
||||
init,
|
||||
};
|
||||
};
|
||||
|
||||
export default ProductManager;
|
||||
@@ -0,0 +1,32 @@
|
||||
// Helper
|
||||
export function parents(el, selector) {
|
||||
const parents = [];
|
||||
while ((el = el.parentNode) && el !== document) {
|
||||
if (!selector || el.matches(selector)) parents.push(el);
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
export function addClass(el, className) {
|
||||
if (Array.isArray(className)) {
|
||||
el.classList.add(...className);
|
||||
} else {
|
||||
el.classList.add(className);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
export function removeClass(el, className) {
|
||||
if (Array.isArray(className)) {
|
||||
el.classList.remove(...className);
|
||||
} else {
|
||||
el.classList.remove(className);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
export default {
|
||||
parents,
|
||||
addClass,
|
||||
removeClass,
|
||||
};
|
||||
@@ -2,3 +2,4 @@
|
||||
@import 'header-main';
|
||||
@import 'logo-main';
|
||||
@import 'menu-main';
|
||||
@import 'product-manager';
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/* Mobile first */
|
||||
.product-manager table tfoot .row > * {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.product-manager table tfoot .row > * {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.product-manager table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.product-manager table tbody:empty::after {
|
||||
content: 'loading products...';
|
||||
color: #999;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.product-manager table td button {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.product-manager table .th-actions {
|
||||
width: 185px;
|
||||
}
|
||||
.product-manager table .th-dragger {
|
||||
width: 50px;
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
|
||||
<ul class="sublist">
|
||||
<li><a href="./projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
|
||||
<li><a href="#">Project 2</a></li>
|
||||
<li><a href="/projects/02_product-manager.html">02 Product Manager</a></li>
|
||||
<li><a href="#">Project 3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "website-gulp-js",
|
||||
"name": "website-frontend",
|
||||
"version": "0.0.1",
|
||||
"description": "First Website with sass",
|
||||
"description": "webseite mit sas, rollup, gulp, module",
|
||||
"keywords": [],
|
||||
"license": "ISC",
|
||||
"author": "",
|
||||
@@ -13,9 +13,9 @@
|
||||
"dev": "npx gulp dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^8.0.6",
|
||||
"@babel/core": "^8.0.7",
|
||||
"@babel/plugin-transform-runtime": "^8.0.6",
|
||||
"@babel/preset-env": "^8.0.6",
|
||||
"@babel/preset-env": "^8.0.7",
|
||||
"@rollup/plugin-commonjs": "^29.0.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
@@ -28,7 +28,7 @@
|
||||
"gulp-sass": "^6.0.1",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^4.64.0",
|
||||
"rollup": "^4.64.3",
|
||||
"sass": "^1.105.1",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Webseite mit HTML & CSS (SCSS)</title>
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
|
||||
|
||||
<link rel="icon" type="image/png" href="../favicon/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="../favicon/favicon.svg" />
|
||||
<link rel="shortcut icon" href="../favicon/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../favicon/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="JS Course" />
|
||||
<link rel="manifest" href="../favicon/site.webmanifest" />
|
||||
|
||||
<link rel="stylesheet" href="../assets/css/main.css" />
|
||||
<script src="../assets/js/build.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="menu-main">
|
||||
<ul class="list">
|
||||
<li><a href="../">Home</a></li>
|
||||
<li><a href="../about.html">About</a></li>
|
||||
<li>
|
||||
<a href="#dropdown">Projects</a>
|
||||
<!-- ul.sublist>li*3>a[href="#"]{Project $} -->
|
||||
<ul class="sublist">
|
||||
<li><a href="../projects/01_jokes.html">01 Chuck Norris Jokes</a></li>
|
||||
<li><a href="/projects/02_product-manager.html">02 Product Manager</a></li>
|
||||
<li><a href="#">Project 3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="../contact.html">Contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<!-- ▼ Product Manager ▼ -->
|
||||
<div class="product-manager">
|
||||
<div class="container py-5">
|
||||
<h1>Productmanager</h1>
|
||||
<!-- table.table.table-striped.table-products>thead.table-dark>tr>th*2 -->
|
||||
<table class="table table-striped table-products">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th class="th-dragger">#</th>
|
||||
<th class="th-name">Name</th>
|
||||
<th class="th-price">Price in €</th>
|
||||
<th class="th-actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-lg-6">
|
||||
<input
|
||||
type="text"
|
||||
name="product-name"
|
||||
class="form-control input-product-name"
|
||||
placeholder="Insert product name"
|
||||
aria-label="Product name" />
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">€</span>
|
||||
<input
|
||||
type="number"
|
||||
name="prodcut-price"
|
||||
class="form-control input-product-price"
|
||||
placeholder="00.00"
|
||||
aria-label="Product price"
|
||||
min="0"
|
||||
step="0.01" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg">
|
||||
<button class="btn btn-dark button-product-add">
|
||||
<i class="fas fa-plus"></i>
|
||||
Add Product
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<template class="template-row">
|
||||
<tr>
|
||||
<td class="td-dragger">
|
||||
<button class="btn btn-default button-product-drag">
|
||||
<i class="fas fa-grip-vertical"></i>
|
||||
<span class="visually-hidden">Drag Product</span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="td-name">{PRODUCT_NAME}</td>
|
||||
<td class="td-price">{PRODUCT_PRICE}</td>
|
||||
<td class="td-actions">
|
||||
<button class="btn btn-danger button-product-remove">
|
||||
<i class="fas fa-trash-can"></i>
|
||||
<span class="visually-hidden">Remove Product</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary button-product-move-up">
|
||||
<i class="fas fa-chevron-up"></i>
|
||||
<span class="visually-hidden">Move up</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary button-product-move-down">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
<span class="visually-hidden">Move down</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</div>
|
||||
<!-- ▲ /Product Manager ▲ -->
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user