Compare commits
1 Commits
0.4.0
...
2aab080dec
| Author | SHA1 | Date | |
|---|---|---|---|
| 2aab080dec |
@@ -33,66 +33,29 @@
|
|||||||
type="submit">{{_("Search")}}</button>
|
type="submit">{{_("Search")}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{% if locations %}
|
||||||
<ul id="locations"
|
<ul id="locations"
|
||||||
class="list-group mb-5">
|
class="list-group mb-5">
|
||||||
{% for location in locations %}
|
{% for location in locations %}
|
||||||
<a href="{{ url_for('get_weather_default', provider=request.path_params.provider, location=location.id) }}"
|
<weather-location location="{{location.model_dump() | tojson | forceescape}}"></weather-location>
|
||||||
class="list-group-item list-group-item-action px-4"
|
|
||||||
onclick="saveLocation({id:'{{location.id}}', name:'{{location.name}}'});">
|
|
||||||
<span class="fi fi-{{location.country_code}} me-1"></span>
|
|
||||||
<span class="text-primary">{{location.name}}</span>
|
|
||||||
<span class="small ms-1 text-secondary">
|
|
||||||
{{location.country}}, {{location.district}}, {{location.subdistrict}}
|
|
||||||
</span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
<hr>
|
||||||
|
{% endif %}
|
||||||
|
<ul id="storedLocations"
|
||||||
|
class="list-group mb-5">
|
||||||
|
</ul>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const getStorageKey = () => {
|
|
||||||
const provider = window.location.pathname.split('/').pop();
|
|
||||||
return `locations:${provider}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.loadLocations = () => {
|
|
||||||
const provider = window.location.pathname.split('/').pop();
|
|
||||||
console.log('!', provider);
|
|
||||||
const locations = JSON.parse(window.localStorage.getItem(getStorageKey()) || '{}');
|
|
||||||
const container = document.querySelector('#locations');
|
|
||||||
container.innerHTML = '';
|
|
||||||
for (const [id, name] of Object.entries(locations)) {
|
|
||||||
const element = document.createElement('a');
|
|
||||||
element.href = `${window.location.pathname}/${id}`;
|
|
||||||
element.className = 'list-group-item list-group-item-action px-4 d-flex justify-content-between align-items-start';
|
|
||||||
element.innerHTML = `
|
|
||||||
<span class="text-primary me-auto">${name}</span>
|
|
||||||
<span class="text-danger" onclick="removeLocation('${id}'); event.preventDefault();">✕</span>
|
|
||||||
`;
|
|
||||||
container.appendChild(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.saveLocation = (location) => {
|
|
||||||
const locations = JSON.parse(window.localStorage.getItem(getStorageKey()) || '{}');
|
|
||||||
locations[location.id] = location.name;
|
|
||||||
window.localStorage.setItem(getStorageKey(), JSON.stringify(locations));
|
|
||||||
}
|
|
||||||
|
|
||||||
document.removeLocation = (id) => {
|
|
||||||
const locations = JSON.parse(window.localStorage.getItem(getStorageKey()) || '{}');
|
|
||||||
delete locations[id];
|
|
||||||
window.localStorage.setItem(getStorageKey(), JSON.stringify(locations));
|
|
||||||
document.loadLocations();
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const searchQuery = params.get('query');
|
const searchQuery = params.get('query');
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
document.querySelector('#query').value = searchQuery;
|
document.querySelector('#query').value = searchQuery;
|
||||||
} else {
|
|
||||||
document.loadLocations();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
weatherLocationManager.loadLocations('#storedLocations');
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -3,6 +3,7 @@ import "./components";
|
|||||||
import "./language";
|
import "./language";
|
||||||
import "./main.scss";
|
import "./main.scss";
|
||||||
import "./theme";
|
import "./theme";
|
||||||
|
import "./weather/weather";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (event) => {
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
@import "./lib/weather-icons/weather-icons";
|
@import "./lib/weather-icons/weather-icons";
|
||||||
|
|
||||||
@import "./widget.scss";
|
@import "./widget.scss";
|
||||||
@import "./weather.scss";
|
@import "./weather/weather.scss";
|
||||||
|
|
||||||
.table.table-compact {
|
.table.table-compact {
|
||||||
td {
|
td {
|
||||||
|
|||||||
113
static/src/weather/weather.ts
Normal file
113
static/src/weather/weather.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
export interface Location {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
country_code: string;
|
||||||
|
country?: string;
|
||||||
|
district?: string;
|
||||||
|
subdistrict?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WeatherLocationStorage {
|
||||||
|
private storageKey: string = "weather:locations";
|
||||||
|
|
||||||
|
getAll(): Location[] {
|
||||||
|
const locations = JSON.parse(window.localStorage.getItem(this.storageKey) || "{}");
|
||||||
|
return Object.values(locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(location: Location) {
|
||||||
|
const locations = JSON.parse(window.localStorage.getItem(this.storageKey) || "{}");
|
||||||
|
locations[location.id] = location;
|
||||||
|
window.localStorage.setItem(this.storageKey, JSON.stringify(locations));
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(id: string) {
|
||||||
|
const locations = JSON.parse(window.localStorage.getItem(this.storageKey) || "{}");
|
||||||
|
delete locations[id];
|
||||||
|
window.localStorage.setItem(this.storageKey, JSON.stringify(locations));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WeatherLocationManager {
|
||||||
|
loadLocations(selector: string) {
|
||||||
|
const locations = window.weatherLocationStorage.getAll();
|
||||||
|
const container = document.querySelector(selector);
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = "";
|
||||||
|
for (const location of locations) {
|
||||||
|
const element = new WeatherLocationElement();
|
||||||
|
element.setAttribute("location", JSON.stringify(location));
|
||||||
|
element.setAttribute("removable", "");
|
||||||
|
container.appendChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WeatherLocationElement extends HTMLElement {
|
||||||
|
static observedAttributes = ["location", "removable"];
|
||||||
|
|
||||||
|
location: Location | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
this.handleRemoveClick = this.handleRemoveClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.location = JSON.parse(this.getAttribute("location") || "{}");
|
||||||
|
this.innerHTML = `
|
||||||
|
<a href="${this.location?.provider}/${this.location?.id}"
|
||||||
|
class="list-group-item list-group-item-action px-4 d-flex justify-content-between align-items-start">
|
||||||
|
<span>
|
||||||
|
<span class="fi fi-${this.location?.country_code} me-1"></span>
|
||||||
|
<span class="text-primary">${this.location?.name}</span>
|
||||||
|
<span class="small ms-1 text-secondary">
|
||||||
|
${this.location?.country}, ${this.location?.district}, ${this.location?.subdistrict}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-danger" data-remove>
|
||||||
|
✕
|
||||||
|
</span>
|
||||||
|
</a>`;
|
||||||
|
this.addEventListener("click", this.handleClick);
|
||||||
|
if (this.hasAttribute("removable")) {
|
||||||
|
this.querySelector("[data-remove]")?.addEventListener("click", this.handleRemoveClick);
|
||||||
|
} else {
|
||||||
|
this.querySelector("[data-remove]")?.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.removeEventListener("click", this.handleClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick(event: MouseEvent) {
|
||||||
|
if (this.location) {
|
||||||
|
window.weatherLocationStorage.add(this.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRemoveClick(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.location) {
|
||||||
|
window.weatherLocationStorage.remove(this.location.id);
|
||||||
|
}
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("weather-location", WeatherLocationElement);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
weatherLocationStorage: WeatherLocationStorage;
|
||||||
|
weatherLocationManager: WeatherLocationManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.weatherLocationStorage = new WeatherLocationStorage();
|
||||||
|
window.weatherLocationManager = new WeatherLocationManager();
|
||||||
Reference in New Issue
Block a user