Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions examples/cards-with-remote-content/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Remote Content Demo

This small SQLPage example shows how to:
- lazy-load other page in cards including:
- chart component rendered by sqlpage
- map component rendered by sqlpage
- table component rendered by sqlpage

![remote content screenshot](screenshot.png)

30 changes: 30 additions & 0 deletions examples/cards-with-remote-content/chart-example.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
select
'chart' as component,
'Quarterly Revenue' as title,
'area' as type,
'indigo' as color,
5 as marker,
TRUE as time;
select
'2022-01-01T00:00:00Z' as x,
15 as y;
select
'2022-04-01T00:00:00Z' as x,
46 as y;
select
'2022-07-01T00:00:00Z' as x,
23 as y;
select
'2022-10-01T00:00:00Z' as x,
70 as y;
select
'2023-01-01T00:00:00Z' as x,
35 as y;
select
'2023-04-01T00:00:00Z' as x,
106 as y;
select
'2023-07-01T00:00:00Z' as x,
53 as y;


21 changes: 21 additions & 0 deletions examples/cards-with-remote-content/index.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
select
'card' as component,
2 as columns;
select
'A card with a Markdown description' as title,
'This is a card with a **Markdown** description.

This is useful if you want to display a lot of text in the card, with many options for formatting, such as
- **bold**,
- *italics*,
- [links](index.sql),
- etc.' as description_md;
select
'A card with lazy-loaded chart' as title,
'/chart-example.sql?_sqlpage_embed' as embed;
select
'A card with lazy-loaded map' as title,
'/map-example.sql?_sqlpage_embed' as embed;
select
'A card with lazy-loaded table' as title,
'/table-example.sql?_sqlpage_embed' as embed;
9 changes: 9 additions & 0 deletions examples/cards-with-remote-content/map-example.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
select
'map' as component,
1 as zoom;
select
'New Delhi' as title,
28.6139 as latitude,
77.209 as longitude;


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions examples/cards-with-remote-content/table-example.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
select
'table' as component,
TRUE as sort,
TRUE as search;
select
'Ophir' as Forename,
'Lojkine' as Surname,
'lovasoa' as Pseudonym;
select
'Linus' as Forename,
'Torvalds' as Surname,
'torvalds' as Pseudonym;

6 changes: 3 additions & 3 deletions examples/official-site/custom_components.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Each page in SQLPage is composed of a `shell` component,
which contains the page title and the navigation bar,
and a series of normal components that display the data.

The `shell` component is always present. If you don''t call it explicitly,
it will be invoked with the default parameters automatically before your first component
The `shell` component is always present unless explicitly skipped via the `?_sqlpage_embed` query parameter.
If you don''t call it explicitly, it will be invoked with the default parameters automatically before your first component
invocation that tries to render data on the page.

There can be only one `shell` component per site, but you can customize its appearance as you see fit.
Expand Down Expand Up @@ -135,4 +135,4 @@ Some interesting examples are:
- the `../` syntax to access the parent context,
- and the `@key` to work with objects whose keys are not known in advance.

' as contents_md;
' as contents_md;
5 changes: 3 additions & 2 deletions examples/official-site/safety.sql
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ website, and have it perform an action on your website in the user''s name,
because the browser will not send the cookies to your website.

SQLPage differentiates between POST variables (accessed with the `:variable` syntax), and
variables that can come from URL parameters (accessible with `$variable`).
variables that can come from URL parameters (accessible with `$variable`). Note that URL parameters
prefixed with `_sqlpage_` are reserved for internal use.

When a user submits a form, you should use POST variables to access the form data.
This ensures that you only use data that indeed comes from the form, and not from a
Expand All @@ -138,4 +139,4 @@ create a specific user for SQLPage that only has access to the specific tables y

If your entire application is read-only, you can even create a user that only has the `SELECT` privilege on your database,

' as contents_md;
' as contents_md;
33 changes: 33 additions & 0 deletions examples/official-site/sqlpage/migrations/31_card_docs_update.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
DELETE FROM component WHERE name = 'card';

