diff --git a/Makefile b/Makefile
index d331a85..974694e 100644
--- a/Makefile
+++ b/Makefile
@@ -60,7 +60,9 @@ assets/js/third-party/luxon.min.js:
ICONS = \
solid_sun \
solid_moon \
- solid_adjust
+ solid_adjust \
+ solid_trash \
+ solid_plus
.PHONY: download-icons
download-icons: $(foreach icon,$(ICONS),templates/icon_$(icon).svg)
diff --git a/assets/css/styles.css b/assets/css/styles.css
index 53253f8..919b4fd 100644
--- a/assets/css/styles.css
+++ b/assets/css/styles.css
@@ -66,6 +66,12 @@ footer {
padding: 1.5rem;
}
+/* Web Component Fixes */
+
+template {
+ display: none;
+}
+
/* Essentials */
.list-inline {
@@ -202,7 +208,7 @@ d-zoneerror {
}
/* Left container */
-d-zoneinfo {
+.zoneinfo {
flex: 1 1 0;
width: 0px; /* Force sizing from zero */
@@ -212,7 +218,7 @@ d-zoneinfo {
margin-right: 1rem;
}
@media (max-width: 319px) {
- d-zoneinfo {
+ .zoneinfo {
width: 100%;
}
}
@@ -221,7 +227,7 @@ d-zonename {
display: flex;
justify-content: flex-start;
}
-d-zonename d-zonearea {
+d-zonename .zonearea {
flex: 0 1 auto;
display: block;
@@ -230,7 +236,7 @@ d-zonename d-zonearea {
white-space: nowrap;
text-overflow: ellipsis;
}
-d-zonename d-zonecountry {
+d-zonename .zonecountry {
flex: 0 0 auto;
display: block;
@@ -251,7 +257,7 @@ d-date {
}
/* Right container */
-d-zonefigure {
+.zonefigure {
display: block;
margin-top: 1rem;
margin-bottom: 1rem;
diff --git a/assets/js/interactive.js b/assets/js/interactive.js
index 70b786d..68149a1 100644
--- a/assets/js/interactive.js
+++ b/assets/js/interactive.js
@@ -1 +1,339 @@
-// TODO
+'use strict';
+
+/*
+ * Compatibility Checks
+ */
+
+if (!window.customElements) {
+ console.warn('Custom Elements API is not available. Thus, interactivity is not available');
+}
+
+/*
+ * Custom Elements
+ */
+
+const slotTemplate = document.createElement('template');
+slotTemplate.innerHTML = `
+
+`;
+
+// Icons
+
+const iconSolidTrashTemplate = document.querySelector('#icon-solid-trash');
+class IconSolidTrashElement extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(iconSolidTrashTemplate.content.cloneNode(true));
+ }
+}
+
+const iconSolidPlusTemplate = document.querySelector('#icon-solid-plus');
+class IconSolidPlusElement extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(iconSolidPlusTemplate.content.cloneNode(true));
+ }
+}
+
+// Zone
+
+const zoneTemplate = document.createElement('template');
+zoneTemplate.innerHTML = `
+
+
+
+
+
+`;
+
+class ZoneElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(zoneTemplate.content.cloneNode(true));
+ }
+}
+
+// ZoneName
+
+const zoneNameTemplate = document.createElement('template');
+zoneNameTemplate.innerHTML = `
+
+
+`;
+
+class ZoneNameElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(zoneNameTemplate.content.cloneNode(true));
+ }
+}
+
+// ZoneOffset
+
+const zoneOffsetTemplate = document.createElement('template');
+zoneOffsetTemplate.innerHTML = `
+
+
+`;
+
+class ZoneOffsetElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(zoneOffsetTemplate.content.cloneNode(true));
+ }
+}
+
+// Date
+
+class DateElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(slotTemplate.content.cloneNode(true));
+ }
+}
+
+// Time
+
+class TimeElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(slotTemplate.content.cloneNode(true));
+ }
+}
+
+// SearchList
+
+const zoneSearchTemplate = document.createElement('template');
+zoneSearchTemplate.innerHTML = `
+
+
+
+
+
+
+
+
+ Zone Name+XX:XX
+
+
+
+
+
+
+
+
+`;
+
+class ZoneSearchElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(zoneSearchTemplate.content.cloneNode(true));
+ this.shadowRoot.querySelector('input[type=search]').addEventListener('input', this.input.bind(this));
+ }
+
+ input() {
+ const text = this.shadowRoot.querySelector('input[type=search]').value;
+ const e = new CustomEvent('searchinput', { detail: { text } });
+ this.dispatchEvent(e);
+ }
+
+ show(results) {
+ const resultsElement = this.shadowRoot.querySelector('#results');
+ const template = this.shadowRoot.querySelector('#result-template').content;
+
+ resultsElement.innerHTML = '';
+ for (const result of results) {
+ const resultElement = template.cloneNode(true);
+ resultElement.querySelector('d-zonename').innerText = result.n;
+ resultElement.querySelector('d-zoneoffset').innerText = '+to:do';
+ resultElement.querySelector('.add').dataset.id = result.id;
+ resultElement.querySelector('.add').addEventListener('click', (e) => {
+ this.resultClick(result);
+ });
+ resultsElement.appendChild(resultElement);
+ }
+ }
+
+ resultClick(zone) {
+ console.debug(zone.id);
+ }
+}
+
+// ZoneAdd
+
+const zoneAddTemplate = document.createElement('template');
+zoneAddTemplate.innerHTML = `
+
+
+
+
+
+`;
+
+class ZoneAddElement extends HTMLElement {
+ constructor() {
+ super();
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(zoneAddTemplate.content.cloneNode(true));
+
+ this.searchElement = this.shadowRoot.querySelector('d-zoneadd-search');
+ window.s = this.searchElement;
+ this.searchElement.show([
+ { n: 'St. John\'s, Newfoundland and Labrador, CA', id: 'St_John\'s-Newfoundland_and_Labrador-CA' },
+ { n: 'Singapore, SG', id: 'Singapore-SG' },
+ ]);
+ this.searchElement.addEventListener('searchinput', this.input.bind(this));
+ }
+
+ input({ detail: { text } }) {
+ console.debug(text);
+ }
+}
+
+// Definitions
+
+window.customElements.define('icon-solid-trash', IconSolidTrashElement);
+window.customElements.define('icon-solid-plus', IconSolidPlusElement);
+
+window.customElements.define('d-zone', ZoneElement);
+window.customElements.define('d-zonename', ZoneNameElement);
+window.customElements.define('d-zoneoffset', ZoneOffsetElement);
+window.customElements.define('d-date', DateElement);
+window.customElements.define('d-time', TimeElement);
+// TODO: Instead of relying on , render using attributes
+
+window.customElements.define('d-zoneadd-search', ZoneSearchElement);
+window.customElements.define('d-zoneadd', ZoneAddElement);
diff --git a/templates/icon_solid_plus.svg b/templates/icon_solid_plus.svg
new file mode 100644
index 0000000..c89cf64
--- /dev/null
+++ b/templates/icon_solid_plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/templates/icon_solid_trash.svg b/templates/icon_solid_trash.svg
new file mode 100644
index 0000000..baafaa4
--- /dev/null
+++ b/templates/icon_solid_trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 9de022c..e8f2d5e 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -22,11 +22,11 @@
{{else}}
{{$zt := $t.In .Location}}
-
+
{{if not .IsOffset}}
- {{.FirstName}},
- {{.City.Country.Ref}}
+ {{.FirstName}},
+ {{.City.Country.Ref}}
{{else}}
@@ -37,18 +37,22 @@
{{.TimeOffset $t | formatOffset}}
{{end}}
{{$zt.Format "2006-01-02"}}
-
-
+
+
{{$zt.Format "15:04"}}
-
+
{{end}}
{{end}}
{{end}}
+
+
{{template "footer.html"}}
+ {{template "interactive-icons.html"}}
+