Import tabler.js in the Confetti and Sparkline demo scripts (#3105)

This commit is contained in:
Paweł Kuna authored and Bartek committed 2026-10-02 12:11:23 +02:00
1 parent 41d9b1b7d2
commit 2b0afd20c6
6 files changed
+85 -47

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/preview": patch
---
Fixed the Confetti and Sparkline demo scripts to import from the `tabler.js` ES module instead of a `tabler` global.
+33 -25
View File
@@ -9,21 +9,43 @@ import DocsLink from '@ui/DocsLink.astro'
import Button from '@ui/Button.astro'
import ButtonList from '@ui/ButtonList.astro'
import Badge from '@ui/Badge.astro'
import { tablerJsPath } from '@shared/lib/assets'
// tabler.js is an ES module with no `window.tabler` global, so a script imports
// the component it drives. Built as strings because the import path depends on
// the build (`.min` outside development) and `define:vars` cannot carry an import.
const onLoadScript = `
/*
One burst when the page opens. The instance lives on the body, so no element
on the page has to own it.
*/
import { Confetti } from '${tablerJsPath()}'
Confetti.getOrCreateInstance(document.body).burst()
`
const formScript = `
/*
Bursts confetti from code once the form is "saved". The instance lives on
the form, so the events fire there.
*/
import { Confetti } from '${tablerJsPath()}'
const form = document.getElementById('confetti-form')
const confetti = Confetti.getOrCreateInstance(form, { count: 150, duration: 1500 })
form.addEventListener('submit', (event) => {
event.preventDefault()
confetti.burst()
})
`
---
<DefaultLayout title="Confetti" pageMenu="base.confetti" description="Confetti pours a short shower of colored pieces over the page to celebrate a finished task, a signup or a first success.">
<DocsLink slot="page-header-actions" path="/ui/components/confetti" />
<CaptureScript>
<!-- BEGIN SCRIPT OF CONFETTI ON LOAD -->
<script is:inline>
/*
One burst when the page opens. The instance lives on the body, so no element
on the page has to own it.
*/
document.addEventListener('DOMContentLoaded', () => {
tabler.Confetti.getOrCreateInstance(document.body).burst()
})
</script>
<script is:inline type="module" set:html={onLoadScript} />
<!-- END SCRIPT OF CONFETTI ON LOAD -->
</CaptureScript>
<div class="row row-cards">
@@ -116,7 +138,7 @@ import Badge from '@ui/Badge.astro'
<Card>
<CardBody>
<CardTitle>From JavaScript</CardTitle>
<CardSubtitle>Call <code>burst()</code> on a <code>tabler.Confetti</code> instance when something else finishes, for example a form that was saved.</CardSubtitle>
<CardSubtitle>Call <code>burst()</code> on a <code>Confetti</code> instance when something else finishes, for example a form that was saved.</CardSubtitle>
<form id="confetti-form" class="d-flex align-items-end gap-2">
<div class="flex-fill">
<label class="form-label" for="confetti-project">Project name</label>
@@ -126,21 +148,7 @@ import Badge from '@ui/Badge.astro'
</form>
<CaptureScript>
<!-- BEGIN SCRIPT OF CONFETTI FORM -->
<script is:inline>
/*
Bursts confetti from code once the form is "saved". The instance lives on
the form, so the events fire there.
*/
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('confetti-form')
const confetti = tabler.Confetti.getOrCreateInstance(form, { count: 150, duration: 1500 })
form.addEventListener('submit', (event) => {
event.preventDefault()
confetti.burst()
})
})
</script>
<script is:inline type="module" set:html={formScript} />
<!-- END SCRIPT OF CONFETTI FORM -->
</CaptureScript>
</CardBody>
+25 -20
View File
@@ -11,9 +11,33 @@ import Sparkline from '@ui/Sparkline.astro'
import type { SparklineType } from '@ui/Sparkline.astro'
import site from '@data/site.json'
import { ucFirst } from '@shared/lib/string-format'
import { tablerJsPath } from '@shared/lib/assets'
const colors = Object.keys(site.colors)
// tabler.js is an ES module with no `window.tabler` global, so the script imports
// the component it drives. Built as a string because the import path depends on
// the build (`.min` outside development) and `define:vars` cannot carry an import.
const updateScript = `
/*
Pushes random data into a sparkline and counts the updated.bs.sparkline events.
*/
import { Sparkline } from '${tablerJsPath()}'
const element = document.getElementById('sparkline-orders')
const sparkline = Sparkline.getOrCreateInstance(element)
let updates = 0
document.getElementById('sparkline-update').addEventListener('click', () => {
sparkline.update(Array.from({ length: 10 }, () => Math.floor(Math.random() * 30)))
})
element.addEventListener('updated.bs.sparkline', () => {
updates += 1
document.getElementById('sparkline-updates').textContent = updates + (updates === 1 ? ' update' : ' updates')
})
`
const metrics: { name: string; value: string; color: string; type: SparklineType; fill?: string; values: string }[] = [
{ name: 'Revenue', value: '$12.4k', color: 'success', type: 'line', fill: 'auto', values: '5,6,7,6,8,9,10' },
{ name: 'Orders', value: '1,352', color: 'primary', type: 'bar', values: '3,4,2,6,5,8,7' },
@@ -200,26 +224,7 @@ const progress = [
</div>
<CaptureScript>
<!-- BEGIN SCRIPT OF SPARKLINE -->
<script is:inline>
/*
Pushes random data into a sparkline and counts the updated.bs.sparkline events.
tabler.js is deferred, so the instance is looked up once the DOM is ready.
*/
document.addEventListener('DOMContentLoaded', () => {
const element = document.getElementById('sparkline-orders')
const sparkline = tabler.Sparkline.getOrCreateInstance(element)
let updates = 0
document.getElementById('sparkline-update').addEventListener('click', () => {
sparkline.update(Array.from({ length: 10 }, () => Math.floor(Math.random() * 30)))
})
element.addEventListener('updated.bs.sparkline', () => {
updates += 1
document.getElementById('sparkline-updates').textContent = `${updates} update${updates === 1 ? '' : 's'}`
})
})
</script>
<script is:inline type="module" set:html={updateScript} />
<!-- END SCRIPT OF SPARKLINE -->
</CaptureScript>
</CardBody>
+2 -1
View File
@@ -1,6 +1,7 @@
---
import ThemeSettings from '@shared/components/demo/ThemeSettings.astro'
import { site } from '@shared/lib/site'
import { tablerJsPath } from '@shared/lib/assets'
import PageModals from '@shared/components/PageModals.astro'
import PageScripts from '@shared/components/PageScripts.astro'
import Analytics from '@shared/components/Analytics.astro'
@@ -135,7 +136,7 @@ const libJsFiles = (head: boolean) => pageLibEntries.filter(([, lib]) => Boolean
}
<!-- BEGIN GLOBAL MANDATORY SCRIPTS -->
<script is:inline type="module" src={`${base}/dist/js/tabler${min}.js`}></script>
<script is:inline type="module" src={tablerJsPath(base)}></script>
<!-- END GLOBAL MANDATORY SCRIPTS -->
<PageScripts />
+11 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { staticPath, assetRoot } from './assets'
import { staticPath, assetRoot, tablerJsPath } from './assets'
// TABLER_STATIC_BASE is not defined in the test environment, so the relative
// default ('./static') applies — same as the downloadable preview package.
@@ -18,3 +18,13 @@ describe('assetRoot', () => {
expect(assetRoot).toBe('.')
})
})
describe('tablerJsPath', () => {
it('points at the minified module outside development', () => {
expect(tablerJsPath()).toBe('./dist/js/tabler.min.js')
})
it('takes the page root for nested pages', () => {
expect(tablerJsPath('..')).toBe('../dist/js/tabler.min.js')
})
})
+9
View File
@@ -15,3 +15,12 @@ export const staticPath = (file: string): string => `${base}/${file.replace(/^\/
* relative default, "" when the base is absolute) — see Avatar's `base` prop.
*/
export const assetRoot = base.replace(/\/static$/, '')
const min = (process.env.NODE_ENV || 'production') === 'development' ? '' : '.min'
/**
* Url of the tabler.js ES module, for a demo script that imports a component
* from it. It has to be the very url the layout loads: a module is cached by
* url, so a different spelling would load and initialise tabler.js twice.
*/
export const tablerJsPath = (root = '.'): string => `${root}/dist/js/tabler${min}.js`