mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Prepare 1.0.0 release
This commit is contained in:
@@ -1 +0,0 @@
|
||||
docs/src/assets/styles.css merge=theirs
|
||||
+4
-5
@@ -4,12 +4,11 @@
|
||||
_site/
|
||||
|
||||
node_modules/
|
||||
packages/css/dist/
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
dist/
|
||||
docs/generated/
|
||||
public/assets/js/
|
||||
|
||||
public/fragments/
|
||||
templates/
|
||||
site/dist/
|
||||
site/public/assets/js/
|
||||
site/public/fragments/
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
|
||||
## Source Boundaries
|
||||
- Do not hand-edit build outputs:
|
||||
- `packages/*/dist/**`
|
||||
- `dist/**`
|
||||
- `docs/generated/**`
|
||||
- `public/assets/**`
|
||||
- `templates/**`
|
||||
- `site/dist/**`
|
||||
- `site/public/assets/js/**`
|
||||
- `site/public/fragments/**`
|
||||
- Generated source entrypoints such as `src/css/basecoat-vega.css` are committed, but must be regenerated through `scripts/generate-css-entrypoints.js` or build scripts, not manually maintained.
|
||||
|
||||
## Public API and Markup
|
||||
@@ -64,8 +65,8 @@
|
||||
- component CSS: `src/css/components/<component>.css`
|
||||
- style packs: `src/css/styles/{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
|
||||
- JS: `src/js/<component>.js` when behavior changes
|
||||
- templates: `src/nunjucks/*.njk` and `src/jinja/*.html.jinja` when markup changes
|
||||
- docs: `docs/src/components/<component>.md`
|
||||
- templates: `src/templates/nunjucks/*.njk` and `src/templates/jinja/*.html.jinja` when markup changes
|
||||
- docs: `site/src/docs/components/<component>.mdx`
|
||||
- Map upstream concepts, not implementation details. Do not port `cn-*` selectors directly.
|
||||
- Classify differences as `drift fixed`, `intentional`, or `deferred`.
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
### Added
|
||||
|
||||
- Added Nunjucks and Jinja template files to the `basecoat-css` package.
|
||||
- Added a custom docs 404 page for Cloudflare static asset fallback.
|
||||
|
||||
### Changed
|
||||
|
||||
- Narrowed dark-mode generated selectors to `html.dark` to reduce broad style recalculation work.
|
||||
- Switched the docs site to Astro's sitemap integration and updated `robots.txt` to point at the generated sitemap index.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
## Cloudflare Workers deploys
|
||||
|
||||
Docs are built with 11ty and deployed as Workers Static Assets from `_site`.
|
||||
Docs are built with ReallySimpleDocs/Astro and deployed as Workers Static Assets from `site/dist`.
|
||||
|
||||
- Local preview: `npm run workers:dev`
|
||||
- Production deploy: `npm run workers:deploy`
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
Basecoat is a Tailwind CSS, vanilla HTML/CSS/JavaScript implementation of the shadcn/ui design system. It provides shadcn-style components for any web stack without React, Radix, or framework runtime dependencies.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Semantic HTML-first components.
|
||||
- Tailwind CSS v4 source files and generated CSS bundles.
|
||||
- Small vanilla JavaScript for components that need behavior.
|
||||
- Nunjucks and Jinja templates.
|
||||
- Standalone style packs: Vega, Nova, Maia, Lyra, Mira, Luma, Sera, and Rhea.
|
||||
- Dark mode and CSS variable theming.
|
||||
- Compatible with shadcn/ui themes.
|
||||
- Dark mode.
|
||||
- CDN, npm, and template usage paths.
|
||||
- Nunjucks and Jinja templates.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -20,16 +21,6 @@ Basecoat is a Tailwind CSS, vanilla HTML/CSS/JavaScript implementation of the sh
|
||||
- Installation: [basecoatui.com/installation](https://basecoatui.com/installation)
|
||||
- Customization: [basecoatui.com/customization](https://basecoatui.com/customization)
|
||||
|
||||
## Packages
|
||||
|
||||
This repository publishes one workspace package:
|
||||
|
||||
- `basecoat-css`: CSS, JavaScript, Nunjucks templates, and Jinja templates.
|
||||
|
||||
Package details live in:
|
||||
|
||||
- `packages/css/README.md`
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -62,23 +53,21 @@ Use the styleless base plus a custom style file:
|
||||
|
||||
```text
|
||||
.
|
||||
├── docs/
|
||||
│ ├── generated/ Generated RSD/Astro docs input
|
||||
│ └── src/ Documentation source pages, examples, and fragments
|
||||
├── packages/
|
||||
│ └── css/ Published CSS package
|
||||
├── public/ Generated docs assets for Astro
|
||||
├── dist/ Generated package output
|
||||
├── scripts/ Build and generation scripts
|
||||
└── src/
|
||||
├── components/ Astro overrides for ReallySimpleDocs
|
||||
├── css/
|
||||
│ ├── base/ Shared tokens, base layer, and semantic utilities
|
||||
│ ├── components/ Component structure and behavior hooks
|
||||
│ └── styles/ Style-pack visual rules
|
||||
├── docs.css Documentation-site CSS extension
|
||||
├── jinja/ Jinja component templates
|
||||
├── js/ Vanilla JS components and registry
|
||||
└── nunjucks/ Nunjucks component templates
|
||||
├── site/ ReallySimpleDocs/Astro documentation app
|
||||
│ ├── public/ Static site assets
|
||||
│ └── src/
|
||||
│ ├── components/ RSD component overrides
|
||||
│ ├── docs/ Documentation pages and examples
|
||||
│ ├── fragments/ Site-only rendered fragments
|
||||
│ ├── pages/ Astro routes outside the docs tree
|
||||
│ └── scripts/ Site scripts
|
||||
├── src/ Package source
|
||||
│ ├── css/
|
||||
│ ├── js/
|
||||
│ └── templates/
|
||||
└── templates/ Generated package template output
|
||||
```
|
||||
|
||||
## CSS Architecture
|
||||
@@ -111,8 +100,9 @@ These entrypoints are generated by `scripts/generate-css-entrypoints.js` and by
|
||||
```bash
|
||||
# Install dependencies.
|
||||
npm i
|
||||
npm --prefix site i
|
||||
|
||||
# Generate docs input and run the ReallySimpleDocs/Astro site.
|
||||
# Run the ReallySimpleDocs/Astro site.
|
||||
npm run docs:dev
|
||||
|
||||
# Build package assets.
|
||||
|
||||
Generated
+6
-8801
File diff suppressed because it is too large
Load Diff
+7
-8
@@ -14,9 +14,6 @@
|
||||
"dist/",
|
||||
"templates/"
|
||||
],
|
||||
"workspaces": [
|
||||
"site"
|
||||
],
|
||||
"keywords": [
|
||||
"components",
|
||||
"component library",
|
||||
@@ -125,11 +122,13 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build.js",
|
||||
"docs:build": "npm run build && npm run build --workspace site",
|
||||
"docs:dev": "npm run dev --workspace site",
|
||||
"workers:dev": "npm run build && npm run workers:dev --workspace site",
|
||||
"workers:deploy": "npm run build && npm run workers:deploy --workspace site",
|
||||
"workers:preview": "npm run build && npm run workers:preview --workspace site"
|
||||
"prepack": "node ./scripts/build.js",
|
||||
"docs:build": "npm run build && npm --prefix site run build",
|
||||
"docs:dev": "npm --prefix site run dev",
|
||||
"docs:preview": "npm run docs:build && npm --prefix site run preview",
|
||||
"workers:dev": "npm run build && npm --prefix site run workers:dev",
|
||||
"workers:deploy": "npm run build && npm --prefix site run workers:deploy",
|
||||
"workers:preview": "npm run build && npm --prefix site run workers:preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.1.17",
|
||||
|
||||
+6
-7
@@ -64,23 +64,22 @@ async function copyDirRecursive(src, dest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Main build function to prepare packages for publishing.
|
||||
// Main build function to prepare the package for publishing.
|
||||
async function build() {
|
||||
console.log('Starting build process...');
|
||||
await generateCssEntrypoints();
|
||||
|
||||
// Define all necessary paths
|
||||
const cssPackageDir = path.join(projectRoot, 'packages', 'css');
|
||||
const cssDistDir = path.join(cssPackageDir, 'dist');
|
||||
const cssTemplatesDir = path.join(cssPackageDir, 'templates');
|
||||
const cssDistDir = path.join(projectRoot, 'dist');
|
||||
const cssTemplatesDir = path.join(projectRoot, 'templates');
|
||||
|
||||
const srcDir = path.join(projectRoot, 'src');
|
||||
const srcCssDir = path.join(srcDir, 'css');
|
||||
const srcCssStylesDir = path.join(srcCssDir, 'styles');
|
||||
const srcCssCompatDir = path.join(srcCssDir, 'compat');
|
||||
const srcJsDir = path.join(srcDir, 'js');
|
||||
const srcNunjucksDir = path.join(srcDir, 'nunjucks');
|
||||
const srcJinjaDir = path.join(srcDir, 'jinja');
|
||||
const srcNunjucksDir = path.join(srcDir, 'templates', 'nunjucks');
|
||||
const srcJinjaDir = path.join(srcDir, 'templates', 'jinja');
|
||||
|
||||
// Clean previous build artifacts
|
||||
console.log('Cleaning distribution directories...');
|
||||
@@ -128,7 +127,7 @@ async function build() {
|
||||
|
||||
// Build CSS package
|
||||
console.log('Building CSS package...');
|
||||
await ensureDir(cssDistDir); // Ensure dist dir exists for css package
|
||||
await ensureDir(cssDistDir);
|
||||
const styles = ['vega', 'nova', 'maia', 'lyra', 'mira', 'luma', 'sera', 'rhea'];
|
||||
await fs.copyFile(path.join(srcCssDir, 'basecoat.css'), path.join(cssDistDir, 'basecoat.css'));
|
||||
await fs.copyFile(path.join(srcCssDir, 'basecoat.all.css'), path.join(cssDistDir, 'basecoat.all.css'));
|
||||
|
||||
+21
-10
@@ -1,8 +1,18 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import reallySimpleDocs from 'reallysimpledocs/astro';
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
||||
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
||||
|
||||
const includeInSitemap = (page) => {
|
||||
const { pathname } = new URL(page);
|
||||
|
||||
return pathname === '/' || (
|
||||
pathname.endsWith('/') &&
|
||||
!pathname.startsWith('/fragments/')
|
||||
);
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
site: process.env.SITE_URL || 'https://basecoatui.com',
|
||||
@@ -16,7 +26,7 @@ export default defineConfig({
|
||||
},
|
||||
integrations: [
|
||||
reallySimpleDocs({
|
||||
docsDir: './docs/src',
|
||||
docsDir: './src/docs',
|
||||
routeBase: '/',
|
||||
style: 'vega',
|
||||
css: false,
|
||||
@@ -28,9 +38,9 @@ export default defineConfig({
|
||||
'hx-push-url': 'true',
|
||||
},
|
||||
components: {
|
||||
Head: './docs/src/site/StyleHead.astro',
|
||||
SidebarHeader: './docs/src/site/SidebarHeader.astro',
|
||||
ContentHeader: './docs/src/site/ContentHeader.astro',
|
||||
Head: './src/components/StyleHead.astro',
|
||||
SidebarHeader: './src/components/SidebarHeader.astro',
|
||||
ContentHeader: './src/components/ContentHeader.astro',
|
||||
},
|
||||
site: {
|
||||
title: 'Basecoat',
|
||||
@@ -42,15 +52,16 @@ export default defineConfig({
|
||||
name: 'Ronan Berder',
|
||||
x: '@hunvreus',
|
||||
},
|
||||
assets: {
|
||||
favicon: 'favicon.svg',
|
||||
appleTouchIcon: 'apple-touch-icon.png',
|
||||
socialImage: 'social.png',
|
||||
},
|
||||
favicon: 'favicon.svg',
|
||||
appleTouchIcon: 'apple-touch-icon.png',
|
||||
socialImage: 'social.png',
|
||||
logo: {
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>',
|
||||
},
|
||||
},
|
||||
}),
|
||||
sitemap({
|
||||
filter: includeInSitemap,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
Generated
+5119
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "astro build",
|
||||
"dev": "astro dev",
|
||||
"preview": "astro preview",
|
||||
"workers:dev": "npm run build && wrangler dev",
|
||||
"workers:deploy": "npm run build && wrangler deploy",
|
||||
"workers:preview": "npm run build && wrangler versions upload"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/sitemap": "^3.7.3",
|
||||
"basecoat-css": "file:..",
|
||||
"htmx.org": "^2.0.10",
|
||||
"lucide-static": "^1.17.0",
|
||||
"nunjucks": "^3.2.4",
|
||||
"reallysimpledocs": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^6.4.6",
|
||||
"wrangler": "^4.50.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 499 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 101 KiB |
@@ -1,12 +1,12 @@
|
||||
---
|
||||
import vegaUrl from "../assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../assets/styles/style-rhea.css?url";
|
||||
import vegaUrl from "../docs/assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../docs/assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../docs/assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../docs/assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../docs/assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../docs/assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../docs/assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../docs/assets/styles/style-rhea.css?url";
|
||||
|
||||
const styleUrls = {
|
||||
vega: vegaUrl,
|
||||
@@ -47,7 +47,7 @@ const styleUrls = {
|
||||
};
|
||||
const getStyleHref = (style) => {
|
||||
const href = styleUrls[style];
|
||||
return href?.startsWith("/docs/src/assets/styles/") ? `${href}?direct` : href;
|
||||
return href?.startsWith("/src/docs/assets/styles/") ? `${href}?direct` : href;
|
||||
};
|
||||
|
||||
const applyStyle = (style) => {
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
import vegaUrl from "../assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../assets/styles/style-rhea.css?url";
|
||||
import demoInteractionsUrl from "./demo-interactions.js?url";
|
||||
import vegaUrl from "../docs/assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../docs/assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../docs/assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../docs/assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../docs/assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../docs/assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../docs/assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../docs/assets/styles/style-rhea.css?url";
|
||||
import demoInteractionsUrl from "../scripts/demo-interactions.js?url";
|
||||
import htmxUrl from "htmx.org/dist/htmx.min.js?url";
|
||||
|
||||
const styleUrls = {
|
||||
@@ -104,7 +104,7 @@ const styleUrls = {
|
||||
};
|
||||
const getStyleHref = (style) => {
|
||||
const href = styleUrls[style];
|
||||
return href?.startsWith("/docs/src/assets/styles/") ? `${href}?direct` : href;
|
||||
return href?.startsWith("/src/docs/assets/styles/") ? `${href}?direct` : href;
|
||||
};
|
||||
|
||||
const style = getStoredStyle();
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-luma.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-luma.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/luma";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-lyra.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-lyra.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/lyra";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-maia.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-maia.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/maia";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-mira.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-mira.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/mira";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-nova.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-nova.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/nova";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-rhea.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-rhea.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/rhea";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-sera.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-sera.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/sera";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../../src/pages/**/*.{astro,html,js}";
|
||||
@source "../../../**/*.{astro,html,js,md,mdx}";
|
||||
@source "../../../pages/**/*.{astro,html,js}";
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-vega.css";
|
||||
@reference "../../../../../src/css/base/base.css";
|
||||
@import "../../../../../src/css/basecoat-vega.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/vega";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import basecoatRuntimeSource from "../../../src/js/basecoat.js?raw"
|
||||
import chartRuntimeSource from "../../../src/js/chart.js?raw"
|
||||
import chartExamplesSource from "../site/chart-examples.js?raw"
|
||||
import basecoatRuntimeSource from "../../../../src/js/basecoat.js?raw"
|
||||
import chartRuntimeSource from "../../../../src/js/chart.js?raw"
|
||||
import chartExamplesSource from "../../scripts/chart-examples.js?raw"
|
||||
|
||||
# Chart
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import packageJson from "../../packages/css/package.json"
|
||||
import packageJson from "../../../package.json"
|
||||
|
||||
export const basecoatVersion = packageJson.version
|
||||
|
||||
|
||||
+15
-12
@@ -12,17 +12,17 @@ The templates are versioned with `basecoat-css`, so the generated markup matches
|
||||
|
||||
| Component | Macro | Nunjucks | Jinja |
|
||||
| --- | --- | --- | --- |
|
||||
| [Combobox](/components/combobox) | `combobox()` | [combobox.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/combobox.njk#L18) | [combobox.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/combobox.html.jinja#L4) |
|
||||
| [Command](/components/command) | `command()` | [command.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/command.njk#L12) | [command.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/command.html.jinja#L12) |
|
||||
| [Command](/components/command) | `command_dialog()` | [command.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/command.njk#L79) | [command.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/command.html.jinja#L79) |
|
||||
| [Dialog](/components/dialog) | `dialog()` | [dialog.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/dialog.njk#L19) | [dialog.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/dialog.html.jinja#L19) |
|
||||
| [Dropdown Menu](/components/dropdown-menu) | `dropdown_menu()` | [dropdown-menu.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/dropdown-menu.njk#L11) | [dropdown-menu.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/dropdown-menu.html.jinja#L11) |
|
||||
| [Popover](/components/popover) | `popover()` | [popover.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/popover.njk#L10) | [popover.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/popover.html.jinja#L10) |
|
||||
| [Select](/components/select) | `select()` | [select.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/select.njk#L17) | [select.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/select.html.jinja#L16) |
|
||||
| [Sidebar](/components/sidebar) | `sidebar()` | [sidebar.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/sidebar.njk#L16) | [sidebar.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/sidebar.html.jinja#L16) |
|
||||
| [Tabs](/components/tabs) | `tabs()` | [tabs.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/tabs.njk#L16) | [tabs.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/tabs.html.jinja#L17) |
|
||||
| [Toast](/components/toast) | `toaster()` | [toast.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/toast.njk#L11) | [toast.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/toast.html.jinja#L11) |
|
||||
| [Toast](/components/toast) | `toast()` | [toast.njk](https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/toast.njk#L53) | [toast.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/jinja/toast.html.jinja#L53) |
|
||||
| [Combobox](/components/combobox) | `combobox()` | [combobox.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/combobox.njk) | [combobox.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/combobox.html.jinja) |
|
||||
| [Command](/components/command) | `command()` | [command.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/command.njk) | [command.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/command.html.jinja) |
|
||||
| [Command](/components/command) | `command_dialog()` | [command.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/command.njk) | [command.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/command.html.jinja) |
|
||||
| [Dialog](/components/dialog) | `dialog()` | [dialog.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/dialog.njk) | [dialog.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/dialog.html.jinja) |
|
||||
| [Dropdown Menu](/components/dropdown-menu) | `dropdown_menu()` | [dropdown-menu.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/dropdown-menu.njk) | [dropdown-menu.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/dropdown-menu.html.jinja) |
|
||||
| [Popover](/components/popover) | `popover()` | [popover.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/popover.njk) | [popover.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/popover.html.jinja) |
|
||||
| [Select](/components/select) | `select()` | [select.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/select.njk) | [select.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/select.html.jinja) |
|
||||
| [Sidebar](/components/sidebar) | `sidebar()` | [sidebar.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/sidebar.njk) | [sidebar.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/sidebar.html.jinja) |
|
||||
| [Tabs](/components/tabs) | `tabs()` | [tabs.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/tabs.njk) | [tabs.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/tabs.html.jinja) |
|
||||
| [Toast](/components/toast) | `toaster()` | [toast.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/toast.njk) | [toast.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/toast.html.jinja) |
|
||||
| [Toast](/components/toast) | `toast()` | [toast.njk](https://github.com/hunvreus/basecoat/blob/main/src/templates/nunjucks/toast.njk) | [toast.html.jinja](https://github.com/hunvreus/basecoat/blob/main/src/templates/jinja/toast.html.jinja) |
|
||||
|
||||
Some macro files include internal recursive helpers such as `render_select_items()`. Those helpers are implementation details and are not part of the public API.
|
||||
|
||||
@@ -46,7 +46,7 @@ Some macro files include internal recursive helpers such as `render_select_items
|
||||
|
||||
Copy only the files you use if you do not want the full template folder.
|
||||
|
||||
You can also copy the [Nunjucks templates](https://github.com/hunvreus/basecoat/tree/main/src/nunjucks) or [Jinja templates](https://github.com/hunvreus/basecoat/tree/main/src/jinja) from GitHub.
|
||||
You can also copy the [Nunjucks templates](https://github.com/hunvreus/basecoat/tree/main/src/templates/nunjucks) or [Jinja templates](https://github.com/hunvreus/basecoat/tree/main/src/templates/jinja) from GitHub.
|
||||
</Step>
|
||||
<Step>
|
||||
### Use a Macro
|
||||
@@ -93,6 +93,7 @@ Some macro files include internal recursive helpers such as `render_select_items
|
||||
| `multiple` | `boolean` | `false` | Enables chip-based multiple selection. |
|
||||
| `placeholder` | `string` | `None` / `none` | Visible input placeholder. |
|
||||
| `close_on_select` | `boolean` | `false` | Closes the popover after each option selection in multiple mode. |
|
||||
| `clear` | `boolean` | `false` | Shows a clear button in single-select mode. |
|
||||
| `auto_highlight` | `boolean` | `false` | Highlights the first matching option while filtering. |
|
||||
| `format` | `"value" \| "object"` | `"value"` | Hidden input serialization format. |
|
||||
| `main_attrs` | `object` | `{}` | Extra attributes for the `.combobox` root. |
|
||||
@@ -225,6 +226,8 @@ Some macro files include internal recursive helpers such as `render_select_items
|
||||
| `category` | `"success" \| "error" \| "info" \| "warning"` | `"success"` | Toast category. Controls icon and ARIA role. |
|
||||
| `title` | `string` | `""` | Toast title. |
|
||||
| `description` | `string` | `""` | Toast description. |
|
||||
| `duration` | `number` | default timeout | Timeout in milliseconds. Use `-1` to keep the toast open. |
|
||||
| `icon` | `string` | category icon | Custom icon HTML. |
|
||||
| `action` | `object` | `None` / `none` | Action button/link config. Supports `label`, `onclick`, and `href`. |
|
||||
| `cancel` | `object` | `None` / `none` | Cancel button/link config. Supports `label`, `onclick`, and `href`. |
|
||||
| `attrs` | `object` | `{}` | Extra attributes for the `.toast` element. |
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
import StyleHead from "../components/StyleHead.astro";
|
||||
|
||||
const title = "Page not found";
|
||||
const description = "This page does not exist, or it may have moved.";
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const media = matchMedia("(prefers-color-scheme: dark)");
|
||||
const getStoredTheme = () => {
|
||||
try {
|
||||
return localStorage.getItem("themeMode");
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const syncTheme = () => {
|
||||
const stored = getStoredTheme();
|
||||
document.documentElement.classList.toggle("dark", stored ? stored === "dark" : media.matches);
|
||||
};
|
||||
try {
|
||||
syncTheme();
|
||||
} catch (_) {}
|
||||
})();
|
||||
</script>
|
||||
<StyleHead />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title} | Basecoat</title>
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg" />
|
||||
</head>
|
||||
<body class="min-h-dvh bg-background text-foreground antialiased">
|
||||
<main class="grid min-h-dvh place-items-center px-6 py-16">
|
||||
<section class="empty max-w-sm">
|
||||
<header>
|
||||
<h1>Page not found</h1>
|
||||
<p>This page does not exist, or it may have moved. Try the docs or return home.</p>
|
||||
</header>
|
||||
<footer>
|
||||
<div class="flex gap-2">
|
||||
<a class="btn" data-size="sm" href="/">Home</a>
|
||||
<a class="btn" data-variant="outline" data-size="sm" href="/introduction/">Docs</a>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,8 +5,8 @@ import Nunjucks from 'nunjucks';
|
||||
const types = ['success', 'error', 'info', 'warning'];
|
||||
const env = new Nunjucks.Environment(
|
||||
new Nunjucks.FileSystemLoader([
|
||||
path.resolve('src/nunjucks'),
|
||||
path.resolve('docs/src/fragments/toast'),
|
||||
path.resolve('../src/templates/nunjucks'),
|
||||
path.resolve('src/fragments/toast'),
|
||||
]),
|
||||
{ autoescape: true },
|
||||
);
|
||||
@@ -18,7 +18,7 @@ export function getStaticPaths() {
|
||||
export function GET({ params }) {
|
||||
const type = String(params.type || '');
|
||||
if (!types.includes(type)) return new Response('Not found', { status: 404 });
|
||||
const source = fs.readFileSync(path.resolve('docs/src/fragments/toast', `${type}.njk`), 'utf8');
|
||||
const source = fs.readFileSync(path.resolve('src/fragments/toast', `${type}.njk`), 'utf8');
|
||||
const html = env.renderString(source).trim();
|
||||
return new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8' } });
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,15 +1,15 @@
|
||||
---
|
||||
import { CommandDialog } from "reallysimpledocs/components";
|
||||
import basecoatJsUrl from "basecoat-css/all.min?url";
|
||||
import StyleHead from "../../docs/src/site/StyleHead.astro";
|
||||
import vegaUrl from "../../docs/src/assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../../docs/src/assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../../docs/src/assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../../docs/src/assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../../docs/src/assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../../docs/src/assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../../docs/src/assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../../docs/src/assets/styles/style-rhea.css?url";
|
||||
import StyleHead from "../components/StyleHead.astro";
|
||||
import vegaUrl from "../docs/assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../docs/assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../docs/assets/styles/style-maia.css?url";
|
||||
import lyraUrl from "../docs/assets/styles/style-lyra.css?url";
|
||||
import miraUrl from "../docs/assets/styles/style-mira.css?url";
|
||||
import lumaUrl from "../docs/assets/styles/style-luma.css?url";
|
||||
import seraUrl from "../docs/assets/styles/style-sera.css?url";
|
||||
import rheaUrl from "../docs/assets/styles/style-rhea.css?url";
|
||||
import ArrowRight from "lucide-static/icons/arrow-right.svg?raw";
|
||||
import Activity from "lucide-static/icons/activity.svg?raw";
|
||||
import BanknoteArrowUp from "lucide-static/icons/banknote-arrow-up.svg?raw";
|
||||
@@ -664,7 +664,7 @@ const sidebarSections = [
|
||||
};
|
||||
const applyStyle = (style) => {
|
||||
const nextStyle = Object.prototype.hasOwnProperty.call(styleUrls, style) ? style : "vega";
|
||||
const href = styleUrls[nextStyle]?.startsWith("/docs/src/assets/styles/")
|
||||
const href = styleUrls[nextStyle]?.startsWith("/src/docs/assets/styles/")
|
||||
? `${styleUrls[nextStyle]}?direct`
|
||||
: styleUrls[nextStyle];
|
||||
const link = getStyleLink();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function GET({ site }) {
|
||||
const origin = site?.toString().replace(/\/$/, '') || 'https://basecoatui.com';
|
||||
return new Response(`User-agent: *\nAllow: /\n\nSitemap: ${origin}/sitemap.xml\n`, {
|
||||
return new Response(`User-agent: *\nAllow: /\n\nSitemap: ${origin}/sitemap-index.xml\n`, {
|
||||
headers: { 'content-type': 'text/plain; charset=utf-8' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const routePath = (slug) => slug === 'index' ? '/' : `/${slug.replace(/^\/+|\/+$/g, '')}/`;
|
||||
|
||||
function flatten(menu) {
|
||||
const out = [];
|
||||
const walk = (items = []) => {
|
||||
for (const item of items) {
|
||||
if (typeof item === 'string') out.push(item);
|
||||
else if (item?.slug) out.push(item.slug);
|
||||
else if (item?.type === 'submenu') walk(item.items || []);
|
||||
}
|
||||
};
|
||||
for (const group of menu || []) if (group?.type === 'group') walk(group.items || []);
|
||||
return out;
|
||||
}
|
||||
|
||||
export function GET({ site }) {
|
||||
const origin = site?.toString().replace(/\/$/, '') || 'https://basecoatui.com';
|
||||
const manifest = JSON.parse(fs.readFileSync(path.resolve('docs/src/docs.json'), 'utf8'));
|
||||
const urls = flatten(manifest.menu).map((slug) => ` <url><loc>${origin}${routePath(slug)}</loc></url>`).join('\n');
|
||||
return new Response(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls}\n</urlset>\n`, {
|
||||
headers: { 'content-type': 'application/xml; charset=utf-8' },
|
||||
});
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"$schema": "../node_modules/wrangler/config-schema.json",
|
||||
"name": "basecoat",
|
||||
"compatibility_date": "2026-06-02",
|
||||
"assets": {
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
hidden_input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("combobox-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set input_attrs = input_attrs or {} %}
|
||||
{% set popover_attrs = popover_attrs or {} %}
|
||||
{% set listbox_attrs = listbox_attrs or {} %}
|
||||
{% set hidden_input_attrs = hidden_input_attrs or {} %}
|
||||
{% set selected_entries = namespace(items=[]) %}
|
||||
{% if selected is defined and selected is iterable and selected is not string %}
|
||||
{% set selected_values = selected %}
|
||||
@@ -43,7 +48,7 @@
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="combobox {{ main_attrs.class }}"
|
||||
class="combobox {{ main_attrs.class | default('') }}"
|
||||
{% if format == "object" %}data-format="object"{% endif %}
|
||||
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
|
||||
{% if auto_highlight %}data-auto-highlight="true"{% endif %}
|
||||
@@ -55,7 +60,7 @@
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
class="{{ input_attrs.class }}"
|
||||
class="{{ input_attrs.class | default('') }}"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
@@ -68,7 +73,7 @@
|
||||
{% endfor %}
|
||||
>
|
||||
{% elif clear %}
|
||||
<div class="input-group {{ input_attrs.class }}">
|
||||
<div class="input-group {{ input_attrs.class | default('') }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
@@ -103,7 +108,7 @@
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
class="{{ input_attrs.class }}"
|
||||
class="{{ input_attrs.class | default('') }}"
|
||||
{% for key, value in input_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -127,7 +132,7 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() if caller else '' }}
|
||||
{{ caller() if caller is defined else '' }}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
menu_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("command-" ~ range(100000, 999999) | random | string) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set input_attrs = input_attrs or {} %}
|
||||
{% set menu_attrs = menu_attrs or {} %}
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="command {{ main_attrs.class }}"
|
||||
class="command {{ main_attrs.class | default('') }}"
|
||||
aria-label="Command menu"
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
@@ -50,7 +53,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class | default('') }}"
|
||||
{% for key, value in menu_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -58,7 +61,7 @@
|
||||
{% if items and items|length > 0 %}
|
||||
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,9 +90,12 @@
|
||||
open=False
|
||||
) %}
|
||||
{% set id = id or ("command-dialog-" ~ range(100000, 999999) | random | string) %}
|
||||
{% set dialog_attrs = dialog_attrs or {} %}
|
||||
{% set input_attrs = input_attrs or {} %}
|
||||
{% set menu_attrs = menu_attrs or {} %}
|
||||
<dialog
|
||||
id="{{ id }}"
|
||||
class="command-dialog {{ dialog_attrs.class }}"
|
||||
class="command-dialog {{ dialog_attrs.class | default('') }}"
|
||||
aria-label="Command menu"
|
||||
{% if open %}open{% endif %}
|
||||
onclick="if (event.target === this) this.close()"
|
||||
@@ -121,7 +127,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class | default('') }}"
|
||||
{% for key, value in menu_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -129,7 +135,7 @@
|
||||
{% if items and items|length > 0 %}
|
||||
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,53 +155,54 @@
|
||||
{% macro render_command_items(items, parent_id_prefix="items") %}
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
{% set item_type = item.type | default("item") %}
|
||||
{% if item_type == "group" %}
|
||||
{% set group_label_id = item.id | default("group-label-" + item_id) %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% if item.attrs %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<span role="heading" id="{{ group_label_id }}">{{ item.label | safe }}</span>
|
||||
{{ render_command_items(item['items'], item_id) if item['items'] }}
|
||||
<span role="heading" id="{{ group_label_id }}">{{ item.label | default('') | safe }}</span>
|
||||
{{ render_command_items(item.get('items', []), item_id) if item.get('items') }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
{% elif item_type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% if item.url %}
|
||||
{% elif item_type == "item" %}
|
||||
{% if item.url is defined and item.url %}
|
||||
<a
|
||||
id="{{ item_id }}"
|
||||
href="{{ item.url }}"
|
||||
role="menuitem"
|
||||
{% if item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
||||
{% if item.disabled %}aria-disabled="true"{% endif %}
|
||||
{% if item.attrs %}
|
||||
{% if item.keywords is defined and item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
||||
{% if item.disabled is defined and item.disabled %}aria-disabled="true"{% endif %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "href" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | default('') | safe }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div
|
||||
id="{{ item_id }}"
|
||||
role="menuitem"
|
||||
{% if item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
||||
{% if item.disabled %}aria-disabled="true"{% endif %}
|
||||
{% if item.attrs %}
|
||||
{% if item.keywords is defined and item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
||||
{% if item.disabled is defined and item.disabled %}aria-disabled="true"{% endif %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | default('') | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
@param title {string} [optional] - Title text displayed in the dialog header.
|
||||
@param description {string} [optional] - Description text displayed below the title.
|
||||
@param footer {string} [optional] - HTML content for the dialog footer.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content container.
|
||||
@param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
||||
@@ -32,6 +31,11 @@
|
||||
close_on_overlay_click=true
|
||||
) %}
|
||||
{% set id = id or ("dialog-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set dialog_attrs = dialog_attrs or {} %}
|
||||
{% set trigger_attrs = trigger_attrs or {} %}
|
||||
{% set header_attrs = header_attrs or {} %}
|
||||
{% set body_attrs = body_attrs or {} %}
|
||||
{% set footer_attrs = footer_attrs or {} %}
|
||||
{% if trigger %}
|
||||
<button
|
||||
type="button"
|
||||
@@ -45,9 +49,10 @@
|
||||
{% endif %}
|
||||
<dialog
|
||||
id="{{ id }}"
|
||||
class="dialog {{ dialog_attrs.class }}"
|
||||
aria-labelledby="{{ id }}-title"
|
||||
class="dialog {{ dialog_attrs.class | default('') }}"
|
||||
{% if title %}aria-labelledby="{{ id }}-title"{% endif %}
|
||||
{% if description %}aria-describedby="{{ id }}-description"{% endif %}
|
||||
{% if open %}open{% endif %}
|
||||
{% if close_on_overlay_click %}onclick="if (event.target === this) this.close()"{% endif %}
|
||||
{% for key, value in dialog_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
@@ -60,11 +65,11 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<h2 id="{{ id }}-title">{{ title | safe }}</h2>
|
||||
{% if title %}<h2 id="{{ id }}-title">{{ title | safe }}</h2>{% endif %}
|
||||
{% if description %}<p id="{{ id }}-description">{{ description | safe }}</p>{% endif %}
|
||||
</header>
|
||||
{% endif %}
|
||||
{% if caller %}
|
||||
{% if caller is defined %}
|
||||
<section
|
||||
{% for key, value in body_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
|
||||
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
||||
#}
|
||||
{% macro dropdown_menu(
|
||||
trigger,
|
||||
@@ -18,10 +19,14 @@
|
||||
menu_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("dropdown-menu-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set trigger_attrs = trigger_attrs or {} %}
|
||||
{% set popover_attrs = popover_attrs or {} %}
|
||||
{% set menu_attrs = menu_attrs or {} %}
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="dropdown-menu {{ main_attrs.class }}"
|
||||
class="dropdown-menu {{ main_attrs.class | default('') }}"
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -57,7 +62,7 @@
|
||||
{% if items %}
|
||||
{{ render_dropdown_items(items, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,49 +79,50 @@
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
{% set item_type = item.type | default("item") %}
|
||||
{% if item_type == "group" %}
|
||||
{% set group_label_id = item.id | default("group-label-" + item_id) %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% if item.attrs %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_dropdown_items(item['items'], item_id) if item['items'] }}
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label | default('') }}</div>
|
||||
{{ render_dropdown_items(item.get('items', []), item_id) if item.get('items') }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
{% elif item_type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% if item.url %}
|
||||
{% elif item_type == "item" %}
|
||||
{% if item.url is defined and item.url %}
|
||||
<a
|
||||
id="{{ item_id }}"
|
||||
role="menuitem"
|
||||
href="{{ item.url }}"
|
||||
{% if item.attrs %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% if key != "href" and key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | default('') | safe }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div
|
||||
id="{{ item_id }}"
|
||||
role="menuitem"
|
||||
{% if item.attrs %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | default('') | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
popover_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("popover-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set trigger_attrs = trigger_attrs or {} %}
|
||||
{% set popover_attrs = popover_attrs or {} %}
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="popover {{ main_attrs.class }}"
|
||||
class="popover {{ main_attrs.class | default('') }}"
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -42,7 +45,7 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@param multiple {boolean} [optional] [default=false] - Enables multiple selection mode.
|
||||
@param placeholder {string} [optional] - Placeholder text shown when no option is selected.
|
||||
@param close_on_select {boolean} [optional] [default=false] - Closes the popover when selecting an option in multiple mode.
|
||||
@param format {string} [optional] [default="value"] - Hidden input serialization format: "value" or "object".
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
||||
@@ -29,10 +30,15 @@
|
||||
input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set trigger_attrs = trigger_attrs or {} %}
|
||||
{% set popover_attrs = popover_attrs or {} %}
|
||||
{% set listbox_attrs = listbox_attrs or {} %}
|
||||
{% set input_attrs = input_attrs or {} %}
|
||||
|
||||
{% if selected is defined and selected is iterable and selected is not string %}
|
||||
{% if selected is defined and selected is not none and selected is iterable and selected is not string %}
|
||||
{% set selectedArray = selected %}
|
||||
{% elif selected is defined %}
|
||||
{% elif selected is defined and selected is not none %}
|
||||
{% set selectedArray = [selected] %}
|
||||
{% else %}
|
||||
{% set selectedArray = [] %}
|
||||
@@ -42,8 +48,9 @@
|
||||
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
{% if item.type == "group" %}
|
||||
{% for sub_item in item['items'] %}
|
||||
{% set item_type = item.type | default("item") %}
|
||||
{% if item_type == "group" %}
|
||||
{% for sub_item in item.get('items', []) %}
|
||||
{% if not first_option.item %}
|
||||
{% set first_option.item = sub_item %}
|
||||
{% endif %}
|
||||
@@ -81,7 +88,7 @@
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="select {{ main_attrs.class }}"
|
||||
class="select {{ main_attrs.class | default('') }}"
|
||||
{% if format == "object" %}data-format="object"{% endif %}
|
||||
{% if placeholder %}data-placeholder="{{ placeholder }}"{% endif %}
|
||||
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
|
||||
@@ -91,7 +98,7 @@
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="{{ trigger_attrs.class }}"
|
||||
class="{{ trigger_attrs.class | default('') }}"
|
||||
id="{{ id }}-trigger"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="false"
|
||||
@@ -124,7 +131,7 @@
|
||||
{% if items %}
|
||||
{{ render_select_items(items, selectedArray, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -148,35 +155,36 @@
|
||||
{% macro render_select_items(items, selectedArray, parent_id_prefix="items") %}
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
{% set item_type = item.type | default("item") %}
|
||||
{% if item_type == "group" %}
|
||||
{% set group_label_id = item.id | default("group-label-" + item_id) %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% if item.attrs %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_select_items(item['items'], selectedArray, item_id) if item['items'] }}
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label | default('') }}</div>
|
||||
{{ render_select_items(item.get('items', []), selectedArray, item_id) if item.get('items') }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
{% elif item_type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% elif item_type == "item" %}
|
||||
<div
|
||||
id="{{ item_id }}"
|
||||
role="option"
|
||||
{% if item.value is defined and item.value is not none %}data-value="{{ item.value }}"{% endif %}
|
||||
{% if item.value in selectedArray %}aria-selected="true"{% endif %}
|
||||
{% if item.attrs %}
|
||||
{% if item.value is defined and item.value in selectedArray %}aria-selected="true"{% endif %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
{{ item.label | default(item.value | default('')) | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -26,9 +26,13 @@
|
||||
content_attrs={},
|
||||
footer_attrs={}
|
||||
) %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set header_attrs = header_attrs or {} %}
|
||||
{% set content_attrs = content_attrs or {} %}
|
||||
{% set footer_attrs = footer_attrs or {} %}
|
||||
<aside
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="sidebar {{ main_attrs.class }}"
|
||||
class="sidebar {{ main_attrs.class | default('') }}"
|
||||
data-side="{{ side if side else "left" }}"
|
||||
aria-hidden="{{ "true" if not open else "false" }}"
|
||||
{{ "inert" if not open }}
|
||||
@@ -57,7 +61,7 @@
|
||||
{% if menu %}
|
||||
{{ render_sidebar_content(menu, id ~ "-content" if id else "content") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
@@ -84,59 +88,60 @@
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
{% set item_type = item.type | default("item") %}
|
||||
{% if item_type == "group" %}
|
||||
{% set group_label_id = item.id | default("group-label-" + item_id) %}
|
||||
<div
|
||||
role="group"
|
||||
{% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
|
||||
{% if item.attrs %}
|
||||
{% if item.label is defined and item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.label %}
|
||||
{% if item.label is defined and item.label %}
|
||||
<h3 id="{{ group_label_id }}">{{ item.label }}</h3>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{{ render_sidebar_content(item["items"], item_id) if item["items"] }}
|
||||
{{ render_sidebar_content(item.get("items", []), item_id) if item.get("items") }}
|
||||
</ul>
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
{% elif item_type == "separator" %}
|
||||
<hr role="separator"/>
|
||||
{% elif item.type == "submenu" %}
|
||||
{% elif item_type == "submenu" %}
|
||||
<li>
|
||||
<details
|
||||
id="submenu-{{ item_id }}"
|
||||
{{ "open" if item.open }}
|
||||
{% if item.attrs %}
|
||||
{{ "open" if item.open is defined and item.open }}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label | safe }}</span>
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label | default('') | safe }}</span>
|
||||
</summary>
|
||||
<ul id="submenu-{{ item_id }}-content">
|
||||
{{ render_sidebar_content(item["items"], item_id) if item["items"] }}
|
||||
{{ render_sidebar_content(item.get("items", []), item_id) if item.get("items") }}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% elif item_type == "item" %}
|
||||
<li>
|
||||
<a
|
||||
href="{{ item.url }}"
|
||||
{% if item.current %}aria-current="page"{% endif %}
|
||||
{% if item.attrs %}
|
||||
href="{{ item.url | default('#') }}"
|
||||
{% if item.current is defined and item.current %}aria-current="page"{% endif %}
|
||||
{% if item.attrs is defined and item.attrs %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label | safe }}</span>
|
||||
{% if item.icon is defined and item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label | default('') | safe }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
@param id {string} - Unique identifier for the tabs component.
|
||||
@param tabsets {array} - An array of objects, each representing a tab and its panel.
|
||||
Each object should have:
|
||||
- id {string}: Unique identifier prefix for the tab and panel.
|
||||
- tab {string}: HTML content for the tab button.
|
||||
- panel {string} [optional]: HTML content for the tab panel.
|
||||
- tab_attrs {object} [optional]: Additional HTML attributes for the tab button.
|
||||
@@ -23,9 +22,11 @@
|
||||
)
|
||||
%}
|
||||
{% set id = id or ("tabs-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set orientation = tablist_attrs['aria-orientation'] or 'horizontal' %}
|
||||
{% set main_attrs = main_attrs or {} %}
|
||||
{% set tablist_attrs = tablist_attrs or {} %}
|
||||
{% set orientation = tablist_attrs.get('aria-orientation', 'horizontal') %}
|
||||
<div
|
||||
class="tabs {{ main_attrs.class }}"
|
||||
class="tabs {{ main_attrs.class | default('') }}"
|
||||
id="{{ id }}"
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
@@ -39,15 +40,16 @@
|
||||
{% endfor %}
|
||||
>
|
||||
{% for tabset in tabsets %}
|
||||
{% set tab_attrs = tabset.tab_attrs | default({}) %}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
id="{{ id }}-tab-{{ loop.index }}"
|
||||
aria-controls="{{ id }}-panel-{{ loop.index }}"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
tabindex="{{ '0' if loop.index == default_tab_index and not tabset.tab_attrs.disabled and tabset.tab_attrs['aria-disabled'] != 'true' else '-1' }}"
|
||||
{% if tabset.tab_attrs %}
|
||||
{% for key, value in tabset.tab_attrs.items() %}
|
||||
tabindex="{{ '0' if loop.index == default_tab_index and not (tab_attrs.disabled | default(false)) and (tab_attrs['aria-disabled'] | default('false')) != 'true' else '-1' }}"
|
||||
{% if tab_attrs %}
|
||||
{% for key, value in tab_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -58,7 +60,7 @@
|
||||
</nav>
|
||||
|
||||
{% for tabset in tabsets %}
|
||||
{% if tabset.panel %}
|
||||
{% if tabset.panel is defined and tabset.panel %}
|
||||
<div
|
||||
role="tabpanel"
|
||||
id="{{ id }}-panel-{{ loop.index }}"
|
||||
@@ -66,7 +68,7 @@
|
||||
tabindex="-1"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
{% if loop.index != default_tab_index %}hidden{% endif %}
|
||||
{% if tabset.panel_attrs %}
|
||||
{% if tabset.panel_attrs is defined and tabset.panel_attrs %}
|
||||
{% for key, value in tabset.panel_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
|
||||
@@ -13,24 +13,27 @@
|
||||
toasts=[],
|
||||
attrs={}
|
||||
) %}
|
||||
{% set attrs = attrs or {} %}
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="toaster {{ attrs.class }}"
|
||||
class="toaster {{ attrs.class | default('') }}"
|
||||
{% for key, value in attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% for item in toasts %}
|
||||
{{ toast(
|
||||
category=item.category,
|
||||
title=item.title,
|
||||
description=item.description,
|
||||
action=item.action,
|
||||
cancel=item.cancel,
|
||||
attrs=item.attrs
|
||||
category=item.category | default("success"),
|
||||
title=item.title | default(""),
|
||||
description=item.description | default(""),
|
||||
action=item.action | default(None),
|
||||
cancel=item.cancel | default(None),
|
||||
duration=item.duration | default(None),
|
||||
icon=item.icon | default(None),
|
||||
attrs=item.attrs | default({})
|
||||
) }}
|
||||
{% endfor %}
|
||||
{{ caller() if caller }}
|
||||
{{ caller() if caller is defined }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -40,14 +43,16 @@
|
||||
@param category {string} [optional] [default="success"] - Type of toast ('success', 'error', 'info', 'warning'). Determines icon and ARIA role.
|
||||
@param title {string} [optional] - The main title text of the toast.
|
||||
@param description {string} [optional] - The secondary description text.
|
||||
@param duration {number} [optional] - Timeout in milliseconds. Use -1 to keep the toast open.
|
||||
@param icon {string} [optional] - Custom icon HTML. Defaults to the category icon.
|
||||
@param action {object} [optional] - Defines an action button.
|
||||
- label {string}: Button text.
|
||||
- onclick {string}: JavaScript code to execute on click.
|
||||
- url {string}: URL for an anchor link button.
|
||||
- href {string}: URL for an anchor link button.
|
||||
@param cancel {object} [optional] - Defines a cancel/dismiss button (similar structure to action).
|
||||
- label {string}: Button text.
|
||||
- onclick {string}: JavaScript code to execute on click.
|
||||
- url {string}: URL for an anchor link button.
|
||||
- href {string}: URL for an anchor link button.
|
||||
@param attrs {object} [optional] - Additional HTML attributes for the toast div.
|
||||
#}
|
||||
{% macro toast(
|
||||
@@ -56,20 +61,28 @@
|
||||
description="",
|
||||
action=None,
|
||||
cancel=None,
|
||||
duration=None,
|
||||
icon=None,
|
||||
attrs={}
|
||||
) %}
|
||||
{% set attrs = attrs or {} %}
|
||||
{% set action_href = action.href if action and action.href is defined and action.href else (action.url if action and action.url is defined and action.url else None) %}
|
||||
{% set cancel_href = cancel.href if cancel and cancel.href is defined and cancel.href else (cancel.url if cancel and cancel.url is defined and cancel.url else None) %}
|
||||
<div
|
||||
class="toast {{ attrs.class }}"
|
||||
class="toast {{ attrs.class | default('') }}"
|
||||
role="{{ 'alert' if category == 'error' else 'status' }}"
|
||||
aria-atomic="true"
|
||||
aria-hidden="false"
|
||||
{% if category %}data-category="{{ category }}"{% endif %}
|
||||
{% if duration is not none %}data-duration="{{ duration }}"{% endif %}
|
||||
{% for key, value in attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<div class="toast-content">
|
||||
{% if category in ["error", "success", "info", "warning"] %}
|
||||
{% if icon %}
|
||||
{{ icon | safe }}
|
||||
{% elif category in ["error", "success", "info", "warning"] %}
|
||||
{% if category == "success" %}
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
{% elif category == "error" %}
|
||||
@@ -87,31 +100,40 @@
|
||||
{% if action or cancel %}
|
||||
<footer>
|
||||
{% if action %}
|
||||
{% if action.href %}
|
||||
{% if action_href %}
|
||||
<a
|
||||
href="{{ action.href }}"
|
||||
href="{{ action_href }}"
|
||||
class="btn" data-size="sm"
|
||||
data-toast-action
|
||||
>{{ action.label }}</a>
|
||||
>{{ action.label | default('') }}</a>
|
||||
{% else %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
data-size="sm"
|
||||
data-toast-action
|
||||
{% if action.onclick %}onclick="{{ action.onclick }}"{% endif %}
|
||||
>{{ action.label }}</button>
|
||||
{% if action.onclick is defined and action.onclick %}onclick="{{ action.onclick }}"{% endif %}
|
||||
>{{ action.label | default('') }}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if cancel %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
{% if cancel.onclick %}onclick="{{ cancel.onclick }}"{% endif %}
|
||||
>{{ cancel.label }}</button>
|
||||
{% if cancel_href %}
|
||||
<a
|
||||
href="{{ cancel_href }}"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
>{{ cancel.label | default('') }}</a>
|
||||
{% else %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
{% if cancel.onclick is defined and cancel.onclick %}onclick="{{ cancel.onclick }}"{% endif %}
|
||||
>{{ cancel.label | default('') }}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
@param title {string} [optional] - Title text displayed in the dialog header.
|
||||
@param description {string} [optional] - Description text displayed below the title.
|
||||
@param footer {string} [optional] - HTML content for the dialog footer.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content container.
|
||||
@param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
||||
@@ -46,8 +45,9 @@
|
||||
<dialog
|
||||
id="{{ id }}"
|
||||
class="dialog {{ dialog_attrs.class }}"
|
||||
aria-labelledby="{{ id }}-title"
|
||||
{% if title %}aria-labelledby="{{ id }}-title"{% endif %}
|
||||
{% if description %}aria-describedby="{{ id }}-description"{% endif %}
|
||||
{% if open %}open{% endif %}
|
||||
{% if close_on_overlay_click %}onclick="if (event.target === this) this.close()"{% endif %}
|
||||
{% for key, value in dialog_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
@@ -60,7 +60,7 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<h2 id="{{ id }}-title">{{ title | safe }}</h2>
|
||||
{% if title %}<h2 id="{{ id }}-title">{{ title | safe }}</h2>{% endif %}
|
||||
{% if description %}<p id="{{ id }}-description">{{ description | safe }}</p>{% endif %}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
|
||||
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
||||
#}
|
||||
{% macro dropdown_menu(
|
||||
trigger,
|
||||
@@ -98,7 +99,7 @@
|
||||
href="{{ item.url }}"
|
||||
{% if item.attrs %}
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% if key != "href" and key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
) %}
|
||||
{% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
|
||||
|
||||
{% if selected is defined and selected is iterable and selected is not string %}
|
||||
{% if selected is defined and selected is not none and selected is iterable and selected is not string %}
|
||||
{% set selectedArray = selected %}
|
||||
{% elif selected is defined %}
|
||||
{% elif selected is defined and selected is not none %}
|
||||
{% set selectedArray = [selected] %}
|
||||
{% else %}
|
||||
{% set selectedArray = [] %}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@param side {string} [optional] - Side of the sidebar to display.
|
||||
@param header {string} [optional] - Header content for the sidebar.
|
||||
@param footer {string} [optional] - Footer content for the sidebar.
|
||||
@param items {array} [optional] - Array of menu items for the sidebar.
|
||||
@param menu {array} [optional] - Array of menu items for the sidebar.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param header_attrs {object} [optional] - Additional HTML attributes for the header element.
|
||||
@param content_attrs {object} [optional] - Additional HTML attributes for the content section.
|
||||
|
||||
@@ -23,5 +23,17 @@
|
||||
-%}
|
||||
{%- set id = id or ("tabs-" + (range(100000, 999999) | random | string)) -%}
|
||||
{%- set orientation = tablist_attrs['aria-orientation'] or 'horizontal' -%}
|
||||
<div class="tabs {{ main_attrs.class }}" id="{{ id }}"{% for key, value in main_attrs %}{% if key != 'class' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}><nav role="tablist" aria-orientation="{{ orientation }}"{% for key, value in tablist_attrs %}{% if key != 'aria-orientation' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}>{% for tabset in tabsets %}<button type="button" role="tab" id="{{ id }}-tab-{{ loop.index }}" aria-controls="{{ id }}-panel-{{ loop.index }}" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}" tabindex="{{ '0' if loop.index == default_tab_index and not tabset.tab_attrs.disabled and tabset.tab_attrs['aria-disabled'] != 'true' else '-1' }}"{% if tabset.tab_attrs %}{% for key, value in tabset.tab_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.tab | safe }}</button>{% endfor %}</nav>{% for tabset in tabsets %}{% if tabset.panel %}<div role="tabpanel" id="{{ id }}-panel-{{ loop.index }}" aria-labelledby="{{ id }}-tab-{{ loop.index }}" tabindex="-1" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"{% if loop.index != default_tab_index %} hidden{% endif %}{% if tabset.panel_attrs %}{% for key, value in tabset.panel_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.panel | safe }}</div>{% endif %}{% endfor %}</div>
|
||||
<div class="tabs {{ main_attrs.class }}" id="{{ id }}"{% for key, value in main_attrs %}{% if key != 'class' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}>
|
||||
<nav role="tablist" aria-orientation="{{ orientation }}"{% for key, value in tablist_attrs %}{% if key != 'aria-orientation' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}>
|
||||
{% for tabset in tabsets %}
|
||||
{%- set tab_attrs = tabset.tab_attrs or {} -%}
|
||||
<button type="button" role="tab" id="{{ id }}-tab-{{ loop.index }}" aria-controls="{{ id }}-panel-{{ loop.index }}" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}" tabindex="{{ '0' if loop.index == default_tab_index and not tab_attrs.disabled and tab_attrs['aria-disabled'] != 'true' else '-1' }}"{% if tab_attrs %}{% for key, value in tab_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.tab | safe }}</button>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% for tabset in tabsets %}
|
||||
{% if tabset.panel %}
|
||||
<div role="tabpanel" id="{{ id }}-panel-{{ loop.index }}" aria-labelledby="{{ id }}-tab-{{ loop.index }}" tabindex="-1" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"{% if loop.index != default_tab_index %} hidden{% endif %}{% if tabset.panel_attrs %}{% for key, value in tabset.panel_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.panel | safe }}</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
description=item.description,
|
||||
action=item.action,
|
||||
cancel=item.cancel,
|
||||
attrs=item.attrs
|
||||
duration=item.duration,
|
||||
icon=item.icon,
|
||||
attrs=item.attrs or {}
|
||||
) }}
|
||||
{% endfor %}
|
||||
{{ caller() if caller }}
|
||||
@@ -40,14 +42,16 @@
|
||||
@param category {string} [optional] [default="success"] - Type of toast ('success', 'error', 'info', 'warning'). Determines icon and ARIA role.
|
||||
@param title {string} [optional] - The main title text of the toast.
|
||||
@param description {string} [optional] - The secondary description text.
|
||||
@param duration {number} [optional] - Timeout in milliseconds. Use -1 to keep the toast open.
|
||||
@param icon {string} [optional] - Custom icon HTML. Defaults to the category icon.
|
||||
@param action {object} [optional] - Defines an action button.
|
||||
- label {string}: Button text.
|
||||
- onclick {string}: JavaScript code to execute on click.
|
||||
- url {string}: URL for an anchor link button.
|
||||
- href {string}: URL for an anchor link button.
|
||||
@param cancel {object} [optional] - Defines a cancel/dismiss button (similar structure to action).
|
||||
- label {string}: Button text.
|
||||
- onclick {string}: JavaScript code to execute on click.
|
||||
- url {string}: URL for an anchor link button.
|
||||
- href {string}: URL for an anchor link button.
|
||||
@param attrs {object} [optional] - Additional HTML attributes for the toast div.
|
||||
#}
|
||||
{% macro toast(
|
||||
@@ -56,20 +60,27 @@
|
||||
description="",
|
||||
action=None,
|
||||
cancel=None,
|
||||
duration=None,
|
||||
icon=None,
|
||||
attrs={}
|
||||
) %}
|
||||
{% set action_href = action.href if action and action.href else (action.url if action and action.url else None) %}
|
||||
{% set cancel_href = cancel.href if cancel and cancel.href else (cancel.url if cancel and cancel.url else None) %}
|
||||
<div
|
||||
class="toast {{ attrs.class }}"
|
||||
role="{{ 'alert' if category == 'error' else 'status' }}"
|
||||
aria-atomic="true"
|
||||
aria-hidden="false"
|
||||
{% if category %}data-category="{{ category }}"{% endif %}
|
||||
{% if duration is not none %}data-duration="{{ duration }}"{% endif %}
|
||||
{% for key, value in attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<div class="toast-content">
|
||||
{% if category in ["error", "success", "info", "warning"] %}
|
||||
{% if icon %}
|
||||
{{ icon | safe }}
|
||||
{% elif category in ["error", "success", "info", "warning"] %}
|
||||
{% if category == "success" %}
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
{% elif category == "error" %}
|
||||
@@ -87,9 +98,9 @@
|
||||
{% if action or cancel %}
|
||||
<footer>
|
||||
{% if action %}
|
||||
{% if action.href %}
|
||||
{% if action_href %}
|
||||
<a
|
||||
href="{{ action.href }}"
|
||||
href="{{ action_href }}"
|
||||
class="btn" data-size="sm"
|
||||
data-toast-action
|
||||
>{{ action.label }}</a>
|
||||
@@ -97,21 +108,30 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
data-size="sm"
|
||||
data-toast-action
|
||||
{% if action.onclick %}onclick="{{ action.onclick }}"{% endif %}
|
||||
>{{ action.label }}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if cancel %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
{% if cancel.onclick %}onclick="{{ cancel.onclick }}"{% endif %}
|
||||
>{{ cancel.label }}</button>
|
||||
{% if cancel_href %}
|
||||
<a
|
||||
href="{{ cancel_href }}"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
>{{ cancel.label }}</a>
|
||||
{% else %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn" data-variant="outline" data-size="sm"
|
||||
data-toast-cancel
|
||||
{% if cancel.onclick %}onclick="{{ cancel.onclick }}"{% endif %}
|
||||
>{{ cancel.label }}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
Reference in New Issue
Block a user