XTRAJW55GQBNSQ2I5JYTPOYCYZ745XQ5EEB6ETAV7HFUAFFYJM3QC
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
#ett-header {
background-color: rgb(220, 223, 238);
border-radius: 4px;
padding: .3em;
font-family: sans-serif, monospace;
font-weight: bolder;
}
#ett-title,
#ett-date-container {
font-size: 2em;
line-height: 1.3em;
border-radius: 4px;
}
#ett-title {
color: steelblue;
}
#ett-date-container {
background-color: white;
color: #607072;
text-align: center;
}
#ett-date {
font-family: sans-serif, monospace;
font-size: 1em;
line-height: 1.3em;
text-align: center;
}
.ett-task-name,
.ett-task-time,
.ett-task-total
{
height: 3em;
padding: 0px;
}
.ett-task-index {
border: solid 2px;
border-radius: 4px;
width: 17px;
height: 17px;
margin: 0px;
text-align: center;
margin-right: 4px;
font-family: sans-serif, monospace;
line-height: 17px;
background-color: white;
border-color: steelblue;
color: steelblue;
}
.ett-major-task .ett-task-index {
font-weight: bold;
background-color: darkorange;
border-color: darkorange;
color: white;
}
.task-header {
margin-top: 2em;
}
.task-header .ett-task-time {
color: darkorange;
font-family: sans-serif, monospace;
font-weight: bold;
user-select:none;
-o-user-select:none;
-ms-user-select:none;
-moz-user-select: none;
-webkit-user-select: none;
}
.task-header .ett-task-name input {
color: steelblue;
font-family: sans-serif, monospace;
font-weight: bolder;
font-size: 1em;
word-wrap: wrap;
}
.ett-task-name input {
height: 21px;
text-indent: .2em;
border: none;
border-bottom: solid 2px steelblue;
border-bottom-left-radius: 8px;
border-top: 0px;
border-top-left-radius: 0px;
}
.ett-task-name input:focus {
outline: none;
}
@media only screen and (max-width: 1190px) {
.ett-task-name input {
border-bottom: 0px;
border-bottom-left-radius: 0px;
border-top: solid 2px steelblue;
border-top-left-radius: 8px;
}
}
.ett-task-total {
min-width: 3em;
max-width: 4em;
border: none;
font-family: sans-serif, monospace;
text-align: right;
font-size: .8em;
}
.task-header .ett-task-total {
font-weight: bold;
}
.task-header .ett-task-time {
position: relative;
overflow: visible;
}
.task-header .ett-task-time .bubble-hour-break:nth-child(1)::before {
content: "start";
font-family: sans-serif, monospace;
font-size: .75em;
position: absolute;
top: .3em;
left: -2.5em;
}
.bubble-hour-break {
border-left: solid 1px darkorange;
}
.bubble-15m {
border-color: steelblue;
background-color: #FFF;
}
.bubble-15m.bubble3 > .bubble-5m {
border-color: rgba(70,130,180,.5);
background-color: #FFF;
}
.bubble-hour[data-hour="6"] .bubble-15m,
.bubble-hour[data-hour="12"] .bubble-15m,
.bubble-hour[data-hour="18"] .bubble-15m {
border-color: darkorange;
}
.bubble-hour[data-hour="6"] .bubble-15m.bubble3 > .bubble-5m:nth-child(2),
.bubble-hour[data-hour="12"] .bubble-15m.bubble3 > .bubble-5m:nth-child(2),
.bubble-hour[data-hour="18"] .bubble-15m.bubble3 > .bubble-5m:nth-child(2) {
border-color: rgba(255,165,0,.5);
}
.bubble-15m.fill,
.bubble-15m.bubble3 > .bubble-5m.fill {
background-color: #607072;
}
.task-header .ett-task-name input,
.task-header .ett-task-total {
border-color: transparent;
}
#task-subheader.task-header .ett-task-total {
visibility: hidden;
}
#ett-date-container .datepicker {
font-size: 50%;
filter: drop-shadow(0rem .5rem .5rem rgb(54, 40, 22));
}
#ett-date-container .datepicker-cell.prev:not(.disabled),
#ett-date-container .datepicker-cell.next:not(.disabled) {
font-size: 80%;
color: #9a9a9a;
}
#ett-date-container .datepicker-controls .button:hover,
#ett-date-container .datepicker-cell:not(.disabled):not(.selected):hover {
background-color: #cddbf3;
}
#ett-header {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0px;
border: solid 0px red;
}
#ett-title {
flex-grow: 3;
}
#ett-date-container {
flex-grow: 1;
display: flex;
border: solid 0px orange;
position: relative;
}
#ett-date {
border: 0px;
margin: 0px;
padding: 0px;
background-color: transparent;
width: 100%;
height: 100%;
border: solid 0px red;
}
.ett-task {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0px;
border: solid 0px red;
}
.ett-task-index {
margin: 0px;
padding: 0px;
}
.ett-task-name {
margin: 0px;
padding: 0px;
flex-grow: 1;
flex-shrink: 1;
min-width: 313px;
}
.ett-task-name input {
margin: 0px;
padding: 0px;
border: 0px;
width: 100%;
min-width: 313px;
border: solid 1px black;
}
.task-header .ett-task-index {
visibility: hidden;
}
.task-header .ett-task-name input {
position: relative;
left: -1.5em;
}
.ett-task-total {
flex-grow: 1;
width: 4em;
}
.ett-task-time {
flex-grow: 2;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-evenly;
margin: 0px;
padding: 0px;
max-width: 850px;
min-width: 785px;
}
.bubble-hour {
border: 0px;
margin: 0px;
padding: 0px;
overflow: hidden;
text-overflow: hidden;
width: 3em;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-evenly;
}
.bubble-hour-break {
border: 0px;
margin: 0px;
padding: 0px;
overflow: hidden;
text-overflow: hidden;
width: 0px;
}
.bubble-15m {
border: solid 2px;
border-radius: 4px;
width: 6px;
height: 17px;
margin: 0px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: start;
}
.bubble-15m > .bubble-5m {
visibility: hidden;
}
.bubble-15m.bubble3 > .bubble-5m {
flex: 1;
visibility: visible;
overflow: hidden;
margin: 0px;
padding: 0px;
border: 0px;
width: 6px;
height: 5px;
}
.bubble-15m.bubble3 > .bubble-5m:nth-child(1) {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.bubble-15m.bubble3 > .bubble-5m:nth-child(2) {
border-top: dotted 1px;
border-bottom: dotted 1px;
}
.bubble-15m.bubble3 > .bubble-5m:nth-child(3) {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
import { EttTask, BubbleHour, Bubble, Bubble3 } from './bubbles.js';
document.addEventListener('DOMContentLoaded', function() {
/// this should be dynamically loaded from local storage
const ettConfig = {
date: new Date().toLocaleDateString('en-CA'),
startHour: 7,
numHours: 12,
numTasks: 7,
numMajor: 3,
tasks: {
1: {
name: 'first task',
hours: {
7: { 15:15, 60:15 }
}
}
}
}
/// pull ETT elements out of the DOM
const ettTemplate = document.getElementById('ett-task-template')
const ettDate = document.getElementById('ett-date')
const ettTaskContainer = document.getElementById('ett-tasks')
const taskTemplate = ettTemplate.content.firstElementChild.cloneNode(true)
const hourBreakTemplate = taskTemplate.querySelector('.bubble-hour-break').cloneNode(true)
const hourTemplate = taskTemplate.querySelector('.bubble-hour').cloneNode(true)
const bubbleTemplate = taskTemplate.querySelector('.bubble-15m').cloneNode(true)
const header = taskTemplate.cloneNode(true)
header.setAttribute('role','ett-header')
const ettTotal = header.querySelector('.ett-task-total')
const subHeader = taskTemplate.cloneNode(true)
subHeader.setAttribute('role','ett-subheader')
ettDate.value = ettConfig.date
// add tasks
for ( let t=1; t<=ettConfig.numTasks; t++ ) {
// new header
if ( t == 1 ) {
// add top header - just a specially formatted task
header.classList.add('task-header')
// header title
let headerInput = header.querySelector('.ett-task-name input')
headerInput.value = 'THE MAJOR TASKS FOR TODAY'
headerInput.setAttribute('readonly','readonly')
// header hours
let hoursContainer = header.querySelector('.ett-task-time')
hoursContainer.innerHTML = ''
for ( let h=0; h<ettConfig.numHours; h++ ) {
let newHour = hourTemplate.cloneNode(true)
let hour = ettConfig.startHour+h;
if ( hour > 12 ) { hour = hour - 12; hour += "PM" }
else if ( hour == 12 ) { hour += "PM" }
else if ( hour == 24 ) { hour += "AM" }
else { hour += "AM" }
newHour.innerHTML = hour
let newBreak = hourBreakTemplate.cloneNode(true)
hoursContainer.appendChild(newBreak)
hoursContainer.appendChild(newHour)
}
let newBreak = hourBreakTemplate.cloneNode(true)
hoursContainer.appendChild(newBreak)
header.setAttribute('id','task-header')
header.setAttribute('data-date',ettConfig.date)
ettTaskContainer.appendChild(header)
// new sub-header
} else if ( t == (ettConfig.numMajor+1) ) {
// add sub header - just a specially formatted task
subHeader.classList.add('task-header')
// header title
let headerInput = subHeader.querySelector('.ett-task-name input')
headerInput.value = 'STUFF THAT "JUST HAPPENED"'
headerInput.setAttribute('readonly','readonly')
// header hours
let hoursContainer = subHeader.querySelector('.ett-task-time')
hoursContainer.innerHTML = ''
for ( let h=0; h<ettConfig.numHours; h++ ) {
let newHour = hourTemplate.cloneNode(true)
let hour = ettConfig.startHour+h;
if ( hour > 12 ) { hour = hour - 12; hour += "PM" }
else if ( hour == 12 ) { hour += "PM" }
else if ( hour == 24 ) { hour += "AM" }
else { hour += "AM" }
newHour.innerHTML = hour
let newBreak = hourBreakTemplate.cloneNode(true)
hoursContainer.appendChild(newBreak)
hoursContainer.appendChild(newHour)
}
let newBreak = hourBreakTemplate.cloneNode(true)
hoursContainer.appendChild(newBreak)
subHeader.setAttribute('id','task-subheader')
subHeader.setAttribute('data-date',ettConfig.date)
ettTaskContainer.appendChild(subHeader)
}
// new emergent task
let newTask = taskTemplate.cloneNode(true)
let hourContainer = newTask.querySelector('.ett-task-time')
if ( t in ettConfig.tasks && ettConfig.tasks[t].name )
{
let taskName = newTask.querySelector('.ett-task-name input')
if ( taskName ) {
taskName.value = ettConfig.tasks[t].name
}
}
hourContainer.innerHTML = ''
for ( let hh=0; hh<ettConfig.numHours; hh++ ) {
let newHour = hourTemplate.cloneNode(true)
let h = ettConfig.startHour+hh;
let hourId = 'task-'+t+'-hour-'+h
newHour.setAttribute('id',hourId)
newHour.setAttribute('data-hour',h)
newHour.innerHTML = '';
for ( let b=0; b<4; b++ ) {
let m = (b+1)*15;
let newBubble = bubbleTemplate.cloneNode(true)
newBubble.setAttribute('id',hourId+'-minute-'+m)
newBubble.setAttribute('data-hour-index',b)
newBubble.setAttribute('data-task-index',(4*hh)+b)
if ( t in ettConfig.tasks &&
h in ettConfig.tasks[t].hours &&
m in ettConfig.tasks[t].hours[h] &&
ettConfig.tasks[t].hours[h][m] > 0 )
{
newBubble.setAttribute('data-time',ettConfig.tasks[t].hours[h][m])
}
newHour.appendChild(newBubble)
}
let newBreak = hourBreakTemplate.cloneNode(true)
hourContainer.appendChild(newBreak)
hourContainer.appendChild(newHour)
}
let newBreak = hourBreakTemplate.cloneNode(true)
hourContainer.appendChild(newBreak)
newTask.setAttribute('id','task-'+t)
let indexElement = newTask.querySelector('.ett-task-index')
if ( t < 10 ) {
indexElement.innerHTML = '0'+t
} else {
indexElement.innerHTML = t
}
newTask.setAttribute('data-date',ettConfig.date);
if ( t <= ettConfig.numMajor ) {
newTask.classList.add('ett-major-task')
}
ettTaskContainer.appendChild(newTask);
}
const sumTime = ev => {
let total = 0;
let ettTasks = ettTaskContainer.querySelectorAll('[role=ett-task]')
ettTasks.forEach(ettTask => {
total += parseInt(ettTask.getAttribute('data-time'))
});
let hours = (total/60)
ettTotal.innerHTML = hours.toFixed(2)+' H';
};
let ettTasks = ettTaskContainer.querySelectorAll('[role=ett-task]')
ettTasks.forEach(ettTaskElement => {
let ettTask = new EttTask(ettTaskElement,sumTime)
});
/// DATE PICKER
const calPopup = document.querySelector('#ett-date');
const datepicker = new Datepicker(calPopup, {
format: 'yyyy-mm-dd'
});
calPopup.addEventListener('focus',ev => {
let dp = ev.target.parentNode.querySelector('.datepicker')
dp.style.marginLeft = '-128px';
dp.style.left = '50%';
})
/// PERSISTENCE
document.addEventListener('persist',ev => {
console.log('SAVE')
})
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Emergent Task Timer</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="ett.layout.css">
<link rel="stylesheet" href="ett.styles.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.2.0/dist/css/datepicker.min.css">
</head>
<!-- https://developer.okta.com/blog/2019/12/18/vanilla-javascript-components -->
<body id="ett">
<header id="ett-header">
<div id="ett-title">Emergent Task Timer</div>
<div id="ett-date-container">
<input id="ett-date" value="YYYY-MM-DD" />
</div>
</header>
<main id="ett-tasks"></main>
<template id="ett-task-template">
<div class="ett-task" role="ett-task" data-date="" data-time="0" data-name="">
<div class="ett-task-index"><br /></div
><div class="ett-task-name"><input name="ett-task-name" /></div
><div class="ett-task-time"
unselectable="on"
onselectstart="return false;"
onmousedown="return false;">
<div class="bubble-hour-break"><br /></div>
<div class="bubble-hour" role="bubble-hour" data-time="0">
<div class="bubble-15m" role="bubble-15m" data-time="0">
<div class="bubble-5m" data-value="15"><br /></div>
<div class="bubble-5m" data-value="10"><br /></div>
<div class="bubble-5m" data-value="5"><br /></div>
</div>
</div>
<div class="bubble-hour-break"><br /></div>
</div
><div class="ett-task-total">0</div>
</div>
</template>
<script type="module" src="ett.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.2.0/dist/js/datepicker.min.js"></script>
</body>
</html>
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path d="M2904.7,4993.8c-142.2-44.2-244.1-132.6-305.6-271c-38.4-82.6-40.4-123-40.4-918.6c0-703.4,3.9-841.7,28.8-901.3c78.8-190.2,244.1-299.8,442-299.8c194.1,0,349.8,98,436.2,271c44.2,90.3,44.2,92.3,44.2,916.7c0,914.8,0,916.7-123,1053.1C3264,4980.4,3064.2,5041.9,2904.7,4993.8z"/><path d="M6809.7,4988c-99.9-34.6-246-176.8-286.4-280.6c-30.7-82.6-34.6-163.4-34.6-916.7v-826.4l44.2-90.3c90.3-182.6,305.6-301.7,493.9-274.8c128.8,21.1,276.7,117.2,345.9,228.7l57.7,94.2l5.8,847.5c3.8,751.4,1.9,857.1-26.9,932.1c-40.4,107.6-126.9,205.6-226.8,255.6C7092.2,5003.4,6902,5018.8,6809.7,4988z"/><path d="M1017.5,3756.2c-228.7-78.8-417-240.2-522.7-449.7c-90.3-178.7-88.4-76.9-88.4-3784c0-2409.9,5.8-3514.9,21.1-3593.7c44.2-244.1,221-492,434.3-607.3c215.2-119.2-38.4-111.5,4137.6-111.5c4174.1,0,3922.3-5.8,4135.6,109.5c213.3,115.3,392,367.1,436.3,609.2c30.7,163.3,28.8,7022.1,0,7164.3c-69.2,326.7-320.9,595.8-640,680.3c-71.1,19.2-242.1,26.9-640,26.9h-543.8V3393c0-284.4-7.7-440.1-26.9-507.3c-61.5-228.7-213.3-415.1-430.5-520.8c-99.9-50-128.8-53.8-320.9-53.8c-199.9,0-219.1,3.8-340.2,63.4c-161.4,78.8-320.9,246-382.4,401.7c-42.3,103.8-46.1,144.1-51.9,568.8l-5.8,455.5H4997.5H3807.9v-418.9c0-326.7-7.7-442-30.7-524.7c-61.5-209.5-226.8-399.7-426.6-493.9c-98-46.1-132.6-51.9-320.9-51.9c-192.2,0-221,3.8-320.9,53.8c-222.9,109.5-388.2,315.2-436.2,547.7c-11.5,55.7-21.1,278.7-21.1,493.9v394h-551.5C1173.2,3798.5,1140.5,3796.5,1017.5,3756.2z M8992.8-1401.8c-5.8-2594.4-5.8-2629-44.2-2680.9c-21.1-26.9-63.4-67.3-96.1-86.5c-55.7-32.7-205.6-34.6-3853.1-34.6H1203.9l-69.2,44.2c-142.2,88.4-132.6-123-132.6,2778.9v2605.9h3997.3h3997.3L8992.8-1401.8z"/><path d="M2307,527.6c-142.2-75-171-148-171-443.9c0-219.1,3.9-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,518.9-42.3s455.5,1.9,518.9,42.3c38.4,23,88.4,76.9,111.5,117.2c36.5,67.3,42.3,103.8,42.3,320.9c0,228.7-3.8,251.8-48,328.6c-30.8,53.8-74.9,94.2-130.7,121.1c-76.9,36.5-115.3,40.4-497.7,40.4C2408.9,571.8,2387.7,569.9,2307,527.6z"/><path d="M4497.8,527.6c-142.2-75-171-148-171-443.9c0-219.1,3.8-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,518.9-42.3c432.4,0,455.5,1.9,518.9,42.3c38.4,23,88.4,76.9,111.5,117.2c38.4,67.3,42.3,103.8,42.3,322.9c0,296-28.8,369-171,443.9c-80.7,42.3-101.9,44.2-501.6,44.2C4599.7,571.8,4578.5,569.9,4497.8,527.6z"/><path d="M6688.6,527.6c-142.2-75-171-148-171-443.9c0-219.1,3.8-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,518.9-42.3c432.4,0,455.4,1.9,518.9,42.3c38.4,23,88.4,76.9,111.5,117.2c38.4,67.3,42.3,103.8,42.3,322.9c0,296-28.8,369-171.1,443.9c-80.7,42.3-101.8,44.2-501.6,44.2S6769.3,569.9,6688.6,527.6z"/><path d="M2308.9-1046.3c-142.2-73-173-148-173-445.8c0-219.1,3.9-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,520.8-42.3c449.7,0,453.5,0,522.7,48c124.9,86.5,148,153.7,148,434.3c0,296-28.8,369-171,443.9c-80.7,42.3-101.9,44.2-499.7,44.2C2416.5-1004,2389.6-1006,2308.9-1046.3z"/><path d="M4497.8-1048.2c-142.2-75-171-148-171-443.9c0-219.1,3.8-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,520.8-42.3c449.7,0,453.5,0,522.7,48c124.9,86.5,148,153.7,148,434.3c0,296-28.8,369-171,443.9c-80.7,42.3-101.9,44.2-501.6,44.2C4599.7-1004,4578.5-1006,4497.8-1048.2z"/><path d="M6688.6-1048.2c-142.2-75-171-148-171-443.9c0-219.1,3.8-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,518.9-42.3c432.4,0,455.4,1.9,518.9,42.3c38.4,23,88.4,76.9,111.5,117.2c38.4,67.3,42.3,103.8,42.3,322.9c0,296-28.8,369-171.1,443.9c-80.7,42.3-101.8,44.2-501.6,44.2S6769.3-1006,6688.6-1048.2z"/><path d="M2308.9-2622.2c-142.2-73-173-148-173-445.8c0-219.1,3.9-255.6,42.3-322.9c82.6-146,126.8-159.5,563.1-165.3c468.9-7.7,528.5,0,618.8,78.8c103.8,90.3,121.1,151.8,121.1,420.9c0,282.5-30.7,357.4-171,432.4c-80.7,42.3-101.9,44.2-499.7,44.2C2416.5-2579.9,2389.6-2581.8,2308.9-2622.2z"/><path d="M4499.7-2622.2c-142.2-73-173-148-173-445.8c0-219.1,3.8-255.6,42.3-322.9c23.1-40.4,73-94.2,111.5-117.2c63.4-40.4,86.5-42.3,520.8-42.3c449.7,0,453.5,0,522.7,48c124.9,86.5,148,153.7,148,434.3c0,296-28.8,369-171,443.9c-80.7,42.3-101.9,44.2-499.7,44.2C4607.4-2579.9,4580.5-2581.8,4499.7-2622.2z"/></g></g>
</svg>
class EttTask {
constructor(taskElement, onUpdate) {
let self = this
/* data */
const bubbleHourElements = taskElement.querySelectorAll('[role=bubble-hour]')
const taskTotal = taskElement.querySelector('.ett-task-total')
// var lineStart = {x:0,y:0}
var multiSelectFill = false
this.element = taskElement
this.bubbleHours = []
this.bubbles = []
this.bubbleStartIndex = null
/* actions */
const sumTime = ev => {
let total = 0
bubbleHourElements.forEach(bubbleHour => {
total += parseInt(bubbleHour.getAttribute('data-time'))
})
taskElement.setAttribute('data-time', total)
let hours = (total / 60)
taskTotal.innerHTML = hours.toFixed(2) + ' H'
onUpdate()
}
/* init */
bubbleHourElements.forEach(bubbleHourElement => {
let bubbleHour = new BubbleHour(bubbleHourElement, sumTime, self)
self.bubbleHours.push(bubbleHour)
})
taskElement.ett = this
sumTime()
return this
}
}
class BubbleHour {
constructor(hourElement, onUpdate, task) {
let self = this
/* data */
this.element = hourElement
this.bubbleElements = hourElement.querySelectorAll('[role=bubble-15m]')
this.bubbles = []
/* actions */
const sumTime = ev => {
let total = 0
// let bubbles = ev.currentTarget.querySelectorAll('> [role=bubble-15m]')
self.bubbleElements.forEach(bubbleElement => {
total += parseInt(bubbleElement.getAttribute('data-time'))
})
hourElement.setAttribute('data-time', total)
onUpdate()
}
/* init */
this.bubbleElements.forEach(bubbleElement => {
let bubble = new Bubble(bubbleElement, sumTime, task, self)
self.bubbles.push(bubble)
task.bubbles.push(bubble)
})
hourElement.ett = this
sumTime()
return this
}
}
class Bubble3 {
constructor(bubbleElement, onUpdate, task) {
let self = this
/* data */
this.element = bubbleElement
this.bubbles5m = bubbleElement.querySelectorAll('.bubble-5m')
/* actions */
const fillBubble = (time) => {
bubbles5m.forEach(bubble => {
time = parseInt(time)
let minTrigger = parseInt(bubble.getAttribute('data-value')) - 4
if (time > 0 && time >= minTrigger) {
bubble.classList.add('fill')
} else {
bubble.classList.remove('fill')
}
})
}
const sumTime = ev => {
const currentTime = bubbleElement.getAttribute('data-time')
fillBubble(currentTime)
}
const cycleTime = ev => {
let currentTime = parseInt(bubbleElement.getAttribute('data-time'))
if (currentTime >= 15) {
currentTime = 0
} else {
currentTime += 5
}
bubbleElement.setAttribute('data-time', currentTime)
fillBubble(currentTime)
onUpdate()
}
/* init */
bubbleElement.addEventListener('click', cycleTime)
bubbleElement.classList.add('bubble3')
bubbleElement.ett = this
sumTime()
return this
}
}
class Bubble {
constructor(bubbleElement, onUpdate, task, hour) {
let self = this
/* data */
this.element = bubbleElement
/* actions */
this.fillBubble = (time) => {
bubbleElement.setAttribute('data-time', parseInt(time))
if (parseInt(time) > 0) {
bubbleElement.classList.add('fill')
} else {
bubbleElement.classList.remove('fill')
}
onUpdate()
}
const sumTime = ev => {
const currentTime = bubbleElement.getAttribute('data-time')
self.fillBubble(currentTime)
}
const cycleTime = ev => {
var currentTime = parseInt(bubbleElement.getAttribute('data-time'))
if (currentTime > 0) {
currentTime = 0
} else {
currentTime = 15
}
self.fillBubble(currentTime)
document.dispatchEvent(new Event('persist'))
}
/* init */
const preMultiFill = ev => {
bubbleElement.addEventListener('mouseup', cycleTime)
bubbleElement.addEventListener('mouseout', multiSelectActivate)
document.addEventListener('mouseup', multiSelectCancel)
task.bubbleStartIndex = parseInt(bubbleElement.getAttribute('data-task-index'))
}
const multiSelectActivate = ev => {
bubbleElement.removeEventListener('mouseup', cycleTime)
bubbleElement.removeEventListener('mouseout', multiSelectActivate)
task.multiSelectActive = true
const currentTime = parseInt(bubbleElement.getAttribute('data-time'))
if (currentTime > 0) {
task.multiSelectFill = false
self.fillBubble(0)
document.body.style.cursor = 'not-allowed'
} else {
task.multiSelectFill = true
self.fillBubble(15)
document.body.style.cursor = 'copy'
}
}
const multiSelectCancel = ev => {
task.multiSelectActive = false
bubbleElement.removeEventListener('mouseout', multiSelectActivate)
document.removeEventListener('mouseup', multiSelectCancel)
task.bubbleStartIndex = null
document.body.style.cursor = 'default'
document.dispatchEvent(new Event('persist'))
}
const multiFill = ev => {
if (task.multiSelectActive) {
if (task.multiSelectFill) {
self.fillBubble(15)
} else {
self.fillBubble(0)
}
let bubbleIndex = parseInt(bubbleElement.getAttribute('data-task-index'))
let bubbleStartIndex = parseInt(task.bubbleStartIndex)
if (bubbleStartIndex !== null && bubbleStartIndex !== bubbleIndex) {
let startIndex = Math.min(bubbleIndex,task.bubbleStartIndex);
let endIndex = Math.max(bubbleIndex,task.bubbleStartIndex);
for (let i = startIndex; i <= endIndex; i++) {
let betweenBubble = task.element.querySelector('[data-task-index="' + i + '"]')
if ( betweenBubble ) {
if (task.multiSelectFill) {
betweenBubble.ett.fillBubble(15)
} else {
betweenBubble.ett.fillBubble(0)
}
}
}
}
}
}
bubbleElement.addEventListener('mousedown', preMultiFill)
bubbleElement.addEventListener('mouseenter', multiFill)
bubbleElement.ett = this
sumTime()
return this
}
}
export { EttTask, BubbleHour, Bubble, Bubble3 }
.git
.DS_Store