1
0
mirror of https://github.com/tabler/tabler.git synced 2026-08-07 03:42:27 +04:00
Files
tabler/shared/layouts/BaseLayout.astro
codecalm f27c70ad38 Fix Prettier formatting regression and Astro type-check errors
`Render Modal as real markup` (#2742) reformatted ~140 files back to
tabs/semicolons, breaking the Prettier config added by the Astro
quality gates (#2749). Re-run prettier --write to restore it, and fix
the type errors it had been masking: HTMLElement casts and `this`
typing in ChangePasswordModalContent/ConfirmDeleteModalContent
scripts, plain-JS syntax in the inline-injected progress.astro script,
narrowed prop unions in Button/Check/Spinner, nullable icon svg casts
in Icons/Rating, and a duplicate firstLetters/invalid `placeholder`
attribute in Select.astro. Also fix shared/vitest.config.mts's stale
`astro/lib/**/*.test.ts` include glob left over from the shared source
restructure, which made `pnpm test` report zero test files.
2026-08-03 01:26:19 +02:00

213 lines
7.4 KiB
Plaintext

---
import ThemeSettings from '@ui/ThemeSettings.astro'
import { site } from '@shared/lib/site'
import PageModals from '@shared/components/PageModals.astro'
import PageScripts from '@shared/components/PageScripts.astro'
import libs from '@tabler/core/libs.json'
interface Props {
title?: string
/** Third-party libs from libs.json, e.g. ['apexcharts', 'jsvectormap'] */
pageLibs?: string[]
/** Extra body classes, e.g. "layout-boxed" / "layout-fluid" */
bodyClass?: string
/** dir="rtl" + RTL variants of the tabler/plugin stylesheets */
rtl?: boolean
/** Relative asset base: '.' for root pages, '..' one level deep */
base?: string
}
const { title, pageLibs = [], bodyClass, rtl = false, base = '.' } = Astro.props
// Development build: unminified assets, dev favicon.
const environment = 'development'
const min = environment === 'development' ? '' : '.min'
// RTL swaps tabler/plugin stylesheets for their .rtl.css variants (demo stays LTR)
const rtlSuffix = rtl ? '.rtl' : ''
type Lib = { npm?: string; js?: string[]; css?: string[]; head?: boolean }
const pageLibEntries = Object.entries(libs as Record<string, Lib>).filter(([name]) => pageLibs.includes(name))
const googleMapsKey = site.googleMapsDevKey
// External URLs (http/https) are emitted verbatim (with the maps-key placeholder
// replaced); local files as ./dist/libs/{npm}/{file}.
const libHref = (lib: Lib, file: string) => (/^https?:\/\//.test(file) ? file.replace('GOOGLE_MAPS_KEY', googleMapsKey) : `${base}/dist/libs/${lib.npm}/${file}`)
const libCssFiles = pageLibEntries.flatMap(([, lib]) => (lib.css ?? []).map((file) => libHref(lib, file)))
const libJsFiles = (head: boolean) => pageLibEntries.filter(([, lib]) => Boolean(lib.head) === head).flatMap(([, lib]) => (lib.js ?? []).map((file) => libHref(lib, file)))
---
<!doctype html>
<html lang="en" dir={rtl ? 'rtl' : undefined}>
<head>
<!-- BEGIN META -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>{title ? `${title} - ` : ''}{site.title} - {site.descriptionShort}</title>
<link rel="icon" href={`${base}/favicon-dev.ico`} type="image/x-icon" />
<link rel="shortcut icon" href={`${base}/favicon-dev.ico`} type="image/x-icon" />
<!-- END META -->
{
libCssFiles.length > 0 && (
<Fragment>
<Fragment set:html={'<!-- BEGIN PAGE LEVEL STYLES -->'} />
{libCssFiles.map((href) => (
<link href={href} rel="stylesheet" />
))}
<Fragment set:html={'<!-- END PAGE LEVEL STYLES -->'} />
</Fragment>
)
}
<!-- BEGIN GLOBAL MANDATORY STYLES -->
<link href={`${base}/dist/css/tabler${rtlSuffix}${min}.css`} rel="stylesheet" />
<!-- END GLOBAL MANDATORY STYLES -->
<!-- BEGIN PLUGINS STYLES -->
{site.cssPlugins.map((plugin) => <link href={`${base}/dist/css/tabler-${plugin}${rtlSuffix}${min}.css`} rel="stylesheet" />)}
<!-- END PLUGINS STYLES -->
<!-- BEGIN DEMO STYLES -->
<link href={`${base}/preview/css/demo${min}.css`} rel="stylesheet" />
<!-- END DEMO STYLES -->
{
libJsFiles(true).length > 0 && (
<Fragment>
<Fragment set:html={'<!-- BEGIN PAGE LIBRARIES -->'} />
{libJsFiles(true).map((src) => (
<script is:inline src={src} defer />
))}
<Fragment set:html={'<!-- END PAGE LIBRARIES -->'} />
</Fragment>
)
}
<!-- BEGIN CUSTOM FONT -->
{/* is:inline: keep the tag verbatim — Astro's scoped-style pipeline would add data-astro-cid-* attributes across the page */}
<style is:inline>
@import url('https://rsms.me/inter/inter.css');
</style>
<!-- END CUSTOM FONT -->
</head>
<body class={bodyClass}>
<a href="#content" class="visually-hidden skip-link">Skip to main content</a>
<!-- BEGIN GLOBAL THEME SCRIPT -->
<script is:inline src={`${base}/dist/js/tabler-theme${min}.js`}></script>
<!-- END GLOBAL THEME SCRIPT -->
<slot />
<PageModals />
<ThemeSettings />
{
libJsFiles(false).length > 0 && (
<Fragment>
<Fragment set:html={'<!-- BEGIN PAGE LIBRARIES -->'} />
{libJsFiles(false).map((src) => (
<script is:inline src={src} defer />
))}
<Fragment set:html={'<!-- END PAGE LIBRARIES -->'} />
</Fragment>
)
}
<!-- BEGIN GLOBAL MANDATORY SCRIPTS -->
<script is:inline src={`${base}/dist/js/tabler${min}.js`} defer></script>
<!-- END GLOBAL MANDATORY SCRIPTS -->
<!-- BEGIN DEMO SCRIPTS -->
<script is:inline src={`${base}/preview/js/demo${min}.js`} defer></script>
<!-- END DEMO SCRIPTS -->
<PageScripts />
<!-- BEGIN PAGE SCRIPTS -->
<script is:inline>
/*
This script handles the theme settings offcanvas functionality
It saves the selected settings to localStorage and applies them to the document
It also updates the URL with the selected settings as query parameters
It also has a reset button to clear all settings and revert to default
This is just a demo script to show how to implement theme settings. You can modify it as needed.
*/
document.addEventListener('DOMContentLoaded', function () {
var themeConfig = {
'theme': 'light',
'theme-base': 'gray',
'theme-font': 'sans-serif',
'theme-primary': 'blue',
'theme-radius': '1',
}
var url = new URL(window.location)
var form = document.getElementById('offcanvas-settings')
var resetButton = document.getElementById('reset-changes')
var checkItems = function () {
for (var key in themeConfig) {
var value = window.localStorage['tabler-' + key] || themeConfig[key]
if (!!value) {
var radios = form.querySelectorAll(`[name="${key}"]`)
if (!!radios) {
radios.forEach((radio) => {
radio.checked = radio.value === value
})
}
}
}
}
form.addEventListener('change', function (event) {
var target = event.target,
name = target.name,
value = target.value
for (var key in themeConfig) {
if (name === key) {
var applied = value
if (key === 'theme' && value === 'auto') {
applied = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
if (applied !== themeConfig[key]) {
document.documentElement.setAttribute('data-bs-' + key, applied)
} else {
document.documentElement.removeAttribute('data-bs-' + key)
}
window.localStorage.setItem('tabler-' + key, value)
url.searchParams.set(key, value)
}
}
window.history.pushState({}, '', url)
})
resetButton.addEventListener('click', function () {
for (var key in themeConfig) {
var value = themeConfig[key]
document.documentElement.removeAttribute('data-bs-' + key)
window.localStorage.removeItem('tabler-' + key)
url.searchParams.delete(key)
}
checkItems()
window.history.pushState({}, '', url)
})
checkItems()
})
</script>
<!-- END PAGE SCRIPTS -->
</body>
</html>