INSERT INTO component(name, icon, description) VALUES
('card', 'credit-card', 'A grid where each element is a small card that displays a piece of data.');
INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'card', * FROM (VALUES
-- top level
('title', 'Text header at the top of the list of cards.', 'TEXT', TRUE, TRUE),
('description', 'A short paragraph displayed below the title.', 'TEXT', TRUE, TRUE),
('description_md', 'A short paragraph displayed below the title - formatted using markdown.', 'TEXT', TRUE, TRUE),
('columns', 'The number of columns in the grid of cards. This is just a hint, the grid will adjust dynamically to the user''s screen size, rendering fewer columns if needed to fit the contents.', 'INTEGER', TRUE, TRUE),
-- item level
('title', 'Name of the card, displayed at the top.', 'TEXT', FALSE, FALSE),
('description', 'The body of the card, where you put the main text contents of the card.
This does not support rich text formatting, only plain text.
If you want to use rich text formatting, use the `description_md` property instead.', 'TEXT', FALSE, TRUE),
('description_md', '
The body of the card, in Markdown format.
This is useful if you want to display a lot of text in the card, with many options for formatting, such as
line breaks, **bold**, *italics*, lists, #titles, [links](target.sql), ![images](photo.jpg), etc.', 'TEXT', FALSE, TRUE),
('top_image', 'The URL (absolute or relative) of an image to display at the top of the card.', 'URL', FALSE, TRUE),
('footer', 'Muted text to display at the bottom of the card.', 'TEXT', FALSE, TRUE),
('footer_md', 'Muted text to display at the bottom of the card, with rich text formatting in Markdown format.', 'TEXT', FALSE, TRUE),
('link', 'An URL to which the user should be taken when they click on the card.', 'URL', FALSE, TRUE),
('footer_link', 'An URL to which the user should be taken when they click on the footer.', 'URL', FALSE, TRUE),
('icon', 'Name of an icon to display on the left side of the card.', 'ICON', FALSE, TRUE),
('color', 'The name of a color, to be displayed on the left of the card to highlight it.', 'COLOR', FALSE, TRUE),
('active', 'Whether this item in the grid is considered "active". Active items are displayed more prominently.', 'BOOLEAN', FALSE, TRUE),
('embed', 'A url whose contents will be fetched and injected into the body of this card.
This can be used to inject arbitrary html content, but is especially useful for injecting
the output of other sql files rendered by SQLPage. For the latter case you can pass the
`?_sqlpage_embed` query parameter, which will skip the shell layout', 'TEXT', FALSE, TRUE)
) x;

5 changes: 3 additions & 2 deletions sqlpage/apexcharts.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function sqlpage_chart() {
return new_series;
}

for (const c of document.getElementsByClassName("chart")) {
for (const c of document.querySelectorAll("[data-pre-init=chart]")) {
try {
const data = JSON.parse(c.querySelector("data").innerText);
const is_timeseries = !!data.time;
Expand Down Expand Up @@ -147,6 +147,7 @@ function sqlpage_chart() {
chart.render();
if (window.charts) window.charts.push(chart);
else window.charts = [chart];
c.removeAttribute("data-pre-init");
} catch (e) { console.log(e) }
}
}
Expand Down Expand Up @@ -186,4 +187,4 @@ function bubbleTooltip({ series, seriesIndex, dataPointIndex, w }) {
return tooltip.outerHTML;
}

sqlpage_chart();
sqlpage_chart();
49 changes: 43 additions & 6 deletions sqlpage/sqlpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* !include https://cdn.jsdelivr.net/npm/list.js-fixed@2.3.4/dist/list.min.js */

function sqlpage_chart() {
let first_chart = document.querySelector("[data-js]");
let first_chart = document.querySelector("[data-pre-init=chart]");
if (first_chart) {
// Add the apexcharts js to the page
const apexcharts_js = document.createElement("script");
Expand All @@ -11,20 +11,45 @@ function sqlpage_chart() {
}
}

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_table(){
// Tables
for (const r of document.getElementsByClassName("data-list")) {
for (const r of document.querySelectorAll("[data-pre-init=table]")) {
new List(r, {
valueNames: [...r.getElementsByTagName("th")].map(t => t.textContent),
searchDelay: 100,
indexAsync: true
});
r.removeAttribute("data-pre-init");
}
}

let is_leaflet_injected = false;
let is_leaflet_loaded = false;

function sqlpage_map() {
const maps = document.getElementsByClassName("leaflet");
if (maps.length) {
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";
Expand All @@ -38,8 +63,14 @@ function sqlpage_map() {
leaflet_js.crossOrigin = "anonymous";
leaflet_js.onload = onLeafletLoad;
document.head.appendChild(leaflet_js);
is_leaflet_injected = true;
}
if (first_map && is_leaflet_loaded) {
onLeafletLoad();
}
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;
Expand All @@ -51,6 +82,7 @@ function sqlpage_map() {
for (const marker_elem of m.getElementsByClassName("marker")) {
setTimeout(addMarker, 0, marker_elem, map);
}
m.removeAttribute("data-pre-init");
}
}
function addMarker(marker_elem, map) {
Expand Down Expand Up @@ -101,8 +133,13 @@ function get_tabler_color(name) {
return getComputedStyle(document.documentElement).getPropertyValue('--tblr-' + name);
}

document.addEventListener('DOMContentLoaded', function () {
function init_components() {
sqlpage_table();
sqlpage_chart();
sqlpage_map();
})
sqlpage_card();
}

document.addEventListener('DOMContentLoaded', init_components);

document.addEventListener('fragment-loaded', init_components);
11 changes: 8 additions & 3 deletions sqlpage/templates/card.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
mt-1 mb-3">
{{#each_row}}
<div class="col">
<div class="card h-100 {{#if active}}card-active{{/if}}">
<div class="card h-100 {{#if active}}card-active{{/if}}" {{#if embed }}data-pre-init="card" data-embed="{{embed}}"{{/if}}>
{{#if link}}
<a href="{{link}}" style="text-decoration: inherit; color: inherit">
{{/if}}
Expand All @@ -35,12 +35,17 @@
{{/if}}
<div class="card-body">
{{#if title}}<h2 class="card-title fs-3 me-3">{{title}}</h2>{{/if}}
<div class="remove-bottom-margin">
<div class="card-content remove-bottom-margin">
{{~description~}}
{{~#if description_md~}}
{{{markdown description_md}}}
{{~/if~}}
</div>
{{~#if embed~}}
<div class="spinner-border card-loading-placeholder" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Loading...</span>
</div>
{{~/if~}}
</div>
{{#if link}}
</a>
Expand All @@ -67,4 +72,4 @@
</div>
</div>
{{/each_row}}
</div>
</div>
4 changes: 2 additions & 2 deletions sqlpage/templates/chart.handlebars
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="card my-2" data-js="/{{static_path 'apexcharts.js'}}">
<div class="card my-2" data-pre-init="chart" data-js="/{{static_path 'apexcharts.js'}}">
<div class="card-body">
<div class="d-flex">
<h3 class="card-title">{{title}}</h3>
Expand Down Expand Up @@ -45,4 +45,4 @@
</data>
</div>
</div>
</div>
</div>
2 changes: 2 additions & 0 deletions sqlpage/templates/fragment-shell.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{~#each_row~}}{{~/each_row~}}

3 changes: 2 additions & 1 deletion sqlpage/templates/map.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
data-attribution="{{default attribution '© OpenStreetMap'}}"
data-tile_source="{{default tile_source 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'}}"
data-max_zoom="{{default max_zoom 18}}"
data-pre-init="map"
>
<div class="d-flex justify-content-center h-100 align-items-center">
<div
Expand Down Expand Up @@ -53,4 +54,4 @@
</div>
</div>
</div>
</div>
</div>
4 changes: 2 additions & 2 deletions sqlpage/templates/table.handlebars
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="card my-2">
<div class="card-body">
<div class="table-responsive {{#if (or sort search)}}data-list{{/if}}">
<div class="table-responsive" {{#if (or sort search)}}data-pre-init="table"{{/if}}>
{{#if search}}
<div class="p-2">
<input type="search" class="form-control form-control-rounded fs-6 search" placeholder="Search…">
Expand Down Expand Up @@ -55,4 +55,4 @@
</table>
</div>
</div>
</div>
</div>
Loading