Skip to content

Commit

Permalink
feat: add lazy component
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Sep 22, 2024
1 parent 0a9390f commit 3f0b814
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 152 deletions.
10 changes: 10 additions & 0 deletions examples/cards-with-remote-content/lazy.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
select 'lazy' as component;

select
'/chart-example.sql?_sqlpage_embed' as embed,
'card my-2' as class,
'height:340px' as style;

select '/map-example.sql' as embed;

select '/table-example.sql?_sqlpage_embed' as embed;
288 changes: 148 additions & 140 deletions sqlpage/sqlpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,50 @@

const nonce = document.currentScript.nonce;

function sqlpage_card() {
for (const c of document.querySelectorAll("[data-pre-init=card]")) {
const source = c.dataset.embed;
fetch(c.dataset.embed)
.then(res => res.text())
.then(html => {
const body = c.querySelector(".card-content");
body.innerHTML = html;
c.removeAttribute("data-pre-init");
const spinner = c.querySelector(".card-loading-placeholder");
if (spinner) {
spinner.parentNode.removeChild(spinner);
}
const fragLoadedEvt = new CustomEvent("fragment-loaded", {
bubbles: true
});
c.dispatchEvent(fragLoadedEvt);
})
function sqlpage_embed() {
for (const c of document.querySelectorAll("[data-embed]")) {
if (c.ariaBusy === "true") return;
c.ariaBusy = true;
const [source, params] = c.dataset.embed.split("?");
if (!source) return;
const search = new URLSearchParams(params);
if (!search.has("_sqlpage_embed")) {
search.set("_sqlpage_embed", "true");
}
fetch(`${source}?${search}`)
.then(res => res.text())
.then(html => {
c.innerHTML = html;
c.ariaBusy = false;
delete c.dataset.embed;
c.dispatchEvent(new CustomEvent("fragment-loaded", {
bubbles: true
}));
})
.catch(err => console.error("Fetch error: ", err));
}
}

function sqlpage_table(){
// Tables
for (const r of document.querySelectorAll("[data-pre-init=table]")) {
new List(r, {
valueNames: [...r.getElementsByTagName("th")].map(t => t.textContent),
searchDelay: 100,
// Hurts performance, but prevents https://github.com/lovasoa/SQLpage/issues/542
// indexAsync: true
});
r.removeAttribute("data-pre-init");
}
function sqlpage_table() {
// Tables
for (const r of document.querySelectorAll("[data-pre-init=table]")) {
new List(r, {
valueNames: [...r.getElementsByTagName("th")].map(t => t.textContent),
searchDelay: 100,
// Hurts performance, but prevents https://github.com/lovasoa/SQLpage/issues/542
// indexAsync: true
});
r.removeAttribute("data-pre-init");
}
}

function sqlpage_select_dropdown(){
function sqlpage_select_dropdown() {
const selects = document.querySelectorAll("[data-pre-init=select-dropdown]");
if (!selects.length) return;
const src = "https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.popular.min.js";
if (!window.TomSelect) {
const script = document.createElement("script");
script.src= src;
script.src = src;
script.integrity = "sha384-aAqv9vleUwO75zAk1sGKd5VvRqXamBXwdxhtihEUPSeq1HtxwmZqQG/HxQnq7zaE";
script.crossOrigin = "anonymous";
script.nonce = nonce;
Expand All @@ -52,137 +55,142 @@ function sqlpage_select_dropdown(){
return;
}
for (const s of selects) {
new TomSelect(s, {
create: s.dataset.create_new
});
new TomSelect(s, {
create: s.dataset.create_new
});
}
}

let is_leaflet_injected = false;
let is_leaflet_loaded = false;

function sqlpage_map() {
const first_map = document.querySelector("[data-pre-init=map]");
if (first_map && !is_leaflet_injected) {
// Add the leaflet js and css to the page
const leaflet_css = document.createElement("link");
leaflet_css.rel = "stylesheet";
leaflet_css.href = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css";
leaflet_css.integrity = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
leaflet_css.crossOrigin = "anonymous";
document.head.appendChild(leaflet_css);
const leaflet_js = document.createElement("script");
leaflet_js.src = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js";
leaflet_js.integrity = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";
leaflet_js.crossOrigin = "anonymous";
leaflet_js.nonce = nonce;
leaflet_js.onload = onLeafletLoad;
document.head.appendChild(leaflet_js);
is_leaflet_injected = true;
}
if (first_map && is_leaflet_loaded) {
onLeafletLoad();
}
function parseCoords(coords) {
return coords && coords.split(",").map(c => parseFloat(c));
}
function onLeafletLoad() {
is_leaflet_loaded = true;
const maps = document.querySelectorAll("[data-pre-init=map]");
for (const m of maps) {
const tile_source = m.dataset.tile_source;
const maxZoom = +m.dataset.max_zoom;
const attribution = m.dataset.attribution;
const map = L.map(m, { attributionControl: !!attribution });
const zoom = m.dataset.zoom;
let center = parseCoords(m.dataset.center);
if (tile_source) L.tileLayer(tile_source, { attribution, maxZoom }).addTo(map);
map._sqlpage_markers = [];
for (const marker_elem of m.getElementsByClassName("marker")) {
setTimeout(addMarker, 0, marker_elem, map);
}
setTimeout(() => {
if (center == null && map._sqlpage_markers.length) {
map.fitBounds(map._sqlpage_markers.map(m =>
m.getLatLng ? m.getLatLng() : m.getBounds()
));
if (zoom != null) map.setZoom(+zoom);
} else map.setView(center, +zoom);
}, 100);
m.removeAttribute("data-pre-init");
m.getElementsByClassName("spinner-border")[0]?.remove();
const first_map = document.querySelector("[data-pre-init=map]");
if (first_map && !is_leaflet_injected) {
// Add the leaflet js and css to the page
const leaflet_css = document.createElement("link");
leaflet_css.rel = "stylesheet";
leaflet_css.href = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css";
leaflet_css.integrity = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
leaflet_css.crossOrigin = "anonymous";
document.head.appendChild(leaflet_css);
const leaflet_js = document.createElement("script");
leaflet_js.src = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js";
leaflet_js.integrity = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";
leaflet_js.crossOrigin = "anonymous";
leaflet_js.nonce = nonce;
leaflet_js.onload = onLeafletLoad;
document.head.appendChild(leaflet_js);
is_leaflet_injected = true;
}
if (first_map && is_leaflet_loaded) {
onLeafletLoad();
}
function parseCoords(coords) {
return coords && coords.split(",").map(c => parseFloat(c));
}
function onLeafletLoad() {
is_leaflet_loaded = true;
const maps = document.querySelectorAll("[data-pre-init=map]");
for (const m of maps) {
const tile_source = m.dataset.tile_source;
const maxZoom = +m.dataset.max_zoom;
const attribution = m.dataset.attribution;
const map = L.map(m, { attributionControl: !!attribution });
const zoom = m.dataset.zoom;
let center = parseCoords(m.dataset.center);
if (tile_source) L.tileLayer(tile_source, { attribution, maxZoom }).addTo(map);
map._sqlpage_markers = [];
for (const marker_elem of m.getElementsByClassName("marker")) {
setTimeout(addMarker, 0, marker_elem, map);
}
setTimeout(() => {
if (center == null && map._sqlpage_markers.length) {
map.fitBounds(map._sqlpage_markers.map(m =>
m.getLatLng ? m.getLatLng() : m.getBounds()
));
if (zoom != null) map.setZoom(+zoom);
} else map.setView(center, +zoom);
}, 100);
m.removeAttribute("data-pre-init");
m.getElementsByClassName("spinner-border")[0]?.remove();
}
}

function addMarker(marker_elem, map) {
const { dataset } = marker_elem;
const options = {
color: marker_elem.dataset.color,
title: marker_elem.getElementsByTagName("h3")[0].textContent.trim(),
};
const marker =
dataset.coords ? createMarker(marker_elem, options)
: createGeoJSONMarker(marker_elem, options);
marker.addTo(map);
map._sqlpage_markers.push(marker);
if (options.title) marker.bindPopup(marker_elem);
else if (marker_elem.dataset.link) marker.on('click', () => window.location = marker_elem.dataset.link);
function addMarker(marker_elem, map) {
const { dataset } = marker_elem;
const options = {
color: marker_elem.dataset.color,
title: marker_elem.getElementsByTagName("h3")[0].textContent.trim(),
};
const marker =
dataset.coords ? createMarker(marker_elem, options)
: createGeoJSONMarker(marker_elem, options);
marker.addTo(map);
map._sqlpage_markers.push(marker);
if (options.title) marker.bindPopup(marker_elem);
else if (marker_elem.dataset.link) marker.on('click', () => window.location = marker_elem.dataset.link);
}
function createMarker(marker_elem, options) {
const coords = parseCoords(marker_elem.dataset.coords);
const icon_obj = marker_elem.getElementsByClassName("mapicon")[0];
if (icon_obj) {
const size = 1.5 * +(options.size || icon_obj.firstChild?.getAttribute('width') || 24);
options.icon = L.divIcon({
html: icon_obj,
className: `border-0 bg-${options.color || 'primary'} bg-gradient text-white rounded-circle shadow d-flex justify-content-center align-items-center`,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2],
});
}
function createMarker(marker_elem, options) {
const coords = parseCoords(marker_elem.dataset.coords);
const icon_obj = marker_elem.getElementsByClassName("mapicon")[0];
if (icon_obj) {
const size = 1.5 * +(options.size || icon_obj.firstChild?.getAttribute('width') || 24);
options.icon = L.divIcon({
html: icon_obj,
className: `border-0 bg-${options.color || 'primary'} bg-gradient text-white rounded-circle shadow d-flex justify-content-center align-items-center`,
iconSize: [size, size],
iconAnchor: [size/2, size/2],
});
}
return L.marker(coords, options);
return L.marker(coords, options);
}
function createGeoJSONMarker(marker_elem, options) {
let geojson = JSON.parse(marker_elem.dataset.geojson);
if (options.color) {
options.color = get_tabler_color(options.color) || options.color;
}
function createGeoJSONMarker(marker_elem, options) {
let geojson = JSON.parse(marker_elem.dataset.geojson);
if (options.color) {
options.color = get_tabler_color(options.color) || options.color;
}
function style({ properties }) {
if (typeof properties !== "object") return options;
return {...options, ...properties};
}
function pointToLayer(feature, latlng) {
marker_elem.dataset.coords = latlng.lat + "," + latlng.lng;
return createMarker(marker_elem, { ...options, ...feature.properties });
}
return L.geoJSON(geojson, { style, pointToLayer });
function style({ properties }) {
if (typeof properties !== "object") return options;
return { ...options, ...properties };
}
function pointToLayer(feature, latlng) {
marker_elem.dataset.coords = latlng.lat + "," + latlng.lng;
return createMarker(marker_elem, { ...options, ...feature.properties });
}
return L.geoJSON(geojson, { style, pointToLayer });
}
}

function sqlpage_form() {
const file_inputs = document.querySelectorAll("input[type=file][data-max-size]");
for (const input of file_inputs) {
const max_size = +input.dataset.maxSize;
input.addEventListener("change", function() {
input.classList.remove("is-invalid");
input.setCustomValidity("");
for (const {size} of this.files) {
if (size > max_size){
input.classList.add("is-invalid");
return input.setCustomValidity(`File size must be less than ${max_size/1000} kB.`);
}
const file_inputs = document.querySelectorAll("input[type=file][data-max-size]");
for (const input of file_inputs) {
const max_size = +input.dataset.maxSize;
input.addEventListener("change", function () {
input.classList.remove("is-invalid");
input.setCustomValidity("");
for (const { size } of this.files) {
if (size > max_size) {
input.classList.add("is-invalid");
return input.setCustomValidity(`File size must be less than ${max_size / 1000} kB.`);
}
});
}
}
});
}
}

function get_tabler_color(name) {
return getComputedStyle(document.documentElement).getPropertyValue('--tblr-' + name);
function create_tabler_color() {
const style = getComputedStyle(document.documentElement);
return function get_tabler_color(name) {
return style.getPropertyValue('--tblr-' + name);
}
}

const get_tabler_color = create_tabler_color();

function load_scripts() {
let addjs = document.querySelectorAll("[data-sqlpage-js]");
for (const js of new Set([...addjs].map(({dataset}) => dataset.sqlpageJs))) {
for (const js of new Set([...addjs].map(({ dataset }) => dataset.sqlpageJs))) {
const script = document.createElement("script");
script.src = js;
document.head.appendChild(script);
Expand All @@ -198,6 +206,6 @@ function add_init_fn(f) {

add_init_fn(sqlpage_table);
add_init_fn(sqlpage_map);
add_init_fn(sqlpage_card);
add_init_fn(sqlpage_embed);
add_init_fn(sqlpage_form);
add_init_fn(load_scripts);
Loading

0 comments on commit 3f0b814

Please sign in to comment.