mirror of
https://github.com/tabler/tabler.git
synced 2026-08-29 13:21:29 +04:00
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.
This commit is contained in:
+65
-65
@@ -30,8 +30,8 @@ const args = process.argv.slice(2)
|
||||
const flags = args.filter((arg) => arg.startsWith('--'))
|
||||
const [scssDir, outDir] = args.filter((arg) => !arg.startsWith('--'))
|
||||
if (!scssDir || !outDir) {
|
||||
console.error('usage: tsx build-css.ts <scssDir> <outDir> [--rtl] [--minify]')
|
||||
process.exit(1)
|
||||
console.error('usage: tsx build-css.ts <scssDir> <outDir> [--rtl] [--minify]')
|
||||
process.exit(1)
|
||||
}
|
||||
const withRtl = flags.includes('--rtl')
|
||||
const withMinify = flags.includes('--minify')
|
||||
@@ -43,96 +43,96 @@ const pendingWrites: { file: string; content: string }[] = []
|
||||
// Skipping identical content keeps file watchers quiet for outputs a given scss
|
||||
// edit did not actually change (the dev servers full-reload on these writes).
|
||||
function queueWrite(file: string, content: string) {
|
||||
if (existsSync(file) && readFileSync(file, 'utf8') === content) return
|
||||
pendingWrites.push({ file, content })
|
||||
if (existsSync(file) && readFileSync(file, 'utf8') === content) return
|
||||
pendingWrites.push({ file, content })
|
||||
}
|
||||
|
||||
// sass + autoprefixer for one entry: scss/x.scss → outDir/x.css (+ .map)
|
||||
async function compile(entry: string): Promise<{ outFile: string; result: Result }> {
|
||||
const outFile = join(outDir, `${basename(entry, '.scss')}.css`)
|
||||
const compiled = compileSass(join(scssDir, entry), { loadPaths: ['node_modules'], style: 'expanded' })
|
||||
// The sass CLI ends its output files with a newline; the JS API's css string
|
||||
// does not. Keep the byte-identical CLI behavior (the annotation comment then
|
||||
// lands after a blank line, exactly like `postcss --replace` produced).
|
||||
const result = await postcss([autoprefixer({ cascade: false })]).process(`${compiled.css}\n`, {
|
||||
from: outFile,
|
||||
to: outFile,
|
||||
map: mapOptions,
|
||||
})
|
||||
queueWrite(outFile, result.css)
|
||||
if (result.map) queueWrite(`${outFile}.map`, result.map.toString())
|
||||
written.push(outFile)
|
||||
return { outFile, result }
|
||||
const outFile = join(outDir, `${basename(entry, '.scss')}.css`)
|
||||
const compiled = compileSass(join(scssDir, entry), { loadPaths: ['node_modules'], style: 'expanded' })
|
||||
// The sass CLI ends its output files with a newline; the JS API's css string
|
||||
// does not. Keep the byte-identical CLI behavior (the annotation comment then
|
||||
// lands after a blank line, exactly like `postcss --replace` produced).
|
||||
const result = await postcss([autoprefixer({ cascade: false })]).process(`${compiled.css}\n`, {
|
||||
from: outFile,
|
||||
to: outFile,
|
||||
map: mapOptions,
|
||||
})
|
||||
queueWrite(outFile, result.css)
|
||||
if (result.map) queueWrite(`${outFile}.map`, result.map.toString())
|
||||
written.push(outFile)
|
||||
return { outFile, result }
|
||||
}
|
||||
|
||||
// rtlcss over the prefixed output: outDir/x.css → outDir/x.rtl.css (+ .map)
|
||||
async function rtl(entry: string, outFile: string, base: Result): Promise<void> {
|
||||
const rtlFile = join(outDir, `${basename(entry, '.scss')}.rtl.css`)
|
||||
// Same input the CLI read from disk: the prefixed css including its
|
||||
// sourceMappingURL annotation, so postcss picks up the previous map.
|
||||
// The previous map is not on disk yet (writes are buffered) — pass it in.
|
||||
const result = await postcss([autoprefixer({ cascade: false }), rtlcss()]).process(base.css, {
|
||||
from: outFile,
|
||||
to: rtlFile,
|
||||
map: { ...mapOptions, prev: base.map.toString() },
|
||||
})
|
||||
queueWrite(rtlFile, result.css)
|
||||
if (result.map) queueWrite(`${rtlFile}.map`, result.map.toString())
|
||||
written.push(rtlFile)
|
||||
const rtlFile = join(outDir, `${basename(entry, '.scss')}.rtl.css`)
|
||||
// Same input the CLI read from disk: the prefixed css including its
|
||||
// sourceMappingURL annotation, so postcss picks up the previous map.
|
||||
// The previous map is not on disk yet (writes are buffered) — pass it in.
|
||||
const result = await postcss([autoprefixer({ cascade: false }), rtlcss()]).process(base.css, {
|
||||
from: outFile,
|
||||
to: rtlFile,
|
||||
map: { ...mapOptions, prev: base.map.toString() },
|
||||
})
|
||||
queueWrite(rtlFile, result.css)
|
||||
if (result.map) queueWrite(`${rtlFile}.map`, result.map.toString())
|
||||
written.push(rtlFile)
|
||||
}
|
||||
|
||||
// All compile work is done before the first write: compiles take seconds while
|
||||
// writes take milliseconds, so watchers see one tight burst instead of writes
|
||||
// spread across the whole build (which would need a long reload debounce).
|
||||
function flushWrites(): void {
|
||||
for (const { file, content } of pendingWrites) {
|
||||
writeFileSync(file, content)
|
||||
console.log(`build-css: ${file}`)
|
||||
}
|
||||
for (const { file, content } of pendingWrites) {
|
||||
writeFileSync(file, content)
|
||||
console.log(`build-css: ${file}`)
|
||||
}
|
||||
}
|
||||
|
||||
// clean-css over every produced file: outDir/x.css → outDir/x.min.css (+ .map).
|
||||
// Mirrors clean-css-cli's option coercion and batch output naming/annotation
|
||||
// (see the clean-css-cli package's index.js).
|
||||
async function minify(files: string[]): Promise<void> {
|
||||
const minified = await new CleanCSS({
|
||||
batch: true,
|
||||
format: 'breakWith=lf',
|
||||
inline: 'local',
|
||||
level: { 1: true },
|
||||
rebase: true,
|
||||
rebaseTo: resolve(outDir),
|
||||
returnPromise: true,
|
||||
sourceMap: true,
|
||||
sourceMapInlineSources: true,
|
||||
}).minify(files)
|
||||
for (const inputFile of files) {
|
||||
const fileResult = minified[inputFile]
|
||||
if (!fileResult) throw new Error(`build-css: no minify result for ${inputFile}`)
|
||||
if (fileResult.errors.length > 0) throw new Error(fileResult.errors.join('\n'))
|
||||
for (const warning of fileResult.warnings) console.warn(`build-css: ${warning}`)
|
||||
const minFile = inputFile.replace(/\.css$/, '.min.css')
|
||||
writeFileSync(minFile, `${fileResult.styles}${EOL}/*# sourceMappingURL=${basename(minFile)}.map */`)
|
||||
writeFileSync(`${minFile}.map`, fileResult.sourceMap.toString())
|
||||
console.log(`build-css: ${minFile}`)
|
||||
}
|
||||
const minified = await new CleanCSS({
|
||||
batch: true,
|
||||
format: 'breakWith=lf',
|
||||
inline: 'local',
|
||||
level: { 1: true },
|
||||
rebase: true,
|
||||
rebaseTo: resolve(outDir),
|
||||
returnPromise: true,
|
||||
sourceMap: true,
|
||||
sourceMapInlineSources: true,
|
||||
}).minify(files)
|
||||
for (const inputFile of files) {
|
||||
const fileResult = minified[inputFile]
|
||||
if (!fileResult) throw new Error(`build-css: no minify result for ${inputFile}`)
|
||||
if (fileResult.errors.length > 0) throw new Error(fileResult.errors.join('\n'))
|
||||
for (const warning of fileResult.warnings) console.warn(`build-css: ${warning}`)
|
||||
const minFile = inputFile.replace(/\.css$/, '.min.css')
|
||||
writeFileSync(minFile, `${fileResult.styles}${EOL}/*# sourceMappingURL=${basename(minFile)}.map */`)
|
||||
writeFileSync(`${minFile}.map`, fileResult.sourceMap.toString())
|
||||
console.log(`build-css: ${minFile}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapped in a function because tsx compiles root-level .ts as CJS, where
|
||||
// top-level await is unavailable.
|
||||
async function main() {
|
||||
const entries = readdirSync(scssDir).filter((file) => file.endsWith('.scss') && !file.startsWith('_'))
|
||||
mkdirSync(outDir, { recursive: true })
|
||||
const entries = readdirSync(scssDir).filter((file) => file.endsWith('.scss') && !file.startsWith('_'))
|
||||
mkdirSync(outDir, { recursive: true })
|
||||
|
||||
for (const entry of entries) {
|
||||
const { outFile, result } = await compile(entry)
|
||||
if (withRtl) await rtl(entry, outFile, result)
|
||||
}
|
||||
flushWrites()
|
||||
if (withMinify) await minify(written)
|
||||
for (const entry of entries) {
|
||||
const { outFile, result } = await compile(entry)
|
||||
if (withRtl) await rtl(entry, outFile, result)
|
||||
}
|
||||
flushWrites()
|
||||
if (withMinify) await minify(written)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
+115
-115
@@ -15,23 +15,23 @@ import { dirname, join, relative, sep } from 'node:path'
|
||||
// node_modules), and `astro check` verifies the produced object against the
|
||||
// real AstroIntegration type at the usage site in each astro.config.mjs anyway.
|
||||
interface Logger {
|
||||
info(message: string): void
|
||||
warn(message: string): void
|
||||
info(message: string): void
|
||||
warn(message: string): void
|
||||
}
|
||||
interface DevServer {
|
||||
watcher: {
|
||||
add(paths: string[]): void
|
||||
on(event: string, callback: (file: string) => void): void
|
||||
}
|
||||
hot: { send(payload: { type: string }): void }
|
||||
watcher: {
|
||||
add(paths: string[]): void
|
||||
on(event: string, callback: (file: string) => void): void
|
||||
}
|
||||
hot: { send(payload: { type: string }): void }
|
||||
}
|
||||
interface Integration {
|
||||
name: string
|
||||
hooks: {
|
||||
'astro:config:setup'?: (options: { command: string }) => void
|
||||
'astro:config:done'?: (options: { logger: Logger }) => void
|
||||
'astro:server:setup'?: (options: { server: DevServer; logger: Logger }) => void
|
||||
}
|
||||
name: string
|
||||
hooks: {
|
||||
'astro:config:setup'?: (options: { command: string }) => void
|
||||
'astro:config:done'?: (options: { logger: Logger }) => void
|
||||
'astro:server:setup'?: (options: { server: DevServer; logger: Logger }) => void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,115 +42,115 @@ interface Integration {
|
||||
* concurrently).
|
||||
*/
|
||||
interface CopyEntry {
|
||||
from: string
|
||||
to: string
|
||||
label: string
|
||||
required?: boolean
|
||||
requiredFile?: string
|
||||
allowDestinationFallback?: boolean
|
||||
from: string
|
||||
to: string
|
||||
label: string
|
||||
required?: boolean
|
||||
requiredFile?: string
|
||||
allowDestinationFallback?: boolean
|
||||
}
|
||||
|
||||
interface CopyAssetsOptions {
|
||||
/** repo root, for log-friendly relative paths */
|
||||
repo: string
|
||||
/** the package's public/ directory (fully generated) */
|
||||
publicDir: string
|
||||
copies: CopyEntry[]
|
||||
/** generated source dirs to live-sync into publicDir while `astro dev` runs */
|
||||
syncDirs?: { from: string; to: string }[]
|
||||
/** dirs written to directly by watchers — already in place, only trigger a browser reload */
|
||||
reloadDirs?: string[]
|
||||
/** repo root, for log-friendly relative paths */
|
||||
repo: string
|
||||
/** the package's public/ directory (fully generated) */
|
||||
publicDir: string
|
||||
copies: CopyEntry[]
|
||||
/** generated source dirs to live-sync into publicDir while `astro dev` runs */
|
||||
syncDirs?: { from: string; to: string }[]
|
||||
/** dirs written to directly by watchers — already in place, only trigger a browser reload */
|
||||
reloadDirs?: string[]
|
||||
}
|
||||
|
||||
export function copyAssets({ repo, publicDir, copies, syncDirs = [], reloadDirs = [] }: CopyAssetsOptions): Integration {
|
||||
let command: string
|
||||
let command: string
|
||||
|
||||
function rebuildPublicDir(logger: Logger) {
|
||||
// Always start from a clean public/ (mirrors Bootstrap's own docs
|
||||
// integration): running this twice in a row, or without a prior
|
||||
// `pnpm run clean`, must never accumulate stale/nested content —
|
||||
// see preview/.build/vite.config.mts for the unbounded-growth story.
|
||||
rmSync(publicDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
|
||||
mkdirSync(publicDir, { recursive: true })
|
||||
function rebuildPublicDir(logger: Logger) {
|
||||
// Always start from a clean public/ (mirrors Bootstrap's own docs
|
||||
// integration): running this twice in a row, or without a prior
|
||||
// `pnpm run clean`, must never accumulate stale/nested content —
|
||||
// see preview/.build/vite.config.mts for the unbounded-growth story.
|
||||
rmSync(publicDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
|
||||
mkdirSync(publicDir, { recursive: true })
|
||||
|
||||
for (const { from, to, label, required = true, requiredFile, allowDestinationFallback } of copies) {
|
||||
if (!existsSync(from) || (requiredFile && !existsSync(requiredFile))) {
|
||||
const message = `copy-assets: missing ${requiredFile ?? from}`
|
||||
if (required) throw new Error(`${message} — build ${label} first`)
|
||||
logger.warn(`${message} (skipped — build ${label} to get it)`)
|
||||
continue
|
||||
}
|
||||
mkdirSync(dirname(to), { recursive: true })
|
||||
if (statSync(from).isFile()) {
|
||||
copyFileSync(from, to)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
// dereference: sources may be symlinks (e.g. preview/static → shared/static)
|
||||
cpSync(from, to, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
filter: (src) => !src.includes('/.vscode') && !src.includes('\\.vscode'),
|
||||
})
|
||||
} catch (error) {
|
||||
// In turbo dev, @tabler/core can clean dist while we copy it.
|
||||
// If fallback is allowed and destination exists, keep current assets.
|
||||
if (allowDestinationFallback && error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT' && existsSync(to)) {
|
||||
logger.warn(`copy-assets: source changed during copy ${from} (using existing ${to})`)
|
||||
continue
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
logger.info('public/ rebuilt from workspace assets')
|
||||
}
|
||||
for (const { from, to, label, required = true, requiredFile, allowDestinationFallback } of copies) {
|
||||
if (!existsSync(from) || (requiredFile && !existsSync(requiredFile))) {
|
||||
const message = `copy-assets: missing ${requiredFile ?? from}`
|
||||
if (required) throw new Error(`${message} — build ${label} first`)
|
||||
logger.warn(`${message} (skipped — build ${label} to get it)`)
|
||||
continue
|
||||
}
|
||||
mkdirSync(dirname(to), { recursive: true })
|
||||
if (statSync(from).isFile()) {
|
||||
copyFileSync(from, to)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
// dereference: sources may be symlinks (e.g. preview/static → shared/static)
|
||||
cpSync(from, to, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
filter: (src) => !src.includes('/.vscode') && !src.includes('\\.vscode'),
|
||||
})
|
||||
} catch (error) {
|
||||
// In turbo dev, @tabler/core can clean dist while we copy it.
|
||||
// If fallback is allowed and destination exists, keep current assets.
|
||||
if (allowDestinationFallback && error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT' && existsSync(to)) {
|
||||
logger.warn(`copy-assets: source changed during copy ${from} (using existing ${to})`)
|
||||
continue
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
logger.info('public/ rebuilt from workspace assets')
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'copy-assets',
|
||||
hooks: {
|
||||
'astro:config:setup': (options) => {
|
||||
command = options.command
|
||||
},
|
||||
'astro:config:done': ({ logger }) => {
|
||||
// `astro check`/`astro sync` (command 'sync') runs these hooks too, but
|
||||
// only needs types — public/ is irrelevant there and CI's type-check
|
||||
// job has no built workspace assets to copy.
|
||||
if (command === 'sync') return
|
||||
rebuildPublicDir(logger)
|
||||
},
|
||||
'astro:server:setup': ({ server, logger }) => {
|
||||
if (command !== 'dev') return
|
||||
// The copy above runs once at startup, so edits rebuilt into the source
|
||||
// dirs during `pnpm run dev` (e.g. core/dist by core's watchers) would
|
||||
// never reach the served public/ copy. Watch them, sync changed files
|
||||
// into public/, and trigger a browser reload.
|
||||
server.watcher.add([...syncDirs.map((dir) => dir.from), ...reloadDirs])
|
||||
let reloadTimer: ReturnType<typeof setTimeout> | undefined
|
||||
const scheduleReload = (file: string) => {
|
||||
// Source maps piggyback on their css/js file's reload. build-css.ts
|
||||
// buffers all writes into one tight burst, so a short quiet window is
|
||||
// enough to coalesce a whole rebuild into a single reload.
|
||||
if (file.endsWith('.map')) return
|
||||
clearTimeout(reloadTimer)
|
||||
reloadTimer = setTimeout(() => {
|
||||
server.hot.send({ type: 'full-reload' })
|
||||
logger.info(`reloaded after change in ${relative(repo, file)}`)
|
||||
}, 250)
|
||||
}
|
||||
const sync = (file: string) => {
|
||||
for (const { from, to } of syncDirs) {
|
||||
if (!file.startsWith(from + sep)) continue
|
||||
const dest = join(to, relative(from, file))
|
||||
mkdirSync(dirname(dest), { recursive: true })
|
||||
copyFileSync(file, dest)
|
||||
scheduleReload(file)
|
||||
return
|
||||
}
|
||||
if (reloadDirs.some((dir) => file.startsWith(dir + sep))) scheduleReload(file)
|
||||
}
|
||||
server.watcher.on('add', sync)
|
||||
server.watcher.on('change', sync)
|
||||
},
|
||||
},
|
||||
}
|
||||
return {
|
||||
name: 'copy-assets',
|
||||
hooks: {
|
||||
'astro:config:setup': (options) => {
|
||||
command = options.command
|
||||
},
|
||||
'astro:config:done': ({ logger }) => {
|
||||
// `astro check`/`astro sync` (command 'sync') runs these hooks too, but
|
||||
// only needs types — public/ is irrelevant there and CI's type-check
|
||||
// job has no built workspace assets to copy.
|
||||
if (command === 'sync') return
|
||||
rebuildPublicDir(logger)
|
||||
},
|
||||
'astro:server:setup': ({ server, logger }) => {
|
||||
if (command !== 'dev') return
|
||||
// The copy above runs once at startup, so edits rebuilt into the source
|
||||
// dirs during `pnpm run dev` (e.g. core/dist by core's watchers) would
|
||||
// never reach the served public/ copy. Watch them, sync changed files
|
||||
// into public/, and trigger a browser reload.
|
||||
server.watcher.add([...syncDirs.map((dir) => dir.from), ...reloadDirs])
|
||||
let reloadTimer: ReturnType<typeof setTimeout> | undefined
|
||||
const scheduleReload = (file: string) => {
|
||||
// Source maps piggyback on their css/js file's reload. build-css.ts
|
||||
// buffers all writes into one tight burst, so a short quiet window is
|
||||
// enough to coalesce a whole rebuild into a single reload.
|
||||
if (file.endsWith('.map')) return
|
||||
clearTimeout(reloadTimer)
|
||||
reloadTimer = setTimeout(() => {
|
||||
server.hot.send({ type: 'full-reload' })
|
||||
logger.info(`reloaded after change in ${relative(repo, file)}`)
|
||||
}, 250)
|
||||
}
|
||||
const sync = (file: string) => {
|
||||
for (const { from, to } of syncDirs) {
|
||||
if (!file.startsWith(from + sep)) continue
|
||||
const dest = join(to, relative(from, file))
|
||||
mkdirSync(dirname(dest), { recursive: true })
|
||||
copyFileSync(file, dest)
|
||||
scheduleReload(file)
|
||||
return
|
||||
}
|
||||
if (reloadDirs.some((dir) => file.startsWith(dir + sep))) scheduleReload(file)
|
||||
}
|
||||
server.watcher.on('add', sync)
|
||||
server.watcher.on('change', sync)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+12
-12
@@ -1,19 +1,19 @@
|
||||
// Minimal ambient typings for build-tool dependencies that ship no types.
|
||||
// Only the surface used by .build/build-css.ts is declared.
|
||||
declare module 'rtlcss' {
|
||||
import type { Plugin } from 'postcss'
|
||||
export default function rtlcss(config?: unknown): Plugin
|
||||
import type { Plugin } from 'postcss'
|
||||
export default function rtlcss(config?: unknown): Plugin
|
||||
}
|
||||
|
||||
declare module 'clean-css' {
|
||||
interface MinifyResult {
|
||||
styles: string
|
||||
errors: string[]
|
||||
warnings: string[]
|
||||
sourceMap: { toString(): string }
|
||||
}
|
||||
export default class CleanCSS {
|
||||
constructor(options: Record<string, unknown>)
|
||||
minify(input: string[]): Promise<Record<string, MinifyResult>>
|
||||
}
|
||||
interface MinifyResult {
|
||||
styles: string
|
||||
errors: string[]
|
||||
warnings: string[]
|
||||
sourceMap: { toString(): string }
|
||||
}
|
||||
export default class CleanCSS {
|
||||
constructor(options: Record<string, unknown>)
|
||||
minify(input: string[]): Promise<Record<string, MinifyResult>>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import { copyAssets } from '../.build/copy-assets'
|
||||
|
||||
/** @param {string} p */
|
||||
const path = p => fileURLToPath(new URL(p, import.meta.url))
|
||||
const path = (p) => fileURLToPath(new URL(p, import.meta.url))
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
|
||||
@@ -1,86 +1,74 @@
|
||||
---
|
||||
// Menu tree from shared/data/docs.json.
|
||||
import Subheader from '@ui/Subheader.astro';
|
||||
import docs from '@data/docs.json';
|
||||
import { pathSlug as slug } from '@shared/lib/string-format';
|
||||
import Subheader from '@ui/Subheader.astro'
|
||||
import docs from '@data/docs.json'
|
||||
import { pathSlug as slug } from '@shared/lib/string-format'
|
||||
|
||||
interface MenuLeaf {
|
||||
title: string;
|
||||
url: string;
|
||||
title: string
|
||||
url: string
|
||||
}
|
||||
|
||||
interface MenuGroup extends MenuLeaf {
|
||||
children?: MenuLeaf[];
|
||||
children?: MenuLeaf[]
|
||||
}
|
||||
|
||||
interface MenuSection {
|
||||
title: string;
|
||||
children?: MenuGroup[];
|
||||
title: string
|
||||
children?: MenuGroup[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
|
||||
url: string;
|
||||
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
|
||||
url: string
|
||||
}
|
||||
|
||||
const { url } = Astro.props;
|
||||
const menu = docs.menu as MenuSection[];
|
||||
const { url } = Astro.props
|
||||
const menu = docs.menu as MenuSection[]
|
||||
---
|
||||
|
||||
<!-- BEGIN DOCS MENU -->
|
||||
<nav class="space-y space-y-5" id="menu">
|
||||
{
|
||||
menu.map((level1) => (
|
||||
<div>
|
||||
<Subheader class="mb-2">{level1.title}</Subheader>
|
||||
{level1.children && level1.children.length > 0 && (
|
||||
<nav class="nav nav-vertical">
|
||||
{level1.children.map((level2) => {
|
||||
const hasChildren = Boolean(level2.children && level2.children.length > 0);
|
||||
// Expanded only for the group's own page or one of its direct children —
|
||||
// a prefix check would also expand "Introduction" on /ui/getting-started/frameworks/* pages.
|
||||
const expanded = url === level2.url || Boolean(level2.children?.some((child) => child.url === url));
|
||||
return (
|
||||
<div>
|
||||
{/* href/data-bs-* only when the element has children */}
|
||||
{hasChildren ? (
|
||||
<a
|
||||
class={`nav-link${expanded ? ' active' : ''}`}
|
||||
href={level2.url}
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target={`#collapse-${slug(level2.url)}`}
|
||||
aria-expanded={expanded ? 'true' : 'false'}
|
||||
>
|
||||
{level2.title} <span class="nav-link-toggle" />
|
||||
</a>
|
||||
) : (
|
||||
<a class={`nav-link${expanded ? ' active' : ''}`}>{level2.title}</a>
|
||||
)}
|
||||
{
|
||||
menu.map((level1) => (
|
||||
<div>
|
||||
<Subheader class="mb-2">{level1.title}</Subheader>
|
||||
{level1.children && level1.children.length > 0 && (
|
||||
<nav class="nav nav-vertical">
|
||||
{level1.children.map((level2) => {
|
||||
const hasChildren = Boolean(level2.children && level2.children.length > 0)
|
||||
// Expanded only for the group's own page or one of its direct children —
|
||||
// a prefix check would also expand "Introduction" on /ui/getting-started/frameworks/* pages.
|
||||
const expanded = url === level2.url || Boolean(level2.children?.some((child) => child.url === url))
|
||||
return (
|
||||
<div>
|
||||
{/* href/data-bs-* only when the element has children */}
|
||||
{hasChildren ? (
|
||||
<a class={`nav-link${expanded ? ' active' : ''}`} href={level2.url} data-bs-toggle="collapse" data-bs-target={`#collapse-${slug(level2.url)}`} aria-expanded={expanded ? 'true' : 'false'}>
|
||||
{level2.title} <span class="nav-link-toggle" />
|
||||
</a>
|
||||
) : (
|
||||
<a class={`nav-link${expanded ? ' active' : ''}`}>{level2.title}</a>
|
||||
)}
|
||||
|
||||
{hasChildren && (
|
||||
<nav
|
||||
class={`nav nav-vertical collapse${expanded ? ' show' : ''}`}
|
||||
id={`collapse-${slug(level2.url)}`}
|
||||
>
|
||||
{level2.children!.map((level3) => (
|
||||
<div>
|
||||
<a
|
||||
class={`nav-link${url === level3.url ? ' active' : ''}`}
|
||||
href={level3.url}
|
||||
>
|
||||
{level3.title}
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{hasChildren && (
|
||||
<nav class={`nav nav-vertical collapse${expanded ? ' show' : ''}`} id={`collapse-${slug(level2.url)}`}>
|
||||
{level2.children!.map((level3) => (
|
||||
<div>
|
||||
<a class={`nav-link${url === level3.url ? ' active' : ''}`} href={level3.url}>
|
||||
{level3.title}
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</nav>
|
||||
<!-- END DOCS MENU -->
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
---
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import DocsLogo from './DocsLogo.astro';
|
||||
import { site } from '@shared/lib/site';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import DocsLogo from './DocsLogo.astro'
|
||||
import { site } from '@shared/lib/site'
|
||||
|
||||
// Eventually belongs in src/lib/site.ts (out of scope for this task).
|
||||
const previewUrl = 'https://preview.tabler.io';
|
||||
const changelogUrl = 'https://tabler.io/changelog';
|
||||
const previewUrl = 'https://preview.tabler.io'
|
||||
const changelogUrl = 'https://tabler.io/changelog'
|
||||
---
|
||||
|
||||
<!-- BEGIN DOCS NAVBAR -->
|
||||
<div class="navbar navbar-expand sticky-top">
|
||||
<div class="container">
|
||||
<div class="row flex-fill align-items-md-center">
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center gap-4">
|
||||
{/* href="." — relative link for docs root */}
|
||||
<a href="." class="navbar-brand navbar-brand-autodark gap-4">
|
||||
<DocsLogo />
|
||||
</a>
|
||||
<div>
|
||||
<span class="badge">v{site.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block col">
|
||||
<div id="docsearch"></div>
|
||||
</div>
|
||||
<div class="col d-flex">
|
||||
<ul class="navbar-nav ms-auto gap-2 align-items-center">
|
||||
<div class="nav-item d-none d-md-block">
|
||||
<a href={previewUrl} class="nav-link">Preview</a>
|
||||
</div>
|
||||
<div class="nav-item d-none d-md-block">
|
||||
<a href={changelogUrl} class="nav-link">Changelog</a>
|
||||
</div>
|
||||
<li class="nav-item hide-theme-dark">
|
||||
<a href="?theme=dark" class="btn btn-icon">
|
||||
<Icon name="moon" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item hide-theme-light">
|
||||
<a href="?theme=light" class="btn btn-icon">
|
||||
<Icon name="sun" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={site.githubUrl} class="btn btn-icon" target="_blank">
|
||||
<Icon name="brand-github" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={previewUrl} class="btn btn-primary" target="_blank">
|
||||
<Icon name="eye" /> Preview
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row flex-fill align-items-md-center">
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center gap-4">
|
||||
{/* href="." — relative link for docs root */}
|
||||
<a href="." class="navbar-brand navbar-brand-autodark gap-4">
|
||||
<DocsLogo />
|
||||
</a>
|
||||
<div>
|
||||
<span class="badge">v{site.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block col">
|
||||
<div id="docsearch"></div>
|
||||
</div>
|
||||
<div class="col d-flex">
|
||||
<ul class="navbar-nav ms-auto gap-2 align-items-center">
|
||||
<div class="nav-item d-none d-md-block">
|
||||
<a href={previewUrl} class="nav-link">Preview</a>
|
||||
</div>
|
||||
<div class="nav-item d-none d-md-block">
|
||||
<a href={changelogUrl} class="nav-link">Changelog</a>
|
||||
</div>
|
||||
<li class="nav-item hide-theme-dark">
|
||||
<a href="?theme=dark" class="btn btn-icon">
|
||||
<Icon name="moon" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item hide-theme-light">
|
||||
<a href="?theme=light" class="btn btn-icon">
|
||||
<Icon name="sun" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={site.githubUrl} class="btn btn-icon" target="_blank">
|
||||
<Icon name="brand-github" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={previewUrl} class="btn btn-primary" target="_blank">
|
||||
<Icon name="eye" /> Preview
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END DOCS NAVBAR -->
|
||||
|
||||
@@ -1,119 +1,113 @@
|
||||
---
|
||||
import DocsCard from './DocsCard.astro';
|
||||
import docs from '@data/docs.json';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import { getDocsChildren } from '@shared/lib/docs-children';
|
||||
import type { DocsChildPage } from '@shared/lib/docs-children';
|
||||
import DocsCard from './DocsCard.astro'
|
||||
import docs from '@data/docs.json'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import { getDocsChildren } from '@shared/lib/docs-children'
|
||||
import type { DocsChildPage } from '@shared/lib/docs-children'
|
||||
|
||||
interface MenuLeaf {
|
||||
title: string;
|
||||
url: string;
|
||||
title: string
|
||||
url: string
|
||||
}
|
||||
|
||||
interface MenuNode {
|
||||
title: string;
|
||||
url?: string;
|
||||
children?: MenuNode[];
|
||||
title: string
|
||||
url?: string
|
||||
children?: MenuNode[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
|
||||
url: string;
|
||||
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
|
||||
url: string
|
||||
}
|
||||
|
||||
const { url } = Astro.props;
|
||||
const { url } = Astro.props
|
||||
|
||||
const children = getDocsChildren(url);
|
||||
const children = getDocsChildren(url)
|
||||
|
||||
type Pagination = {
|
||||
prev: MenuLeaf | null;
|
||||
next: MenuLeaf | null;
|
||||
found: boolean;
|
||||
};
|
||||
prev: MenuLeaf | null
|
||||
next: MenuLeaf | null
|
||||
found: boolean
|
||||
}
|
||||
|
||||
function findPage(nodes: MenuNode[]): Pagination | null {
|
||||
const index = nodes.findIndex((item) => item.url === url);
|
||||
const index = nodes.findIndex((item) => item.url === url)
|
||||
|
||||
if (index !== -1) {
|
||||
const previousNode = index > 0 ? nodes[index - 1] : null;
|
||||
const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null;
|
||||
if (index !== -1) {
|
||||
const previousNode = index > 0 ? nodes[index - 1] : null
|
||||
const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null
|
||||
|
||||
return {
|
||||
prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null,
|
||||
next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null,
|
||||
found: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null,
|
||||
next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null,
|
||||
found: true,
|
||||
}
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
const result = node.children ? findPage(node.children) : null;
|
||||
if (result) return result;
|
||||
}
|
||||
for (const node of nodes) {
|
||||
const result = node.children ? findPage(node.children) : null
|
||||
if (result) return result
|
||||
}
|
||||
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
const { prev, next, found } = findPage(docs.menu as MenuNode[]) ?? {
|
||||
prev: null,
|
||||
next: null,
|
||||
found: false,
|
||||
};
|
||||
prev: null,
|
||||
next: null,
|
||||
found: false,
|
||||
}
|
||||
---
|
||||
|
||||
<!-- BEGIN DOCS PAGINATION -->
|
||||
{
|
||||
children.length > 0 && (
|
||||
<div class="mt-6 pt-6">
|
||||
<div class="row row-deck row-cards">
|
||||
{children.map((child: DocsChildPage) => (
|
||||
<DocsCard
|
||||
href={child.url}
|
||||
title={child.title}
|
||||
description={child.description}
|
||||
icon={child.icon}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
<!-- BEGIN DOCS PAGINATION -->{
|
||||
children.length > 0 && (
|
||||
<div class="mt-6 pt-6">
|
||||
<div class="row row-deck row-cards">
|
||||
{children.map((child: DocsChildPage) => (
|
||||
<DocsCard href={child.url} title={child.title} description={child.description} icon={child.icon} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
children.length === 0 && found && (
|
||||
<div class="mt-6 pt-6">
|
||||
<ul class="pagination">
|
||||
{prev && (
|
||||
<li class="page-item page-prev">
|
||||
<a class="page-link" href={prev.url}>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Icon name="chevron-left" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="page-item-subtitle">previous</div>
|
||||
<div class="page-item-title">{prev.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{next && (
|
||||
<li class="page-item page-next">
|
||||
<a class="page-link" href={next.url}>
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-item-subtitle">next</div>
|
||||
<div class="page-item-title">{next.title}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="chevron-right" />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
children.length === 0 && found && (
|
||||
<div class="mt-6 pt-6">
|
||||
<ul class="pagination">
|
||||
{prev && (
|
||||
<li class="page-item page-prev">
|
||||
<a class="page-link" href={prev.url}>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Icon name="chevron-left" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="page-item-subtitle">previous</div>
|
||||
<div class="page-item-title">{prev.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{next && (
|
||||
<li class="page-item page-next">
|
||||
<a class="page-link" href={next.url}>
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-item-subtitle">next</div>
|
||||
<div class="page-item-title">{next.title}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="chevron-right" />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<!-- END DOCS PAGINATION -->
|
||||
|
||||
@@ -3,44 +3,43 @@
|
||||
// markdown content, excluding <!--EXAMPLE-->...<!--/EXAMPLE--> blocks).
|
||||
|
||||
export interface TocItem {
|
||||
/** 2 or 3 (h2/h3); h3 gets the ms-3 indent */
|
||||
level: number;
|
||||
text: string;
|
||||
/** anchor without '#', e.g. "default-markup" */
|
||||
id: string;
|
||||
/** 2 or 3 (h2/h3); h3 gets the ms-3 indent */
|
||||
level: number
|
||||
text: string
|
||||
/** anchor without '#', e.g. "default-markup" */
|
||||
id: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
toc?: TocItem[];
|
||||
toc?: TocItem[]
|
||||
}
|
||||
|
||||
const { toc = [] } = Astro.props;
|
||||
const { toc = [] } = Astro.props
|
||||
|
||||
// shared/data/illustrations.json has 100 entries.
|
||||
const illustrationsCount = 100;
|
||||
const illustrationsCount = 100
|
||||
---
|
||||
|
||||
<!-- BEGIN DOCS TOC -->
|
||||
{
|
||||
toc.length > 0 && (
|
||||
<Fragment>
|
||||
<h3>Table of Contents</h3>
|
||||
<div class="nav nav-vertical" id="toc">
|
||||
{toc.map((item) => (
|
||||
<a href={`#${item.id}`} class={`nav-link${item.level === 3 ? ' ms-3' : ''}`}>
|
||||
{item.text}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
<!-- BEGIN DOCS TOC -->{
|
||||
toc.length > 0 && (
|
||||
<Fragment>
|
||||
<h3>Table of Contents</h3>
|
||||
<div class="nav nav-vertical" id="toc">
|
||||
{toc.map((item) => (
|
||||
<a href={`#${item.id}`} class={`nav-link${item.level === 3 ? ' ms-3' : ''}`}>
|
||||
{item.text}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
<a href="https://r.tabler.io/illustrations" class="card card-sm mt-6 shadow-none" target="_blank">
|
||||
<div class="card-body">
|
||||
<img src="/img/banner-carbon.png" class="mb-3" alt="" />
|
||||
<div class="card-body">
|
||||
<img src="/img/banner-carbon.png" class="mb-3" alt="" />
|
||||
|
||||
<h4>{illustrationsCount} sleek illustrations for your startup's visual identity.</h4>
|
||||
</div>
|
||||
<h4>{illustrationsCount} sleek illustrations for your startup's visual identity.</h4>
|
||||
</div>
|
||||
</a>
|
||||
<!-- END DOCS TOC -->
|
||||
|
||||
@@ -1,97 +1,66 @@
|
||||
---
|
||||
// Example code from the `html` prop or the rendered slot; copy button uses data-clipboard-text.
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example'
|
||||
|
||||
interface Props {
|
||||
/** raw HTML of the example; when absent — the slot is rendered */
|
||||
html?: string;
|
||||
/** code shown in the panel instead of html */
|
||||
code?: string;
|
||||
raw?: boolean;
|
||||
overflow?: string;
|
||||
bg?: string;
|
||||
class?: string;
|
||||
height?: string;
|
||||
column?: boolean;
|
||||
centered?: boolean;
|
||||
vertical?: boolean;
|
||||
columnFullWidth?: boolean;
|
||||
hideCode?: boolean;
|
||||
codeOnly?: boolean;
|
||||
/** raw HTML of the example; when absent — the slot is rendered */
|
||||
html?: string
|
||||
/** code shown in the panel instead of html */
|
||||
code?: string
|
||||
raw?: boolean
|
||||
overflow?: string
|
||||
bg?: string
|
||||
class?: string
|
||||
height?: string
|
||||
column?: boolean
|
||||
centered?: boolean
|
||||
vertical?: boolean
|
||||
columnFullWidth?: boolean
|
||||
hideCode?: boolean
|
||||
codeOnly?: boolean
|
||||
}
|
||||
|
||||
const {
|
||||
html: htmlProp,
|
||||
code,
|
||||
raw,
|
||||
overflow = 'auto',
|
||||
bg,
|
||||
class: className,
|
||||
height,
|
||||
column,
|
||||
centered,
|
||||
vertical,
|
||||
columnFullWidth,
|
||||
hideCode,
|
||||
codeOnly,
|
||||
} = Astro.props;
|
||||
const { html: htmlProp, code, raw, overflow = 'auto', bg, class: className, height, column, centered, vertical, columnFullWidth, hideCode, codeOnly } = Astro.props
|
||||
|
||||
// Strip empty lines from the example HTML.
|
||||
let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, '');
|
||||
let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, '')
|
||||
|
||||
// JSX reserializes the slot markup: empty SVG elements get explicit closing
|
||||
// tags, and apostrophes in text are escaped. We restore the source form so the
|
||||
// code in the panel looks like hand-written HTML.
|
||||
html = html
|
||||
.replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />')
|
||||
.replace(/'/g, "'");
|
||||
html = html.replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />').replace(/'/g, "'")
|
||||
|
||||
const exampleClasses = [
|
||||
'example fs-base border rounded my-5',
|
||||
!raw && 'd-flex flex-wrap justify-content-center',
|
||||
`overflow-${overflow}`,
|
||||
'position-relative',
|
||||
bg ? `bg-${bg}` : 'bg-pattern-rectangles',
|
||||
className,
|
||||
]
|
||||
const exampleClasses = ['example fs-base border rounded my-5', !raw && 'd-flex flex-wrap justify-content-center', `overflow-${overflow}`, 'position-relative', bg ? `bg-${bg}` : 'bg-pattern-rectangles', className]
|
||||
|
||||
const innerClasses = [
|
||||
'p-6 w-full',
|
||||
column && 'd-flex gap-3 flex-column',
|
||||
!column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`,
|
||||
columnFullWidth && 'd-flex flex-fill flex-column gap-2',
|
||||
]
|
||||
const innerClasses = ['p-6 w-full', column && 'd-flex gap-3 flex-column', !column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`, columnFullWidth && 'd-flex flex-fill flex-column gap-2']
|
||||
|
||||
const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html');
|
||||
const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html')
|
||||
---
|
||||
|
||||
<!--EXAMPLE-->
|
||||
{
|
||||
!codeOnly && (
|
||||
<div class:list={exampleClasses} style={height ? `height: ${height}` : undefined}>
|
||||
{
|
||||
raw ? (
|
||||
<Fragment set:html={removeHref(html)} />
|
||||
) : (
|
||||
<div class:list={innerClasses} style={column ? 'max-width: 25rem;' : undefined}>
|
||||
<Fragment set:html={removeHref(html)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
<!--EXAMPLE-->{
|
||||
!codeOnly && (
|
||||
<div class:list={exampleClasses} style={height ? `height: ${height}` : undefined}>
|
||||
{raw ? (
|
||||
<Fragment set:html={removeHref(html)} />
|
||||
) : (
|
||||
<div class:list={innerClasses} style={column ? 'max-width: 25rem;' : undefined}>
|
||||
<Fragment set:html={removeHref(html)} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!hideCode && (
|
||||
<div class="position-relative">
|
||||
<a class="btn btn-icon btn-dark position-absolute m-2 top-0 end-0 z-3" data-clipboard-text={html}>
|
||||
<Icon name="clipboard" />
|
||||
<Icon name="check" class="d-none" />
|
||||
</a>
|
||||
<Fragment set:html={highlighted} />
|
||||
</div>
|
||||
)
|
||||
!hideCode && (
|
||||
<div class="position-relative">
|
||||
<a class="btn btn-icon btn-dark position-absolute m-2 top-0 end-0 z-3" data-clipboard-text={html}>
|
||||
<Icon name="clipboard" />
|
||||
<Icon name="check" class="d-none" />
|
||||
</a>
|
||||
<Fragment set:html={highlighted} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<!--/EXAMPLE-->
|
||||
|
||||
@@ -16,7 +16,7 @@ interface MdxFrontmatter {
|
||||
'summary'?: string
|
||||
'description'?: string
|
||||
'seoDescription'?: string
|
||||
'added-in'?: string;
|
||||
'added-in'?: string
|
||||
'docs-libs'?: string[]
|
||||
'hide-pagination'?: boolean
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import RedirectLayout from '@shared/layouts/RedirectLayout.astro';
|
||||
import RedirectLayout from '@shared/layouts/RedirectLayout.astro'
|
||||
---
|
||||
|
||||
<RedirectLayout base="../../.." />
|
||||
|
||||
+159
-159
@@ -1,172 +1,172 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import { satteri } from '@astrojs/markdown-satteri';
|
||||
import beautify from 'js-beautify';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { globSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { devNull } from 'node:os';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { copyAssets } from '../.build/copy-assets';
|
||||
import { defineConfig } from 'astro/config'
|
||||
import mdx from '@astrojs/mdx'
|
||||
import { satteri } from '@astrojs/markdown-satteri'
|
||||
import beautify from 'js-beautify'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { globSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { devNull } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { copyAssets } from '../.build/copy-assets'
|
||||
|
||||
/** @param {string} p */
|
||||
const path = (p) => fileURLToPath(new URL(p, import.meta.url));
|
||||
const path = (p) => fileURLToPath(new URL(p, import.meta.url))
|
||||
|
||||
/**
|
||||
* After build, format page HTML with prettier (users copy it 1:1).
|
||||
* @returns {import('astro').AstroIntegration}
|
||||
*/
|
||||
function prettifyHtml() {
|
||||
return {
|
||||
name: 'prettify-html',
|
||||
hooks: {
|
||||
'astro:build:done': async ({ dir, logger }) => {
|
||||
const outDir = fileURLToPath(dir);
|
||||
// dist/preview/ and dist/dist/ are copy-assets.mjs's copies of public/{preview,dist}
|
||||
// (demo css/js and @tabler/core's dist, including vendored libs) — not pages, and
|
||||
// some vendored libs ship their own malformed docs/*.html that trips the parser below.
|
||||
/** @param {string} file */
|
||||
const isVendorCopy = (file) => file.includes(`${outDir}preview/`) || file.includes(`${outDir}dist/`);
|
||||
// Astro appends "overflow-x: auto" to the shiki <pre> style — the
|
||||
// Eleventy pipeline doesn't have it and HTML is the product: restore 1:1.
|
||||
// node:fs is imported statically — a dynamic import() here would go
|
||||
// through Vite's module runner, which is already closed by the time
|
||||
// the astro:build:done hook runs (the config is bundled by vite-node
|
||||
// because it imports a .ts module).
|
||||
for (const file of globSync(`${outDir}**/*.html`, { exclude: isVendorCopy })) {
|
||||
const content = readFileSync(file, 'utf8');
|
||||
const cleaned = content.replaceAll('; overflow-x: auto;', '');
|
||||
if (cleaned !== content) writeFileSync(file, cleaned);
|
||||
}
|
||||
execFileSync(
|
||||
'npx',
|
||||
[
|
||||
'prettier',
|
||||
'--write',
|
||||
'--parser',
|
||||
'html',
|
||||
// Prettier's default ignore-path is [.gitignore, .prettierignore], and both
|
||||
// list "dist" (needed elsewhere so normal lint/format passes skip build
|
||||
// output) — that silently no-ops this pass on the very directory it targets.
|
||||
// Point at devNull to opt this one deliberate pass out of those ignores.
|
||||
'--ignore-path',
|
||||
devNull,
|
||||
`${outDir}**/*.html`,
|
||||
`!${outDir}preview/**`,
|
||||
`!${outDir}dist/**`,
|
||||
],
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
logger.info('HTML formatted with prettier');
|
||||
},
|
||||
},
|
||||
};
|
||||
return {
|
||||
name: 'prettify-html',
|
||||
hooks: {
|
||||
'astro:build:done': async ({ dir, logger }) => {
|
||||
const outDir = fileURLToPath(dir)
|
||||
// dist/preview/ and dist/dist/ are copy-assets.mjs's copies of public/{preview,dist}
|
||||
// (demo css/js and @tabler/core's dist, including vendored libs) — not pages, and
|
||||
// some vendored libs ship their own malformed docs/*.html that trips the parser below.
|
||||
/** @param {string} file */
|
||||
const isVendorCopy = (file) => file.includes(`${outDir}preview/`) || file.includes(`${outDir}dist/`)
|
||||
// Astro appends "overflow-x: auto" to the shiki <pre> style — the
|
||||
// Eleventy pipeline doesn't have it and HTML is the product: restore 1:1.
|
||||
// node:fs is imported statically — a dynamic import() here would go
|
||||
// through Vite's module runner, which is already closed by the time
|
||||
// the astro:build:done hook runs (the config is bundled by vite-node
|
||||
// because it imports a .ts module).
|
||||
for (const file of globSync(`${outDir}**/*.html`, { exclude: isVendorCopy })) {
|
||||
const content = readFileSync(file, 'utf8')
|
||||
const cleaned = content.replaceAll('; overflow-x: auto;', '')
|
||||
if (cleaned !== content) writeFileSync(file, cleaned)
|
||||
}
|
||||
execFileSync(
|
||||
'npx',
|
||||
[
|
||||
'prettier',
|
||||
'--write',
|
||||
'--parser',
|
||||
'html',
|
||||
// Prettier's default ignore-path is [.gitignore, .prettierignore], and both
|
||||
// list "dist" (needed elsewhere so normal lint/format passes skip build
|
||||
// output) — that silently no-ops this pass on the very directory it targets.
|
||||
// Point at devNull to opt this one deliberate pass out of those ignores.
|
||||
'--ignore-path',
|
||||
devNull,
|
||||
`${outDir}**/*.html`,
|
||||
`!${outDir}preview/**`,
|
||||
`!${outDir}dist/**`,
|
||||
],
|
||||
{ stdio: 'inherit' },
|
||||
)
|
||||
logger.info('HTML formatted with prettier')
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
// pages live at the package root (./pages) — all components/lib/data are
|
||||
// shared (see the @shared alias)
|
||||
srcDir: '.',
|
||||
server: {
|
||||
port: 3000,
|
||||
// bind on all interfaces so the dev server is reachable from Docker
|
||||
// port mappings and other devices on the local network
|
||||
host: true,
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
// Demo data in shared/data (single source of truth).
|
||||
'@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
|
||||
// Components/lib shared with docs.
|
||||
'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
|
||||
'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
|
||||
'@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
|
||||
// Used by @shared/lib/docs-children's glob.
|
||||
'@pages': fileURLToPath(new URL('./pages', import.meta.url)),
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
// Emit sign-in.html instead of sign-in/index.html (HTML is the product).
|
||||
format: 'file',
|
||||
},
|
||||
// Keep readable HTML; prettier formats after the build.
|
||||
compressHTML: false,
|
||||
integrations: [
|
||||
copyAssets({
|
||||
repo: path('..'),
|
||||
publicDir: path('./public'),
|
||||
copies: [
|
||||
{
|
||||
// @tabler/core dist (css/js/fonts/img/libs) — same as the Eleventy passthrough.
|
||||
// Fallback keeps current assets if core rebuilds dist mid-copy in turbo dev.
|
||||
from: path('./node_modules/@tabler/core/dist'),
|
||||
to: path('./public/dist'),
|
||||
label: '@tabler/core',
|
||||
allowDestinationFallback: true,
|
||||
},
|
||||
{
|
||||
// demo css/js built by this package's sass/vite pipeline. Source is
|
||||
// tmp-assets/ (NOT dist/) on purpose — dist/ is Astro's own build output,
|
||||
// and copying from a path Astro also writes to caused unbounded growth
|
||||
// across repeated builds. See preview/.build/vite.config.mts.
|
||||
from: path('./tmp-assets'),
|
||||
to: path('./public/preview'),
|
||||
label: '@tabler/preview',
|
||||
},
|
||||
{
|
||||
// docs.css built by the @tabler/docs sass pipeline (used by docs pages)
|
||||
from: path('../docs/dist/css'),
|
||||
to: path('./public/css'),
|
||||
label: '@tabler/docs',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
// static assets (photos, avatars, tracks, brand svgs...). The real source,
|
||||
// because preview/static is a symlink that may not survive deployment packaging.
|
||||
from: path('../shared/static'),
|
||||
to: path('./public/static'),
|
||||
label: 'shared assets',
|
||||
},
|
||||
// favicons (source assets of @tabler/preview)
|
||||
{ from: path('./assets/favicon.ico'), to: path('./public/favicon.ico'), label: '@tabler/preview' },
|
||||
{ from: path('./assets/favicon-dev.ico'), to: path('./public/favicon-dev.ico'), label: '@tabler/preview' },
|
||||
],
|
||||
// Watch the real core/dist, not the node_modules symlink the startup copy
|
||||
// reads from — same directory.
|
||||
syncDirs: [
|
||||
{ from: path('../core/dist'), to: path('./public/dist') },
|
||||
{ from: path('../shared/static'), to: path('./public/static') },
|
||||
],
|
||||
// watch-css/watch-js write straight into public/preview — the file is already
|
||||
// in place, but Astro does not reload the browser on public/ changes.
|
||||
reloadDirs: [path('./public/preview')],
|
||||
}),
|
||||
mdx(),
|
||||
prettifyHtml(),
|
||||
],
|
||||
markdown: {
|
||||
// No typographic quote rewriting.
|
||||
processor: satteri({ features: { smartPunctuation: false } }),
|
||||
shikiConfig: {
|
||||
theme: 'github-dark',
|
||||
transformers: [
|
||||
{
|
||||
// Beautify html fences before highlighting.
|
||||
preprocess(code) {
|
||||
if (this.options.lang === 'html') {
|
||||
return beautify.html(code, { indent_size: 2, wrap_line_length: 80 });
|
||||
}
|
||||
},
|
||||
// Keep shiki classes only (drop Astro's astro-code / data-language).
|
||||
pre(node) {
|
||||
node.properties.class = 'shiki github-dark';
|
||||
delete node.properties.dataLanguage;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
// pages live at the package root (./pages) — all components/lib/data are
|
||||
// shared (see the @shared alias)
|
||||
srcDir: '.',
|
||||
server: {
|
||||
port: 3000,
|
||||
// bind on all interfaces so the dev server is reachable from Docker
|
||||
// port mappings and other devices on the local network
|
||||
host: true,
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
// Demo data in shared/data (single source of truth).
|
||||
'@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
|
||||
// Components/lib shared with docs.
|
||||
'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
|
||||
'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
|
||||
'@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
|
||||
// Used by @shared/lib/docs-children's glob.
|
||||
'@pages': fileURLToPath(new URL('./pages', import.meta.url)),
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
// Emit sign-in.html instead of sign-in/index.html (HTML is the product).
|
||||
format: 'file',
|
||||
},
|
||||
// Keep readable HTML; prettier formats after the build.
|
||||
compressHTML: false,
|
||||
integrations: [
|
||||
copyAssets({
|
||||
repo: path('..'),
|
||||
publicDir: path('./public'),
|
||||
copies: [
|
||||
{
|
||||
// @tabler/core dist (css/js/fonts/img/libs) — same as the Eleventy passthrough.
|
||||
// Fallback keeps current assets if core rebuilds dist mid-copy in turbo dev.
|
||||
from: path('./node_modules/@tabler/core/dist'),
|
||||
to: path('./public/dist'),
|
||||
label: '@tabler/core',
|
||||
allowDestinationFallback: true,
|
||||
},
|
||||
{
|
||||
// demo css/js built by this package's sass/vite pipeline. Source is
|
||||
// tmp-assets/ (NOT dist/) on purpose — dist/ is Astro's own build output,
|
||||
// and copying from a path Astro also writes to caused unbounded growth
|
||||
// across repeated builds. See preview/.build/vite.config.mts.
|
||||
from: path('./tmp-assets'),
|
||||
to: path('./public/preview'),
|
||||
label: '@tabler/preview',
|
||||
},
|
||||
{
|
||||
// docs.css built by the @tabler/docs sass pipeline (used by docs pages)
|
||||
from: path('../docs/dist/css'),
|
||||
to: path('./public/css'),
|
||||
label: '@tabler/docs',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
// static assets (photos, avatars, tracks, brand svgs...). The real source,
|
||||
// because preview/static is a symlink that may not survive deployment packaging.
|
||||
from: path('../shared/static'),
|
||||
to: path('./public/static'),
|
||||
label: 'shared assets',
|
||||
},
|
||||
// favicons (source assets of @tabler/preview)
|
||||
{ from: path('./assets/favicon.ico'), to: path('./public/favicon.ico'), label: '@tabler/preview' },
|
||||
{ from: path('./assets/favicon-dev.ico'), to: path('./public/favicon-dev.ico'), label: '@tabler/preview' },
|
||||
],
|
||||
// Watch the real core/dist, not the node_modules symlink the startup copy
|
||||
// reads from — same directory.
|
||||
syncDirs: [
|
||||
{ from: path('../core/dist'), to: path('./public/dist') },
|
||||
{ from: path('../shared/static'), to: path('./public/static') },
|
||||
],
|
||||
// watch-css/watch-js write straight into public/preview — the file is already
|
||||
// in place, but Astro does not reload the browser on public/ changes.
|
||||
reloadDirs: [path('./public/preview')],
|
||||
}),
|
||||
mdx(),
|
||||
prettifyHtml(),
|
||||
],
|
||||
markdown: {
|
||||
// No typographic quote rewriting.
|
||||
processor: satteri({ features: { smartPunctuation: false } }),
|
||||
shikiConfig: {
|
||||
theme: 'github-dark',
|
||||
transformers: [
|
||||
{
|
||||
// Beautify html fences before highlighting.
|
||||
preprocess(code) {
|
||||
if (this.options.lang === 'html') {
|
||||
return beautify.html(code, { indent_size: 2, wrap_line_length: 80 })
|
||||
}
|
||||
},
|
||||
// Keep shiki classes only (drop Astro's astro-code / data-language).
|
||||
pre(node) {
|
||||
node.properties.class = 'shiki github-dark'
|
||||
delete node.properties.dataLanguage
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,93 +1,82 @@
|
||||
---
|
||||
import FormFooter from '@ui/FormFooter.astro';
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import FormFooter from '@ui/FormFooter.astro'
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
---
|
||||
|
||||
<SingleLayout title="2-Step Verification">
|
||||
<form class="card card-md" action="./" method="get" autocomplete="off" novalidate>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title card-title-lg text-center mb-4">Authenticate Your Account</h2>
|
||||
<form class="card card-md" action="./" method="get" autocomplete="off" novalidate>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title card-title-lg text-center mb-4">Authenticate Your Account</h2>
|
||||
|
||||
<p class="my-4 text-center">
|
||||
Please confirm your account by entering the authorization code sent to <strong
|
||||
>+1 856-672-8552</strong
|
||||
>.
|
||||
</p>
|
||||
<p class="my-4 text-center">
|
||||
Please confirm your account by entering the authorization code sent to <strong>+1 856-672-8552</strong>.
|
||||
</p>
|
||||
|
||||
<div class="my-5">
|
||||
<div class="row g-4">
|
||||
{
|
||||
Array.from({ length: 2 }).map(() => (
|
||||
<div class="col">
|
||||
<div class="row g-2">
|
||||
{Array.from({ length: 3 }).map(() => (
|
||||
<div class="col">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control form-control-lg text-center px-3 py-3"
|
||||
maxlength="1"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
data-code-input
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<div class="row g-4">
|
||||
{
|
||||
Array.from({ length: 2 }).map(() => (
|
||||
<div class="col">
|
||||
<div class="row g-2">
|
||||
{Array.from({ length: 3 }).map(() => (
|
||||
<div class="col">
|
||||
<input type="text" class="form-control form-control-lg text-center px-3 py-3" maxlength="1" inputmode="numeric" pattern="[0-9]*" data-code-input />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<label class="form-check">
|
||||
<input type="checkbox" class="form-check-input" />
|
||||
Dont't ask for codes again on this device
|
||||
</label>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<label class="form-check">
|
||||
<input type="checkbox" class="form-check-input" />
|
||||
Dont't ask for codes again on this device
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<FormFooter>
|
||||
<ButtonList class="flex-nowrap">
|
||||
<Button text="Cancel" block href="2-step-verification.html" />
|
||||
<Button text="Verify" block color="primary" />
|
||||
</ButtonList>
|
||||
</FormFooter>
|
||||
</div>
|
||||
</form>
|
||||
<FormFooter>
|
||||
<ButtonList class="flex-nowrap">
|
||||
<Button text="Cancel" block href="2-step-verification.html" />
|
||||
<Button text="Verify" block color="primary" />
|
||||
</ButtonList>
|
||||
</FormFooter>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<CaptureScript>
|
||||
<!-- BEGIN CODE INPUT SCRIPT -->
|
||||
<script is:inline>
|
||||
const inputs = document.querySelectorAll('[data-code-input]');
|
||||
<CaptureScript>
|
||||
<!-- BEGIN CODE INPUT SCRIPT -->
|
||||
<script is:inline>
|
||||
const inputs = document.querySelectorAll('[data-code-input]')
|
||||
|
||||
// Attach an event listener to each input element
|
||||
inputs.forEach((input, i) => {
|
||||
input.addEventListener('input', (e) => {
|
||||
const target = e.target;
|
||||
// If the input field has a character, and there is a next input field, focus it
|
||||
if (target.value.length === target.maxLength && i + 1 < inputs.length) {
|
||||
inputs[i + 1].focus();
|
||||
}
|
||||
});
|
||||
// Attach an event listener to each input element
|
||||
inputs.forEach((input, i) => {
|
||||
input.addEventListener('input', (e) => {
|
||||
const target = e.target
|
||||
// If the input field has a character, and there is a next input field, focus it
|
||||
if (target.value.length === target.maxLength && i + 1 < inputs.length) {
|
||||
inputs[i + 1].focus()
|
||||
}
|
||||
})
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
const target = e.target;
|
||||
// If the input field is empty and Backspace is pressed, and there is a previous input field, focus it
|
||||
if (target.value.length === 0 && e.key === 'Backspace' && i > 0) {
|
||||
inputs[i - 1].focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- END CODE INPUT SCRIPT -->
|
||||
</CaptureScript>
|
||||
input.addEventListener('keydown', (e) => {
|
||||
const target = e.target
|
||||
// If the input field is empty and Backspace is pressed, and there is a previous input field, focus it
|
||||
if (target.value.length === 0 && e.key === 'Backspace' && i > 0) {
|
||||
inputs[i - 1].focus()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<!-- END CODE INPUT SCRIPT -->
|
||||
</CaptureScript>
|
||||
|
||||
<div class="text-center text-secondary mt-3">
|
||||
It may take a minute to receive your code. Haven't received it? <a href="./"
|
||||
>Resend a new code.</a
|
||||
>
|
||||
</div>
|
||||
<div class="text-center text-secondary mt-3">
|
||||
It may take a minute to receive your code. Haven't received it? <a href="./">Resend a new code.</a>
|
||||
</div>
|
||||
</SingleLayout>
|
||||
|
||||
+772
-810
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
---
|
||||
// Title is "Forgot password" (matches auth-lock card, not page name).
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro';
|
||||
import AuthLockCard from '@shared/components/cards/AuthLockCard.astro';
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro'
|
||||
import AuthLockCard from '@shared/components/cards/AuthLockCard.astro'
|
||||
---
|
||||
|
||||
<SingleLayout title="Forgot password">
|
||||
<AuthLockCard />
|
||||
<AuthLockCard />
|
||||
</SingleLayout>
|
||||
|
||||
+155
-155
@@ -1,166 +1,166 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import AvatarUpload from '@ui/AvatarUpload.astro';
|
||||
import AvatarList from '@ui/AvatarList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import { site } from '@shared/lib/site';
|
||||
import { firstLetters } from '@shared/lib/string-format';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import AvatarUpload from '@ui/AvatarUpload.astro'
|
||||
import AvatarList from '@ui/AvatarList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { firstLetters } from '@shared/lib/string-format'
|
||||
|
||||
const iconIcons = ['user', 'settings', 'car', 'balloon', 'users', 'users-group', 'apps', 'ghost'];
|
||||
const iconIcons = ['user', 'settings', 'car', 'balloon', 'users', 'users-group', 'apps', 'ghost']
|
||||
|
||||
// themeColors keys (blue..cyan).
|
||||
const colors = site.themeColors;
|
||||
const colors = site.themeColors
|
||||
|
||||
const people8 = people.slice(0, 8);
|
||||
const people5 = people.slice(0, 5);
|
||||
const sizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl'];
|
||||
const listSizes = ['xxs', 'xs', 'sm', 'md', 'lg'];
|
||||
const uploadSizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'];
|
||||
const statusColors = ['red', 'green', 'blue', 'yellow', 'secondary'];
|
||||
const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch'];
|
||||
const people8 = people.slice(0, 8)
|
||||
const people5 = people.slice(0, 5)
|
||||
const sizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl']
|
||||
const listSizes = ['xxs', 'xs', 'sm', 'md', 'lg']
|
||||
const uploadSizes = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl']
|
||||
const statusColors = ['red', 'green', 'blue', 'yellow', 'secondary']
|
||||
const brands = ['netflix', 'amazon', 'messenger', 'figma', 'twitch']
|
||||
---
|
||||
|
||||
<DefaultLayout title="Avatars" pageMenu="base.avatars" pageHeader="Avatars">
|
||||
<div class="row row-cards">
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Default avatar</CardTitle>
|
||||
<Avatar />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar with icon</CardTitle>
|
||||
<div class="row row-cards">
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Default avatar</CardTitle>
|
||||
<Avatar />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar with icon</CardTitle>
|
||||
|
||||
<div class="avatar-list">
|
||||
{iconIcons.map((icon) => <Avatar icon={icon} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar with icon</CardTitle>
|
||||
<div class="avatar-list">
|
||||
{iconIcons.map((icon) => <Avatar icon={icon} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar with icon</CardTitle>
|
||||
|
||||
<div class="avatar-list">
|
||||
{colors.map((color) => <Avatar icon="user" color={color} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Simple avatar</CardTitle>
|
||||
<div class="avatar-list">
|
||||
{people8.map((person) => <Avatar person={person} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar placeholder</CardTitle>
|
||||
<div class="avatar-list">
|
||||
{people8.map((person) => <Avatar placeholder={firstLetters(person.full_name)} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar shapes</CardTitle>
|
||||
<div class="avatar-list">
|
||||
<Avatar />
|
||||
<Avatar class="rounded-circle" />
|
||||
<Avatar class="rounded-0" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar sizes</CardTitle>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="avatar-list">
|
||||
{sizes.map((size) => <Avatar size={size} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="avatar-list">
|
||||
{sizes.map((size) => <Avatar placeholder="PK" size={size} />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar lists</CardTitle>
|
||||
<div class="row g-3">
|
||||
{
|
||||
listSizes.map((size) => (
|
||||
<>
|
||||
<div class="col-6">
|
||||
<AvatarList stacked={true} size={size}>
|
||||
{people5.map((person) => (
|
||||
<Avatar person={person} />
|
||||
))}
|
||||
<Avatar icon="plus" link />
|
||||
</AvatarList>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<AvatarList stacked={true} size={size}>
|
||||
{people5.map((person) => (
|
||||
<Avatar person={person} class="rounded-circle" />
|
||||
))}
|
||||
<Avatar icon="plus" link class="rounded-circle" />
|
||||
</AvatarList>
|
||||
</div>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar placeholder</CardTitle>
|
||||
{uploadSizes.map((size) => <AvatarUpload size={size} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar statuses</CardTitle>
|
||||
{statusColors.map((color, i) => <Avatar personId={i + 1} class="rounded-circle" status={color} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar brands</CardTitle>
|
||||
{brands.map((brand, i) => <Avatar personId={i + 1} brand={brand} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avatar-list">
|
||||
{colors.map((color) => <Avatar icon="user" color={color} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Simple avatar</CardTitle>
|
||||
<div class="avatar-list">
|
||||
{people8.map((person) => <Avatar person={person} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar placeholder</CardTitle>
|
||||
<div class="avatar-list">
|
||||
{people8.map((person) => <Avatar placeholder={firstLetters(person.full_name)} />)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar shapes</CardTitle>
|
||||
<div class="avatar-list">
|
||||
<Avatar />
|
||||
<Avatar class="rounded-circle" />
|
||||
<Avatar class="rounded-0" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar sizes</CardTitle>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="avatar-list">
|
||||
{sizes.map((size) => <Avatar size={size} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="avatar-list">
|
||||
{sizes.map((size) => <Avatar placeholder="PK" size={size} />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar lists</CardTitle>
|
||||
<div class="row g-3">
|
||||
{
|
||||
listSizes.map((size) => (
|
||||
<>
|
||||
<div class="col-6">
|
||||
<AvatarList stacked={true} size={size}>
|
||||
{people5.map((person) => (
|
||||
<Avatar person={person} />
|
||||
))}
|
||||
<Avatar icon="plus" link />
|
||||
</AvatarList>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<AvatarList stacked={true} size={size}>
|
||||
{people5.map((person) => (
|
||||
<Avatar person={person} class="rounded-circle" />
|
||||
))}
|
||||
<Avatar icon="plus" link class="rounded-circle" />
|
||||
</AvatarList>
|
||||
</div>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar placeholder</CardTitle>
|
||||
{uploadSizes.map((size) => <AvatarUpload size={size} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar statuses</CardTitle>
|
||||
{statusColors.map((color, i) => <Avatar personId={i + 1} class="rounded-circle" status={color} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Avatar brands</CardTitle>
|
||||
{brands.map((brand, i) => <Avatar personId={i + 1} brand={brand} />)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+190
-191
@@ -1,206 +1,205 @@
|
||||
---
|
||||
|
||||
// colors = ['default'] + site.colors keys + ['dark', 'light']
|
||||
|
||||
import BadgesList from '@ui/BadgesList.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro';
|
||||
import site from '@data/site.json';
|
||||
import { ucFirst } from '@shared/lib/string-format';
|
||||
import BadgesList from '@ui/BadgesList.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro'
|
||||
import site from '@data/site.json'
|
||||
import { ucFirst } from '@shared/lib/string-format'
|
||||
|
||||
const colors = ['default', ...Object.keys(site.colors), 'dark', 'light'];
|
||||
const sizes = ['sm', 'md', 'lg'];
|
||||
const colors = ['default', ...Object.keys(site.colors), 'dark', 'light']
|
||||
const sizes = ['sm', 'md', 'lg']
|
||||
---
|
||||
|
||||
<DefaultLayout title="Badges" pageHeader="Badges" pageMenu="base.badges">
|
||||
<div class="row row-cards">
|
||||
<div class="col-4">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<h1>Example heading <span class="badge">New</span></h1>
|
||||
<h2>Example heading <span class="badge">New</span></h2>
|
||||
<h3>Example heading <span class="badge">New</span></h3>
|
||||
<h4>Example heading <span class="badge">New</span></h4>
|
||||
<h5>Example heading <span class="badge">New</span></h5>
|
||||
<h6>Example heading <span class="badge">New</span></h6>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Badge sizes</CardTitle>
|
||||
<div class="row row-cards">
|
||||
<div class="col-4">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<h1>Example heading <span class="badge">New</span></h1>
|
||||
<h2>Example heading <span class="badge">New</span></h2>
|
||||
<h3>Example heading <span class="badge">New</span></h3>
|
||||
<h4>Example heading <span class="badge">New</span></h4>
|
||||
<h5>Example heading <span class="badge">New</span></h5>
|
||||
<h6>Example heading <span class="badge">New</span></h6>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Badge sizes</CardTitle>
|
||||
|
||||
<div class="space-y">
|
||||
{
|
||||
sizes.map((size) => (
|
||||
<BadgesList>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>Default</span>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
<Icon name="check" /> Left icon
|
||||
</span>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
Right icon<Icon name="arrow-right" />
|
||||
</span>
|
||||
<span class={`badge badge-icononly${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
<Icon name="star" type="filled" />
|
||||
</span>
|
||||
</BadgesList>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Positioned badges</CardTitle>
|
||||
<div class="space-y">
|
||||
{
|
||||
sizes.map((size) => (
|
||||
<BadgesList>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>Default</span>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
<Icon name="check" /> Left icon
|
||||
</span>
|
||||
<span class={`badge${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
Right icon
|
||||
<Icon name="arrow-right" />
|
||||
</span>
|
||||
<span class={`badge badge-icononly${size !== 'md' ? ` badge-${size}` : ''}`}>
|
||||
<Icon name="star" type="filled" />
|
||||
</span>
|
||||
</BadgesList>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Positioned badges</CardTitle>
|
||||
|
||||
<ButtonList>
|
||||
<button type="button" class="btn">Notifications <span class="badge text-bg-secondary ms-2">4</span></button>
|
||||
<ButtonList>
|
||||
<button type="button" class="btn">Notifications <span class="badge text-bg-secondary ms-2">4</span></button>
|
||||
|
||||
<button type="button" class="btn">
|
||||
Inbox
|
||||
<span class="badge bg-red badge-notification text-red-fg">
|
||||
9+
|
||||
<span class="visually-hidden">unread messages</span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn">
|
||||
Inbox
|
||||
<span class="badge bg-red badge-notification text-red-fg">
|
||||
9+
|
||||
<span class="visually-hidden">unread messages</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn">
|
||||
Profile
|
||||
<span class="badge badge-dot bg-red badge-notification"></span>
|
||||
</button>
|
||||
<button type="button" class="btn">
|
||||
Profile
|
||||
<span class="badge badge-dot bg-red badge-notification"></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn">
|
||||
Settings
|
||||
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
|
||||
</button>
|
||||
<button type="button" class="btn">
|
||||
Settings
|
||||
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-icon">
|
||||
<Icon name="bell" />
|
||||
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-icon">
|
||||
<Icon name="bell" />
|
||||
<span class="badge badge-dot bg-red badge-notification badge-blink"></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-icon btn-action">
|
||||
<Icon name="bell" />
|
||||
<span class="badge badge-dot bg-red badge-notification"></span>
|
||||
</button>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Basic badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge bg-${color} text-${color}-fg`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Light badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge bg-${color}-lt`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Outline badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge badge-outline text-${color}`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Badges with icons</CardTitle>
|
||||
<BadgesList>
|
||||
{
|
||||
colors.map((color) => (
|
||||
<span class={`badge bg-${color} text-${color}-fg`}>
|
||||
{' '}
|
||||
<Icon name="star" type="filled" /> {ucFirst(color)}{' '}
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3"><DropdownMenu show badge arrow /></div>
|
||||
<div class="col-sm-6 col-lg-9">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<button class="btn">
|
||||
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg ms-2`}>{index + 1}</span>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<button class="btn position-relative">
|
||||
{ucFirst(color)} badge{' '}
|
||||
<span class={`badge bg-${color} text-${color}-fg badge-notification badge-pill`}>{index + 1}</span>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color) => (
|
||||
<button class="btn position-relative">
|
||||
{ucFirst(color)} badge <span class={`badge bg-${color} badge-notification badge-blink`} />
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-icon btn-action">
|
||||
<Icon name="bell" />
|
||||
<span class="badge badge-dot bg-red badge-notification"></span>
|
||||
</button>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Basic badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge bg-${color} text-${color}-fg`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Light badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge bg-${color}-lt`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Outline badges</CardTitle>
|
||||
<BadgesList>
|
||||
{colors.map((color) => <span class={`badge badge-outline text-${color}`}>{ucFirst(color)}</span>)}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Badges with icons</CardTitle>
|
||||
<BadgesList>
|
||||
{
|
||||
colors.map((color) => (
|
||||
<span class={`badge bg-${color} text-${color}-fg`}>
|
||||
{' '}
|
||||
<Icon name="star" type="filled" /> {ucFirst(color)}{' '}
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</BadgesList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3"><DropdownMenu show badge arrow /></div>
|
||||
<div class="col-sm-6 col-lg-9">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<button class="btn">
|
||||
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg ms-2`}>{index + 1}</span>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<button class="btn position-relative">
|
||||
{ucFirst(color)} badge <span class={`badge bg-${color} text-${color}-fg badge-notification badge-pill`}>{index + 1}</span>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map((color) => (
|
||||
<button class="btn position-relative">
|
||||
{ucFirst(color)} badge <span class={`badge bg-${color} badge-notification badge-blink`} />
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
// front matter keys unused — empty page header comes from missing `page-header` key.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Empty from '@ui/Empty.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Empty from '@ui/Empty.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Blank page" pageMenu="base.blank" containerCentered>
|
||||
<Empty buttonText="Add your first client" buttonIcon="plus" illustration="computer-fix.svg" />
|
||||
<Empty buttonText="Add your first client" buttonIcon="plus" illustration="computer-fix.svg" />
|
||||
</DefaultLayout>
|
||||
|
||||
+191
-181
@@ -1,191 +1,201 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import site from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import site from '@data/site.json'
|
||||
|
||||
type ColorEntry = [string, { title: string; icon?: string }];
|
||||
type ColorEntry = [string, { title: string; icon?: string }]
|
||||
|
||||
// site.json objects iterated as [key, value] pairs.
|
||||
// themeColors/colors entries have no icon — Icon renders nothing there.
|
||||
const themeColors = Object.entries(site.themeColors) as ColorEntry[];
|
||||
const colors = Object.entries(site.colors) as ColorEntry[];
|
||||
const socialColors = Object.entries(site.socialColors) as ColorEntry[];
|
||||
const themeColors = Object.entries(site.themeColors) as ColorEntry[]
|
||||
const colors = Object.entries(site.colors) as ColorEntry[]
|
||||
const socialColors = Object.entries(site.socialColors) as ColorEntry[]
|
||||
|
||||
const actions = ['edit', 'copy', 'settings', 'clipboard', 'x'];
|
||||
const sizes = ['sm', 'md', 'lg', 'xl'];
|
||||
const actions = ['edit', 'copy', 'settings', 'clipboard', 'x']
|
||||
const sizes = ['sm', 'md', 'lg', 'xl']
|
||||
---
|
||||
|
||||
<DefaultLayout title="Buttons" pageHeader="Buttons" pageMenu="base.buttons">
|
||||
<div class="row row-cards">
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Standard Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Outline Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-outline btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Ghost Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-ghost btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Square Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-square btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Pill Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-pill btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Extra colors" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map(([name, color]) => (
|
||||
<a class={`btn btn-${name}`}>{color.icon && <Icon name={color.icon} />} {color.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Icon buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
socialColors.map(([name, app]) => (
|
||||
<a class={`btn btn-icon btn-${name}`}>{app.icon && <Icon name={app.icon} />}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Social colors" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
socialColors.map(([name, app]) => (
|
||||
<a class={`btn btn-${name}`}>{app.icon && <Icon name={app.icon} />} {app.title}</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Action buttons" />
|
||||
<CardBody>
|
||||
<div class="btn-actions">
|
||||
{
|
||||
actions.map((action) => (
|
||||
<a class="btn btn-action">
|
||||
<Icon name={action} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Buttons with icon" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
<a class="btn btn-animate-icon">Save <Icon name="arrow-right" class="icon-end" /></a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="plus" /> Add</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-shake"><Icon name="bell" /> Notifications</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="settings" /> Settings</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-pulse"><Icon name="heart" /> Love</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="x" /> Close</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-tada"><Icon name="check" /> Confirm</a>
|
||||
<a class="btn btn-animate-icon">Next <Icon name="chevron-right" class="icon-end" /></a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-move-start"><Icon name="chevron-left" /> Previous</a>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Buttons size" />
|
||||
<CardBody>
|
||||
<div class="space-y">
|
||||
{
|
||||
sizes.map((size) => (
|
||||
<ButtonList>
|
||||
<Button size={size} text="Button" />
|
||||
<Button size={size} icon="star" iconOnly />
|
||||
<Button size={size} icon="star" text="Button" />
|
||||
<Button size={size} iconEnd="star" text="Button" />
|
||||
</ButtonList>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Standard Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Outline Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-outline btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Ghost Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-ghost btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Square Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-square btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Pill Buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
themeColors.map(([name, color]) => (
|
||||
<a class={`btn btn-pill btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Extra colors" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
colors.map(([name, color]) => (
|
||||
<a class={`btn btn-${name}`}>
|
||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Icon buttons" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{socialColors.map(([name, app]) => <a class={`btn btn-icon btn-${name}`}>{app.icon && <Icon name={app.icon} />}</a>)}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Social colors" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
socialColors.map(([name, app]) => (
|
||||
<a class={`btn btn-${name}`}>
|
||||
{app.icon && <Icon name={app.icon} />} {app.title}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Action buttons" />
|
||||
<CardBody>
|
||||
<div class="btn-actions">
|
||||
{
|
||||
actions.map((action) => (
|
||||
<a class="btn btn-action">
|
||||
<Icon name={action} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Buttons with icon" />
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
<a class="btn btn-animate-icon">Save <Icon name="arrow-right" class="icon-end" /></a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="plus" /> Add</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-shake"><Icon name="bell" /> Notifications</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="settings" /> Settings</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-pulse"><Icon name="heart" /> Love</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="x" /> Close</a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-tada"><Icon name="check" /> Confirm</a>
|
||||
<a class="btn btn-animate-icon">Next <Icon name="chevron-right" class="icon-end" /></a>
|
||||
<a class="btn btn-animate-icon btn-animate-icon-move-start"><Icon name="chevron-left" /> Previous</a>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card>
|
||||
<CardHeader title="Buttons size" />
|
||||
<CardBody>
|
||||
<div class="space-y">
|
||||
{
|
||||
sizes.map((size) => (
|
||||
<ButtonList>
|
||||
<Button size={size} text="Button" />
|
||||
<Button size={size} icon="star" iconOnly />
|
||||
<Button size={size} icon="star" text="Button" />
|
||||
<Button size={size} iconEnd="star" text="Button" />
|
||||
</ButtonList>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
import ProseLayout from '@shared/layouts/ProseLayout.astro';
|
||||
import { renderMarkdown } from '@shared/lib/render-markdown';
|
||||
import changelog from '../../core/CHANGELOG.md?raw';
|
||||
import ProseLayout from '@shared/layouts/ProseLayout.astro'
|
||||
import { renderMarkdown } from '@shared/lib/render-markdown'
|
||||
import changelog from '../../core/CHANGELOG.md?raw'
|
||||
|
||||
const changelogHtml = renderMarkdown(changelog);
|
||||
const changelogHtml = renderMarkdown(changelog)
|
||||
---
|
||||
|
||||
<ProseLayout title="Changelog" pageMenu="changelog">
|
||||
<Fragment set:html={changelogHtml} />
|
||||
<Fragment set:html={changelogHtml} />
|
||||
</ProseLayout>
|
||||
|
||||
@@ -1,34 +1,29 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Colorpicker from '@ui/Colorpicker.astro';
|
||||
import site from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import Colorpicker from '@ui/Colorpicker.astro'
|
||||
import site from '@data/site.json'
|
||||
|
||||
const colors = Object.values(site.colors) as { hex: string }[];
|
||||
const colors = Object.values(site.colors) as { hex: string }[]
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Color picker"
|
||||
pageHeader="Color picker"
|
||||
pageMenu="plugins.colorpicker"
|
||||
pageLibs={['coloris.js']}
|
||||
>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Basic</CardTitle>
|
||||
<div class="row g-3">
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<div class="col-2">
|
||||
<div>
|
||||
<Colorpicker value={color.hex} id={index + 1} format="hex" />
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<DefaultLayout title="Color picker" pageHeader="Color picker" pageMenu="plugins.colorpicker" pageLibs={['coloris.js']}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Basic</CardTitle>
|
||||
<div class="row g-3">
|
||||
{
|
||||
colors.map((color, index) => (
|
||||
<div class="col-2">
|
||||
<div>
|
||||
<Colorpicker value={color.hex} id={index + 1} format="hex" />
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</DefaultLayout>
|
||||
|
||||
+220
-231
@@ -1,247 +1,236 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import site from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import site from '@data/site.json'
|
||||
|
||||
type ColorEntry = [string, { title: string; hex: string; abbr?: string; icon?: string }];
|
||||
type ColorEntry = [string, { title: string; hex: string; abbr?: string; icon?: string }]
|
||||
|
||||
const colors = Object.entries(site.colors) as ColorEntry[];
|
||||
const lightColors = Object.entries(site.lightColors) as ColorEntry[];
|
||||
const grayColors = Object.entries(site.grayColors) as ColorEntry[];
|
||||
const socialColors = Object.entries(site.socialColors) as ColorEntry[];
|
||||
const colors = Object.entries(site.colors) as ColorEntry[]
|
||||
const lightColors = Object.entries(site.lightColors) as ColorEntry[]
|
||||
const grayColors = Object.entries(site.grayColors) as ColorEntry[]
|
||||
const socialColors = Object.entries(site.socialColors) as ColorEntry[]
|
||||
|
||||
const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'transparent'];
|
||||
const gradientColors = [...Object.keys(site.colors), 'inverted', 'white', 'transparent']
|
||||
---
|
||||
|
||||
<DefaultLayout title="Colors" pageHeader="Colors" pageMenu="base.colors">
|
||||
<div class="row row-cards">
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
colors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
lightColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name}-lt text-${name}-lt-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
grayColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
socialColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} icon={color.icon} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Gradient</CardTitle>
|
||||
<form action="">
|
||||
<div class="row g-4">
|
||||
<div class="col">
|
||||
<FormGroup label="From" class="">
|
||||
<select class="form-select" name="color-from">
|
||||
{gradientColors.map((color) => <option value={color}>{color}</option>)}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="To" class="mt-3">
|
||||
<select class="form-select" name="color-to">
|
||||
{
|
||||
gradientColors.map((color) => (
|
||||
<option value={color} selected={color === 'transparent'}>
|
||||
{color}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col">
|
||||
<FormGroup label="Via" class="">
|
||||
<select class="form-select" name="color-via">
|
||||
<option></option>
|
||||
{gradientColors.map((color) => <option value={color}>{color}</option>)}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Direction" class="mt-3">
|
||||
<select class="form-select" name="color-direction">
|
||||
<option value="to-t">to top</option>
|
||||
<option value="to-te">to top right</option>
|
||||
<option value="to-r" selected>to right</option>
|
||||
<option value="to-be">to bottom right</option>
|
||||
<option value="to-b">to bottom</option>
|
||||
<option value="to-bs">to bottom left</option>
|
||||
<option value="to-s">to left</option>
|
||||
<option value="to-ts">to top left</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="border rounded bg-pattern-transparent overflow-hidden">
|
||||
<div
|
||||
id="gradient-preview"
|
||||
class="border rounded bg-gradient bg-gradient-from-primary bg-gradient-to-transparent"
|
||||
>
|
||||
<div class=" px-4 py-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="space-y">
|
||||
{
|
||||
gradientColors.map((color) => (
|
||||
<div class={`border rounded bg-gradient bg-gradient-from-${color} bg-gradient-to-transparent px-4 py-2`} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="space-y">
|
||||
{
|
||||
gradientColors.map((color) => (
|
||||
<div class={`border rounded bg-gradient bg-gradient-to-${color} bg-gradient-from-transparent px-4 py-2`} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
colors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
lightColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name}-lt text-${name}-lt-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
grayColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} placeholder={color.abbr} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-3">
|
||||
{
|
||||
socialColors.map(([name, color]) => (
|
||||
<div class="col-12">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<Avatar square class={`bg-${name} text-${name}-fg`} icon={color.icon} />
|
||||
</div>
|
||||
<div class="col">
|
||||
{color.title}
|
||||
<br />
|
||||
<code>{color.hex}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Gradient</CardTitle>
|
||||
<form action="">
|
||||
<div class="row g-4">
|
||||
<div class="col">
|
||||
<FormGroup label="From" class="">
|
||||
<select class="form-select" name="color-from">
|
||||
{gradientColors.map((color) => <option value={color}>{color}</option>)}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="To" class="mt-3">
|
||||
<select class="form-select" name="color-to">
|
||||
{
|
||||
gradientColors.map((color) => (
|
||||
<option value={color} selected={color === 'transparent'}>
|
||||
{color}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col">
|
||||
<FormGroup label="Via" class="">
|
||||
<select class="form-select" name="color-via">
|
||||
<option></option>
|
||||
{gradientColors.map((color) => <option value={color}>{color}</option>)}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Direction" class="mt-3">
|
||||
<select class="form-select" name="color-direction">
|
||||
<option value="to-t">to top</option>
|
||||
<option value="to-te">to top right</option>
|
||||
<option value="to-r" selected>to right</option>
|
||||
<option value="to-be">to bottom right</option>
|
||||
<option value="to-b">to bottom</option>
|
||||
<option value="to-bs">to bottom left</option>
|
||||
<option value="to-s">to left</option>
|
||||
<option value="to-ts">to top left</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="border rounded bg-pattern-transparent overflow-hidden">
|
||||
<div id="gradient-preview" class="border rounded bg-gradient bg-gradient-from-primary bg-gradient-to-transparent">
|
||||
<div class="px-4 py-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="space-y">
|
||||
{gradientColors.map((color) => <div class={`border rounded bg-gradient bg-gradient-from-${color} bg-gradient-to-transparent px-4 py-2`} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="space-y">
|
||||
{gradientColors.map((color) => <div class={`border rounded bg-gradient bg-gradient-to-${color} bg-gradient-from-transparent px-4 py-2`} />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CaptureScript>
|
||||
<!-- BEGIN GRADIENT SCRIPT -->
|
||||
<script is:inline>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var gradientPreview = document.getElementById('gradient-preview');
|
||||
var colorFrom = document.querySelector('[name="color-from"]');
|
||||
var colorTo = document.querySelector('[name="color-to"]');
|
||||
var colorVia = document.querySelector('[name="color-via"]');
|
||||
var colorDirection = document.querySelector('[name="color-direction"]');
|
||||
<CaptureScript>
|
||||
<!-- BEGIN GRADIENT SCRIPT -->
|
||||
<script is:inline>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var gradientPreview = document.getElementById('gradient-preview')
|
||||
var colorFrom = document.querySelector('[name="color-from"]')
|
||||
var colorTo = document.querySelector('[name="color-to"]')
|
||||
var colorVia = document.querySelector('[name="color-via"]')
|
||||
var colorDirection = document.querySelector('[name="color-direction"]')
|
||||
|
||||
function updateGradient() {
|
||||
var from = colorFrom.value;
|
||||
var to = colorTo.value;
|
||||
var via = colorVia.value;
|
||||
var direction = colorDirection.value;
|
||||
function updateGradient() {
|
||||
var from = colorFrom.value
|
||||
var to = colorTo.value
|
||||
var via = colorVia.value
|
||||
var direction = colorDirection.value
|
||||
|
||||
var gradient = 'bg-gradient bg-gradient-from-' + from + ' bg-gradient-to-' + to;
|
||||
var gradient = 'bg-gradient bg-gradient-from-' + from + ' bg-gradient-to-' + to
|
||||
|
||||
if (via) {
|
||||
gradient += ' bg-gradient-via-' + via;
|
||||
}
|
||||
if (via) {
|
||||
gradient += ' bg-gradient-via-' + via
|
||||
}
|
||||
|
||||
gradient += ' bg-gradient-' + direction;
|
||||
gradient += ' bg-gradient-' + direction
|
||||
|
||||
gradientPreview.className = gradient;
|
||||
}
|
||||
gradientPreview.className = gradient
|
||||
}
|
||||
|
||||
colorFrom.addEventListener('change', updateGradient);
|
||||
colorTo.addEventListener('change', updateGradient);
|
||||
colorVia.addEventListener('change', updateGradient);
|
||||
colorDirection.addEventListener('change', updateGradient);
|
||||
colorFrom.addEventListener('change', updateGradient)
|
||||
colorTo.addEventListener('change', updateGradient)
|
||||
colorVia.addEventListener('change', updateGradient)
|
||||
colorDirection.addEventListener('change', updateGradient)
|
||||
|
||||
updateGradient();
|
||||
});
|
||||
</script>
|
||||
<!-- END GRADIENT SCRIPT -->
|
||||
</CaptureScript>
|
||||
updateGradient()
|
||||
})
|
||||
</script>
|
||||
<!-- END GRADIENT SCRIPT -->
|
||||
</CaptureScript>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,337 +1,332 @@
|
||||
---
|
||||
import CardActions from '@ui/CardActions.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Trending from '@ui/Trending.astro';
|
||||
import CardDropdown from '@ui/CardDropdown.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import NavSegmented from '@ui/NavSegmented.astro';
|
||||
import Chart from '@ui/Chart.astro';
|
||||
import SwitchIcon from '@ui/SwitchIcon.astro';
|
||||
import cryptoCurrencies from '@data/crypto-currencies.json';
|
||||
import cryptoMarkets from '@data/crypto-markets.json';
|
||||
import cryptoOrders from '@data/crypto-orders.json';
|
||||
import { parseCurrency, roundTo } from '@shared/lib/string-format';
|
||||
import CardActions from '@ui/CardActions.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Trending from '@ui/Trending.astro'
|
||||
import CardDropdown from '@ui/CardDropdown.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import NavSegmented from '@ui/NavSegmented.astro'
|
||||
import Chart from '@ui/Chart.astro'
|
||||
import SwitchIcon from '@ui/SwitchIcon.astro'
|
||||
import cryptoCurrencies from '@data/crypto-currencies.json'
|
||||
import cryptoMarkets from '@data/crypto-markets.json'
|
||||
import cryptoOrders from '@data/crypto-orders.json'
|
||||
import { parseCurrency, roundTo } from '@shared/lib/string-format'
|
||||
|
||||
interface Currency {
|
||||
symbol: string;
|
||||
price: string;
|
||||
p24h: number;
|
||||
'volume-24h': string;
|
||||
'symbol': string
|
||||
'price': string
|
||||
'p24h': number
|
||||
'volume-24h': string
|
||||
}
|
||||
|
||||
const currencies = cryptoCurrencies as Currency[];
|
||||
const find = (symbol: string) => currencies.find((c) => c.symbol === symbol)!;
|
||||
const btc = find('BTC');
|
||||
const ltc = find('LTC');
|
||||
const eth = find('ETH');
|
||||
const xmr = find('XMR');
|
||||
const currencies = cryptoCurrencies as Currency[]
|
||||
const find = (symbol: string) => currencies.find((c) => c.symbol === symbol)!
|
||||
const btc = find('BTC')
|
||||
const ltc = find('LTC')
|
||||
const eth = find('ETH')
|
||||
const xmr = find('XMR')
|
||||
|
||||
const btcBalance = 2.3;
|
||||
const btcPriceNum = parseCurrency(btc.price);
|
||||
const totalUsd = Math.round(btcPriceNum * btcBalance).toLocaleString('en-US');
|
||||
const btcBalance = 2.3
|
||||
const btcPriceNum = parseCurrency(btc.price)
|
||||
const totalUsd = Math.round(btcPriceNum * btcBalance).toLocaleString('en-US')
|
||||
// divided_by then round to 8 decimals (trailing zeros dropped in output)
|
||||
const ltcBtc = roundTo(parseCurrency(ltc.price) / btcPriceNum);
|
||||
const ethBtc = roundTo(parseCurrency(eth.price) / btcPriceNum);
|
||||
const xmrBtc = roundTo(parseCurrency(xmr.price) / btcPriceNum);
|
||||
const ltcBtc = roundTo(parseCurrency(ltc.price) / btcPriceNum)
|
||||
const ethBtc = roundTo(parseCurrency(eth.price) / btcPriceNum)
|
||||
const xmrBtc = roundTo(parseCurrency(xmr.price) / btcPriceNum)
|
||||
|
||||
const markets = (cryptoMarkets as { coin: string; price: string; volume: string; change: string }[]).slice(0, 10);
|
||||
const orders = cryptoOrders as { sell_orders: { price: string; btc: string; sum: string }[]; buy_orders: { price: string; btc: string; sum: string }[] };
|
||||
const operationCurrencies = currencies.slice(0, 20);
|
||||
const markets = (cryptoMarkets as { coin: string; price: string; volume: string; change: string }[]).slice(0, 10)
|
||||
const orders = cryptoOrders as { sell_orders: { price: string; btc: string; sum: string }[]; buy_orders: { price: string; btc: string; sum: string }[] }
|
||||
const operationCurrencies = currencies.slice(0, 20)
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Crypto Dashboard"
|
||||
pageHeader="Crypto Dashboard"
|
||||
pageMenu="dashboards.crypto"
|
||||
pageLibs={['apexcharts']}
|
||||
>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">Total balance</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-dollar" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">${totalUsd}</span>
|
||||
<span class="text-muted">{btcBalance} {btc.symbol}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<Trending value={btc.p24h} />
|
||||
<span class="text-muted">Since last week</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<DefaultLayout title="Crypto Dashboard" pageHeader="Crypto Dashboard" pageMenu="dashboards.crypto" pageLibs={['apexcharts']}>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">Total balance</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-dollar" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">${totalUsd}</span>
|
||||
<span class="text-muted">{btcBalance} {btc.symbol}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<Trending value={btc.p24h} />
|
||||
<span class="text-muted">Since last week</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">USD/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-bitcoin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{btc.price}</span>
|
||||
<span class="text-muted">{btc.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {btc['volume-24h']}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">USD/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-bitcoin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{btc.price}</span>
|
||||
<span class="text-muted">{btc.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {btc['volume-24h']}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">LTC/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-litecoin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{ltcBtc}</span>
|
||||
<span class="text-muted">{ltc.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {ltc['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">LTC/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-litecoin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{ltcBtc}</span>
|
||||
<span class="text-muted">{ltc.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {ltc['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">ETH/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-ethereum" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{ethBtc}</span>
|
||||
<span class="text-muted">{eth.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {eth['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">ETH/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-ethereum" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{ethBtc}</span>
|
||||
<span class="text-muted">{eth.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {eth['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">XMR/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-monero" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{xmrBtc}</span>
|
||||
<span class="text-muted">{xmr.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {xmr['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-5">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Markets</h5>
|
||||
<CardActions>
|
||||
<CardDropdown />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<SwitchIcon icon="star" iconBColor="yellow" variant="slide-up" />
|
||||
</th>
|
||||
<th>Coin</th>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">Volume</th>
|
||||
<th class="d-none d-xl-table-cell text-end">Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
markets.map((market) => (
|
||||
<tr>
|
||||
<td>
|
||||
<SwitchIcon icon="star" iconBColor="yellow" variant="slide-up" />
|
||||
</td>
|
||||
<td>{market.coin}</td>
|
||||
<td class="">{market.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{market.volume}</td>
|
||||
<td class="d-none d-xl-table-cell text-end">
|
||||
<Trending value={market.change as unknown as number} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xxl">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">XMR/BTC</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Avatar icon="currency-monero" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<span class="h3">{xmrBtc}</span>
|
||||
<span class="text-muted">{xmr.price}</span>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<span class="text-muted">Volume: {xmr['volume-24h'].replace('$', '')}</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-5">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Markets</h5>
|
||||
<CardActions>
|
||||
<CardDropdown />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<SwitchIcon icon="star" iconBColor="yellow" variant="slide-up" />
|
||||
</th>
|
||||
<th>Coin</th>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">Volume</th>
|
||||
<th class="d-none d-xl-table-cell text-end">Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
markets.map((market) => (
|
||||
<tr>
|
||||
<td>
|
||||
<SwitchIcon icon="star" iconBColor="yellow" variant="slide-up" />
|
||||
</td>
|
||||
<td>{market.coin}</td>
|
||||
<td class="">{market.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{market.volume}</td>
|
||||
<td class="d-none d-xl-table-cell text-end">
|
||||
<Trending value={market.change as unknown as number} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-7">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">LTC/BTC</h5>
|
||||
<CardActions>
|
||||
<NavSegmented items={['1m', '5m', '30m', '1h', '1d']} name="timeframe" size="sm" default={2} />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Chart chartId="dashboard-crypto-candlestick" height={28} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-12 col-xxl-3">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Operations</h5>
|
||||
<CardActions>
|
||||
<NavSegmented items={['Buy', 'Sell', 'Send']} name="operations" size="sm" />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<div class="col-12 col-lg-7">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">LTC/BTC</h5>
|
||||
<CardActions>
|
||||
<NavSegmented items={['1m', '5m', '30m', '1h', '1d']} name="timeframe" size="sm" default={2} />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Chart chartId="dashboard-crypto-candlestick" height={28} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-12 col-xxl-3">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Operations</h5>
|
||||
<CardActions>
|
||||
<NavSegmented items={['Buy', 'Sell', 'Send']} name="operations" size="sm" />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<p>Place new order:</p>
|
||||
<CardBody>
|
||||
<p>Place new order:</p>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Amount</label>
|
||||
<select class="form-select">
|
||||
{
|
||||
operationCurrencies.map((currency, i) => (
|
||||
<option value={currency.symbol} selected={i === 0}>
|
||||
{currency.symbol}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
<input type="text" class="form-control" value="0.25" />
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Amount</label>
|
||||
<select class="form-select">
|
||||
{
|
||||
operationCurrencies.map((currency, i) => (
|
||||
<option value={currency.symbol} selected={i === 0}>
|
||||
{currency.symbol}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
<input type="text" class="form-control" value="0.25" />
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Price</label>
|
||||
<input type="text" class="form-control" readonly="" value="23,077.05" />
|
||||
<label class="input-group-text">$</label>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Price</label>
|
||||
<input type="text" class="form-control" readonly="" value="23,077.05" />
|
||||
<label class="input-group-text">$</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Total</label>
|
||||
<input type="text" class="form-control" readonly="" value="5,769.27" />
|
||||
<label class="input-group-text">$</label>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text">Total</label>
|
||||
<input type="text" class="form-control" readonly="" value="5,769.27" />
|
||||
<label class="input-group-text">$</label>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="button" class="btn btn-primary mb-3">Process to wallet</button>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="button" class="btn btn-primary mb-3">Process to wallet</button>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mb-0 small">The final amount could change depending on current market conditions.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<p class="text-muted mb-0 small">The final amount could change depending on current market conditions.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 col-xxl">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Sell Orders</h5>
|
||||
<CardActions>
|
||||
<button class="btn btn-sm">View all</button>
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">BTC</th>
|
||||
<th>Sum(BTC)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
orders.sell_orders.map((order) => (
|
||||
<tr>
|
||||
<td>{order.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{order.btc}</td>
|
||||
<td>{order.sum}</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xxl">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Sell Orders</h5>
|
||||
<CardActions>
|
||||
<button class="btn btn-sm">View all</button>
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">BTC</th>
|
||||
<th>Sum(BTC)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
orders.sell_orders.map((order) => (
|
||||
<tr>
|
||||
<td>{order.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{order.btc}</td>
|
||||
<td>{order.sum}</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 col-xxl">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Buy Orders</h5>
|
||||
<CardActions>
|
||||
<button class="btn btn-sm">View all</button>
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">BTC</th>
|
||||
<th>Sum(BTC)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
orders.buy_orders.map((order) => (
|
||||
<tr>
|
||||
<td>{order.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{order.btc}</td>
|
||||
<td>{order.sum}</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xxl">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="card-title mb-0">Buy Orders</h5>
|
||||
<CardActions>
|
||||
<button class="btn btn-sm">View all</button>
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<table class="table card-table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<th class="d-none d-xl-table-cell">BTC</th>
|
||||
<th>Sum(BTC)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
orders.buy_orders.map((order) => (
|
||||
<tr>
|
||||
<td>{order.price}</td>
|
||||
<td class="d-none d-xl-table-cell">{order.btc}</td>
|
||||
<td>{order.sum}</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+198
-191
@@ -1,208 +1,215 @@
|
||||
---
|
||||
import Subheader from '@ui/Subheader.astro'
|
||||
import ButtonGroup from '@ui/ButtonGroup.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import Offcanvas from '@ui/Offcanvas.astro'
|
||||
import Progress from '@ui/Progress.astro'
|
||||
import Modal from '@shared/components/modals/Modal.astro'
|
||||
import CaptureModal from '@shared/components/CaptureModal.astro'
|
||||
import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro'
|
||||
import mails from '@data/mails.json'
|
||||
---
|
||||
|
||||
import Subheader from '@ui/Subheader.astro';
|
||||
import ButtonGroup from '@ui/ButtonGroup.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import Offcanvas from '@ui/Offcanvas.astro';
|
||||
import Progress from '@ui/Progress.astro';
|
||||
import Modal from '@shared/components/modals/Modal.astro';
|
||||
import CaptureModal from '@shared/components/CaptureModal.astro';
|
||||
import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro';
|
||||
import mails from '@data/mails.json';
|
||||
---
|
||||
<DefaultLayout title="Email inbox" pageHeader="Inbox" pageMenu="extra.email-inbox" pageLibs={['hugerte']}>
|
||||
<Card>
|
||||
<div class="row g-0">
|
||||
<div class="col-xxl-3 email-border border-end">
|
||||
<Offcanvas size="xxl" direction="start" class="h-100 file-offcanvas" tabindex="-1" id="emailSidebaroffcanvas">
|
||||
<div class="card-body h-100">
|
||||
<div>
|
||||
<Button icon="pencil" text="Compose" color="primary" class="d-none d-sm-block" modalId="new-email" />
|
||||
</div>
|
||||
|
||||
<DefaultLayout
|
||||
title="Email inbox"
|
||||
pageHeader="Inbox"
|
||||
pageMenu="extra.email-inbox"
|
||||
pageLibs={['hugerte']}
|
||||
>
|
||||
<Card>
|
||||
<div class="row g-0">
|
||||
<div class="col-xxl-3 email-border border-end">
|
||||
<Offcanvas size="xxl" direction="start" class="h-100 file-offcanvas" tabindex="-1" id="emailSidebaroffcanvas">
|
||||
<div class="card-body h-100">
|
||||
<div>
|
||||
<Button icon="pencil" text="Compose" color="primary" class="d-none d-sm-block" modalId="new-email" />
|
||||
</div>
|
||||
<div class="mt-3 nav nav-vertical">
|
||||
<a href="#" class="nav-link text-danger fw-bold">
|
||||
<Icon name="inbox" class="me-2" />
|
||||
Inbox<span class="badge badge-danger ms-auto">{mails.length}</span>
|
||||
</a>
|
||||
<a href="#" class="nav-link"><Icon name="star" class="me-2" />Starred</a>
|
||||
<a href="#" class="nav-link"><Icon name="clock" class="me-2" />Snoozed</a>
|
||||
<a href="#" class="nav-link"><Icon name="file" class="me-2" />Draft<span class="badge badge-info ms-auto">32</span></a>
|
||||
<a href="#" class="nav-link"><Icon name="mail-up" class="me-2" />Sent Mail</a>
|
||||
<a href="#" class="nav-link"><Icon name="trash" class="me-2" />Trash</a>
|
||||
<a href="#" class="nav-link"><Icon name="tag" class="me-2" />Important</a>
|
||||
<a href="#" class="nav-link"><Icon name="alert-octagon" class="me-2" />Spam</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 nav nav-vertical">
|
||||
<a href="#" class="nav-link text-danger fw-bold">
|
||||
<Icon name="inbox" class="me-2" />
|
||||
Inbox<span class="badge badge-danger ms-auto">{mails.length}</span>
|
||||
</a>
|
||||
<a href="#" class="nav-link"><Icon name="star" class="me-2" />Starred</a>
|
||||
<a href="#" class="nav-link"><Icon name="clock" class="me-2" />Snoozed</a>
|
||||
<a href="#" class="nav-link "><Icon name="file" class="me-2" />Draft<span class="badge badge-info ms-auto">32</span></a>
|
||||
<a href="#" class="nav-link"><Icon name="mail-up" class="me-2" />Sent Mail</a>
|
||||
<a href="#" class="nav-link"><Icon name="trash" class="me-2" />Trash</a>
|
||||
<a href="#" class="nav-link"><Icon name="tag" class="me-2" />Important</a>
|
||||
<a href="#" class="nav-link"><Icon name="alert-octagon" class="me-2" />Spam</a>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<Subheader as="h6">Labels</Subheader>
|
||||
<div class="mt-2 nav nav-vertical">
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-info me-2"></div> Updates
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-warning me-2"></div> Friends
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-success me-2"></div> Family
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-primary me-2"></div> Social
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-danger me-2"></div> Important
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-purple me-2"></div> Promotions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Subheader as="h6">Labels</Subheader>
|
||||
<div class="mt-2 nav nav-vertical">
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-info me-2"></div> Updates
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-warning me-2"></div> Friends
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-success me-2"></div> Family
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-primary me-2"></div> Social
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-danger me-2"></div> Important
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-purple me-2"></div> Promotions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<Subheader as="h6">Storage</Subheader>
|
||||
<Progress value={46} class="my-2" />
|
||||
|
||||
<div class="mt-5">
|
||||
<Subheader as="h6">Storage</Subheader>
|
||||
<Progress value={46} class="my-2" />
|
||||
<p class="text-muted font-13 mb-0">7.02 GB (46%) of 15 GB used</p>
|
||||
</div>
|
||||
</div>
|
||||
</Offcanvas>
|
||||
</div>
|
||||
|
||||
<p class="text-muted font-13 mb-0">7.02 GB (46%) of 15 GB used</p>
|
||||
</div>
|
||||
<div class="col-xxl-9">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<div class="d-xxl-none d-inline-flex">
|
||||
<button class="btn btn-icon" type="button" data-bs-toggle="offcanvas" data-bs-target="#emailSidebaroffcanvas" aria-controls="emailSidebaroffcanvas">
|
||||
<Icon name="menu-2" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button icon="archive" iconOnly />
|
||||
<Button icon="alert-octagon" iconOnly />
|
||||
<Button icon="trash" iconOnly />
|
||||
</ButtonGroup>
|
||||
|
||||
</div>
|
||||
</Offcanvas>
|
||||
</div>
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="folder" />
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">Move to</span>
|
||||
<a class="dropdown-item" href="#">Social</a>
|
||||
<a class="dropdown-item" href="#">Promotions</a>
|
||||
<a class="dropdown-item" href="#">Updates</a>
|
||||
<a class="dropdown-item" href="#">Forums</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
|
||||
<div class="col-xxl-9">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<div class="d-xxl-none d-inline-flex">
|
||||
<button class="btn btn-icon" type="button" data-bs-toggle="offcanvas"
|
||||
data-bs-target="#emailSidebaroffcanvas" aria-controls="emailSidebaroffcanvas">
|
||||
<Icon name="menu-2" />
|
||||
</button>
|
||||
</div>
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="tag" />
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">Label as:</span>
|
||||
<a class="dropdown-item" href="#">Updates</a>
|
||||
<a class="dropdown-item" href="#">Social</a>
|
||||
<a class="dropdown-item" href="#">Promotions</a>
|
||||
<a class="dropdown-item" href="#">Forums</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button icon="archive" iconOnly />
|
||||
<Button icon="alert-octagon" iconOnly />
|
||||
<Button icon="trash" iconOnly />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="dots" /> More
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">More Options :</span>
|
||||
<a class="dropdown-item" href="#">Mark as Unread</a>
|
||||
<a class="dropdown-item" href="#">Add to Tasks</a>
|
||||
<a class="dropdown-item" href="#">Add Star</a>
|
||||
<a class="dropdown-item" href="#">Mute</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="folder" />
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">Move to</span>
|
||||
<a class="dropdown-item" href="#">Social</a>
|
||||
<a class="dropdown-item" href="#">Promotions</a>
|
||||
<a class="dropdown-item" href="#">Updates</a>
|
||||
<a class="dropdown-item" href="#">Forums</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
<div class="mt-3">
|
||||
<ul class="email-list">
|
||||
{
|
||||
mails && mails.length > 0 ? (
|
||||
mails.map((mail) => (
|
||||
<li>
|
||||
<div class="email-sender-info">
|
||||
<div class="checkbox-wrapper-mail">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id={`mail-${mail.id}`} />
|
||||
<label class="form-check-label" for={`mail-${mail.id}`} />
|
||||
</div>
|
||||
</div>
|
||||
<span class="star-toggle">
|
||||
<Icon name="star" />
|
||||
</span>
|
||||
<a href="#" class="email-title">
|
||||
{mail.sender}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="tag" />
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">Label as:</span>
|
||||
<a class="dropdown-item" href="#">Updates</a>
|
||||
<a class="dropdown-item" href="#">Social</a>
|
||||
<a class="dropdown-item" href="#">Promotions</a>
|
||||
<a class="dropdown-item" href="#">Forums</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
<div class="email-content">
|
||||
<a href="#" class="email-subject">
|
||||
{mail.subject} –
|
||||
<span>{mail.preview}</span>
|
||||
</a>
|
||||
<div class="email-date">{mail.date}</div>
|
||||
</div>
|
||||
|
||||
<ButtonGroup>
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="dots" /> More
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">More Options :</span>
|
||||
<a class="dropdown-item" href="#">Mark as Unread</a>
|
||||
<a class="dropdown-item" href="#">Add to Tasks</a>
|
||||
<a class="dropdown-item" href="#">Add Star</a>
|
||||
<a class="dropdown-item" href="#">Mute</a>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<div class="email-action-icons">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<Icon name="archive" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<Icon name="trash" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<Icon name="mail-opened" class="email-action-icons-item" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<Icon name="clock" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li class="text-muted">No emails</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<ul class="email-list">
|
||||
{mails && mails.length > 0 ? (
|
||||
mails.map((mail) => (
|
||||
<li>
|
||||
<div class="email-sender-info">
|
||||
<div class="checkbox-wrapper-mail">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id={`mail-${mail.id}`} />
|
||||
<label class="form-check-label" for={`mail-${mail.id}`}></label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="star-toggle"><Icon name="star" /></span>
|
||||
<a href="#" class="email-title">{mail.sender}</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-7 mt-1">
|
||||
Showing 1 - {mails.length} of {mails.length}
|
||||
</div>
|
||||
<!-- end col-->
|
||||
<div class="col-5">
|
||||
<ButtonGroup class="float-end">
|
||||
<button type="button" class="btn btn-icon"><Icon name="chevron-left" /></button>
|
||||
<button type="button" class="btn btn-icon"><Icon name="chevron-right" /></button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<!-- end col-->
|
||||
</div>
|
||||
<!-- end row-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div class="email-content">
|
||||
<a href="#" class="email-subject">{mail.subject} –
|
||||
<span>{mail.preview}</span>
|
||||
</a>
|
||||
<div class="email-date">{mail.date}</div>
|
||||
</div>
|
||||
|
||||
<div class="email-action-icons">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<a href="#"><Icon name="archive" /></a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#"><Icon name="trash" /></a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<Icon name="mail-opened" class="email-action-icons-item" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#"><Icon name="clock" /></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li class="text-muted">No emails</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-7 mt-1">
|
||||
Showing 1 - {mails.length} of {mails.length}
|
||||
</div> <!-- end col-->
|
||||
<div class="col-5">
|
||||
<ButtonGroup class="float-end">
|
||||
<button type="button" class="btn btn-icon"><Icon name="chevron-left" /></button>
|
||||
<button type="button" class="btn btn-icon"><Icon name="chevron-right" /></button>
|
||||
</ButtonGroup>
|
||||
</div> <!-- end col-->
|
||||
</div> <!-- end row-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<CaptureModal>
|
||||
<Modal modalId="new-email">
|
||||
<NewEmailModalContent />
|
||||
</Modal>
|
||||
</CaptureModal>
|
||||
<CaptureModal>
|
||||
<Modal modalId="new-email">
|
||||
<NewEmailModalContent />
|
||||
</Modal>
|
||||
</CaptureModal>
|
||||
</DefaultLayout>
|
||||
|
||||
+87
-96
@@ -1,107 +1,98 @@
|
||||
---
|
||||
// Masonry auto-inits from the data-masonry attribute (page-lib, no init script,
|
||||
// like cards-masonry.astro); fslightbox is a pure page-lib (no init script).
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import CardStamp from '@ui/CardStamp.astro';
|
||||
import Prose from '@ui/Prose.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import { site } from '@shared/lib/site';
|
||||
import emails from '@data/emails.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import CardStamp from '@ui/CardStamp.astro'
|
||||
import Prose from '@ui/Prose.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import { site } from '@shared/lib/site'
|
||||
import emails from '@data/emails.json'
|
||||
|
||||
const emailEntries = Object.entries(emails);
|
||||
const emailEntries = Object.entries(emails)
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
pageHeader="Email templates"
|
||||
pageMenu="addons.emails"
|
||||
pageLibs={['masonry', 'fslightbox']}
|
||||
>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card card-md">
|
||||
<CardStamp size="lg" icon="mail" color="primary" />
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-10">
|
||||
<h3 class="h1">Tabler Emails</h3>
|
||||
<Prose class="text-secondary fs-3">
|
||||
{emailEntries.length} eye-catching, customizable and responsive email templates to improve your email communication. No coding skills needed.
|
||||
</Prose>
|
||||
<div class="mt-3">
|
||||
<a href={site.emails.buy_link} class="btn btn-primary" target="_blank">Buy all emails for {site.emails.price}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards" data-masonry={'{"percentPosition": true }'}>
|
||||
{emailEntries.map(([key, email]) => (
|
||||
<div class="col-4">
|
||||
<a
|
||||
href={`./static/emails/${key}.jpg`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#email-modal"
|
||||
data-bs-title={email.descriptionShort}
|
||||
data-bs-description={email.descriptionLong}
|
||||
data-bs-image={`./static/emails/${key}.jpg`}
|
||||
>
|
||||
<img src={`./static/emails/${key}.jpg`} class="img-fluid rounded" alt={email.descriptionShort} width={email.width} height={email.height} />
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DefaultLayout pageHeader="Email templates" pageMenu="addons.emails" pageLibs={['masonry', 'fslightbox']}>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card card-md">
|
||||
<CardStamp size="lg" icon="mail" color="primary" />
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-10">
|
||||
<h3 class="h1">Tabler Emails</h3>
|
||||
<Prose class="text-secondary fs-3">
|
||||
{emailEntries.length} eye-catching, customizable and responsive email templates to improve your email communication. No coding skills needed.
|
||||
</Prose>
|
||||
<div class="mt-3">
|
||||
<a href={site.emails.buy_link} class="btn btn-primary" target="_blank">Buy all emails for {site.emails.price}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row row-cards" data-masonry={'{"percentPosition": true }'}>
|
||||
{
|
||||
emailEntries.map(([key, email]) => (
|
||||
<div class="col-4">
|
||||
<a href={`./static/emails/${key}.jpg`} data-bs-toggle="modal" data-bs-target="#email-modal" data-bs-title={email.descriptionShort} data-bs-description={email.descriptionLong} data-bs-image={`./static/emails/${key}.jpg`}>
|
||||
<img src={`./static/emails/${key}.jpg`} class="img-fluid rounded" alt={email.descriptionShort} width={email.width} height={email.height} />
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="email-modal" aria-hidden="true" aria-labelledby="email-modal-label" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<div class="modal-body p-0">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
<div class="p-6 bg-surface-secondary rounded-start">
|
||||
<img src="" class="img-fluid rounded-start" data-email-image />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-7">
|
||||
<Prose>
|
||||
<h3 data-email-title></h3>
|
||||
<p data-email-description></p>
|
||||
</Prose>
|
||||
<div class="modal fade" id="email-modal" aria-hidden="true" aria-labelledby="email-modal-label" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<div class="modal-body p-0">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
<div class="p-6 bg-surface-secondary rounded-start">
|
||||
<img src="" class="img-fluid rounded-start" data-email-image />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-7">
|
||||
<Prose>
|
||||
<h3 data-email-title></h3>
|
||||
<p data-email-description></p>
|
||||
</Prose>
|
||||
|
||||
<div class="mt-6">
|
||||
<a href={site.emails.buy_link} class="btn btn-primary w-100" target="_blank">Buy {site.emails.count} emails for {site.emails.price}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN EMAIL MODAL SCRIPT -->
|
||||
<script is:inline>
|
||||
const emailModal = document.getElementById('email-modal');
|
||||
if (emailModal) {
|
||||
emailModal.addEventListener('show.bs.modal', function (e) {
|
||||
const button = e.relatedTarget;
|
||||
if (!(button instanceof HTMLElement)) return;
|
||||
<div class="mt-6">
|
||||
<a href={site.emails.buy_link} class="btn btn-primary w-100" target="_blank">Buy {site.emails.count} emails for {site.emails.price}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN EMAIL MODAL SCRIPT -->
|
||||
<script is:inline>
|
||||
const emailModal = document.getElementById('email-modal')
|
||||
if (emailModal) {
|
||||
emailModal.addEventListener('show.bs.modal', function (e) {
|
||||
const button = e.relatedTarget
|
||||
if (!(button instanceof HTMLElement)) return
|
||||
|
||||
const image = button.getAttribute('data-bs-image'),
|
||||
title = button.getAttribute('data-bs-title'),
|
||||
description = button.getAttribute('data-bs-description');
|
||||
const image = button.getAttribute('data-bs-image'),
|
||||
title = button.getAttribute('data-bs-title'),
|
||||
description = button.getAttribute('data-bs-description')
|
||||
|
||||
emailModal.querySelector('[data-email-title]').textContent = title;
|
||||
emailModal.querySelector('[data-email-description]').textContent = description;
|
||||
emailModal.querySelector('[data-email-image]').src = image;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!-- END EMAIL MODAL SCRIPT -->
|
||||
</CaptureScript>
|
||||
emailModal.querySelector('[data-email-title]').textContent = title
|
||||
emailModal.querySelector('[data-email-description]').textContent = description
|
||||
emailModal.querySelector('[data-email-image]').src = image
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<!-- END EMAIL MODAL SCRIPT -->
|
||||
</CaptureScript>
|
||||
</DefaultLayout>
|
||||
|
||||
+25
-30
@@ -1,37 +1,32 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import flags from '@data/flags.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import flags from '@data/flags.json'
|
||||
|
||||
// 21 filler divs for flexbox layout.
|
||||
const fillers = Array.from({ length: 21 });
|
||||
const fillers = Array.from({ length: 21 })
|
||||
---
|
||||
|
||||
<DefaultLayout title="Flags" pageHeader="Flags" pageMenu="addons.flags">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">List of all flags</div>
|
||||
</CardHeader>
|
||||
<CardBody class="p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
flags.map((country) => (
|
||||
<span
|
||||
class="demo-icons-list-item"
|
||||
title={country.name}
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
>
|
||||
<span class={`flag flag-country-${country.flag.toLowerCase()}`} />
|
||||
</span>
|
||||
))
|
||||
}
|
||||
{fillers.map(() => <div></div>)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">List of all flags</div>
|
||||
</CardHeader>
|
||||
<CardBody class="p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
flags.map((country) => (
|
||||
<span class="demo-icons-list-item" title={country.name} data-bs-toggle="tooltip" data-bs-placement="top">
|
||||
<span class={`flag flag-country-${country.flag.toLowerCase()}`} />
|
||||
</span>
|
||||
))
|
||||
}
|
||||
{fillers.map(() => <div />)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</DefaultLayout>
|
||||
|
||||
+19
-25
@@ -1,32 +1,26 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import GalleryPhoto from '@shared/components/cards/GalleryPhoto.astro';
|
||||
import Pagination from '@ui/Pagination.astro';
|
||||
import photos from '@data/photos.json';
|
||||
import people from '@data/people.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import GalleryPhoto from '@shared/components/cards/GalleryPhoto.astro'
|
||||
import Pagination from '@ui/Pagination.astro'
|
||||
import photos from '@data/photos.json'
|
||||
import people from '@data/people.json'
|
||||
|
||||
// Horizontal photos, limit 15; person = people[loop index].
|
||||
const galleryPhotos = photos.filter((photo) => photo.horizontal).slice(0, 15);
|
||||
const galleryPhotos = photos.filter((photo) => photo.horizontal).slice(0, 15)
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Gallery"
|
||||
pageHeader="Gallery"
|
||||
description="1-15 of 241 photos"
|
||||
actions="photos"
|
||||
pageMenu="extra.gallery"
|
||||
>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
galleryPhotos.map((photo, index) => (
|
||||
<div class="col-sm-6 col-lg-4">
|
||||
<GalleryPhoto photo={photo} person={people[index]} index={index + 1} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<DefaultLayout title="Gallery" pageHeader="Gallery" description="1-15 of 241 photos" actions="photos" pageMenu="extra.gallery">
|
||||
<div class="row row-cards">
|
||||
{
|
||||
galleryPhotos.map((photo, index) => (
|
||||
<div class="col-sm-6 col-lg-4">
|
||||
<GalleryPhoto photo={photo} person={people[index]} index={index + 1} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="d-flex mt-5">
|
||||
<Pagination class="ms-auto" />
|
||||
</div>
|
||||
<div class="d-flex mt-5">
|
||||
<Pagination class="ms-auto" />
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+173
-187
@@ -1,207 +1,193 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import CardStamp from '@ui/CardStamp.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import Prose from '@ui/Prose.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import freeIllustrations from '@data/free-illustrations.json';
|
||||
import illustrationsList from '@data/illustrations.json';
|
||||
import siteData from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import CardStamp from '@ui/CardStamp.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import Prose from '@ui/Prose.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import freeIllustrations from '@data/free-illustrations.json'
|
||||
import illustrationsList from '@data/illustrations.json'
|
||||
import siteData from '@data/site.json'
|
||||
|
||||
const autodark = (freeIllustrations as { autodark: Record<string, string> }).autodark;
|
||||
const autodarkEntries = Object.entries(autodark);
|
||||
const autodark = (freeIllustrations as { autodark: Record<string, string> }).autodark
|
||||
const autodarkEntries = Object.entries(autodark)
|
||||
|
||||
// Last autodark entry (loop overwrites each pass).
|
||||
const firstIllustration = autodarkEntries.length
|
||||
? autodarkEntries[autodarkEntries.length - 1][1]
|
||||
: '';
|
||||
const firstIllustration = autodarkEntries.length ? autodarkEntries[autodarkEntries.length - 1][1] : ''
|
||||
|
||||
const withClass = (svg: string) => svg.replaceAll('<svg ', '<svg class="w-100 h-auto" ');
|
||||
const withClass = (svg: string) => svg.replaceAll('<svg ', '<svg class="w-100 h-auto" ')
|
||||
|
||||
const colors = siteData.colors as Record<string, { hex: string; class: string; prop: string }>;
|
||||
const skinColors = siteData.skinColors as Record<string, { hex: string; class: string }>;
|
||||
const colorEntries = Object.values(colors);
|
||||
const skinEntries = Object.values(skinColors);
|
||||
const colors = siteData.colors as Record<string, { hex: string; class: string; prop: string }>
|
||||
const skinColors = siteData.skinColors as Record<string, { hex: string; class: string }>
|
||||
const colorEntries = Object.values(colors)
|
||||
const skinEntries = Object.values(skinColors)
|
||||
|
||||
const skinFirst = skinEntries[0];
|
||||
const buyLink = (siteData.illustrations as { buy_link: string }).buy_link;
|
||||
const skinFirst = skinEntries[0]
|
||||
const buyLink = (siteData.illustrations as { buy_link: string }).buy_link
|
||||
|
||||
const moreCount = illustrationsList.length - 4;
|
||||
const moreCount = illustrationsList.length - 4
|
||||
|
||||
const illustrationsData = Object.fromEntries(
|
||||
autodarkEntries.map(([key, svg]) => [key, { svg: withClass(svg) }]),
|
||||
);
|
||||
const illustrationsData = Object.fromEntries(autodarkEntries.map(([key, svg]) => [key, { svg: withClass(svg) }]))
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="SVG Illustrations"
|
||||
pageHeader="SVG Illustrations"
|
||||
pageMenu="addons.illustrations"
|
||||
>
|
||||
<div
|
||||
class="mb-7"
|
||||
style={`--tblr-illustrations-primary: var(--tblr-color-primary); --tblr-illustrations-skin: ${skinFirst.hex};`}
|
||||
id="current-illustration-style"
|
||||
>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row row-cards row-deck g-4">
|
||||
<div class="col-md-7">
|
||||
<Card>
|
||||
<CardBody class="d-flex align-items-center">
|
||||
<div id="current-illustration" set:html={withClass(firstIllustration)} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div>
|
||||
<div class="form-label">Primary color</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value="var(--tblr-color-primary)" class="form-colorinput-input js-select-color" checked />
|
||||
<span class="form-colorinput-color bg-primary"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value="var(--tblr-bg-surface-inverted)" class="form-colorinput-input js-select-color" />
|
||||
<span class="form-colorinput-color bg-inverted"></span>
|
||||
</label>
|
||||
</div>
|
||||
{
|
||||
colorEntries.map((color) => (
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value={color.hex} class="form-colorinput-input js-select-color" />
|
||||
<span class={`form-colorinput-color bg-${color.class}`} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<DefaultLayout title="SVG Illustrations" pageHeader="SVG Illustrations" pageMenu="addons.illustrations">
|
||||
<div class="mb-7" style={`--tblr-illustrations-primary: var(--tblr-color-primary); --tblr-illustrations-skin: ${skinFirst.hex};`} id="current-illustration-style">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row row-cards row-deck g-4">
|
||||
<div class="col-md-7">
|
||||
<Card>
|
||||
<CardBody class="d-flex align-items-center">
|
||||
<div id="current-illustration" set:html={withClass(firstIllustration)} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div>
|
||||
<div class="form-label">Primary color</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value="var(--tblr-color-primary)" class="form-colorinput-input js-select-color" checked />
|
||||
<span class="form-colorinput-color bg-primary"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value="var(--tblr-bg-surface-inverted)" class="form-colorinput-input js-select-color" />
|
||||
<span class="form-colorinput-color bg-inverted"></span>
|
||||
</label>
|
||||
</div>
|
||||
{
|
||||
colorEntries.map((color) => (
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="color" type="radio" value={color.hex} class="form-colorinput-input js-select-color" />
|
||||
<span class={`form-colorinput-color bg-${color.class}`} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-label mt-4">Skin color</div>
|
||||
<div class="row g-2">
|
||||
{
|
||||
skinEntries.map((color, i) => (
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="skin-color" type="radio" value={color.hex} class="form-colorinput-input js-select-skin-color" checked={i === 0} />
|
||||
<span class="form-colorinput-color" style={`background-color: ${color.hex}`} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="form-label mt-4">Skin color</div>
|
||||
<div class="row g-2">
|
||||
{
|
||||
skinEntries.map((color, i) => (
|
||||
<div class="col-auto">
|
||||
<label class="form-colorinput">
|
||||
<input name="skin-color" type="radio" value={color.hex} class="form-colorinput-input js-select-skin-color" checked={i === 0} />
|
||||
<span class="form-colorinput-color" style={`background-color: ${color.hex}`} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-label mt-4">Select SVG illustration</div>
|
||||
<div class="row">
|
||||
{
|
||||
autodarkEntries.map(([key, svg], i) => (
|
||||
<div class="col-3">
|
||||
<label class="form-imagecheck mb-2">
|
||||
<input name="form-imagecheck" type="radio" value={key} class="form-imagecheck-input js-select-illustration" checked={i === autodarkEntries.length - 1} />
|
||||
<span class="form-imagecheck-figure" set:html={withClass(svg)} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-label mt-4">Select SVG illustration</div>
|
||||
<div class="row">
|
||||
{
|
||||
autodarkEntries.map(([key, svg], i) => (
|
||||
<div class="col-3">
|
||||
<label class="form-imagecheck mb-2">
|
||||
<input name="form-imagecheck" type="radio" value={key} class="form-imagecheck-input js-select-illustration" checked={i === autodarkEntries.length - 1} />
|
||||
<span class="form-imagecheck-figure" set:html={withClass(svg)} />
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="page-title my-5">
|
||||
{moreCount} more SVG Illustrations
|
||||
</h2>
|
||||
<h2 class="page-title my-5">
|
||||
{moreCount} more SVG Illustrations
|
||||
</h2>
|
||||
|
||||
<div class="row row-cards">
|
||||
<div class="col-lg-4">
|
||||
<Card size="md" class="sticky-top">
|
||||
<CardStamp size="lg" icon="brand-figma" color="primary" />
|
||||
<CardBody>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-10">
|
||||
<h3 class="h1">Tabler Illustrations</h3>
|
||||
<Prose class="text-secondary">
|
||||
Access a wide range of SVG illustrations for various projects. Effortlessly customize any illustration to align perfectly with your chosen color scheme!
|
||||
</Prose>
|
||||
<div class="mt-3">
|
||||
<a href={buyLink} class="btn btn-primary" target="_blank" rel="noopener">
|
||||
<Icon name="download" />
|
||||
Get lifetime access
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="row row-cards">
|
||||
{
|
||||
illustrationsList.map((illustration) => (
|
||||
<div class="col-6 col-md-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<a href={buyLink} target="_blank">
|
||||
<img src={`./static/illustrations/light/${illustration}.png`} alt={illustration} class="img-light" />
|
||||
<img src={`./static/illustrations/dark/${illustration}.png`} alt={illustration} class="img-dark" />
|
||||
</a>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN ILLUSTRATIONS SCRIPT -->
|
||||
<script is:inline define:vars={{ illustrationsData }}>
|
||||
// @ts-nocheck — illustrationsData is injected at runtime by define:vars, which
|
||||
// the type checker can't see since is:inline skips TS processing of this script.
|
||||
let skinColor = 'var()',
|
||||
primaryColor = 'var()';
|
||||
<div class="row row-cards">
|
||||
<div class="col-lg-4">
|
||||
<Card size="md" class="sticky-top">
|
||||
<CardStamp size="lg" icon="brand-figma" color="primary" />
|
||||
<CardBody>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-10">
|
||||
<h3 class="h1">Tabler Illustrations</h3>
|
||||
<Prose class="text-secondary"> Access a wide range of SVG illustrations for various projects. Effortlessly customize any illustration to align perfectly with your chosen color scheme! </Prose>
|
||||
<div class="mt-3">
|
||||
<a href={buyLink} class="btn btn-primary" target="_blank" rel="noopener">
|
||||
<Icon name="download" />
|
||||
Get lifetime access
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="row row-cards">
|
||||
{
|
||||
illustrationsList.map((illustration) => (
|
||||
<div class="col-6 col-md-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<a href={buyLink} target="_blank">
|
||||
<img src={`./static/illustrations/light/${illustration}.png`} alt={illustration} class="img-light" />
|
||||
<img src={`./static/illustrations/dark/${illustration}.png`} alt={illustration} class="img-dark" />
|
||||
</a>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN ILLUSTRATIONS SCRIPT -->
|
||||
<script is:inline define:vars={{ illustrationsData }}>
|
||||
// @ts-nocheck — illustrationsData is injected at runtime by define:vars, which
|
||||
// the type checker can't see since is:inline skips TS processing of this script.
|
||||
let skinColor = 'var()',
|
||||
primaryColor = 'var()'
|
||||
|
||||
const illustrations = illustrationsData;
|
||||
const currentIllustration = document.getElementById('current-illustration');
|
||||
const illustrations = illustrationsData
|
||||
const currentIllustration = document.getElementById('current-illustration')
|
||||
|
||||
document.querySelectorAll('.js-select-illustration').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
const selectedIllustration = illustrations[e.target.value];
|
||||
currentIllustration.innerHTML = selectedIllustration.svg;
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.js-select-illustration').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
const selectedIllustration = illustrations[e.target.value]
|
||||
currentIllustration.innerHTML = selectedIllustration.svg
|
||||
})
|
||||
})
|
||||
|
||||
document.querySelectorAll('.js-select-color').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
primaryColor = e.target.value;
|
||||
document.getElementById('current-illustration-style').style.setProperty('--tblr-illustrations-primary', primaryColor);
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.js-select-color').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
primaryColor = e.target.value
|
||||
document.getElementById('current-illustration-style').style.setProperty('--tblr-illustrations-primary', primaryColor)
|
||||
})
|
||||
})
|
||||
|
||||
document.querySelectorAll('.js-select-skin-color').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
skinColor = e.target.value;
|
||||
document.getElementById('current-illustration-style').style.setProperty('--tblr-illustrations-skin', skinColor);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- END ILLUSTRATIONS SCRIPT -->
|
||||
</CaptureScript>
|
||||
document.querySelectorAll('.js-select-skin-color').forEach((elem) => {
|
||||
elem.addEventListener('change', (e) => {
|
||||
skinColor = e.target.value
|
||||
document.getElementById('current-illustration-style').style.setProperty('--tblr-illustrations-skin', skinColor)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<!-- END ILLUSTRATIONS SCRIPT -->
|
||||
</CaptureScript>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,34 +1,29 @@
|
||||
---
|
||||
// One card per inline-player provider.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import InlinePlayer from '@ui/InlinePlayer.astro';
|
||||
import players from '@data/inline-players.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import InlinePlayer from '@ui/InlinePlayer.astro'
|
||||
import players from '@data/inline-players.json'
|
||||
|
||||
type Provider = { title: string; type: string; id: string; 'embed-id': string | number };
|
||||
type Provider = { 'title': string; 'type': string; 'id': string; 'embed-id': string | number }
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Inline Player"
|
||||
pageHeader="Inline Player"
|
||||
pageMenu="plugins.plyr"
|
||||
pageLibs={['plyr']}
|
||||
>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
(players as Provider[]).map((provider) => (
|
||||
<div class="col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>{provider.title}</CardTitle>
|
||||
<DefaultLayout title="Inline Player" pageHeader="Inline Player" pageMenu="plugins.plyr" pageLibs={['plyr']}>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
(players as Provider[]).map((provider) => (
|
||||
<div class="col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>{provider.title}</CardTitle>
|
||||
|
||||
<InlinePlayer id={provider.id} type={provider.type} embedId={provider['embed-id']} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<InlinePlayer id={provider.id} type={provider.type} embedId={provider['embed-id']} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+133
-135
@@ -1,151 +1,149 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Check from '@ui/form/Check.astro';
|
||||
import jobsData from '@data/jobs.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Check from '@ui/form/Check.astro'
|
||||
import jobsData from '@data/jobs.json'
|
||||
|
||||
interface Job {
|
||||
company: string;
|
||||
location: string;
|
||||
title: string;
|
||||
type: string;
|
||||
image: string;
|
||||
salary?: string;
|
||||
tags: string[];
|
||||
company: string
|
||||
location: string
|
||||
title: string
|
||||
type: string
|
||||
image: string
|
||||
salary?: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
const jobs = jobsData as Job[];
|
||||
const jobs = jobsData as Job[]
|
||||
|
||||
const types = ['Programming', 'Design', 'Management / Finance', 'Customer Support', 'Sales / Marketing'];
|
||||
const salaries = ['$20K - $50K', '$50K - $100K', '> $100K', 'Drawing / Painting'];
|
||||
const types = ['Programming', 'Design', 'Management / Finance', 'Customer Support', 'Sales / Marketing']
|
||||
const salaries = ['$20K - $50K', '$50K - $100K', '> $100K', 'Drawing / Painting']
|
||||
---
|
||||
|
||||
<DefaultLayout title="Search for Jobs" pageHeader="Search for Jobs" actions="add-job" pageMenu="extra.job-listing">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<form action="./" method="get" autocomplete="off" novalidate class="sticky-top">
|
||||
<div class="form-label">Job Types</div>
|
||||
<div class="mb-4">
|
||||
{
|
||||
types.map((type, i) => (
|
||||
<label class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="form-type[]" value={i + 1} checked={i + 1 <= 2} />
|
||||
<span class="form-check-label">{type}</span>
|
||||
</label>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<form action="./" method="get" autocomplete="off" novalidate class="sticky-top">
|
||||
<div class="form-label">Job Types</div>
|
||||
<div class="mb-4">
|
||||
{
|
||||
types.map((type, i) => (
|
||||
<label class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="form-type[]" value={i + 1} checked={i + 1 <= 2} />
|
||||
<span class="form-check-label">{type}</span>
|
||||
</label>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-label">Remote</div>
|
||||
<div class="mb-4">
|
||||
<Check switch={true} titleOn="On" titleOff="Off" />
|
||||
</div>
|
||||
<div class="form-label">Remote</div>
|
||||
<div class="mb-4">
|
||||
<Check switch={true} titleOn="On" titleOff="Off" />
|
||||
</div>
|
||||
|
||||
<div class="form-label">Salary Range</div>
|
||||
<div class="mb-4">
|
||||
{
|
||||
salaries.map((salary, i) => (
|
||||
<label class="form-check">
|
||||
<input type="radio" class="form-check-input" name="form-salary" value={i + 1} checked={i + 1 <= 2} />
|
||||
<span class="form-check-label">{salary}</span>
|
||||
</label>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="form-label">Salary Range</div>
|
||||
<div class="mb-4">
|
||||
{
|
||||
salaries.map((salary, i) => (
|
||||
<label class="form-check">
|
||||
<input type="radio" class="form-check-input" name="form-salary" value={i + 1} checked={i + 1 <= 2} />
|
||||
<span class="form-check-label">{salary}</span>
|
||||
</label>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-label">Immigration</div>
|
||||
<div class="mb-4">
|
||||
<Check switch={true} titleOn="On" titleOff="Off" />
|
||||
<div class="form-label">Immigration</div>
|
||||
<div class="mb-4">
|
||||
<Check switch={true} titleOn="On" titleOff="Off" />
|
||||
|
||||
<div class="small text-secondary">Only show companies that can sponsor a visa</div>
|
||||
</div>
|
||||
<div class="small text-secondary">Only show companies that can sponsor a visa</div>
|
||||
</div>
|
||||
|
||||
<label class="form-label" for="job-location">Location</label>
|
||||
<div class="mb-4">
|
||||
<select class="form-select" id="job-location" name="location">
|
||||
<option>Anywhere</option>
|
||||
<option>London</option>
|
||||
<option>San Francisco</option>
|
||||
<option>New York</option>
|
||||
<option>Berlin</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="form-label" for="job-location">Location</label>
|
||||
<div class="mb-4">
|
||||
<select class="form-select" id="job-location" name="location">
|
||||
<option>Anywhere</option>
|
||||
<option>London</option>
|
||||
<option>San Francisco</option>
|
||||
<option>New York</option>
|
||||
<option>Berlin</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<button class="btn btn-primary w-100">
|
||||
Confirm changes
|
||||
</button>
|
||||
<a href="#" class="btn btn-link w-100">
|
||||
Reset to defaults
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="row row-cards">
|
||||
<div class="space-y">
|
||||
{
|
||||
jobs.map((job) => (
|
||||
<Card>
|
||||
<div class="row g-0">
|
||||
<div class="col-auto">
|
||||
<div class="card-body">
|
||||
<Avatar src={`static/jobs/${job.image}`} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card-body ps-0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3 class="mb-0">
|
||||
<a href="#">{job.title}</a>
|
||||
</h3>
|
||||
</div>
|
||||
{job.salary && <div class="col-auto fs-3 text-green">{job.salary}</div>}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="mt-3 list-inline list-inline-dots mb-0 text-secondary d-sm-block d-none">
|
||||
<div class="list-inline-item">
|
||||
<Icon name="building-community" class="icon-inline" /> {job.company}
|
||||
</div>
|
||||
<div class="list-inline-item">
|
||||
<Icon name="license" class="icon-inline" /> {job.type}
|
||||
</div>
|
||||
<div class="list-inline-item">
|
||||
<Icon name="map-pin" class="icon-inline" /> {job.location}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 list mb-0 text-secondary d-block d-sm-none">
|
||||
<div class="list-item">
|
||||
<Icon name="building-community" class="icon-inline" /> {job.company}
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<Icon name="license" class="icon-inline" /> {job.type}
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<Icon name="map-pin" class="icon-inline" /> {job.location}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<div class="mt-3 badges">
|
||||
{job.tags.map((tag) => (
|
||||
<a href="#" class="badge badge-outline text-secondary fw-normal badge-pill">{tag}</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<button class="btn btn-primary w-100"> Confirm changes </button>
|
||||
<a href="#" class="btn btn-link w-100"> Reset to defaults </a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="row row-cards">
|
||||
<div class="space-y">
|
||||
{
|
||||
jobs.map((job) => (
|
||||
<Card>
|
||||
<div class="row g-0">
|
||||
<div class="col-auto">
|
||||
<div class="card-body">
|
||||
<Avatar src={`static/jobs/${job.image}`} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card-body ps-0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3 class="mb-0">
|
||||
<a href="#">{job.title}</a>
|
||||
</h3>
|
||||
</div>
|
||||
{job.salary && <div class="col-auto fs-3 text-green">{job.salary}</div>}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="mt-3 list-inline list-inline-dots mb-0 text-secondary d-sm-block d-none">
|
||||
<div class="list-inline-item">
|
||||
<Icon name="building-community" class="icon-inline" /> {job.company}
|
||||
</div>
|
||||
<div class="list-inline-item">
|
||||
<Icon name="license" class="icon-inline" /> {job.type}
|
||||
</div>
|
||||
<div class="list-inline-item">
|
||||
<Icon name="map-pin" class="icon-inline" /> {job.location}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 list mb-0 text-secondary d-block d-sm-none">
|
||||
<div class="list-item">
|
||||
<Icon name="building-community" class="icon-inline" /> {job.company}
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<Icon name="license" class="icon-inline" /> {job.type}
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<Icon name="map-pin" class="icon-inline" /> {job.location}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<div class="mt-3 badges">
|
||||
{job.tags.map((tag) => (
|
||||
<a href="#" class="badge badge-outline text-secondary fw-normal badge-pill">
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+54
-60
@@ -1,10 +1,10 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardFooter from '@ui/CardFooter.astro';
|
||||
import Prose from '@ui/Prose.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardFooter from '@ui/CardFooter.astro'
|
||||
import Prose from '@ui/Prose.astro'
|
||||
|
||||
const licenseHtml = `
|
||||
<p>This is a legal agreement between you, the Purchaser, and Tabler. Purchasing or downloading of any Tabler product (Tabler Free, Tabler PRO, Tabler Email), constitutes your acceptance of the terms of this license, <a href="https://tabler.io/terms-of-service.html">Tabler terms of service</a> and <a href="https://tabler.io/privacy-policy.html">Tabler private policy</a>.</p>
|
||||
@@ -31,66 +31,60 @@ const licenseHtml = `
|
||||
<li>You cannot add our source code to any open source repository.</li>
|
||||
<li>The source code may not be placed on any website in a complete or archived downloadable format.</li>
|
||||
</ol>
|
||||
`;
|
||||
`
|
||||
---
|
||||
|
||||
<DefaultLayout title="License" pageHeader="Tabler License" pageMenu="extra.license">
|
||||
<div class="row row-cards">
|
||||
<div class="col-lg-8">
|
||||
<Card size="lg">
|
||||
<CardBody>
|
||||
<Prose>
|
||||
<Fragment set:html={licenseHtml} />
|
||||
</Prose>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="me-3">
|
||||
<Icon name="scale" size="md" />
|
||||
</div>
|
||||
<div>
|
||||
<small class="text-secondary">tabler/tabler is licensed under the</small>
|
||||
<h3 class="lh-1">MIT License</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
<div class="col-lg-8">
|
||||
<Card size="lg">
|
||||
<CardBody>
|
||||
<Prose>
|
||||
<Fragment set:html={licenseHtml} />
|
||||
</Prose>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="me-3">
|
||||
<Icon name="scale" size="md" />
|
||||
</div>
|
||||
<div>
|
||||
<small class="text-secondary">tabler/tabler is licensed under the</small>
|
||||
<h3 class="lh-1">MIT License</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-secondary mb-3">
|
||||
A short and simple permissive license with conditions only requiring preservation of copyright and
|
||||
license notices. Licensed works, modifications, and larger works may be distributed under different terms
|
||||
and without source code.
|
||||
</div>
|
||||
<div class="text-secondary mb-3">A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.</div>
|
||||
|
||||
<h4>Permissions</h4>
|
||||
|
||||
<h4>Permissions</h4>
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="check" class="text-green" /> Commercial use</li>
|
||||
<li><Icon name="check" class="text-green" /> Modification</li>
|
||||
<li><Icon name="check" class="text-green" /> Distribution</li>
|
||||
<li><Icon name="check" class="text-green" /> Private use</li>
|
||||
</ul>
|
||||
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="check" class="text-green" /> Commercial use</li>
|
||||
<li><Icon name="check" class="text-green" /> Modification</li>
|
||||
<li><Icon name="check" class="text-green" /> Distribution</li>
|
||||
<li><Icon name="check" class="text-green" /> Private use</li>
|
||||
</ul>
|
||||
<h4>Limitations</h4>
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="x" class="text-red" /> Liability</li>
|
||||
<li><Icon name="x" class="text-red" /> Warranty</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Limitations</h4>
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="x" class="text-red" /> Liability</li>
|
||||
<li><Icon name="x" class="text-red" /> Warranty</li>
|
||||
</ul>
|
||||
|
||||
<h4>Conditions</h4>
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="info-circle" class="text-blue" /> License and copyright notice</li>
|
||||
</ul>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
This is not legal advice.
|
||||
<a href="#" target="_blank">Learn more about repository licenses.</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<h4>Conditions</h4>
|
||||
<ul class="list-unstyled space-y-1">
|
||||
<li><Icon name="info-circle" class="text-blue" /> License and copyright notice</li>
|
||||
</ul>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
This is not legal advice.
|
||||
<a href="#" target="_blank">Learn more about repository licenses.</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
---
|
||||
// Gallery uses horizontal photos only.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Photo from '@ui/Photo.astro';
|
||||
import photos from '@data/photos.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Photo from '@ui/Photo.astro'
|
||||
import photos from '@data/photos.json'
|
||||
|
||||
const filteredPhotos = photos.filter((photo) => photo.horizontal);
|
||||
const filteredPhotos = photos.filter((photo) => photo.horizontal)
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Lightbox"
|
||||
pageHeader="Lightbox"
|
||||
pageMenu="plugins.lightbox"
|
||||
pageLibs={['fslightbox']}
|
||||
>
|
||||
<div class="row row-cols-3 row-cols-md-4 row-cols-lg-6 g-3">
|
||||
{
|
||||
filteredPhotos.map((photo) => (
|
||||
<div class="col">
|
||||
<a data-fslightbox="gallery" href={`./static/photos/${photo.file}`}>
|
||||
<Photo photo={photo} class="rounded border" ratio="1x1" />
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<DefaultLayout title="Lightbox" pageHeader="Lightbox" pageMenu="plugins.lightbox" pageLibs={['fslightbox']}>
|
||||
<div class="row row-cols-3 row-cols-md-4 row-cols-lg-6 g-3">
|
||||
{
|
||||
filteredPhotos.map((photo) => (
|
||||
<div class="col">
|
||||
<a data-fslightbox="gallery" href={`./static/photos/${photo.file}`}>
|
||||
<Photo photo={photo} class="rounded border" ratio="1x1" />
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,37 +1,31 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
pageMenu="plugins.map-fullsize"
|
||||
pageLibs={['google-maps']}
|
||||
sidebar
|
||||
hideTopbar
|
||||
wrapperFull
|
||||
>
|
||||
<div class="map flex-fill" id="map-google"></div>
|
||||
<!--
|
||||
<DefaultLayout pageMenu="plugins.map-fullsize" pageLibs={['google-maps']} sidebar hideTopbar wrapperFull>
|
||||
<div class="map flex-fill" id="map-google"></div>
|
||||
<!--
|
||||
is:inline: google-maps loads via a deferred page-lib <script>; see
|
||||
FormElements1.astro for why a processed (module) script here would run too
|
||||
early and find window.google undefined.
|
||||
-->
|
||||
<CaptureScript>
|
||||
<!-- BEGIN MAP SCRIPT -->
|
||||
<script is:inline>
|
||||
window.tabler_map ??= {};
|
||||
<CaptureScript>
|
||||
<!-- BEGIN MAP SCRIPT -->
|
||||
<script is:inline>
|
||||
window.tabler_map ??= {}
|
||||
|
||||
function initMap() {
|
||||
const map = new google.maps.Map(document.getElementById('map-google'), {
|
||||
center: { lat: -34.397, lng: 150.644 },
|
||||
zoom: 8,
|
||||
});
|
||||
function initMap() {
|
||||
const map = new google.maps.Map(document.getElementById('map-google'), {
|
||||
center: { lat: -34.397, lng: 150.644 },
|
||||
zoom: 8,
|
||||
})
|
||||
|
||||
window.tabler_map['map-google'] = map;
|
||||
}
|
||||
window.tabler_map['map-google'] = map
|
||||
}
|
||||
|
||||
document.readyState !== 'loading' ? initMap() : document.addEventListener('DOMContentLoaded', initMap, { once: true });
|
||||
</script>
|
||||
<!-- END MAP SCRIPT -->
|
||||
</CaptureScript>
|
||||
document.readyState !== 'loading' ? initMap() : document.addEventListener('DOMContentLoaded', initMap, { once: true })
|
||||
</script>
|
||||
<!-- END MAP SCRIPT -->
|
||||
</CaptureScript>
|
||||
</DefaultLayout>
|
||||
|
||||
+29
-29
@@ -1,36 +1,36 @@
|
||||
---
|
||||
// Card maps span full-width column with no card-body; non-card maps get card-body with title.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Map from '@ui/Map.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import maps from '@data/maps.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Map from '@ui/Map.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import maps from '@data/maps.json'
|
||||
|
||||
type MapData = { title?: string; card?: boolean };
|
||||
const mapEntries = Object.entries(maps as Record<string, MapData>);
|
||||
type MapData = { title?: string; card?: boolean }
|
||||
const mapEntries = Object.entries(maps as Record<string, MapData>)
|
||||
---
|
||||
|
||||
<DefaultLayout title="Maps" pageHeader="Maps" pageMenu="plugins.maps" pageLibs={['mapbox']}>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
mapEntries.map(([mapId, data]) =>
|
||||
data.card ? (
|
||||
<div class="col-lg-12">
|
||||
<Card>
|
||||
<Map mapId={mapId} ratio="21x9" />
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<div class="col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="card-title">{data.title}</div>
|
||||
<Map mapId={mapId} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
mapEntries.map(([mapId, data]) =>
|
||||
data.card ? (
|
||||
<div class="col-lg-12">
|
||||
<Card>
|
||||
<Map mapId={mapId} ratio="21x9" />
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<div class="col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="card-title">{data.title}</div>
|
||||
<Map mapId={mapId} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import RedirectLayout from '@shared/layouts/RedirectLayout.astro';
|
||||
import RedirectLayout from '@shared/layouts/RedirectLayout.astro'
|
||||
---
|
||||
|
||||
<RedirectLayout />
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
import MarketingLayout from '@shared/layouts/MarketingLayout.astro';
|
||||
import Pricing from '@shared/components/marketing/sections/Pricing.astro';
|
||||
import PricingBanner from '@shared/components/marketing/sections/PricingBanner.astro';
|
||||
import Faq from '@shared/components/marketing/sections/Faq.astro';
|
||||
import MarketingLayout from '@shared/layouts/MarketingLayout.astro'
|
||||
import Pricing from '@shared/components/marketing/sections/Pricing.astro'
|
||||
import PricingBanner from '@shared/components/marketing/sections/PricingBanner.astro'
|
||||
import Faq from '@shared/components/marketing/sections/Faq.astro'
|
||||
---
|
||||
|
||||
<MarketingLayout title="Pricing">
|
||||
<header class="hero">
|
||||
<div class="container">
|
||||
<h2 class="hero-title">Simple, transparent pricing</h2>
|
||||
<p class="hero-description">Get early access to 100+ components and free updates every month. Make it yours today!</p>
|
||||
</div>
|
||||
</header>
|
||||
<header class="hero">
|
||||
<div class="container">
|
||||
<h2 class="hero-title">Simple, transparent pricing</h2>
|
||||
<p class="hero-description">Get early access to 100+ components and free updates every month. Make it yours today!</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<Pricing class="pt-0" />
|
||||
<PricingBanner />
|
||||
<Faq background="light" />
|
||||
<Pricing class="pt-0" />
|
||||
<PricingBanner />
|
||||
<Faq background="light" />
|
||||
</MarketingLayout>
|
||||
|
||||
+156
-161
@@ -1,169 +1,164 @@
|
||||
---
|
||||
// Every modal is rendered inline via ModalInline (in place, unlike Modal.astro
|
||||
// which drains to <body>).
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import ModalInline from '@shared/components/modals/ModalInline.astro';
|
||||
import SimpleModalContent from '@shared/components/modals/SimpleModalContent.astro';
|
||||
import LargeModalContent from '@shared/components/modals/LargeModalContent.astro';
|
||||
import SmallModalContent from '@shared/components/modals/SmallModalContent.astro';
|
||||
import FullWidthModalContent from '@shared/components/modals/FullWidthModalContent.astro';
|
||||
import ScrollableModalContent from '@shared/components/modals/ScrollableModalContent.astro';
|
||||
import ReportModalContent from '@shared/components/modals/ReportModalContent.astro';
|
||||
import SuccessModalContent from '@shared/components/modals/SuccessModalContent.astro';
|
||||
import DangerModalContent from '@shared/components/modals/DangerModalContent.astro';
|
||||
import TeamModalContent from '@shared/components/modals/TeamModalContent.astro';
|
||||
import SignatureModalContent from '@shared/components/modals/SignatureModalContent.astro';
|
||||
import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro';
|
||||
import NewEventModalContent from '@shared/components/modals/NewEventModalContent.astro';
|
||||
import NewTaskModalContent from '@shared/components/modals/NewTaskModalContent.astro';
|
||||
import EditProfileModalContent from '@shared/components/modals/EditProfileModalContent.astro';
|
||||
import ConfirmDeleteModalContent from '@shared/components/modals/ConfirmDeleteModalContent.astro';
|
||||
import ChangePasswordModalContent from '@shared/components/modals/ChangePasswordModalContent.astro';
|
||||
import AddTaskModalContent from '@shared/components/modals/AddTaskModalContent.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import ModalInline from '@shared/components/modals/ModalInline.astro'
|
||||
import SimpleModalContent from '@shared/components/modals/SimpleModalContent.astro'
|
||||
import LargeModalContent from '@shared/components/modals/LargeModalContent.astro'
|
||||
import SmallModalContent from '@shared/components/modals/SmallModalContent.astro'
|
||||
import FullWidthModalContent from '@shared/components/modals/FullWidthModalContent.astro'
|
||||
import ScrollableModalContent from '@shared/components/modals/ScrollableModalContent.astro'
|
||||
import ReportModalContent from '@shared/components/modals/ReportModalContent.astro'
|
||||
import SuccessModalContent from '@shared/components/modals/SuccessModalContent.astro'
|
||||
import DangerModalContent from '@shared/components/modals/DangerModalContent.astro'
|
||||
import TeamModalContent from '@shared/components/modals/TeamModalContent.astro'
|
||||
import SignatureModalContent from '@shared/components/modals/SignatureModalContent.astro'
|
||||
import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro'
|
||||
import NewEventModalContent from '@shared/components/modals/NewEventModalContent.astro'
|
||||
import NewTaskModalContent from '@shared/components/modals/NewTaskModalContent.astro'
|
||||
import EditProfileModalContent from '@shared/components/modals/EditProfileModalContent.astro'
|
||||
import ConfirmDeleteModalContent from '@shared/components/modals/ConfirmDeleteModalContent.astro'
|
||||
import ChangePasswordModalContent from '@shared/components/modals/ChangePasswordModalContent.astro'
|
||||
import AddTaskModalContent from '@shared/components/modals/AddTaskModalContent.astro'
|
||||
|
||||
const cardClass = 'position-relative rounded d-block bg-surface-backdrop py-6 w-auto h-auto z-0';
|
||||
const cardClass = 'position-relative rounded d-block bg-surface-backdrop py-6 w-auto h-auto z-0'
|
||||
// add-task bakes `show` into the class string (no `show` flag) → aria-hidden="true".
|
||||
const addTaskClass = 'position-relative rounded d-block show bg-surface-backdrop py-6 w-auto h-auto z-0';
|
||||
const addTaskClass = 'position-relative rounded d-block show bg-surface-backdrop py-6 w-auto h-auto z-0'
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Modals"
|
||||
pageHeader="Modals"
|
||||
pageMenu="base.modals"
|
||||
pageLibs={['signature_pad', 'hugerte', 'litepicker']}
|
||||
>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-5">
|
||||
<div class="d-none d-md-block col-3">
|
||||
<div class="nav nav-vertical sticky-top pt-4">
|
||||
<a href="#modal-simple" class="nav-link">Simple modal</a>
|
||||
<a href="#modal-large" class="nav-link">Large modal</a>
|
||||
<a href="#modal-small" class="nav-link">Small modal</a>
|
||||
<a href="#modal-full-width" class="nav-link">Full width modal</a>
|
||||
<a href="#modal-scrollable" class="nav-link">Scrollable modal</a>
|
||||
<a href="#modal-report" class="nav-link">Modal with form</a>
|
||||
<a href="#modal-success" class="nav-link">Success modal</a>
|
||||
<a href="#modal-danger" class="nav-link">Danger modal</a>
|
||||
<a href="#modal-team" class="nav-link">Modal with simple form</a>
|
||||
<a href="#modal-signature" class="nav-link">Modal with signature form</a>
|
||||
<a href="#modal-new-email" class="nav-link">New email modal</a>
|
||||
<a href="#modal-new-event" class="nav-link">New event modal</a>
|
||||
<a href="#modal-new-task" class="nav-link">New task modal</a>
|
||||
<a href="#modal-edit-profile" class="nav-link">Edit profile modal</a>
|
||||
<a href="#modal-confirm-delete" class="nav-link">Confirm delete modal</a>
|
||||
<a href="#modal-change-password" class="nav-link">Change password modal</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3>Simple modal</h3>
|
||||
<ModalInline class={cardClass} modalId="simple" show>
|
||||
<SimpleModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Large modal</h3>
|
||||
<ModalInline class={cardClass} modalId="large" size="lg" show>
|
||||
<LargeModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Small modal</h3>
|
||||
<ModalInline class={cardClass} modalId="small" size="sm" show>
|
||||
<SmallModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Full width modal</h3>
|
||||
<ModalInline class={cardClass} modalId="full-width" size="full-width" show>
|
||||
<FullWidthModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Scrollable modal</h3>
|
||||
<ModalInline class={cardClass} modalId="scrollable" scrollable style="max-height: 30rem" show>
|
||||
<ScrollableModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with form</h3>
|
||||
<ModalInline class={cardClass} modalId="report" size="lg" show>
|
||||
<ReportModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Success modal</h3>
|
||||
<ModalInline class={cardClass} modalId="success" size="sm" show>
|
||||
<SuccessModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Danger modal</h3>
|
||||
<ModalInline class={cardClass} modalId="danger" size="sm" show>
|
||||
<DangerModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with simple form</h3>
|
||||
<ModalInline class={cardClass} modalId="team" show>
|
||||
<TeamModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with signature form</h3>
|
||||
<ModalInline class={cardClass} modalId="signature" show>
|
||||
<SignatureModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New email modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-email" show>
|
||||
<NewEmailModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New event modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-event" show>
|
||||
<NewEventModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New task modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-task" show>
|
||||
<NewTaskModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Edit profile modal</h3>
|
||||
<ModalInline class={cardClass} modalId="edit-profile" size="lg" show>
|
||||
<EditProfileModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Confirm delete modal</h3>
|
||||
<ModalInline class={cardClass} modalId="confirm-delete" size="sm" show>
|
||||
<ConfirmDeleteModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Change password modal</h3>
|
||||
<ModalInline class={cardClass} modalId="change-password" show>
|
||||
<ChangePasswordModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Add task modal</h3>
|
||||
<ModalInline class={addTaskClass} modalId="add-task">
|
||||
<AddTaskModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<DefaultLayout title="Modals" pageHeader="Modals" pageMenu="base.modals" pageLibs={['signature_pad', 'hugerte', 'litepicker']}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row g-5">
|
||||
<div class="d-none d-md-block col-3">
|
||||
<div class="nav nav-vertical sticky-top pt-4">
|
||||
<a href="#modal-simple" class="nav-link">Simple modal</a>
|
||||
<a href="#modal-large" class="nav-link">Large modal</a>
|
||||
<a href="#modal-small" class="nav-link">Small modal</a>
|
||||
<a href="#modal-full-width" class="nav-link">Full width modal</a>
|
||||
<a href="#modal-scrollable" class="nav-link">Scrollable modal</a>
|
||||
<a href="#modal-report" class="nav-link">Modal with form</a>
|
||||
<a href="#modal-success" class="nav-link">Success modal</a>
|
||||
<a href="#modal-danger" class="nav-link">Danger modal</a>
|
||||
<a href="#modal-team" class="nav-link">Modal with simple form</a>
|
||||
<a href="#modal-signature" class="nav-link">Modal with signature form</a>
|
||||
<a href="#modal-new-email" class="nav-link">New email modal</a>
|
||||
<a href="#modal-new-event" class="nav-link">New event modal</a>
|
||||
<a href="#modal-new-task" class="nav-link">New task modal</a>
|
||||
<a href="#modal-edit-profile" class="nav-link">Edit profile modal</a>
|
||||
<a href="#modal-confirm-delete" class="nav-link">Confirm delete modal</a>
|
||||
<a href="#modal-change-password" class="nav-link">Change password modal</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3>Simple modal</h3>
|
||||
<ModalInline class={cardClass} modalId="simple" show>
|
||||
<SimpleModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Large modal</h3>
|
||||
<ModalInline class={cardClass} modalId="large" size="lg" show>
|
||||
<LargeModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Small modal</h3>
|
||||
<ModalInline class={cardClass} modalId="small" size="sm" show>
|
||||
<SmallModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Full width modal</h3>
|
||||
<ModalInline class={cardClass} modalId="full-width" size="full-width" show>
|
||||
<FullWidthModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Scrollable modal</h3>
|
||||
<ModalInline class={cardClass} modalId="scrollable" scrollable style="max-height: 30rem" show>
|
||||
<ScrollableModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with form</h3>
|
||||
<ModalInline class={cardClass} modalId="report" size="lg" show>
|
||||
<ReportModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Success modal</h3>
|
||||
<ModalInline class={cardClass} modalId="success" size="sm" show>
|
||||
<SuccessModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Danger modal</h3>
|
||||
<ModalInline class={cardClass} modalId="danger" size="sm" show>
|
||||
<DangerModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with simple form</h3>
|
||||
<ModalInline class={cardClass} modalId="team" show>
|
||||
<TeamModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Modal with signature form</h3>
|
||||
<ModalInline class={cardClass} modalId="signature" show>
|
||||
<SignatureModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New email modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-email" show>
|
||||
<NewEmailModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New event modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-event" show>
|
||||
<NewEventModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>New task modal</h3>
|
||||
<ModalInline class={cardClass} modalId="new-task" show>
|
||||
<NewTaskModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Edit profile modal</h3>
|
||||
<ModalInline class={cardClass} modalId="edit-profile" size="lg" show>
|
||||
<EditProfileModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Confirm delete modal</h3>
|
||||
<ModalInline class={cardClass} modalId="confirm-delete" size="sm" show>
|
||||
<ConfirmDeleteModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Change password modal</h3>
|
||||
<ModalInline class={cardClass} modalId="change-password" show>
|
||||
<ChangePasswordModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Add task modal</h3>
|
||||
<ModalInline class={addTaskClass} modalId="add-task">
|
||||
<AddTaskModalContent />
|
||||
</ModalInline>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</DefaultLayout>
|
||||
|
||||
+21
-21
@@ -1,28 +1,28 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import TracksList from '@shared/components/cards/music/TracksList.astro';
|
||||
import TrackInfo from '@shared/components/cards/music/TrackInfo.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import TracksList from '@shared/components/cards/music/TracksList.astro'
|
||||
import TrackInfo from '@shared/components/cards/music/TrackInfo.astro'
|
||||
|
||||
const topTrackIds = [8, 9, 10, 11, 12, 13];
|
||||
const topTrackIds = [8, 9, 10, 11, 12, 13]
|
||||
---
|
||||
|
||||
<DefaultLayout title="Music components" pageHeader="Music components" pageMenu="extra.music">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<TracksList />
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<h3 class="mb-3">Top tracks</h3>
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<TracksList />
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<h3 class="mb-3">Top tracks</h3>
|
||||
|
||||
<div class="row row-cards">
|
||||
{
|
||||
topTrackIds.map((trackId) => (
|
||||
<div class="col-md-6 col-lg-12">
|
||||
<TrackInfo trackId={trackId} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
topTrackIds.map((trackId) => (
|
||||
<div class="col-md-6 col-lg-12">
|
||||
<TrackInfo trackId={trackId} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -2,48 +2,38 @@
|
||||
// with variant params, rendered with the shared Navbar component.
|
||||
//
|
||||
// fluid-search (variant 5) is a no-op — search block in condensed branch is dead code.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Navbar from '@shared/components/navbar/Navbar.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Navbar from '@shared/components/navbar/Navbar.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Navigation" pageHeader="Navigation" pageMenu="base.navigation">
|
||||
<div class="box">
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed transparent personId={3} />
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed transparent personId={3} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed personId={4} />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed personId={4} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed dark hideLogo showTitle hideIcons hideUsername personId={5} />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed dark hideLogo showTitle hideIcons hideUsername personId={5} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed dark smallLogo backgroundColor="#7952b3" hideSearch personId={6} />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed dark smallLogo backgroundColor="#7952b3" hideSearch personId={6} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar
|
||||
sample
|
||||
condensed
|
||||
dark
|
||||
background="primary"
|
||||
hideBrand
|
||||
hideIcons
|
||||
fluidSearch
|
||||
hideUsername
|
||||
personId={7}
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample condensed dark background="primary" hideBrand hideIcons fluidSearch hideUsername personId={7} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar sample smallLogo showTitle personId={8} />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample smallLogo showTitle personId={8} />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<Navbar sample dark personId={9} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<Navbar sample dark personId={9} />
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+118
-118
@@ -1,126 +1,126 @@
|
||||
---
|
||||
// NOTE: `current=2` passed to ProgressSteps is a no-op (reads `active`, not `current`) — only step 1 is active.
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro';
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro';
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro'
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro'
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
---
|
||||
|
||||
<BaseLayout title="Onboarding">
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="row w-100 align-items-center">
|
||||
<div class="col me-auto">
|
||||
<NavbarLogo class="logo-gray" />
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<ProgressSteps count={5} />
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<a href="." class="btn btn-ghost">Skip<span class="d-none d-md-inline"> to dashboard</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="row w-100 align-items-center">
|
||||
<div class="col me-auto">
|
||||
<NavbarLogo class="logo-gray" />
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<ProgressSteps count={5} />
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<a href="." class="btn btn-ghost">Skip<span class="d-none d-md-inline"> to dashboard</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="py-5">
|
||||
<div class="container container-tight">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title"> Let's set up your account </h1>
|
||||
</div>
|
||||
<div class="card mt-5">
|
||||
<div class="card-body space-y-4">
|
||||
<FormGroup label="Full name" class="">
|
||||
<input type="text" class="form-control" placeholder="Enter your full name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Company name" class="">
|
||||
<input type="text" class="form-control" placeholder="Enter your company name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Role" class="">
|
||||
<select class="form-select">
|
||||
<option value="">Select your role</option>
|
||||
<option value="developer">Developer</option>
|
||||
<option value="designer">Designer</option>
|
||||
<option value="manager">Manager</option>
|
||||
<option value="founder">Founder</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Team size" class="">
|
||||
<div class="form-selectgroup">
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="1" class="form-selectgroup-input" checked />
|
||||
<span class="form-selectgroup-label">Just me</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="2-10" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">2-10 people</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="11-50" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">11-50 people</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="50+" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">50+ people</span>
|
||||
</label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="What are you planning to use this for?" class="">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="personal" id="use-personal" checked />
|
||||
<label class="form-check-label" for="use-personal"> Personal projects </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="business" id="use-business" />
|
||||
<label class="form-check-label" for="use-business"> Business applications </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="client" id="use-client" />
|
||||
<label class="form-check-label" for="use-client"> Client work </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="learning" id="use-learning" />
|
||||
<label class="form-check-label" for="use-learning"> Learning and experimentation </label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="How did you hear about us?" class="">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="search" id="ref-search" />
|
||||
<label class="form-check-label" for="ref-search"> Search engine </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="social" id="ref-social" />
|
||||
<label class="form-check-label" for="ref-social"> Social media </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="friend" id="ref-friend" />
|
||||
<label class="form-check-label" for="ref-friend"> Friend or colleague </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="blog" id="ref-blog" />
|
||||
<label class="form-check-label" for="ref-blog"> Blog or article </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="other" id="ref-other" />
|
||||
<label class="form-check-label" for="ref-other"> Other </label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="notifications" checked />
|
||||
<label class="form-check-label" for="notifications"> Send me product updates and tips via email </label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main class="py-5">
|
||||
<div class="container container-tight">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Let's set up your account</h1>
|
||||
</div>
|
||||
<div class="card mt-5">
|
||||
<div class="card-body space-y-4">
|
||||
<FormGroup label="Full name" class="">
|
||||
<input type="text" class="form-control" placeholder="Enter your full name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Company name" class="">
|
||||
<input type="text" class="form-control" placeholder="Enter your company name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Role" class="">
|
||||
<select class="form-select">
|
||||
<option value="">Select your role</option>
|
||||
<option value="developer">Developer</option>
|
||||
<option value="designer">Designer</option>
|
||||
<option value="manager">Manager</option>
|
||||
<option value="founder">Founder</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Team size" class="">
|
||||
<div class="form-selectgroup">
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="1" class="form-selectgroup-input" checked />
|
||||
<span class="form-selectgroup-label">Just me</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="2-10" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">2-10 people</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="11-50" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">11-50 people</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="team-size" value="50+" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">50+ people</span>
|
||||
</label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="What are you planning to use this for?" class="">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="personal" id="use-personal" checked />
|
||||
<label class="form-check-label" for="use-personal"> Personal projects </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="business" id="use-business" />
|
||||
<label class="form-check-label" for="use-business"> Business applications </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="client" id="use-client" />
|
||||
<label class="form-check-label" for="use-client"> Client work </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" value="learning" id="use-learning" />
|
||||
<label class="form-check-label" for="use-learning"> Learning and experimentation </label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="How did you hear about us?" class="">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="search" id="ref-search" />
|
||||
<label class="form-check-label" for="ref-search"> Search engine </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="social" id="ref-social" />
|
||||
<label class="form-check-label" for="ref-social"> Social media </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="friend" id="ref-friend" />
|
||||
<label class="form-check-label" for="ref-friend"> Friend or colleague </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="blog" id="ref-blog" />
|
||||
<label class="form-check-label" for="ref-blog"> Blog or article </label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="referral" value="other" id="ref-other" />
|
||||
<label class="form-check-label" for="ref-other"> Other </label>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="notifications" checked />
|
||||
<label class="form-check-label" for="notifications"> Send me product updates and tips via email </label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ButtonList class="justify-content-between mt-4">
|
||||
<Button text="Back" color="link link-secondary" />
|
||||
<Button text="Continue" color="primary" />
|
||||
</ButtonList>
|
||||
</div>
|
||||
</main>
|
||||
<ButtonList class="justify-content-between mt-4">
|
||||
<Button text="Back" color="link link-secondary" />
|
||||
<Button text="Continue" color="primary" />
|
||||
</ButtonList>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
|
||||
+76
-119
@@ -1,133 +1,90 @@
|
||||
---
|
||||
// tabler-payments is not in core/libs.json — only imask emits a script tag.
|
||||
import PayLayout from '@shared/layouts/PayLayout.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Payment from '@ui/Payment.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import PayLayout from '@shared/layouts/PayLayout.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Payment from '@ui/Payment.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
---
|
||||
|
||||
<PayLayout title="Pay" pageLibs={['tabler-payments', 'imask']}>
|
||||
<div class="bg-dark bg-cover" style="background-image: url(./static/bg-cover.jpg)">
|
||||
<div class="bg-dark bg-opacity-75 pt-5 bg-blur">
|
||||
<div class="container container-tight bg-overlay">
|
||||
<img src="./static/bg-cover.jpg" alt="" class="img-fluid rounded-lg rounded-top" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-dark bg-cover" style="background-image: url(./static/bg-cover.jpg)">
|
||||
<div class="bg-dark bg-opacity-75 pt-5 bg-blur">
|
||||
<div class="container container-tight bg-overlay">
|
||||
<img src="./static/bg-cover.jpg" alt="" class="img-fluid rounded-lg rounded-top" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container container-tight">
|
||||
<div class="card rounded-top-0 border-top-0">
|
||||
<div class="card-body pt-0">
|
||||
<div class="mb-3 text-center">
|
||||
<Avatar personId={1} size="xl" class="avatar-cover rounded-circle mb-3" />
|
||||
<div class="mb-4">
|
||||
<h2 class="h2">Front-End Learning</h2>
|
||||
<div class="text-secondary">
|
||||
Learn to build web apps with HTML & CSS. Get started quickly with included templates.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container container-tight">
|
||||
<div class="card rounded-top-0 border-top-0">
|
||||
<div class="card-body pt-0">
|
||||
<div class="mb-3 text-center">
|
||||
<Avatar personId={1} size="xl" class="avatar-cover rounded-circle mb-3" />
|
||||
<div class="mb-4">
|
||||
<h2 class="h2">Front-End Learning</h2>
|
||||
<div class="text-secondary">Learn to build web apps with HTML & CSS. Get started quickly with included templates.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav nav-segmented w-100 mb-4" role="tablist">
|
||||
<button data-bs-target="#tab-card" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="tab-card" aria-selected="true">
|
||||
<Icon name="credit-card" />
|
||||
<span>Pay With Card</span>
|
||||
</button>
|
||||
<button data-bs-target="#tab-paypal" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="tab-paypal" aria-selected="false">
|
||||
<Icon name="brand-paypal" />
|
||||
<span>Pay With PayPal</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<nav class="nav nav-segmented w-100 mb-4" role="tablist">
|
||||
<button
|
||||
data-bs-target="#tab-card"
|
||||
class="nav-link active"
|
||||
data-bs-toggle="tab"
|
||||
role="tab"
|
||||
aria-controls="tab-card"
|
||||
aria-selected="true"
|
||||
>
|
||||
<Icon name="credit-card" />
|
||||
<span>Pay With Card</span>
|
||||
</button>
|
||||
<button
|
||||
data-bs-target="#tab-paypal"
|
||||
class="nav-link"
|
||||
data-bs-toggle="tab"
|
||||
role="tab"
|
||||
aria-controls="tab-paypal"
|
||||
aria-selected="false"
|
||||
>
|
||||
<Icon name="brand-paypal" />
|
||||
<span>Pay With PayPal</span>
|
||||
</button>
|
||||
</nav>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active show" id="tab-card" role="tabpanel">
|
||||
<form>
|
||||
<div class="space-y">
|
||||
<FormGroup label="Card Number" for="card-number" class="">
|
||||
<div class="input-group input-group-flat">
|
||||
<span class="input-group-text">
|
||||
<Payment payment="visa" size="xs" />
|
||||
</span>
|
||||
<input type="text" class="form-control" placeholder="0000 0000 0000 0000" autocomplete="off" id="card-number-addon" />
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active show" id="tab-card" role="tabpanel">
|
||||
<form>
|
||||
<div class="space-y">
|
||||
<FormGroup label="Card Number" for="card-number" class="">
|
||||
<div class="input-group input-group-flat">
|
||||
<span class="input-group-text">
|
||||
<Payment payment="visa" size="xs" />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="0000 0000 0000 0000"
|
||||
autocomplete="off"
|
||||
id="card-number-addon"
|
||||
/>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<div class="row g-3">
|
||||
<FormGroup label="Expiry Date" for="card-expiry" class="col-sm-6">
|
||||
<input type="text" class="form-control" id="card-expiry" placeholder="MM/YY" inputmode="numeric" aria-required="true" maxlength="5" />
|
||||
</FormGroup>
|
||||
<FormGroup label="CVC" for="card-cvc" class="col-sm-6">
|
||||
<input type="text" class="form-control" id="card-cvc" placeholder="CVC" inputmode="numeric" aria-required="true" maxlength="3" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<FormGroup label="Expiry Date" for="card-expiry" class="col-sm-6">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="card-expiry"
|
||||
placeholder="MM/YY"
|
||||
inputmode="numeric"
|
||||
aria-required="true"
|
||||
maxlength="5"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup label="CVC" for="card-cvc" class="col-sm-6">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="card-cvc"
|
||||
placeholder="CVC"
|
||||
inputmode="numeric"
|
||||
aria-required="true"
|
||||
maxlength="3"
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<FormGroup label="Name on Card" for="card-name" class="">
|
||||
<input type="text" class="form-control" id="card-name" placeholder="Full name" aria-required="true" />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Name on Card" for="card-name" class="">
|
||||
<input type="text" class="form-control" id="card-name" placeholder="Full name" aria-required="true" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Email" for="card-email" class="">
|
||||
<input type="email" class="form-control" id="card-email" placeholder="you@example.com" aria-required="true" />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Email" for="card-email" class="">
|
||||
<input type="email" class="form-control" id="card-email" placeholder="you@example.com" aria-required="true" />
|
||||
</FormGroup>
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary w-100"> Pay $253 </button>
|
||||
<div class="text-secondary text-center small mt-3">You'll be charged $253, including $48 for VAT in Poland</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary w-100"> Pay $253 </button>
|
||||
<div class="text-secondary text-center small mt-3">
|
||||
You'll be charged $253, including $48 for VAT in Poland
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab-paypal" role="tabpanel">
|
||||
<button type="button" class="btn btn-primary w-100">
|
||||
<Icon name="brand-paypal" />
|
||||
<span class="ms-2">Pay with PayPal - $253</span>
|
||||
</button>
|
||||
<div class="text-secondary text-center small mt-3">
|
||||
You'll be charged $253, including $48 for VAT in Poland
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab-paypal" role="tabpanel">
|
||||
<button type="button" class="btn btn-primary w-100">
|
||||
<Icon name="brand-paypal" />
|
||||
<span class="ms-2">Pay with PayPal - $253</span>
|
||||
</button>
|
||||
<div class="text-secondary text-center small mt-3">You'll be charged $253, including $48 for VAT in Poland</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PayLayout>
|
||||
|
||||
@@ -1,324 +1,324 @@
|
||||
---
|
||||
import ButtonGroup from '@ui/ButtonGroup.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import Badge from '@ui/Badge.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import AvatarList from '@ui/AvatarList.astro';
|
||||
import Pagination from '@ui/Pagination.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import ButtonGroup from '@ui/ButtonGroup.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import Badge from '@ui/Badge.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import AvatarList from '@ui/AvatarList.astro'
|
||||
import Pagination from '@ui/Pagination.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Categories playground">
|
||||
<!-- Header -->
|
||||
<div class="row align-items-center gy-3 mb-5">
|
||||
<div class="col">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-1">
|
||||
<li class="breadcrumb-item">
|
||||
<a class="text-secondary" href="#">
|
||||
<Icon name="arrow-left" class="icon-inline me-1" />
|
||||
Back
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h1 class="fs-3 mb-0">Categories</h1>
|
||||
</div>
|
||||
<div class="col-12 col-lg-auto order-last order-lg-0">
|
||||
<div class="row align-items-center gx-3">
|
||||
<div class="col col-lg-auto">
|
||||
<div class="input-group input-group-flat rounded-pill">
|
||||
<input type="text" class="form-control" placeholder="Search" aria-label="Search" aria-describedby="playground-categories-search">
|
||||
<span class="input-group-text" id="playground-categories-search">
|
||||
<Icon name="search" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="dropdown">
|
||||
<Button element="button" type="button" icon="filter" iconOnly text="Open filters" />
|
||||
<div class="dropdown-menu rounded-3 p-6">
|
||||
<h4 class="fs-lg mb-4">Filter</h4>
|
||||
<form id="playgroundCategoriesFilterForm" style="width: 350px">
|
||||
<div class="row align-items-center mb-3">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterUser">User</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterUser" aria-label="User">
|
||||
<option value="Emily Thompson" selected>Emily Thompson</option>
|
||||
<option value="Michael Johnson">Michael Johnson</option>
|
||||
<option value="Robert Garcia">Robert Garcia</option>
|
||||
<option value="Jessica Miller">Jessica Miller</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center mb-3">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterCompany">Company</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterCompany" aria-label="Company">
|
||||
<option value="TechPinnacle Solutions" selected>TechPinnacle Solutions</option>
|
||||
<option value="Quantum Dynamics">Quantum Dynamics</option>
|
||||
<option value="Pinnacle Technologies">Pinnacle Technologies</option>
|
||||
<option value="Apex Innovations">Apex Innovations</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterLocation">Location</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterLocation" aria-label="Location">
|
||||
<option value="San Francisco, CA" selected>San Francisco, CA</option>
|
||||
<option value="Austin, TX">Austin, TX</option>
|
||||
<option value="Miami, FL">Miami, FL</option>
|
||||
<option value="Seattle, WA">Seattle, WA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto ms-n2">
|
||||
<div class="dropdown">
|
||||
<Button element="button" type="button" icon="sort-ascending-letters" iconOnly text="Open sort options" />
|
||||
<div class="dropdown-menu rounded-3 p-6">
|
||||
<h4 class="fs-lg mb-4">Sort</h4>
|
||||
<form id="playgroundCategoriesSortForm" style="width: 350px">
|
||||
<div class="row gx-3">
|
||||
<div class="col">
|
||||
<select class="form-select" id="playgroundSortField" aria-label="Sort by">
|
||||
<option value="user" selected>User</option>
|
||||
<option value="company">Company</option>
|
||||
<option value="phone">Phone</option>
|
||||
<option value="location">Location</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<ButtonGroup aria-label="Sort direction">
|
||||
<input type="radio" class="btn-check" name="playgroundSortDir" id="playgroundSortAsc" autocomplete="off" checked>
|
||||
<label class="btn btn-light px-0 btn-icon" aria-label="Ascending"><Icon name="arrow-up" /></label>
|
||||
<input type="radio" class="btn-check" name="playgroundSortDir" id="playgroundSortDesc" autocomplete="off">
|
||||
<label class="btn btn-light px-0 btn-icon" aria-label="Descending"><Icon name="arrow-down" /></label>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" type="button">New category</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Header -->
|
||||
<div class="row align-items-center gy-3 mb-5">
|
||||
<div class="col">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-1">
|
||||
<li class="breadcrumb-item">
|
||||
<a class="text-secondary" href="#">
|
||||
<Icon name="arrow-left" class="icon-inline me-1" />
|
||||
Back
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h1 class="fs-3 mb-0">Categories</h1>
|
||||
</div>
|
||||
<div class="col-12 col-lg-auto order-last order-lg-0">
|
||||
<div class="row align-items-center gx-3">
|
||||
<div class="col col-lg-auto">
|
||||
<div class="input-group input-group-flat rounded-pill">
|
||||
<input type="text" class="form-control" placeholder="Search" aria-label="Search" aria-describedby="playground-categories-search" />
|
||||
<span class="input-group-text" id="playground-categories-search">
|
||||
<Icon name="search" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="dropdown">
|
||||
<Button element="button" type="button" icon="filter" iconOnly text="Open filters" />
|
||||
<div class="dropdown-menu rounded-3 p-6">
|
||||
<h4 class="fs-lg mb-4">Filter</h4>
|
||||
<form id="playgroundCategoriesFilterForm" style="width: 350px">
|
||||
<div class="row align-items-center mb-3">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterUser">User</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterUser" aria-label="User">
|
||||
<option value="Emily Thompson" selected>Emily Thompson</option>
|
||||
<option value="Michael Johnson">Michael Johnson</option>
|
||||
<option value="Robert Garcia">Robert Garcia</option>
|
||||
<option value="Jessica Miller">Jessica Miller</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center mb-3">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterCompany">Company</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterCompany" aria-label="Company">
|
||||
<option value="TechPinnacle Solutions" selected>TechPinnacle Solutions</option>
|
||||
<option value="Quantum Dynamics">Quantum Dynamics</option>
|
||||
<option value="Pinnacle Technologies">Pinnacle Technologies</option>
|
||||
<option value="Apex Innovations">Apex Innovations</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-3">
|
||||
<label class="form-label mb-0" for="playgroundFilterLocation">Location</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<select class="form-select" id="playgroundFilterLocation" aria-label="Location">
|
||||
<option value="San Francisco, CA" selected>San Francisco, CA</option>
|
||||
<option value="Austin, TX">Austin, TX</option>
|
||||
<option value="Miami, FL">Miami, FL</option>
|
||||
<option value="Seattle, WA">Seattle, WA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto ms-n2">
|
||||
<div class="dropdown">
|
||||
<Button element="button" type="button" icon="sort-ascending-letters" iconOnly text="Open sort options" />
|
||||
<div class="dropdown-menu rounded-3 p-6">
|
||||
<h4 class="fs-lg mb-4">Sort</h4>
|
||||
<form id="playgroundCategoriesSortForm" style="width: 350px">
|
||||
<div class="row gx-3">
|
||||
<div class="col">
|
||||
<select class="form-select" id="playgroundSortField" aria-label="Sort by">
|
||||
<option value="user" selected>User</option>
|
||||
<option value="company">Company</option>
|
||||
<option value="phone">Phone</option>
|
||||
<option value="location">Location</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<ButtonGroup aria-label="Sort direction">
|
||||
<input type="radio" class="btn-check" name="playgroundSortDir" id="playgroundSortAsc" autocomplete="off" checked />
|
||||
<label class="btn btn-light px-0 btn-icon" aria-label="Ascending"><Icon name="arrow-up" /></label>
|
||||
<input type="radio" class="btn-check" name="playgroundSortDir" id="playgroundSortDesc" autocomplete="off" />
|
||||
<label class="btn btn-light px-0 btn-icon" aria-label="Descending"><Icon name="arrow-down" /></label>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" type="button">New category</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-7">
|
||||
<hr class="my-7" />
|
||||
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col">
|
||||
<Badge text="Featured" color="primary" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="confetti" class="icon-lg text-primary opacity-50" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="fs-5 mb-1 text-truncate"><a class="text-body" href="./empty.html">Announcements</a></h3>
|
||||
<p class="text-secondary text-truncate">For company updates, system changes, or important notices that everyone should see.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 2 days ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="20 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col">
|
||||
<Badge text="Featured" color="primary" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="confetti" class="icon-lg text-primary opacity-50" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="fs-5 mb-1 text-truncate"><a class="text-body" href="./empty.html">Announcements</a></h3>
|
||||
<p class="text-secondary text-truncate">For company updates, system changes, or important notices that everyone should see.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 2 days ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="20 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col">
|
||||
<Badge text="Featured" color="success" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="slideshow" class="icon-lg text-success opacity-50" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="fs-5 mb-1 text-truncate"><a class="text-body" href="./empty.html">Marketing campaigns</a></h3>
|
||||
<p class="text-secondary text-truncate">Posts related to new campaigns, marketing strategies, and promotions.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 1 day ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true} size="xs" offset={3} limit={3} />
|
||||
<div class="text-end">
|
||||
<Badge text="62 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col">
|
||||
<Badge text="Featured" color="success" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Icon name="slideshow" class="icon-lg text-success opacity-50" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="fs-5 mb-1 text-truncate"><a class="text-body" href="./empty.html">Marketing campaigns</a></h3>
|
||||
<p class="text-secondary text-truncate">Posts related to new campaigns, marketing strategies, and promotions.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 1 day ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true} size="xs" offset={3} limit={3} />
|
||||
<div class="text-end">
|
||||
<Badge text="62 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="chart-pie" class="icon-lg text-danger opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">12 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Reports & analytics</a></h3>
|
||||
<p class="text-secondary text-truncate">Data-driven insights and performance reports across all departments.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 5 days ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="28 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="chart-pie" class="icon-lg text-danger opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">12 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Reports & analytics</a></h3>
|
||||
<p class="text-secondary text-truncate">Data-driven insights and performance reports across all departments.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 5 days ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="28 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="id-badge" class="icon-lg text-warning opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">16 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Events & webinars</a></h3>
|
||||
<p class="text-secondary text-truncate">Upcoming events, webinar recordings, and community meetups.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 3 days ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/005f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Olivia Davis" />
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="34 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="id-badge" class="icon-lg text-warning opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">16 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Events & webinars</a></h3>
|
||||
<p class="text-secondary text-truncate">Upcoming events, webinar recordings, and community meetups.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 3 days ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/005f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Olivia Davis" />
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="34 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="user" class="icon-lg text-warning opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">9 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Client success stories</a></h3>
|
||||
<p class="text-secondary text-truncate">Case studies and testimonials from our most satisfied customers.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 1 week ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/006m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="James Wilson" />
|
||||
<Avatar src="static/avatars/004f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Jessica Miller" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="64 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="user" class="icon-lg text-warning opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">9 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Client success stories</a></h3>
|
||||
<p class="text-secondary text-truncate">Case studies and testimonials from our most satisfied customers.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 1 week ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/006m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="James Wilson" />
|
||||
<Avatar src="static/avatars/004f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Jessica Miller" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="64 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="message" class="icon-lg text-primary opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">10 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Team highlights</a></h3>
|
||||
<p class="text-secondary text-truncate">Spotlights, shoutouts, and stories from people across the team.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 4 days ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="42 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="message" class="icon-lg text-primary opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">10 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Team highlights</a></h3>
|
||||
<p class="text-secondary text-truncate">Spotlights, shoutouts, and stories from people across the team.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 4 days ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/002m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Michael Johnson" />
|
||||
<Avatar src="static/avatars/001f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Emily Thompson" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="42 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="building-bank" class="icon-lg text-danger opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">26 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Training & resources</a></h3>
|
||||
<p class="text-secondary text-truncate">Access to learning materials, tutorials, or resources for users and staff.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 6 days ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
<Avatar src="static/avatars/005f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Olivia Davis" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="38 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="building-bank" class="icon-lg text-danger opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">26 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Training & resources</a></h3>
|
||||
<p class="text-secondary text-truncate">Access to learning materials, tutorials, or resources for users and staff.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 6 days ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/003m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Robert Garcia" />
|
||||
<Avatar src="static/avatars/005f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Olivia Davis" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="38 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="brand-apple" class="icon-lg text-primary opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">6 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Industry news</a></h3>
|
||||
<p class="text-secondary text-truncate">Trends, news, and commentary from around the industry landscape.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 2 weeks ago</p>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/006m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="James Wilson" />
|
||||
<Avatar src="static/avatars/004f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Jessica Miller" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="59 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<Card class="position-relative overflow-hidden">
|
||||
<Icon name="brand-apple" class="icon-lg text-primary opacity-25 position-absolute top-0 end-0 m-3" />
|
||||
<CardBody>
|
||||
<small class="text-secondary d-block mb-4">6 followers</small>
|
||||
<h3 class="fs-6 mb-1 text-truncate"><a class="text-body" href="./empty.html">Industry news</a></h3>
|
||||
<p class="text-secondary text-truncate">Trends, news, and commentary from around the industry landscape.</p>
|
||||
<p class="fs-sm text-secondary mb-0">Last post 2 weeks ago</p>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<AvatarList stacked={true}>
|
||||
<Avatar src="static/avatars/006m.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="James Wilson" />
|
||||
<Avatar src="static/avatars/004f.jpg" size="xs" data-bs-toggle="tooltip" data-bs-title="Jessica Miller" />
|
||||
</AvatarList>
|
||||
<div class="text-end">
|
||||
<Badge text="59 posts" class="bg-light text-body" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row align-items-center mt-7">
|
||||
<div class="col">
|
||||
<p class="text-secondary mb-0">1 – 8 (8 total)</p>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<nav aria-label="Page navigation">
|
||||
<Pagination count={3} offset={3} activeItem={1} class="mb-0" />
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center mt-7">
|
||||
<div class="col">
|
||||
<p class="text-secondary mb-0">1 – 8 (8 total)</p>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<nav aria-label="Page navigation">
|
||||
<Pagination count={3} offset={3} activeItem={1} class="mb-0" />
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Layout playground"
|
||||
pageHeader="Layout playground"
|
||||
pretitle="Playground"
|
||||
sidebar
|
||||
sidebarDark
|
||||
>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Sandbox</CardTitle>
|
||||
<p class="text-secondary mb-0">Edit this page to try layout options, components, and styles.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<DefaultLayout title="Layout playground" pageHeader="Layout playground" pretitle="Playground" sidebar sidebarDark>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle as="h2">Sandbox</CardTitle>
|
||||
<p class="text-secondary mb-0">Edit this page to try layout options, components, and styles.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+219
-232
@@ -1,250 +1,237 @@
|
||||
---
|
||||
import Subheader from '@ui/Subheader.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro';
|
||||
import Datepicker from '@ui/Datepicker.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import Trending from '@ui/Trending.astro';
|
||||
import Subheader from '@ui/Subheader.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro'
|
||||
import Datepicker from '@ui/Datepicker.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
import Trending from '@ui/Trending.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Playground" pageHeader="Project Settings" pageLibs={['vanilla-calendar-pro']}>
|
||||
<div class="row row-cards row-deck">
|
||||
<div class="row row-cards row-deck">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="d-flex flex-wrap gap-4">
|
||||
<div class="bg-pattern-diagonal w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-2 w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-dots w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-lines w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-rectangles w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-lines-vertical w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-grid w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-grid-diagonal w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-blueprint w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-cross-dots w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-stripes w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-stripes-2 w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-zigzag w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-vertical-stripes w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-horizontal-stripes w-10 h-10 border rounded"></div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-color);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-color-translucent);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-dark-color);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-dark-color-translucent);">Button</a>
|
||||
<a href="" class="btn btn-primary">Button</a>
|
||||
</ButtonList>
|
||||
<ButtonList class="mt-4">
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn btn-primary">Button</a>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="d-flex flex-wrap gap-4">
|
||||
<div class="bg-pattern-diagonal w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-2 w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-dots w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-lines w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-rectangles w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-lines-vertical w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-grid w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-grid-diagonal w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-blueprint w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-cross-dots w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-stripes w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-diagonal-stripes-2 w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-zigzag w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-vertical-stripes w-10 h-10 border rounded"></div>
|
||||
<div class="bg-pattern-horizontal-stripes w-10 h-10 border rounded"></div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-color);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-color-translucent);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-dark-color);">Button</a>
|
||||
<a href="" class="btn" style="border-color: var(--tblr-border-dark-color-translucent);">Button</a>
|
||||
<a href="" class="btn btn-primary">Button</a>
|
||||
</ButtonList>
|
||||
<ButtonList class="mt-4">
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn">Button</a>
|
||||
<a href="" class="btn btn-primary">Button</a>
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody> Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur earum perferendis expedita suscipit quasi maiores quas sint harum dolor! Dolorem cumque autem error ea fuga. Ea excepturi temporibus officiis perspiciatis. </CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua.
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="space-y">
|
||||
<ProgressSteps labels={['Step A', 'Step B', 'Step C']} active={2} />
|
||||
<ProgressSteps count={9} active={4} />
|
||||
<ProgressSteps count={9} active={9} color="success" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<Card>
|
||||
<CardBody>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua.
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader title="Datepicker Examples" />
|
||||
<CardBody>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Basic Datepicker</h4>
|
||||
<FormGroup label="Default datepicker">
|
||||
<Datepicker id="playground-default" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="With icon">
|
||||
<Datepicker id="playground-icon" layout="icon" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Icon prepend">
|
||||
<Datepicker id="playground-icon-prepend" layout="icon-prepend" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Datepicker Options</h4>
|
||||
<FormGroup label="With date range (min/max)">
|
||||
<Datepicker id="playground-range" value="2024-06-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Multiple selection">
|
||||
<Datepicker id="playground-multiple" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Date range selection">
|
||||
<Datepicker id="playground-range-selection" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Placement Options</h4>
|
||||
<FormGroup label="Left placement">
|
||||
<Datepicker id="playground-placement-left" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Center placement">
|
||||
<Datepicker id="playground-placement-center" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Right placement">
|
||||
<Datepicker id="playground-placement-right" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Advanced Options</h4>
|
||||
<FormGroup label="Multiple months">
|
||||
<Datepicker id="playground-multiple-months" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Custom first weekday (Monday)">
|
||||
<Datepicker id="playground-weekday" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="With pre-selected dates">
|
||||
<Datepicker id="playground-preselected" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<h4 class="mb-3">Inline Datepicker</h4>
|
||||
<FormGroup label="Inline calendar">
|
||||
<Datepicker id="playground-inline" inline value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Inline with multiple selection">
|
||||
<Datepicker id="playground-inline-multiple" inline value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<p class="card-title mb-4">Cohort Statistics</p>
|
||||
<dl class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Users</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">25,784</span>
|
||||
<Trending value={23} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur earum perferendis expedita suscipit quasi maiores quas sint harum dolor! Dolorem cumque autem error ea fuga. Ea excepturi temporibus officiis perspiciatis.
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Average CSAT Score</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">86.9</span>
|
||||
<Trending value={-4} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="space-y">
|
||||
<ProgressSteps labels={['Step A', 'Step B', 'Step C']} active={2} />
|
||||
<ProgressSteps count={9} active={4} />
|
||||
<ProgressSteps count={9} active={9} color="success" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Average Response Time</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">7.2m</span>
|
||||
<Trending value={8} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader title="Datepicker Examples" />
|
||||
<CardBody>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Basic Datepicker</h4>
|
||||
<FormGroup label="Default datepicker">
|
||||
<Datepicker id="playground-default" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="With icon">
|
||||
<Datepicker id="playground-icon" layout="icon" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Icon prepend">
|
||||
<Datepicker id="playground-icon-prepend" layout="icon-prepend" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Datepicker Options</h4>
|
||||
<FormGroup label="With date range (min/max)">
|
||||
<Datepicker id="playground-range" value="2024-06-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Multiple selection">
|
||||
<Datepicker id="playground-multiple" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Date range selection">
|
||||
<Datepicker id="playground-range-selection" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Placement Options</h4>
|
||||
<FormGroup label="Left placement">
|
||||
<Datepicker id="playground-placement-left" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Center placement">
|
||||
<Datepicker id="playground-placement-center" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Right placement">
|
||||
<Datepicker id="playground-placement-right" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-3">Advanced Options</h4>
|
||||
<FormGroup label="Multiple months">
|
||||
<Datepicker id="playground-multiple-months" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Custom first weekday (Monday)">
|
||||
<Datepicker id="playground-weekday" value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="With pre-selected dates">
|
||||
<Datepicker id="playground-preselected" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<h4 class="mb-3">Inline Datepicker</h4>
|
||||
<FormGroup label="Inline calendar">
|
||||
<Datepicker id="playground-inline" inline value="2024-01-15" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Inline with multiple selection">
|
||||
<Datepicker id="playground-inline-multiple" inline value="2024-01-15" />
|
||||
</FormGroup>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<p class="card-title mb-4">Cohort Statistics</p>
|
||||
<dl class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Tickets</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">51,960</span>
|
||||
<Trending value={-7} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Users</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">25,784</span>
|
||||
<Trending value={23} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Resolution Rate</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">90.0%</span>
|
||||
<Trending value={0} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Average CSAT Score</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">86.9</span>
|
||||
<Trending value={-4} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Cohorts</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">10</span>
|
||||
<Trending value={15} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Average Response Time</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">7.2m</span>
|
||||
<Trending value={8} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Avg. Handling Time</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">14.1m</span>
|
||||
<Trending value={-12} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Tickets</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">51,960</span>
|
||||
<Trending value={-7} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">First Contact Resolution</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">80.0%</span>
|
||||
<Trending value={6} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Resolution Rate</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">90.0%</span>
|
||||
<Trending value={0} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Total Cohorts</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">10</span>
|
||||
<Trending value={15} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Avg. Handling Time</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">14.1m</span>
|
||||
<Trending value={-12} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">First Contact Resolution</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">80.0%</span>
|
||||
<Trending value={6} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Subheader as="dt">Retention Rate</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">28.0%</span>
|
||||
<Trending value={-3} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
</dl>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader as="dt">Retention Rate</Subheader>
|
||||
<dd class="mt-1 d-flex align-items-baseline">
|
||||
<span class="h2 mb-0">28.0%</span>
|
||||
<Trending value={-3} class="ms-2" />
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+221
-235
@@ -1,250 +1,236 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Progress from '@ui/Progress.astro';
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro';
|
||||
import ProgressBg from '@ui/ProgressBg.astro';
|
||||
import ProgressDescription from '@ui/ProgressDescription.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import Progress from '@ui/Progress.astro'
|
||||
import ProgressSteps from '@ui/ProgressSteps.astro'
|
||||
import ProgressBg from '@ui/ProgressBg.astro'
|
||||
import ProgressDescription from '@ui/ProgressDescription.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Progress" pageHeader="Progress" pageMenu="base.progress">
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3">
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="0" />
|
||||
<Progress value="20" />
|
||||
<Progress value="40" />
|
||||
<Progress value="100" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>With value</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="10" showValue size="lg" />
|
||||
<Progress value="20" showValue size="lg" />
|
||||
<Progress value="90" showValue size="lg" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Colors</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress color="blue" value="20" />
|
||||
<Progress color="green" value="40" />
|
||||
<Progress color="yellow" value="60" />
|
||||
<Progress color="red" value="80" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Sizes</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="20" size="sm" />
|
||||
<Progress value="40" />
|
||||
<Progress value="60" size="lg" />
|
||||
<Progress value="80" size="xl" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Indeterminate</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress indeterminate />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Multiple values</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress values={[20, 30, 10]} />
|
||||
<Progress values={[10, 20, 30, 40]} class="progress-separated" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Striped</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress color="blue" value="20" striped />
|
||||
<Progress color="green" value="40" striped />
|
||||
<Progress color="yellow" value="60" striped />
|
||||
<Progress color="red" value="80" striped />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Animated</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="20" striped animated />
|
||||
<Progress value="40" color="green" striped animated />
|
||||
<Progress value="60" color="yellow" striped animated />
|
||||
<Progress value="80" color="red" striped animated />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Animated with JavaScript
|
||||
</CardTitle>
|
||||
<div class="row align-items-center g-0">
|
||||
<div class="col">
|
||||
<Progress value="0" id="progress-animated" />
|
||||
</div>
|
||||
<div class="col-2 text-end" id="progress-animated-value">
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
<ButtonList class="mt-3">
|
||||
<button class="btn btn-sm" id="progress-animated-0">0%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-10">10%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-50">50%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-100">100%</button>
|
||||
<button class="btn btn-sm ms-3" id="progress-animated-minus-10">-10%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-add-10">+10%</button>
|
||||
</ButtonList>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN SCRIPT OF ANIMATION -->
|
||||
<script is:inline>
|
||||
/*
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3">
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="0" />
|
||||
<Progress value="20" />
|
||||
<Progress value="40" />
|
||||
<Progress value="100" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>With value</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="10" showValue size="lg" />
|
||||
<Progress value="20" showValue size="lg" />
|
||||
<Progress value="90" showValue size="lg" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Colors</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress color="blue" value="20" />
|
||||
<Progress color="green" value="40" />
|
||||
<Progress color="yellow" value="60" />
|
||||
<Progress color="red" value="80" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Sizes</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="20" size="sm" />
|
||||
<Progress value="40" />
|
||||
<Progress value="60" size="lg" />
|
||||
<Progress value="80" size="xl" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Indeterminate</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress indeterminate />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Multiple values</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress values={[20, 30, 10]} />
|
||||
<Progress values={[10, 20, 30, 40]} class="progress-separated" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Striped</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress color="blue" value="20" striped />
|
||||
<Progress color="green" value="40" striped />
|
||||
<Progress color="yellow" value="60" striped />
|
||||
<Progress color="red" value="80" striped />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Animated</CardTitle>
|
||||
<div class="space-y">
|
||||
<Progress value="20" striped animated />
|
||||
<Progress value="40" color="green" striped animated />
|
||||
<Progress value="60" color="yellow" striped animated />
|
||||
<Progress value="80" color="red" striped animated />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Animated with JavaScript </CardTitle>
|
||||
<div class="row align-items-center g-0">
|
||||
<div class="col">
|
||||
<Progress value="0" id="progress-animated" />
|
||||
</div>
|
||||
<div class="col-2 text-end" id="progress-animated-value">0%</div>
|
||||
</div>
|
||||
<ButtonList class="mt-3">
|
||||
<button class="btn btn-sm" id="progress-animated-0">0%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-10">10%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-50">50%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-100">100%</button>
|
||||
<button class="btn btn-sm ms-3" id="progress-animated-minus-10">-10%</button>
|
||||
<button class="btn btn-sm" id="progress-animated-add-10">+10%</button>
|
||||
</ButtonList>
|
||||
<CaptureScript>
|
||||
<!-- BEGIN SCRIPT OF ANIMATION -->
|
||||
<script is:inline>
|
||||
/*
|
||||
This script is for animation of the last progress bar.
|
||||
It increases the progress bar value by a random amount every 2 seconds until it reaches 100%.
|
||||
When it reaches 100%, it changes the color to green and stops the animation.
|
||||
|
||||
This is just a demo script to show how to animate the progress bar. You can modify it as needed.
|
||||
*/
|
||||
const progress = document.getElementById('progress-animated')!;
|
||||
let width = 0;
|
||||
const progress = document.getElementById('progress-animated')
|
||||
let width = 0
|
||||
|
||||
const setWidth = (w: number) => {
|
||||
width = Math.min(Math.max(w, 0), 100);
|
||||
const setWidth = (w) => {
|
||||
width = Math.min(Math.max(w, 0), 100)
|
||||
|
||||
const bar = progress.querySelector<HTMLElement>('.progress-bar')!;
|
||||
bar.style.width = `${width}%`;
|
||||
bar.setAttribute('aria-valuenow', `${width}`);
|
||||
document.getElementById('progress-animated-value')!.innerText = `${width}%`;
|
||||
bar.classList.toggle('bg-green', width >= 100);
|
||||
};
|
||||
const bar = progress.querySelector('.progress-bar')
|
||||
bar.style.width = `${width}%`
|
||||
bar.setAttribute('aria-valuenow', `${width}`)
|
||||
document.getElementById('progress-animated-value').innerText = `${width}%`
|
||||
bar.classList.toggle('bg-green', width >= 100)
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setWidth(width + Math.ceil(Math.random() * 10));
|
||||
if (width >= 100) clearInterval(interval);
|
||||
}, 2000);
|
||||
const interval = setInterval(() => {
|
||||
setWidth(width + Math.ceil(Math.random() * 10))
|
||||
if (width >= 100) clearInterval(interval)
|
||||
}, 2000)
|
||||
|
||||
document.getElementById('progress-animated-0')!.addEventListener('click', () => setWidth(0));
|
||||
document.getElementById('progress-animated-add-10')!.addEventListener('click', () => setWidth(width + 10));
|
||||
document.getElementById('progress-animated-minus-10')!.addEventListener('click', () => setWidth(width - 10));
|
||||
document.getElementById('progress-animated-100')!.addEventListener('click', () => setWidth(100));
|
||||
</script>
|
||||
<!-- END SCRIPT OF ANIMATION -->
|
||||
</CaptureScript>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Steps Progress
|
||||
</CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressSteps count={3} />
|
||||
<ProgressSteps count={5} active={4} />
|
||||
<ProgressSteps count={10} color="red" />
|
||||
<ProgressSteps count={8} color="green" active={8} />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Progress Background
|
||||
</CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressBg value="85" text="Poland" showValue />
|
||||
<ProgressBg value="65" text="Germany" showValue />
|
||||
<ProgressBg value="45" text="United States" showValue />
|
||||
<ProgressBg value="25" text="France" showValue />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Progress Background Colors
|
||||
</CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressBg value="75" text="Success" color="success-lt" showValue />
|
||||
<ProgressBg value="60" text="Warning" color="warning-lt" showValue />
|
||||
<ProgressBg value="40" text="Danger" color="danger-lt" showValue />
|
||||
<ProgressBg value="90" text="Info" color="info-lt" showValue />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Progress Description
|
||||
</CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressDescription label="Project completion" value="85" color="green" />
|
||||
<ProgressDescription label="Storage usage" description="2.4GB of 5GB" value="48" color="blue" />
|
||||
<ProgressDescription label="Download progress" value="75" color="yellow" />
|
||||
<ProgressDescription label="Skills assessment" description="HTML/CSS" value="92" color="red" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
Progress Description Sizes
|
||||
</CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressDescription label="Small progress" value="60" size="sm" color="blue" />
|
||||
<ProgressDescription label="Default progress" value="70" color="green" />
|
||||
<ProgressDescription label="Large progress" value="80" size="lg" color="orange" />
|
||||
<ProgressDescription label="Extra large" value="90" size="xl" color="purple" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
document.getElementById('progress-animated-0').addEventListener('click', () => setWidth(0))
|
||||
document.getElementById('progress-animated-add-10').addEventListener('click', () => setWidth(width + 10))
|
||||
document.getElementById('progress-animated-minus-10').addEventListener('click', () => setWidth(width - 10))
|
||||
document.getElementById('progress-animated-100').addEventListener('click', () => setWidth(100))
|
||||
</script>
|
||||
<!-- END SCRIPT OF ANIMATION -->
|
||||
</CaptureScript>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Steps Progress </CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressSteps count={3} />
|
||||
<ProgressSteps count={5} active={4} />
|
||||
<ProgressSteps count={10} color="red" />
|
||||
<ProgressSteps count={8} color="green" active={8} />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Progress Background </CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressBg value="85" text="Poland" showValue />
|
||||
<ProgressBg value="65" text="Germany" showValue />
|
||||
<ProgressBg value="45" text="United States" showValue />
|
||||
<ProgressBg value="25" text="France" showValue />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Progress Background Colors </CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressBg value="75" text="Success" color="success-lt" showValue />
|
||||
<ProgressBg value="60" text="Warning" color="warning-lt" showValue />
|
||||
<ProgressBg value="40" text="Danger" color="danger-lt" showValue />
|
||||
<ProgressBg value="90" text="Info" color="info-lt" showValue />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Progress Description </CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressDescription label="Project completion" value="85" color="green" />
|
||||
<ProgressDescription label="Storage usage" description="2.4GB of 5GB" value="48" color="blue" />
|
||||
<ProgressDescription label="Download progress" value="75" color="yellow" />
|
||||
<ProgressDescription label="Skills assessment" description="HTML/CSS" value="92" color="red" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle> Progress Description Sizes </CardTitle>
|
||||
<div class="space-y">
|
||||
<ProgressDescription label="Small progress" value="60" size="sm" color="blue" />
|
||||
<ProgressDescription label="Default progress" value="70" color="green" />
|
||||
<ProgressDescription label="Large progress" value="80" size="lg" color="orange" />
|
||||
<ProgressDescription label="Extra large" value="90" size="xl" color="purple" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
---
|
||||
// Color swatches use site.themeColors (blue…cyan).
|
||||
// Large prose/nav-segmented block from source template omitted (was commented out).
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro';
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro';
|
||||
import { site } from '@shared/lib/site';
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro'
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro'
|
||||
import { site } from '@shared/lib/site'
|
||||
---
|
||||
|
||||
<BaseLayout pageLibs={['signature_pad']}>
|
||||
<style is:inline>
|
||||
.screenshot {
|
||||
width: 1024px;
|
||||
height: 768px;
|
||||
background: var(--tblr-bg-surface-tertiary) linear-gradient(to top left, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0));
|
||||
position: relative;
|
||||
}
|
||||
<style is:inline>
|
||||
.screenshot {
|
||||
width: 1024px;
|
||||
height: 768px;
|
||||
background: var(--tblr-bg-surface-tertiary) linear-gradient(to top left, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.screenshot-card {
|
||||
padding: 64px;
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 1px 1px 0 rgba(0, 0, 0, 0.02),
|
||||
0 8px 24px -4px rgba(0, 0, 0, 0.04),
|
||||
0 24px 40px -8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.screenshot-card {
|
||||
padding: 64px;
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 1px 1px 0 rgba(0, 0, 0, 0.02),
|
||||
0 8px 24px -4px rgba(0, 0, 0, 0.04),
|
||||
0 24px 40px -8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.screenshot-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 5rem;
|
||||
flex: 1;
|
||||
}
|
||||
.screenshot-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 5rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
position: absolute;
|
||||
width: 26px;
|
||||
height: 37px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 26 37'%3E%3Cg filter='url(%23a)'%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M8.56 13.902c-.284-.358-.63-1.092-1.243-1.983-.348-.505-1.211-1.454-1.468-1.935-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z' clip-rule='evenodd'/%3E%3Cpath stroke='%23000' stroke-linecap='round' stroke-linejoin='round' d='M16.794 14.257v-3.459m-2.015 3.47-.016-3.472m-1.98.031.02 3.426m-4.243-.35c-.284-.36-.63-1.094-1.243-1.985-.348-.503-1.211-1.452-1.468-1.934-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='a' width='30' height='40' x='-2' y='-1' color-interpolation-filters='sRGB' filterUnits='userSpaceOnUse'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.05 0'/%3E%3CfeBlend in2='BackgroundImageFix' result='effect1_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='3'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.04 0'/%3E%3CfeBlend in2='effect1_dropShadow_55_1571' result='effect2_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='8'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.03 0'/%3E%3CfeBlend in2='effect2_dropShadow_55_1571' result='effect3_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='14'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.01 0'/%3E%3CfeBlend in2='effect3_dropShadow_55_1571' result='effect4_dropShadow_55_1571'/%3E%3CfeBlend in='SourceGraphic' in2='effect4_dropShadow_55_1571' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E");
|
||||
top: calc(100% - 10px);
|
||||
left: calc(50% - 10px);
|
||||
}
|
||||
.cursor {
|
||||
position: absolute;
|
||||
width: 26px;
|
||||
height: 37px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 26 37'%3E%3Cg filter='url(%23a)'%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M8.56 13.902c-.284-.358-.63-1.092-1.243-1.983-.348-.505-1.211-1.454-1.468-1.935-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z' clip-rule='evenodd'/%3E%3Cpath stroke='%23000' stroke-linecap='round' stroke-linejoin='round' d='M16.794 14.257v-3.459m-2.015 3.47-.016-3.472m-1.98.031.02 3.426m-4.243-.35c-.284-.36-.63-1.094-1.243-1.985-.348-.503-1.211-1.452-1.468-1.934-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='a' width='30' height='40' x='-2' y='-1' color-interpolation-filters='sRGB' filterUnits='userSpaceOnUse'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.05 0'/%3E%3CfeBlend in2='BackgroundImageFix' result='effect1_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='3'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.04 0'/%3E%3CfeBlend in2='effect1_dropShadow_55_1571' result='effect2_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='8'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.03 0'/%3E%3CfeBlend in2='effect2_dropShadow_55_1571' result='effect3_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='14'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.01 0'/%3E%3CfeBlend in2='effect3_dropShadow_55_1571' result='effect4_dropShadow_55_1571'/%3E%3CfeBlend in='SourceGraphic' in2='effect4_dropShadow_55_1571' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E");
|
||||
top: calc(100% - 10px);
|
||||
left: calc(50% - 10px);
|
||||
}
|
||||
|
||||
.avatar-2 {
|
||||
background-image: url(/static/avatars/032f.jpg);
|
||||
}
|
||||
</style>
|
||||
.avatar-2 {
|
||||
background-image: url(/static/avatars/032f.jpg);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="page page-center align-items-center">
|
||||
<div class="screenshot d-flex flex-column justify-content-center align-items-center px-12">
|
||||
<div class="screenshot-logo">
|
||||
<NavbarLogo class="brand-gray" />
|
||||
</div>
|
||||
<div class="screenshot-card card p-6">
|
||||
<div class="row g-4">
|
||||
{
|
||||
site.themeColors.map((color) => (
|
||||
<div class="col-2">
|
||||
<div class={`p-5 bg-gradient bg-gradient-from-${color} bg-gradient-to-transparent bg-gradient-to-ts rounded ratio ratio-1x1 border`} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="page page-center align-items-center">
|
||||
<div class="screenshot d-flex flex-column justify-content-center align-items-center px-12">
|
||||
<div class="screenshot-logo">
|
||||
<NavbarLogo class="brand-gray" />
|
||||
</div>
|
||||
<div class="screenshot-card card p-6">
|
||||
<div class="row g-4">
|
||||
{
|
||||
site.themeColors.map((color) => (
|
||||
<div class="col-2">
|
||||
<div class={`p-5 bg-gradient bg-gradient-from-${color} bg-gradient-to-transparent bg-gradient-to-ts rounded ratio ratio-1x1 border`} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="screenshot-logo"></div>
|
||||
</div>
|
||||
<div class="screenshot-logo"></div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-segmented mt-4">
|
||||
<a href="?theme=light" class="nav-link hide-theme-light">Light</a>
|
||||
<a href="?theme=light" class="nav-link active hide-theme-dark">Light</a>
|
||||
<a href="?theme=dark" class="nav-link hide-theme-dark">Dark</a>
|
||||
<a href="?theme=dark" class="nav-link active hide-theme-light">Dark</a>
|
||||
</nav>
|
||||
</div>
|
||||
<nav class="nav-segmented mt-4">
|
||||
<a href="?theme=light" class="nav-link hide-theme-light">Light</a>
|
||||
<a href="?theme=light" class="nav-link active hide-theme-dark">Light</a>
|
||||
<a href="?theme=dark" class="nav-link hide-theme-dark">Dark</a>
|
||||
<a href="?theme=dark" class="nav-link active hide-theme-light">Dark</a>
|
||||
</nav>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
---
|
||||
// Photo id=11 → 12th entry of the unfiltered photos list; no horizontal filter.
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro';
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro';
|
||||
import SignInForm from '@shared/components/cards/SignInForm.astro';
|
||||
import Photo from '@ui/Photo.astro';
|
||||
import photos from '@data/photos.json';
|
||||
import BaseLayout from '@shared/layouts/BaseLayout.astro'
|
||||
import NavbarLogo from '@shared/components/navbar/NavbarLogo.astro'
|
||||
import SignInForm from '@shared/components/cards/SignInForm.astro'
|
||||
import Photo from '@ui/Photo.astro'
|
||||
import photos from '@data/photos.json'
|
||||
---
|
||||
|
||||
<BaseLayout title="Sign in with cover" bodyClass="d-flex flex-column bg-white">
|
||||
<div class="row g-0 flex-fill">
|
||||
<div class="col-12 col-lg-6 col-xl-4 border-top-wide border-primary d-flex flex-column justify-content-center">
|
||||
<div class="container container-tight my-5 px-lg-5">
|
||||
<div class="text-center mb-4">
|
||||
<NavbarLogo />
|
||||
</div>
|
||||
<div class="row g-0 flex-fill">
|
||||
<div class="col-12 col-lg-6 col-xl-4 border-top-wide border-primary d-flex flex-column justify-content-center">
|
||||
<div class="container container-tight my-5 px-lg-5">
|
||||
<div class="text-center mb-4">
|
||||
<NavbarLogo />
|
||||
</div>
|
||||
|
||||
<h2 class="h3 text-center mb-3"> Login to your account </h2>
|
||||
<h2 class="h3 text-center mb-3">Login to your account</h2>
|
||||
|
||||
<SignInForm />
|
||||
<SignInForm />
|
||||
|
||||
<div class="text-center text-secondary mt-3">
|
||||
Don't have account yet? <a href="./sign-up.html" tabindex="-1">Sign up</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl-8 d-none d-lg-block">
|
||||
<Photo photo={photos[11]} class="bg-cover h-100 min-vh-100" background />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center text-secondary mt-3">
|
||||
Don't have account yet? <a href="./sign-up.html" tabindex="-1">Sign up</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl-8 d-none d-lg-block">
|
||||
<Photo photo={photos[11]} class="bg-cover h-100 min-vh-100" background />
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
---
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro';
|
||||
import SingleLayout from '@shared/layouts/SingleLayout.astro'
|
||||
|
||||
// TODO: add `email` to src/lib/site.ts — kept local here to avoid cross-agent conflicts.
|
||||
const siteEmail = 'support@tabler.io';
|
||||
const siteEmail = 'support@tabler.io'
|
||||
---
|
||||
|
||||
<SingleLayout title="Sign in link">
|
||||
<div class="text-center">
|
||||
<div class="my-5">
|
||||
<h2 class="h1">Check your inbox</h2>
|
||||
<div class="text-center">
|
||||
<div class="my-5">
|
||||
<h2 class="h1">Check your inbox</h2>
|
||||
|
||||
<p class="fs-h3 text-secondary">
|
||||
We've sent you a magic link to <strong>{siteEmail}</strong>.<br />
|
||||
Please click the link to confirm your address.
|
||||
</p>
|
||||
</div>
|
||||
<p class="fs-h3 text-secondary">
|
||||
We've sent you a magic link to <strong>{siteEmail}</strong>.<br />
|
||||
Please click the link to confirm your address.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-secondary mt-3">
|
||||
Can't see the email? Please check the spam folder.<br />
|
||||
Wrong email? Please <a href="#">re-enter your address</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center text-secondary mt-3">
|
||||
Can't see the email? Please check the spam folder.<br />
|
||||
Wrong email? Please <a href="#">re-enter your address</a>.
|
||||
</div>
|
||||
</div>
|
||||
</SingleLayout>
|
||||
|
||||
+90
-102
File diff suppressed because one or more lines are too long
@@ -1,104 +1,99 @@
|
||||
---
|
||||
// plugins: ['social'] front matter is inert — .social styles ship globally in tabler-socials.css.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Trending from '@ui/Trending.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import socialTiles from '@data/social-tiles.json';
|
||||
import socials from '@data/socials.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Trending from '@ui/Trending.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import socialTiles from '@data/social-tiles.json'
|
||||
import socials from '@data/socials.json'
|
||||
|
||||
interface SocialTile {
|
||||
icon: string;
|
||||
title: string;
|
||||
description: string;
|
||||
trending: number;
|
||||
icon: string
|
||||
title: string
|
||||
description: string
|
||||
trending: number
|
||||
}
|
||||
|
||||
interface Social {
|
||||
name: string;
|
||||
file: string;
|
||||
name: string
|
||||
file: string
|
||||
}
|
||||
|
||||
const tiles = socialTiles as SocialTile[];
|
||||
const socialList = socials as Social[];
|
||||
const fillers = Array.from({ length: 21 });
|
||||
const tiles = socialTiles as SocialTile[]
|
||||
const socialList = socials as Social[]
|
||||
const fillers = Array.from({ length: 21 })
|
||||
---
|
||||
|
||||
<DefaultLayout title="Social icons" pageHeader="Social icons" pageMenu="base.social">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row g-3">
|
||||
{
|
||||
tiles.map((tile) => (
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<span class={`social social-md social-app-${tile.icon}`} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div>{tile.title}</div>
|
||||
<div class="text-secondary">{tile.description}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Trending value={tile.trending} />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">Sign in with social media</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
socialList.map((social) => (
|
||||
<button class="btn">
|
||||
<span class={`icon social social-app-${social.file}`} />
|
||||
Sign in with {social.name}
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="row g-3">
|
||||
{
|
||||
tiles.map((tile) => (
|
||||
<div class="col-3">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<span class={`social social-md social-app-${tile.icon}`} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div>{tile.title}</div>
|
||||
<div class="text-secondary">{tile.description}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Trending value={tile.trending} />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">Sign in with social media</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<ButtonList>
|
||||
{
|
||||
socialList.map((social) => (
|
||||
<button class="btn">
|
||||
<span class={`icon social social-app-${social.file}`} />
|
||||
Sign in with {social.name}
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">List of all social media icons</div>
|
||||
</CardHeader>
|
||||
<CardBody class="p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
socialList.map((social) => (
|
||||
<span
|
||||
class="demo-icons-list-item"
|
||||
title={social.name}
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
>
|
||||
<span class={`social social-app-${social.file}`} />
|
||||
</span>
|
||||
))
|
||||
}
|
||||
{fillers.map(() => <div />)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="card-title">List of all social media icons</div>
|
||||
</CardHeader>
|
||||
<CardBody class="p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
socialList.map((social) => (
|
||||
<span class="demo-icons-list-item" title={social.name} data-bs-toggle="tooltip" data-bs-placement="top">
|
||||
<span class={`social social-app-${social.file}`} />
|
||||
</span>
|
||||
))
|
||||
}
|
||||
{fillers.map(() => <div />)}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+97
-97
@@ -1,114 +1,114 @@
|
||||
---
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Tag from '@ui/Tag.astro';
|
||||
import TagsList from '@ui/TagsList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import flags from '@data/flags.json';
|
||||
import siteData from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Tag from '@ui/Tag.astro'
|
||||
import TagsList from '@ui/TagsList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
import flags from '@data/flags.json'
|
||||
import siteData from '@data/site.json'
|
||||
|
||||
const tagIcons = ['bold', 'italic', 'underline', 'copy', 'scissors', 'file-plus', 'file-minus', 'ghost', 'star', 'script', 'photo', 'dog', 'piano'];
|
||||
const tagIcons = ['bold', 'italic', 'underline', 'copy', 'scissors', 'file-plus', 'file-minus', 'ghost', 'star', 'script', 'photo', 'dog', 'piano']
|
||||
|
||||
const range = (a: number, b: number) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
||||
const range = (a: number, b: number) => Array.from({ length: b - a + 1 }, (_, i) => a + i)
|
||||
|
||||
const flags9 = flags.slice(0, 9);
|
||||
const people8 = people.slice(0, 8);
|
||||
const flags9 = flags.slice(0, 9)
|
||||
const people8 = people.slice(0, 8)
|
||||
|
||||
// site.colors yields value objects with .class / .title.
|
||||
const colors = Object.values(siteData.colors) as { class: string; title: string }[];
|
||||
const colors = Object.values(siteData.colors) as { class: string; title: string }[]
|
||||
---
|
||||
|
||||
<DefaultLayout title="Tags" pageHeader="Tags" pageMenu="base.tags">
|
||||
<div class="row row-cards row-cols-1 row-cols-md-2 row-cols-lg-3">
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 14).map((i) => <Tag text={`Label ${i}`} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="row row-cards row-cols-1 row-cols-md-2 row-cols-lg-3">
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 14).map((i) => <Tag text={`Label ${i}`} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with flag</CardTitle>
|
||||
<TagsList>
|
||||
{flags9.map((country) => <Tag text={country.name} flag={(country as { code?: string }).code} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with flag</CardTitle>
|
||||
<TagsList>
|
||||
{flags9.map((country) => <Tag text={country.name} flag={(country as { code?: string }).code} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with icon</CardTitle>
|
||||
<TagsList>
|
||||
{tagIcons.map((icon) => <Tag text={icon} icon={icon} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with icon</CardTitle>
|
||||
<TagsList>
|
||||
{tagIcons.map((icon) => <Tag text={icon} icon={icon} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with avatar</CardTitle>
|
||||
<TagsList>
|
||||
{people8.map((person) => <Tag text={person.full_name} person={person} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with avatar</CardTitle>
|
||||
<TagsList>
|
||||
{people8.map((person) => <Tag text={person.full_name} person={person} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with status</CardTitle>
|
||||
<TagsList>
|
||||
{colors.map((color) => <Tag text={color.title} status={color.class} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with status</CardTitle>
|
||||
<TagsList>
|
||||
{colors.map((color) => <Tag text={color.title} status={color.class} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with legend</CardTitle>
|
||||
<TagsList>
|
||||
{colors.map((color) => <Tag text={color.title} legend={color.class} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Tags with legend</CardTitle>
|
||||
<TagsList>
|
||||
{colors.map((color) => <Tag text={color.title} legend={color.class} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 6).map((i) => <Tag text={`Label ${i}`} checkbox={true} />)}
|
||||
{range(7, 12).map((i) => <Tag text={`Label ${i}`} checkbox={true} checked={true} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 6).map((i) => <Tag text={`Label ${i}`} checkbox={true} />)}
|
||||
{range(7, 12).map((i) => <Tag text={`Label ${i}`} checkbox={true} checked={true} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 12).map((i) => <Tag text="Label" badge={i} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Default tags</CardTitle>
|
||||
<TagsList>
|
||||
{range(1, 12).map((i) => <Tag text="Label" badge={i} />)}
|
||||
</TagsList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+135
-162
@@ -1,182 +1,155 @@
|
||||
---
|
||||
|
||||
import CardActions from '@ui/CardActions.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Badge from '@ui/Badge.astro';
|
||||
import Modal from '@shared/components/modals/Modal.astro';
|
||||
import CaptureModal from '@shared/components/CaptureModal.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import tasks from '@data/tasks.json';
|
||||
import people from '@data/people.json';
|
||||
import CardActions from '@ui/CardActions.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Badge from '@ui/Badge.astro'
|
||||
import Modal from '@shared/components/modals/Modal.astro'
|
||||
import CaptureModal from '@shared/components/CaptureModal.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
import tasks from '@data/tasks.json'
|
||||
import people from '@data/people.json'
|
||||
|
||||
interface Person {
|
||||
id?: string;
|
||||
full_name?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
id?: string
|
||||
full_name?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Task {
|
||||
name?: string;
|
||||
assigned_to?: number;
|
||||
due_date?: string;
|
||||
'due-date'?: string;
|
||||
priority?: string;
|
||||
[key: string]: unknown;
|
||||
'name'?: string
|
||||
'assigned_to'?: number
|
||||
'due_date'?: string
|
||||
'due-date'?: string
|
||||
'priority'?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Column {
|
||||
name: string;
|
||||
tasks: Task[];
|
||||
name: string
|
||||
tasks: Task[]
|
||||
}
|
||||
|
||||
const columns = (tasks as { columns: Column[] }).columns;
|
||||
const peopleList = people as Person[];
|
||||
const columns = (tasks as { columns: Column[] }).columns
|
||||
const peopleList = people as Person[]
|
||||
|
||||
// Assignable people for the add-task modal.
|
||||
const selectedPeople = [5, 6, 2, 3].map((id) => peopleList[id - 1]);
|
||||
const selectedPeople = [5, 6, 2, 3].map((id) => peopleList[id - 1])
|
||||
---
|
||||
|
||||
<DefaultLayout title="Task List" pageHeader="Task List" pageMenu="extra.tasks.list">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{
|
||||
columns.map((section) => (
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title mb-0">{section.name}</h3>
|
||||
<CardActions>
|
||||
<Button text="New Task" icon="plus" modalId="add-task" size="sm" />
|
||||
</CardActions>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter card-table table-selectable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input align-middle table-selectable-check"
|
||||
aria-label="Select all tasks"
|
||||
/>
|
||||
</th>
|
||||
<th class="w-50">Name</th>
|
||||
<th class="d-none d-xl-table-cell">Assigned To</th>
|
||||
<th class="d-none d-xxl-table-cell">Due Date</th>
|
||||
<th>Priority</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{section.tasks.map((task) => {
|
||||
const person = task.assigned_to
|
||||
? peopleList[task.assigned_to - 1]
|
||||
: undefined;
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input align-middle table-selectable-check"
|
||||
aria-label="Select task"
|
||||
/>
|
||||
</td>
|
||||
<td>{task.name}</td>
|
||||
<td>
|
||||
{task.assigned_to && person ? (
|
||||
<div class="d-flex align-items-center">
|
||||
<Avatar personId={task.assigned_to} size="xs" class="me-2" />
|
||||
<span>{person.full_name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span class="text-secondary">Unassigned</span>
|
||||
)}
|
||||
</td>
|
||||
<td class="text-secondary">
|
||||
{task.due_date ? (
|
||||
<Fragment>
|
||||
<Icon name="calendar" class="me-1" />
|
||||
{task.due_date}
|
||||
</Fragment>
|
||||
) : task['due-date'] ? (
|
||||
<Fragment>
|
||||
<Icon name="calendar" class="me-1" />
|
||||
{task['due-date']}
|
||||
</Fragment>
|
||||
) : (
|
||||
<span class="text-muted">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{task.priority === 'High' ? (
|
||||
<Badge text="High" color="red" light />
|
||||
) : task.priority === 'Medium' ? (
|
||||
<Badge text="Medium" color="yellow" light />
|
||||
) : task.priority === 'Low' ? (
|
||||
<Badge text="Low" color="blue" light />
|
||||
) : (
|
||||
<Badge text="—" light />
|
||||
)}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<Button text="View" size="sm" />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{
|
||||
columns.map((section) => (
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title mb-0">{section.name}</h3>
|
||||
<CardActions>
|
||||
<Button text="New Task" icon="plus" modalId="add-task" size="sm" />
|
||||
</CardActions>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter card-table table-selectable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1">
|
||||
<input type="checkbox" class="form-check-input align-middle table-selectable-check" aria-label="Select all tasks" />
|
||||
</th>
|
||||
<th class="w-50">Name</th>
|
||||
<th class="d-none d-xl-table-cell">Assigned To</th>
|
||||
<th class="d-none d-xxl-table-cell">Due Date</th>
|
||||
<th>Priority</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{section.tasks.map((task) => {
|
||||
const person = task.assigned_to ? peopleList[task.assigned_to - 1] : undefined
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="form-check-input align-middle table-selectable-check" aria-label="Select task" />
|
||||
</td>
|
||||
<td>{task.name}</td>
|
||||
<td>
|
||||
{task.assigned_to && person ? (
|
||||
<div class="d-flex align-items-center">
|
||||
<Avatar personId={task.assigned_to} size="xs" class="me-2" />
|
||||
<span>{person.full_name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span class="text-secondary">Unassigned</span>
|
||||
)}
|
||||
</td>
|
||||
<td class="text-secondary">
|
||||
{task.due_date ? (
|
||||
<Fragment>
|
||||
<Icon name="calendar" class="me-1" />
|
||||
{task.due_date}
|
||||
</Fragment>
|
||||
) : task['due-date'] ? (
|
||||
<Fragment>
|
||||
<Icon name="calendar" class="me-1" />
|
||||
{task['due-date']}
|
||||
</Fragment>
|
||||
) : (
|
||||
<span class="text-muted">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td>{task.priority === 'High' ? <Badge text="High" color="red" light /> : task.priority === 'Medium' ? <Badge text="Medium" color="yellow" light /> : task.priority === 'Low' ? <Badge text="Low" color="blue" light /> : <Badge text="—" light />}</td>
|
||||
<td class="text-end">
|
||||
<Button text="View" size="sm" />
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CaptureModal>
|
||||
<Modal modalId="add-task">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Add task</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<CaptureModal>
|
||||
<Modal modalId="add-task">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Add task</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<FormGroup label="Name">
|
||||
<input type="text" class="form-control" placeholder="Task name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Assigned To">
|
||||
<select class="form-select">
|
||||
<option value="">Select person</option>
|
||||
{
|
||||
selectedPeople.map((person) => (
|
||||
<option value={person.id}>{person.full_name}</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Priority">
|
||||
<select class="form-select">
|
||||
<option value="Low">Low</option>
|
||||
<option value="Medium">Medium</option>
|
||||
<option value="High">High</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Description">
|
||||
<textarea class="form-control" rows="3" placeholder="Task description"></textarea>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<FormGroup label="Name">
|
||||
<input type="text" class="form-control" placeholder="Task name" />
|
||||
</FormGroup>
|
||||
<FormGroup label="Assigned To">
|
||||
<select class="form-select">
|
||||
<option value="">Select person</option>
|
||||
{selectedPeople.map((person) => <option value={person.id}>{person.full_name}</option>)}
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Priority">
|
||||
<select class="form-select">
|
||||
<option value="Low">Low</option>
|
||||
<option value="Medium">Medium</option>
|
||||
<option value="High">High</option>
|
||||
</select>
|
||||
</FormGroup>
|
||||
<FormGroup label="Description">
|
||||
<textarea class="form-control" rows="3" placeholder="Task description"></textarea>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary ms-auto" data-bs-dismiss="modal">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</CaptureModal>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary ms-auto" data-bs-dismiss="modal"> Save </button>
|
||||
</div>
|
||||
</Modal>
|
||||
</CaptureModal>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -1,83 +1,97 @@
|
||||
---
|
||||
// Right column renders headings h1..h6 with dynamic tag names. Homepage URL from site.json.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Prose from '@ui/Prose.astro';
|
||||
import siteData from '@data/site.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Prose from '@ui/Prose.astro'
|
||||
import siteData from '@data/site.json'
|
||||
|
||||
const homepage = siteData.homepage;
|
||||
const homepage = siteData.homepage
|
||||
---
|
||||
|
||||
<DefaultLayout title="Text features" pageHeader="Text features" pageMenu="extra.text-features">
|
||||
<div class="row">
|
||||
<div class="col-7">
|
||||
<div class="card card-lg">
|
||||
<div class="card-body">
|
||||
<Prose>
|
||||
<h3>Text features</h3>
|
||||
<div class="row">
|
||||
<div class="col-7">
|
||||
<div class="card card-lg">
|
||||
<div class="card-body">
|
||||
<Prose>
|
||||
<h3>Text features</h3>
|
||||
|
||||
<p>HTML provides various tags to format text and add meaning. For example, <strong>important words</strong> can be highlighted, and <em>emphasized text</em> can be italicized.</p>
|
||||
<p>HTML provides various tags to format text and add meaning. For example, <strong>important words</strong> can be highlighted, and <em>emphasized text</em> can be italicized.</p>
|
||||
|
||||
<p>If you want to visit an interesting website, check out <a href={homepage} target="_blank">this page</a>.</p>
|
||||
<p>If you want to visit an interesting website, check out <a href={homepage} target="_blank">this page</a>.</p>
|
||||
|
||||
<p>The term <abbr data-bs-toggle="tooltip" data-bs-placement="top" title="Hypertext Markup Language">HTML</abbr> is widely used in web development.</p>
|
||||
<p>The term <abbr data-bs-toggle="tooltip" data-bs-placement="top" title="Hypertext Markup Language">HTML</abbr> is widely used in web development.</p>
|
||||
|
||||
<p>Previously, the instruction said <del>"Do not include images."</del> However, <ins>"You may now add images."</ins></p>
|
||||
<p>Previously, the instruction said <del>"Do not include images."</del> However, <ins>"You may now add images."</ins></p>
|
||||
|
||||
<blockquote cite={homepage}>"The best way to predict the future is to create it." – Peter Drucker</blockquote>
|
||||
<blockquote cite={homepage}>"The best way to predict the future is to create it." – Peter Drucker</blockquote>
|
||||
|
||||
<p>Sometimes, <mark>highlighting important text</mark> can improve readability.</p>
|
||||
<p>Sometimes, <mark>highlighting important text</mark> can improve readability.</p>
|
||||
|
||||
<p>In JavaScript, you can log messages using the following code: <code>console.log('Hello, world!');</code></p>
|
||||
<p>In JavaScript, you can log messages using the following code: <code>console.log('Hello, world!');</code></p>
|
||||
|
||||
<p>To copy text on Windows, use <kbd>Ctrl + C</kbd>. On macOS, use <kbd>Cmd + C</kbd>.</p>
|
||||
<p>To copy text on Windows, use <kbd>Ctrl + C</kbd>. On macOS, use <kbd>Cmd + C</kbd>.</p>
|
||||
|
||||
<p>Water is written chemically as H<sub>2</sub>O, while Einstein’s famous equation is E = mc<sup>2</sup>.</p>
|
||||
<p>Water is written chemically as H<sub>2</sub>O, while Einstein’s famous equation is E = mc<sup>2</sup>.</p>
|
||||
|
||||
<p>Many people mistakenly spell <span class="text-incorrect">"recieve"</span> instead of <span class="text-correct">"receive"</span>.</p>
|
||||
<p>Many people mistakenly spell <span class="text-incorrect">"recieve"</span> instead of <span class="text-correct">"receive"</span>.</p>
|
||||
|
||||
<p>The correct way to write the date format is <span class="text-correct">"February 12, 2026"</span>, not <span class="text-incorrect">"12th February, 2026"</span> in American English.</p>
|
||||
<p>The correct way to write the date format is <span class="text-correct">"February 12, 2026"</span>, not <span class="text-incorrect">"12th February, 2026"</span> in American English.</p>
|
||||
|
||||
<p>
|
||||
If you need select text, you can use your mouse or keyboard. To select text using your mouse, click and drag the cursor over the text <span class="text-selected">you want to highlight</span>.
|
||||
</p>
|
||||
<p>
|
||||
If you need select text, you can use your mouse or keyboard. To select text using your mouse, click and drag the cursor over the text <span class="text-selected">you want to highlight</span>.
|
||||
</p>
|
||||
|
||||
<p><small>Disclaimer: This text is for demonstration purposes only.</small></p>
|
||||
</Prose>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card card-lg">
|
||||
<div class="card-body">
|
||||
<Prose>
|
||||
{
|
||||
[1, 2, 3, 4, 5, 6].map((i) => {
|
||||
const Heading = `h${i}`;
|
||||
return (
|
||||
<Heading>
|
||||
Heading {i} by <a class="mention"><span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)"></span><span class="visually-hidden">@</span>JohnDoe</a>
|
||||
</Heading>
|
||||
);
|
||||
})
|
||||
}
|
||||
<p><small>Disclaimer: This text is for demonstration purposes only.</small></p>
|
||||
</Prose>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card card-lg">
|
||||
<div class="card-body">
|
||||
<Prose>
|
||||
{
|
||||
[1, 2, 3, 4, 5, 6].map((i) => {
|
||||
const Heading = `h${i}`
|
||||
return (
|
||||
<Heading>
|
||||
Heading {i} by{' '}
|
||||
<a class="mention">
|
||||
<>
|
||||
<span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)" />
|
||||
<span class="visually-hidden">@</span>
|
||||
</>
|
||||
JohnDoe
|
||||
</a>
|
||||
</Heading>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
<p>
|
||||
Tabler is a modern UI framework which <span class="text-incorrect" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top">provide</span> developers with a lot of <span class="text-incorrect">pre-build</span> components and customizable options. It is
|
||||
<span class="text-incorrect">build</span> on Bootstrap, making it easy to integrate into existing projects. The design is clean, responsive, and accessible, ensuring that <span class="text-incorrect">user</span> can navigate
|
||||
through <span class="text-incorrect">interface</span> easily. Tabler also <span class="text-incorrect">support</span> all modern browsers, but some features may not work properly on Internet Explorer. With
|
||||
<span class="text-incorrect">it's</span> lightweight structure and optimized performance, Tabler helps developers create stunning web applications faster.
|
||||
</p>
|
||||
<p>
|
||||
Tabler is a modern UI framework which <span class="text-incorrect" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top">provide</span> developers with a lot of <span class="text-incorrect">pre-build</span> components and customizable options. It is
|
||||
<span class="text-incorrect">build</span> on Bootstrap, making it easy to integrate into existing projects. The design is clean, responsive, and accessible, ensuring that <span class="text-incorrect">user</span> can navigate through <span class="text-incorrect">interface</span> easily. Tabler also <span
|
||||
class="text-incorrect">support</span
|
||||
> all modern browsers, but some features may not work properly on Internet Explorer. With
|
||||
<span class="text-incorrect">it's</span> lightweight structure and optimized performance, Tabler helps developers create stunning web applications faster.
|
||||
</p>
|
||||
|
||||
<p>Hey <a class="mention"><span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)"></span><span class="visually-hidden">@</span>JohnDoe</a>, have you seen the latest updates on <a class="mention">#WebDevelopment<span class="mention-count">16</span></a>? <a class="mention"><span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)"></span><span class="visually-hidden">@</span>JaneSmith</a> just shared an interesting article about <a class="mention"><span class="mention-app" style="background-image: url(/static/brands/messenger.svg)"></span>Messenger</a> and <a class="mention"><span class="mention-app" style="background-image: url(/static/brands/netflix.svg)"></span>Netflix</a>!</p>
|
||||
<p>
|
||||
Hey <a class="mention"><span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)"></span><span class="visually-hidden">@</span>JohnDoe</a>, have you seen the latest updates on <a class="mention">#WebDevelopment<span class="mention-count">16</span></a>? <a class="mention"
|
||||
><span class="mention-avatar" style="background-image: url(/static/avatars/035f.jpg)"></span><span class="visually-hidden">@</span>JaneSmith</a
|
||||
> just shared an interesting article about <a class="mention"><span class="mention-app" style="background-image: url(/static/brands/messenger.svg)"></span>Messenger</a> and <a class="mention"><span class="mention-app" style="background-image: url(/static/brands/netflix.svg)"></span>Netflix</a>!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The sky is <span class="mention"><span class="mention-color bg-blue"></span>#066fd1</span>, the grass is <span class="mention"><span class="mention-color bg-green"></span>rgb(47, 179, 68)</span>, fire trucks are often <span class="mention"><span class="mention-color bg-red"></span>red</span>, oranges are <span class="mention"><span class="mention-color bg-orange"></span>hsl(24deg, 94.49%, 49.8%)</span>. Some flowers are <span class="mention"><span class="mention-color bg-purple"></span>hwb(288.35deg, 24.31%, 21.18%)</span>.
|
||||
</p>
|
||||
<p>
|
||||
The sky is <span class="mention"><span class="mention-color bg-blue"></span>#066fd1</span>, the grass is <span class="mention"><span class="mention-color bg-green"></span>rgb(47, 179, 68)</span>, fire trucks are often <span class="mention"><span class="mention-color bg-red"></span>red</span>, oranges are <span
|
||||
class="mention"><span class="mention-color bg-orange"></span>hsl(24deg, 94.49%, 49.8%)</span
|
||||
>. Some flowers are <span class="mention"><span class="mention-color bg-purple"></span>hwb(288.35deg, 24.31%, 21.18%)</span>.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
</Prose>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</Prose>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
+212
-221
@@ -1,231 +1,222 @@
|
||||
---
|
||||
import CardActions from '@ui/CardActions.astro';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardHeader from '@ui/CardHeader.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Badge from '@ui/Badge.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import CardActions from '@ui/CardActions.astro'
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import Badge from '@ui/Badge.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Driver Tour"
|
||||
pageHeader="Driver Tour"
|
||||
pageMenu="plugins.tour"
|
||||
pageLibs={['driver.js']}
|
||||
>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader title="Product Tour Example">
|
||||
<CardActions>
|
||||
<Button id="start-tour" text="Start Tour" icon="play" color="primary" element="button" />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p>
|
||||
Click the "Start Tour" button to begin an interactive tour of this page.
|
||||
The tour will guide you through different elements and features.
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card id="tour-card-1">
|
||||
<CardHeader title="Welcome Section" />
|
||||
<CardBody>
|
||||
<p>This is the first card in our tour. It demonstrates how Driver.js highlights elements on the page.</p>
|
||||
<ButtonList>
|
||||
<Button text="Action Button" color="primary" element="button" />
|
||||
<Button text="Secondary" element="button" />
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<DefaultLayout title="Driver Tour" pageHeader="Driver Tour" pageMenu="plugins.tour" pageLibs={['driver.js']}>
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader title="Product Tour Example">
|
||||
<CardActions>
|
||||
<Button id="start-tour" text="Start Tour" icon="play" color="primary" element="button" />
|
||||
</CardActions>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p>Click the "Start Tour" button to begin an interactive tour of this page. The tour will guide you through different elements and features.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card id="tour-card-1">
|
||||
<CardHeader title="Welcome Section" />
|
||||
<CardBody>
|
||||
<p>This is the first card in our tour. It demonstrates how Driver.js highlights elements on the page.</p>
|
||||
<ButtonList>
|
||||
<Button text="Action Button" color="primary" element="button" />
|
||||
<Button text="Secondary" element="button" />
|
||||
</ButtonList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<Card id="tour-card-2">
|
||||
<CardHeader title="Features Section" />
|
||||
<CardBody>
|
||||
<p>This card shows additional features and demonstrates the tour's ability to navigate between different
|
||||
elements.</p>
|
||||
<div class="form-selectgroup">
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="options" value="1" class="form-selectgroup-input" checked>
|
||||
<span class="form-selectgroup-label">Option 1</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="options" value="2" class="form-selectgroup-input">
|
||||
<span class="form-selectgroup-label">Option 2</span>
|
||||
</label>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card id="tour-card-3">
|
||||
<CardHeader title="Navigation Example" />
|
||||
<CardBody>
|
||||
<p>This is a full-width card that demonstrates how the tour works with larger elements.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Role</th>
|
||||
<th class="w-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>John Doe</td>
|
||||
<td><Badge text="Active" color="success" /></td>
|
||||
<td>Developer</td>
|
||||
<td>
|
||||
<Button text="Edit" size="sm" href="#" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Jane Smith</td>
|
||||
<td><Badge text="Pending" color="warning" /></td>
|
||||
<td>Designer</td>
|
||||
<td>
|
||||
<Button text="Edit" size="sm" href="#" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<Card id="tour-card-2">
|
||||
<CardHeader title="Features Section" />
|
||||
<CardBody>
|
||||
<p>This card shows additional features and demonstrates the tour's ability to navigate between different elements.</p>
|
||||
<div class="form-selectgroup">
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="options" value="1" class="form-selectgroup-input" checked />
|
||||
<span class="form-selectgroup-label">Option 1</span>
|
||||
</label>
|
||||
<label class="form-selectgroup-item">
|
||||
<input type="radio" name="options" value="2" class="form-selectgroup-input" />
|
||||
<span class="form-selectgroup-label">Option 2</span>
|
||||
</label>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Card id="tour-card-3">
|
||||
<CardHeader title="Navigation Example" />
|
||||
<CardBody>
|
||||
<p>This is a full-width card that demonstrates how the tour works with larger elements.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Role</th>
|
||||
<th class="w-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>John Doe</td>
|
||||
<td><Badge text="Active" color="success" /></td>
|
||||
<td>Developer</td>
|
||||
<td>
|
||||
<Button text="Edit" size="sm" href="#" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Jane Smith</td>
|
||||
<td><Badge text="Pending" color="warning" /></td>
|
||||
<td>Designer</td>
|
||||
<td>
|
||||
<Button text="Edit" size="sm" href="#" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-4">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="settings" size="48" />
|
||||
</div>
|
||||
<CardTitle>Settings</CardTitle>
|
||||
<p class="text-secondary">Configure your application settings here.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-4">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="settings" size="48" />
|
||||
</div>
|
||||
<CardTitle>Settings</CardTitle>
|
||||
<p class="text-secondary">Configure your application settings here.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-5">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="users" size="48" />
|
||||
</div>
|
||||
<CardTitle>Users</CardTitle>
|
||||
<p class="text-secondary">Manage your team members and permissions.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-5">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="users" size="48" />
|
||||
</div>
|
||||
<CardTitle>Users</CardTitle>
|
||||
<p class="text-secondary">Manage your team members and permissions.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-6">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="chart-bar" size="48" />
|
||||
</div>
|
||||
<CardTitle>Analytics</CardTitle>
|
||||
<p class="text-secondary">View your application statistics and reports.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<Card id="tour-card-6">
|
||||
<CardBody class="text-center">
|
||||
<div class="mb-3">
|
||||
<Icon name="chart-bar" size="48" />
|
||||
</div>
|
||||
<CardTitle>Analytics</CardTitle>
|
||||
<p class="text-secondary">View your application statistics and reports.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CaptureScript>
|
||||
<!-- BEGIN TOUR SCRIPT -->
|
||||
<script is:inline>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const driverObj = driver.js.driver({
|
||||
allowClose: true,
|
||||
overlayClickNext: true,
|
||||
showButtons: ['next', 'previous', 'close'],
|
||||
showProgress: true,
|
||||
steps: [
|
||||
{
|
||||
element: '#start-tour',
|
||||
popover: {
|
||||
title: 'Welcome to the Tour!',
|
||||
description: 'This button starts the interactive tour. Click it to begin exploring the page.',
|
||||
side: 'bottom',
|
||||
align: 'start'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-1',
|
||||
popover: {
|
||||
title: 'Welcome Section',
|
||||
description: 'This is the first card in our tour. It demonstrates how Driver.js highlights elements on the page.',
|
||||
side: 'right',
|
||||
align: 'start'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-2',
|
||||
popover: {
|
||||
title: 'Features Section',
|
||||
description: 'This card shows additional features and demonstrates the tour\'s ability to navigate between different elements.',
|
||||
side: 'left',
|
||||
align: 'start'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-3',
|
||||
popover: {
|
||||
title: 'Data Table',
|
||||
description: 'This table shows how Driver.js works with larger elements. You can highlight entire sections of your page.',
|
||||
side: 'top',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-4',
|
||||
popover: {
|
||||
title: 'Settings Card',
|
||||
description: 'This card demonstrates how the tour works with smaller, centered elements.',
|
||||
side: 'top',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-5',
|
||||
popover: {
|
||||
title: 'Users Card',
|
||||
description: 'Another example card showing user management features.',
|
||||
side: 'top',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#tour-card-6',
|
||||
popover: {
|
||||
title: 'Analytics Card',
|
||||
description: 'The final step of the tour. This card shows analytics features.',
|
||||
side: 'top',
|
||||
align: 'center'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
<CaptureScript>
|
||||
<!-- BEGIN TOUR SCRIPT -->
|
||||
<script is:inline>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const driverObj = driver.js.driver({
|
||||
allowClose: true,
|
||||
overlayClickNext: true,
|
||||
showButtons: ['next', 'previous', 'close'],
|
||||
showProgress: true,
|
||||
steps: [
|
||||
{
|
||||
element: '#start-tour',
|
||||
popover: {
|
||||
title: 'Welcome to the Tour!',
|
||||
description: 'This button starts the interactive tour. Click it to begin exploring the page.',
|
||||
side: 'bottom',
|
||||
align: 'start',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-1',
|
||||
popover: {
|
||||
title: 'Welcome Section',
|
||||
description: 'This is the first card in our tour. It demonstrates how Driver.js highlights elements on the page.',
|
||||
side: 'right',
|
||||
align: 'start',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-2',
|
||||
popover: {
|
||||
title: 'Features Section',
|
||||
description: "This card shows additional features and demonstrates the tour's ability to navigate between different elements.",
|
||||
side: 'left',
|
||||
align: 'start',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-3',
|
||||
popover: {
|
||||
title: 'Data Table',
|
||||
description: 'This table shows how Driver.js works with larger elements. You can highlight entire sections of your page.',
|
||||
side: 'top',
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-4',
|
||||
popover: {
|
||||
title: 'Settings Card',
|
||||
description: 'This card demonstrates how the tour works with smaller, centered elements.',
|
||||
side: 'top',
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-5',
|
||||
popover: {
|
||||
title: 'Users Card',
|
||||
description: 'Another example card showing user management features.',
|
||||
side: 'top',
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
{
|
||||
element: '#tour-card-6',
|
||||
popover: {
|
||||
title: 'Analytics Card',
|
||||
description: 'The final step of the tour. This card shows analytics features.',
|
||||
side: 'top',
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const startButton = document.getElementById('start-tour');
|
||||
if (startButton) {
|
||||
startButton.addEventListener('click', function () {
|
||||
driverObj.drive();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- END TOUR SCRIPT -->
|
||||
</CaptureScript>
|
||||
const startButton = document.getElementById('start-tour')
|
||||
if (startButton) {
|
||||
startButton.addEventListener('click', function () {
|
||||
driverObj.drive()
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!-- END TOUR SCRIPT -->
|
||||
</CaptureScript>
|
||||
</DefaultLayout>
|
||||
|
||||
+45
-57
@@ -1,69 +1,57 @@
|
||||
---
|
||||
// progress and online_counter assigned per person but never rendered — omitted.
|
||||
// rounded=true on avatar has no effect — Avatar has no `rounded` prop.
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Card from '@ui/Card.astro';
|
||||
import CardBody from '@ui/CardBody.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Pagination from '@ui/Pagination.astro';
|
||||
import people from '@data/people.json';
|
||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Pagination from '@ui/Pagination.astro'
|
||||
import people from '@data/people.json'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
job_title?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
job_title?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const users = (people as Person[]).slice(0, 18);
|
||||
const users = (people as Person[]).slice(0, 18)
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Users list"
|
||||
pageHeader="Users"
|
||||
actions="users"
|
||||
description="1-18 of 413 people"
|
||||
pageMenu="extra.users"
|
||||
>
|
||||
<div class="row row-cards">
|
||||
{
|
||||
users.map((person, idx) => {
|
||||
const index = idx + 1;
|
||||
return (
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<Card>
|
||||
<CardBody class="p-4 text-center">
|
||||
<Avatar size="xl" person={person} class="mb-3" />
|
||||
<h3 class="m-0 mb-1">
|
||||
<a href="#">{person.full_name}</a>
|
||||
</h3>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
<DefaultLayout title="Users list" pageHeader="Users" actions="users" description="1-18 of 413 people" pageMenu="extra.users">
|
||||
<div class="row row-cards">
|
||||
{
|
||||
users.map((person, idx) => {
|
||||
const index = idx + 1
|
||||
return (
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<Card>
|
||||
<CardBody class="p-4 text-center">
|
||||
<Avatar size="xl" person={person} class="mb-3" />
|
||||
<h3 class="m-0 mb-1">
|
||||
<a href="#">{person.full_name}</a>
|
||||
</h3>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
|
||||
<div class="mt-3">
|
||||
{index === 1 ? (
|
||||
<span class="badge bg-purple-lt">Owner</span>
|
||||
) : index < 5 ? (
|
||||
<span class="badge bg-green-lt">Admin</span>
|
||||
) : null}
|
||||
</div>
|
||||
</CardBody>
|
||||
<div class="d-flex">
|
||||
<a href="#" class="card-btn">
|
||||
<Icon name="mail" color="muted" class="me-2" /> Email
|
||||
</a>
|
||||
<a href="#" class="card-btn">
|
||||
<Icon name="phone" color="muted" class="me-2" /> Call
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div class="mt-3">{index === 1 ? <span class="badge bg-purple-lt">Owner</span> : index < 5 ? <span class="badge bg-green-lt">Admin</span> : null}</div>
|
||||
</CardBody>
|
||||
<div class="d-flex">
|
||||
<a href="#" class="card-btn">
|
||||
<Icon name="mail" color="muted" class="me-2" /> Email
|
||||
</a>
|
||||
<a href="#" class="card-btn">
|
||||
<Icon name="phone" color="muted" class="me-2" /> Call
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="d-flex mt-4">
|
||||
<Pagination class="ms-auto" />
|
||||
</div>
|
||||
<div class="d-flex mt-4">
|
||||
<Pagination class="ms-auto" />
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Captures slot markup (typically a modal) into page-modals; BaseLayout emits
|
||||
// it at the end of <body> via <PageModals />.
|
||||
// Registration is synchronous (a promise) — see page-modals.ts.
|
||||
import { addPageModal } from '@shared/lib/page-modals';
|
||||
import { addPageModal } from '@shared/lib/page-modals'
|
||||
|
||||
addPageModal(Astro.slots.render('default'));
|
||||
addPageModal(Astro.slots.render('default'))
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Captures slot markup (typically <script>) into page-scripts; BaseLayout /
|
||||
// DocsLayout emit it at the end of <body> via <PageScripts />.
|
||||
// Registration is synchronous (a promise) — see page-scripts.ts.
|
||||
import { addPageScript } from '@shared/lib/page-scripts';
|
||||
import { addPageScript } from '@shared/lib/page-scripts'
|
||||
|
||||
addPageScript(Astro.slots.render('default'));
|
||||
addPageScript(Astro.slots.render('default'))
|
||||
---
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// Separate component so drainPageScripts() executes only at this point of the
|
||||
// render stream (after the page content render has STARTED — registrations are
|
||||
// synchronous, script content may be a promise).
|
||||
import { drainPageScripts } from '@shared/lib/page-scripts';
|
||||
import { drainPageScripts } from '@shared/lib/page-scripts'
|
||||
|
||||
const scripts = await Promise.all(drainPageScripts());
|
||||
const scripts = await Promise.all(drainPageScripts())
|
||||
---
|
||||
|
||||
{scripts.length > 0 && <Fragment set:html={scripts.join('\n')} />}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
import Button from '@ui/Button.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import Button from '@ui/Button.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
|
||||
const person = people[0];
|
||||
const person = people[0]
|
||||
---
|
||||
|
||||
<form class="card card-md" action="./" method="get" autocomplete="off" novalidate>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<CardTitle as="h2">Account Locked</CardTitle>
|
||||
<p class="text-secondary">Please enter your password to unlock your account</p>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<CardTitle as="h2">Account Locked</CardTitle>
|
||||
<p class="text-secondary">Please enter your password to unlock your account</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<Avatar person={person} size="xl" class="mb-3" />
|
||||
<h3>{person.full_name}</h3>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<Avatar person={person} size="xl" class="mb-3" />
|
||||
<h3>{person.full_name}</h3>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<input type="password" class="form-control" placeholder="Password…" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<input type="password" class="form-control" placeholder="Password…" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button color="primary" block icon="lock-open" text="Unlock" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button color="primary" block icon="lock-open" text="Unlock" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
---
|
||||
import CardStamp from '@ui/CardStamp.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import CardStamp from '@ui/CardStamp.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
---
|
||||
|
||||
<div class="card bg-primary text-primary-fg">
|
||||
<CardStamp icon="star" color="white" textColor="primary" />
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with background and icon</CardTitle>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat,
|
||||
ipsa iusto, modi nostrum recusandae reiciendis saepe.
|
||||
</p>
|
||||
</div>
|
||||
<CardStamp icon="star" color="white" textColor="primary" />
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with background and icon</CardTitle>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat, ipsa iusto, modi nostrum recusandae reiciendis saepe.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
---
|
||||
import photos from '@data/photos.json';
|
||||
import photos from '@data/photos.json'
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
right?: boolean;
|
||||
imgId?: number;
|
||||
title?: string
|
||||
right?: boolean
|
||||
imgId?: number
|
||||
}
|
||||
|
||||
const { title, right, imgId = 1 } = Astro.props;
|
||||
const { title, right, imgId = 1 } = Astro.props
|
||||
|
||||
const photo = (photos as { file: string; title: string }[])[imgId];
|
||||
const imgClass = `w-100 h-100 object-cover ${right ? 'card-img-end' : 'card-img-start'}`;
|
||||
const photo = (photos as { file: string; title: string }[])[imgId]
|
||||
const imgClass = `w-100 h-100 object-cover ${right ? 'card-img-end' : 'card-img-start'}`
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="row row-0">
|
||||
<div class={`col-3${right ? ' order-md-last' : ''}`}>
|
||||
<!-- Photo -->
|
||||
<img src={`./static/photos/${photo.file}`} class={imgClass} alt={photo.title} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card-body">
|
||||
{title && <h3 class="card-title" set:html={title} />}
|
||||
<p class="text-secondary">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam deleniti fugit incidunt, iste, itaque minima
|
||||
neque pariatur perferendis sed suscipit velit vitae voluptatem.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-0">
|
||||
<div class={`col-3${right ? ' order-md-last' : ''}`}>
|
||||
<!-- Photo -->
|
||||
<img src={`./static/photos/${photo.file}`} class={imgClass} alt={photo.title} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card-body">
|
||||
{title && <h3 class="card-title" set:html={title} />}
|
||||
<p class="text-secondary">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam deleniti fugit incidunt, iste, itaque minima neque pariatur perferendis sed suscipit velit vitae voluptatem.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
---
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="ribbon bg-red">NEW</div>
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with text ribbon</CardTitle>
|
||||
<p class="text-secondary">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat,
|
||||
ipsa iusto, modi nostrum recusandae reiciendis saepe.
|
||||
</p>
|
||||
</div>
|
||||
<div class="ribbon bg-red">NEW</div>
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with text ribbon</CardTitle>
|
||||
<p class="text-secondary">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat, ipsa iusto, modi nostrum recusandae reiciendis saepe.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
---
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="ribbon ribbon-top bg-yellow"><Icon name="star" /></div>
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with top ribbon</CardTitle>
|
||||
<p class="text-secondary">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat,
|
||||
ipsa iusto, modi nostrum recusandae reiciendis saepe.
|
||||
</p>
|
||||
</div>
|
||||
<div class="ribbon ribbon-top bg-yellow"><Icon name="star" /></div>
|
||||
<div class="card-body">
|
||||
<CardTitle>Card with top ribbon</CardTitle>
|
||||
<p class="text-secondary">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto at consectetur culpa ducimus eum fuga fugiat, ipsa iusto, modi nostrum recusandae reiciendis saepe.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
---
|
||||
// Tabs/content blocks are HTML strings (different order when `bottom`).
|
||||
interface Props {
|
||||
/** tabs-count — present but unused */
|
||||
count?: number;
|
||||
id?: string;
|
||||
bottom?: boolean;
|
||||
borderless?: boolean;
|
||||
/** tabs-count — present but unused */
|
||||
count?: number
|
||||
id?: string
|
||||
bottom?: boolean
|
||||
borderless?: boolean
|
||||
}
|
||||
|
||||
const { id = 'top', bottom, borderless } = Astro.props;
|
||||
const { id = 'top', bottom, borderless } = Astro.props
|
||||
|
||||
const tabs = ['Activity', 'Profile', 'Settings'];
|
||||
const tabs = ['Activity', 'Profile', 'Settings']
|
||||
|
||||
const tabsHtml = `
|
||||
<!-- Cards navigation -->
|
||||
<ul class="nav nav-tabs${bottom ? ' nav-tabs-bottom' : ''}">
|
||||
${tabs
|
||||
.map(
|
||||
(tab, i) =>
|
||||
` <li class="nav-item"><a href="#tab-${id}-${i + 1}" class="nav-link${i === 0 ? ' active' : ''}" data-bs-toggle="tab">${tab}</a></li>`,
|
||||
)
|
||||
.join('\n')}
|
||||
</ul>`;
|
||||
${tabs.map((tab, i) => ` <li class="nav-item"><a href="#tab-${id}-${i + 1}" class="nav-link${i === 0 ? ' active' : ''}" data-bs-toggle="tab">${tab}</a></li>`).join('\n')}
|
||||
</ul>`
|
||||
|
||||
const tabsContentHtml = `
|
||||
<div class="tab-content">
|
||||
${tabs
|
||||
.map(
|
||||
(tab, i) => ` <!-- Content of card #${tab} -->
|
||||
.map(
|
||||
(tab, i) => ` <!-- Content of card #${tab} -->
|
||||
<div id="tab-${id}-${i + 1}" class="card tab-pane${i === 0 ? ' active show' : ''}">
|
||||
<div class="card-body">
|
||||
<div class="card-title">${tab}</div>
|
||||
@@ -36,24 +31,24 @@ ${tabs
|
||||
</p>
|
||||
</div>
|
||||
</div>`,
|
||||
)
|
||||
.join('\n')}
|
||||
</div>`;
|
||||
)
|
||||
.join('\n')}
|
||||
</div>`
|
||||
---
|
||||
|
||||
<!-- Cards with tabs component -->
|
||||
<div class={`card-tabs${borderless ? ' border-0' : ''}`}>
|
||||
{
|
||||
bottom ? (
|
||||
<Fragment>
|
||||
<Fragment set:html={tabsContentHtml} />
|
||||
<Fragment set:html={tabsHtml} />
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Fragment set:html={tabsHtml} />
|
||||
<Fragment set:html={tabsContentHtml} />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
{
|
||||
bottom ? (
|
||||
<Fragment>
|
||||
<Fragment set:html={tabsContentHtml} />
|
||||
<Fragment set:html={tabsHtml} />
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Fragment set:html={tabsHtml} />
|
||||
<Fragment set:html={tabsContentHtml} />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,103 +1,80 @@
|
||||
---
|
||||
// Photos filtered to horizontal=true, then sliced by offset/limit.
|
||||
import photos from '@data/photos.json';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import photos from '@data/photos.json'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
|
||||
interface Props {
|
||||
id?: string;
|
||||
title?: string;
|
||||
indicators?: boolean;
|
||||
indicatorsThumb?: boolean;
|
||||
indicatorsThumbRatio?: boolean;
|
||||
indicatorsDot?: boolean;
|
||||
indicatorsVertical?: boolean;
|
||||
controls?: boolean;
|
||||
captions?: boolean;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
fade?: boolean;
|
||||
id?: string
|
||||
title?: string
|
||||
indicators?: boolean
|
||||
indicatorsThumb?: boolean
|
||||
indicatorsThumbRatio?: boolean
|
||||
indicatorsDot?: boolean
|
||||
indicatorsVertical?: boolean
|
||||
controls?: boolean
|
||||
captions?: boolean
|
||||
offset?: number
|
||||
limit?: number
|
||||
fade?: boolean
|
||||
}
|
||||
|
||||
const {
|
||||
id: idProp,
|
||||
title = 'Carousel',
|
||||
indicators,
|
||||
indicatorsThumb,
|
||||
indicatorsThumbRatio,
|
||||
indicatorsDot,
|
||||
indicatorsVertical,
|
||||
controls,
|
||||
captions,
|
||||
offset = 0,
|
||||
limit = 5,
|
||||
fade,
|
||||
} = Astro.props;
|
||||
const { id: idProp, title = 'Carousel', indicators, indicatorsThumb, indicatorsThumbRatio, indicatorsDot, indicatorsVertical, controls, captions, offset = 0, limit = 5, fade } = Astro.props
|
||||
|
||||
const carouselId = idProp ?? 'carousel';
|
||||
const filteredPhotos = (photos as { file: string; horizontal?: boolean }[]).filter((p) => p.horizontal);
|
||||
const slides = filteredPhotos.slice(offset, offset + limit);
|
||||
const carouselId = idProp ?? 'carousel'
|
||||
const filteredPhotos = (photos as { file: string; horizontal?: boolean }[]).filter((p) => p.horizontal)
|
||||
const slides = filteredPhotos.slice(offset, offset + limit)
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id={`carousel-${carouselId}`} class={`carousel slide${fade ? ' carousel-fade' : ''}`} data-bs-ride="carousel">
|
||||
{
|
||||
indicators && (
|
||||
<div
|
||||
class={`carousel-indicators${indicatorsVertical ? ' carousel-indicators-vertical' : ''}${
|
||||
indicatorsDot ? ' carousel-indicators-dot' : indicatorsThumb ? ' carousel-indicators-thumb' : ''
|
||||
}`}
|
||||
>
|
||||
{slides.map((photo, i) => (
|
||||
<button
|
||||
type="button"
|
||||
data-bs-target={`#carousel-${carouselId}`}
|
||||
data-bs-slide-to={`${i}`}
|
||||
class={`${indicatorsThumbRatio ? ' ratio ratio-4x3' : ''}${i === 0 ? ' active' : ''}`}
|
||||
style={indicatorsThumb ? `background-image: url(./static/photos/${photo.file})` : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id={`carousel-${carouselId}`} class={`carousel slide${fade ? ' carousel-fade' : ''}`} data-bs-ride="carousel">
|
||||
{
|
||||
indicators && (
|
||||
<div class={`carousel-indicators${indicatorsVertical ? ' carousel-indicators-vertical' : ''}${indicatorsDot ? ' carousel-indicators-dot' : indicatorsThumb ? ' carousel-indicators-thumb' : ''}`}>
|
||||
{slides.map((photo, i) => (
|
||||
<button type="button" data-bs-target={`#carousel-${carouselId}`} data-bs-slide-to={`${i}`} class={`${indicatorsThumbRatio ? ' ratio ratio-4x3' : ''}${i === 0 ? ' active' : ''}`} style={indicatorsThumb ? `background-image: url(./static/photos/${photo.file})` : undefined} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="carousel-inner">
|
||||
{
|
||||
slides.map((photo, i) => (
|
||||
<div class={`carousel-item${i === 0 ? ' active' : ''}`}>
|
||||
<img class="d-block w-100" alt="" src={`./static/photos/${photo.file}`} />
|
||||
<div class="carousel-inner">
|
||||
{
|
||||
slides.map((photo, i) => (
|
||||
<div class={`carousel-item${i === 0 ? ' active' : ''}`}>
|
||||
<img class="d-block w-100" alt="" src={`./static/photos/${photo.file}`} />
|
||||
|
||||
{captions && (
|
||||
<Fragment>
|
||||
<div class="carousel-caption-background d-none d-md-block" />
|
||||
<div class="carousel-caption d-none d-md-block">
|
||||
<h3>Slide label</h3>
|
||||
<p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{captions && (
|
||||
<Fragment>
|
||||
<div class="carousel-caption-background d-none d-md-block" />
|
||||
<div class="carousel-caption d-none d-md-block">
|
||||
<h3>Slide label</h3>
|
||||
<p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
controls && (
|
||||
<Fragment>
|
||||
<a class="carousel-control-prev" href={`#carousel-${carouselId}`} role="button" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true" />
|
||||
<span class="visually-hidden">Previous</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href={`#carousel-${carouselId}`} role="button" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true" />
|
||||
<span class="visually-hidden">Next</span>
|
||||
</a>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
controls && (
|
||||
<Fragment>
|
||||
<a class="carousel-control-prev" href={`#carousel-${carouselId}`} role="button" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true" />
|
||||
<span class="visually-hidden">Previous</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href={`#carousel-${carouselId}`} role="button" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true" />
|
||||
<span class="visually-hidden">Next</span>
|
||||
</a>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
// Raw CSS text inside .card-code (no highlight markup).
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
|
||||
const code = `
|
||||
.card-footer {
|
||||
@@ -10,12 +10,12 @@ const code = `
|
||||
border-radius: 0 0 1 2;
|
||||
}
|
||||
}
|
||||
`;
|
||||
`
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<CardTitle>Card with code</CardTitle>
|
||||
</div>
|
||||
<div class="card-code">{code}</div>
|
||||
<div class="card-header">
|
||||
<CardTitle>Card with code</CardTitle>
|
||||
</div>
|
||||
<div class="card-code">{code}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,58 +1,50 @@
|
||||
---
|
||||
import Button from '@ui/Button.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import Button from '@ui/Button.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
|
||||
const months = Array.from({ length: 12 }, (_, i) => i + 1);
|
||||
const years = Array.from({ length: 11 }, (_, i) => 2020 + i);
|
||||
const months = Array.from({ length: 12 }, (_, i) => i + 1)
|
||||
const years = Array.from({ length: 11 }, (_, i) => 2020 + i)
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Card number</div>
|
||||
<input
|
||||
type="text"
|
||||
name="input-mask"
|
||||
class="form-control"
|
||||
data-mask="0000 0000 0000 0000"
|
||||
data-mask-visible="true"
|
||||
placeholder="0000 0000 0000 0000"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Card number</div>
|
||||
<input type="text" name="input-mask" class="form-control" data-mask="0000 0000 0000 0000" data-mask-visible="true" placeholder="0000 0000 0000 0000" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Card name</div>
|
||||
<input type="text" class="form-control" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Card name</div>
|
||||
<input type="text" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<FormGroup label="Expiration date">
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<select class="form-select">
|
||||
{months.map((month) => <option value={month}>{month}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select">
|
||||
{years.map((year) => <option value={year}>{year}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">CVV</div>
|
||||
<input type="number" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<FormGroup label="Expiration date">
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<select class="form-select">
|
||||
{months.map((month) => <option value={month}>{month}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select">
|
||||
{years.map((year) => <option value={year}>{year}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">CVV</div>
|
||||
<input type="number" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<Button text="Pay now" color="primary" block />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Button text="Pay now" color="primary" block />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,59 +1,52 @@
|
||||
---
|
||||
import Subheader from '@ui/Subheader.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Trending from '@ui/Trending.astro';
|
||||
import Subheader from '@ui/Subheader.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Trending from '@ui/Trending.astro'
|
||||
|
||||
interface Props {
|
||||
color?: string;
|
||||
icon?: string;
|
||||
/** lt — light avatar background (bg-{color}-lt) instead of bg-{color} text-white */
|
||||
lt?: boolean;
|
||||
title?: string;
|
||||
description?: string;
|
||||
/** change-value — passed to Trending (keeps its +/- sign) */
|
||||
changeValue?: string;
|
||||
/** change-value-unit — trending unit; defaults to '%' in Trending when unset */
|
||||
changeValueUnit?: string;
|
||||
class?: string;
|
||||
color?: string
|
||||
icon?: string
|
||||
/** lt — light avatar background (bg-{color}-lt) instead of bg-{color} text-white */
|
||||
lt?: boolean
|
||||
title?: string
|
||||
description?: string
|
||||
/** change-value — passed to Trending (keeps its +/- sign) */
|
||||
changeValue?: string
|
||||
/** change-value-unit — trending unit; defaults to '%' in Trending when unset */
|
||||
changeValueUnit?: string
|
||||
class?: string
|
||||
}
|
||||
|
||||
const {
|
||||
color,
|
||||
icon,
|
||||
lt,
|
||||
title = '1700',
|
||||
description = 'Users',
|
||||
changeValue,
|
||||
changeValueUnit,
|
||||
class: className,
|
||||
} = Astro.props;
|
||||
const { color, icon, lt, title = '1700', description = 'Users', changeValue, changeValueUnit, class: className } = Astro.props
|
||||
|
||||
const avatarClass = [color ? `bg-${color}${lt ? '-lt' : ' text-white'}` : '', 'avatar', 'avatar-square'];
|
||||
const avatarClass = [color ? `bg-${color}${lt ? '-lt' : ' text-white'}` : '', 'avatar', 'avatar-square']
|
||||
---
|
||||
|
||||
<div class:list={['card card-sm', className]}>
|
||||
<div class="card-body p-3">
|
||||
<div class="row align-items-center">
|
||||
{
|
||||
icon && (
|
||||
<div class="col-auto">
|
||||
<span class:list={avatarClass}><Icon name={icon} /></span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class="card-body p-3">
|
||||
<div class="row align-items-center">
|
||||
{
|
||||
icon && (
|
||||
<div class="col-auto">
|
||||
<span class:list={avatarClass}>
|
||||
<Icon name={icon} />
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="col">
|
||||
<Subheader>
|
||||
{title}
|
||||
</Subheader>
|
||||
<div class="h3 m-0 p-0">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<Subheader>
|
||||
{title}
|
||||
</Subheader>
|
||||
<div class="h3 m-0 p-0">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<Trending value={changeValue as unknown as number} unit={changeValueUnit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<Trending value={changeValue as unknown as number} unit={changeValueUnit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
import AvatarList from '@ui/AvatarList.astro';
|
||||
import Button from '@ui/Button.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import AvatarList from '@ui/AvatarList.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
---
|
||||
|
||||
<div class="card card-lg card-dashed card-transparent">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<AvatarList stacked={true} />
|
||||
</div>
|
||||
<CardTitle>No Team Members</CardTitle>
|
||||
<p class="text-secondary">Invite your team to<br />collaborate on this project.</p>
|
||||
<div class="mt-4">
|
||||
<Button text="Invite Members" color="primary" icon="plus" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<AvatarList stacked={true} />
|
||||
</div>
|
||||
<CardTitle>No Team Members</CardTitle>
|
||||
<p class="text-secondary">Invite your team to<br />collaborate on this project.</p>
|
||||
<div class="mt-4">
|
||||
<Button text="Invite Members" color="primary" icon="plus" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import FormFooter from '@ui/FormFooter.astro'
|
||||
import Button from '@ui/Button.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
---
|
||||
// `photo` and `index` are passed explicitly from the parent loop.
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import { randomNumber, timeagoLabel } from '@shared/lib/pseudo-random';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import { randomNumber, timeagoLabel } from '@shared/lib/pseudo-random'
|
||||
|
||||
interface Photo {
|
||||
file: string;
|
||||
[key: string]: unknown;
|
||||
file: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
photo: Photo;
|
||||
person: Person;
|
||||
/** 1-based loop index — seeds the deterministic pseudo-random values */
|
||||
index: number;
|
||||
hideLikes?: boolean;
|
||||
photo: Photo
|
||||
person: Person
|
||||
/** 1-based loop index — seeds the deterministic pseudo-random values */
|
||||
index: number
|
||||
hideLikes?: boolean
|
||||
}
|
||||
|
||||
const { photo, person, index, hideLikes } = Astro.props;
|
||||
const { photo, person, index, hideLikes } = Astro.props
|
||||
|
||||
// Heart filled when (index > 2 && index < 9) || index === 10.
|
||||
const heartClass = (index > 2 && index < 9) || index === 10 ? 'icon-filled text-red' : undefined;
|
||||
const heartClass = (index > 2 && index < 9) || index === 10 ? 'icon-filled text-red' : undefined
|
||||
---
|
||||
|
||||
<div class="card card-sm">
|
||||
<a href="#" class="d-block"><img src={`./static/photos/${photo.file}`} class="card-img-top" /></a>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<Avatar person={person} class="me-3 rounded" />
|
||||
<div>
|
||||
<div>{person.full_name}</div>
|
||||
<div class="text-secondary">{timeagoLabel(index, 10)}</div>
|
||||
</div>
|
||||
{
|
||||
!hideLikes && (
|
||||
<div class="ms-auto">
|
||||
<a href="#" class="text-secondary">
|
||||
<Icon name="eye" />
|
||||
{randomNumber(index, 300, 600)}
|
||||
</a>
|
||||
<a href="#" class="ms-3 text-secondary">
|
||||
<Icon name="heart" class={heartClass} />
|
||||
{randomNumber(index, 20, 100)}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="d-block"><img src={`./static/photos/${photo.file}`} class="card-img-top" /></a>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<Avatar person={person} class="me-3 rounded" />
|
||||
<div>
|
||||
<div>{person.full_name}</div>
|
||||
<div class="text-secondary">{timeagoLabel(index, 10)}</div>
|
||||
</div>
|
||||
{
|
||||
!hideLikes && (
|
||||
<div class="ms-auto">
|
||||
<a href="#" class="text-secondary">
|
||||
<Icon name="eye" />
|
||||
{randomNumber(index, 300, 600)}
|
||||
</a>
|
||||
<a href="#" class="ms-3 text-secondary">
|
||||
<Icon name="heart" class={heartClass} />
|
||||
{randomNumber(index, 20, 100)}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,52 +1,41 @@
|
||||
---
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import icons from '@data/icons.json';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import icons from '@data/icons.json'
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
type?: 'outline' | 'filled';
|
||||
title?: string
|
||||
type?: 'outline' | 'filled'
|
||||
}
|
||||
|
||||
const { title, type = 'outline' } = Astro.props;
|
||||
const { title, type = 'outline' } = Astro.props
|
||||
|
||||
// Dev preview limits icons to 20 (see BaseLayout).
|
||||
// The limit applies before the svg[type] filter — the "filled" card shows only filled icons among the first 20.
|
||||
const limit = 20;
|
||||
const limit = 20
|
||||
|
||||
const entries = Object.entries(icons as Record<string, { svg?: Record<string, string> }>).slice(
|
||||
0,
|
||||
limit,
|
||||
);
|
||||
const entries = Object.entries(icons as Record<string, { svg?: Record<string, string | null> }>).slice(0, limit)
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">{title}</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
entries.map(
|
||||
([iconName, icon]) =>
|
||||
icon.svg?.[type] && (
|
||||
<a
|
||||
href={`https://tabler.io/icons/icon/${iconName}`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="demo-icons-list-item"
|
||||
title={iconName}
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
>
|
||||
<Icon name={iconName} type={type} />
|
||||
</a>
|
||||
),
|
||||
)
|
||||
}
|
||||
{/* 21 empty divs (flexbox filler) */}
|
||||
{Array.from({ length: 21 }).map(() => <div />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<div class="card-title">{title}</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="demo-icons-list-wrap">
|
||||
<div class="demo-icons-list">
|
||||
{
|
||||
entries.map(
|
||||
([iconName, icon]) =>
|
||||
icon.svg?.[type] && (
|
||||
<a href={`https://tabler.io/icons/icon/${iconName}`} target="_blank" rel="noopener" class="demo-icons-list-item" title={iconName} data-bs-toggle="tooltip" data-bs-placement="top">
|
||||
<Icon name={iconName} type={type} />
|
||||
</a>
|
||||
),
|
||||
)
|
||||
}
|
||||
{/* 21 empty divs (flexbox filler) */}
|
||||
{Array.from({ length: 21 }).map(() => <div />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card card-lg">
|
||||
|
||||
@@ -1,54 +1,50 @@
|
||||
---
|
||||
// Feature names are a fixed list; the `features` string is split per-character into a 1/0 availability mask.
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
|
||||
interface Props {
|
||||
price?: string;
|
||||
users?: string | number;
|
||||
category?: string;
|
||||
features?: string;
|
||||
/** featured-color — ribbon + button color (e.g. "green") */
|
||||
featuredColor?: string;
|
||||
price?: string
|
||||
users?: string | number
|
||||
category?: string
|
||||
features?: string
|
||||
/** featured-color — ribbon + button color (e.g. "green") */
|
||||
featuredColor?: string
|
||||
}
|
||||
|
||||
const { price = '79', users = 10, category = 'Enterprise', features = '1000', featuredColor } = Astro.props;
|
||||
const { price = '79', users = 10, category = 'Enterprise', features = '1000', featuredColor } = Astro.props
|
||||
|
||||
const featureNames = ['Sharing Tools', 'Design Tools', 'Private Messages', 'Twitter API'];
|
||||
const availableFeatures = features.split('');
|
||||
const featureNames = ['Sharing Tools', 'Design Tools', 'Private Messages', 'Twitter API']
|
||||
const availableFeatures = features.split('')
|
||||
---
|
||||
|
||||
<div class="card card-md">
|
||||
{
|
||||
featuredColor && (
|
||||
<div class={`ribbon ribbon-top ribbon-bookmark bg-${featuredColor}`}>
|
||||
<Icon name="star" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
featuredColor && (
|
||||
<div class={`ribbon ribbon-top ribbon-bookmark bg-${featuredColor}`}>
|
||||
<Icon name="star" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="card-body text-center">
|
||||
<div class="text-uppercase text-secondary fw-medium">{category}</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="text-uppercase text-secondary fw-medium">{category}</div>
|
||||
|
||||
<div class="display-5 fw-bold my-3">${price}</div>
|
||||
<div class="display-5 fw-bold my-3">${price}</div>
|
||||
|
||||
<ul class="list-unstyled lh-lg">
|
||||
<li><strong>{users}</strong> Users</li>
|
||||
{
|
||||
featureNames.map((feature, i) => (
|
||||
<li>
|
||||
{availableFeatures[i] === '1' ? (
|
||||
<Icon name="check" class="me-1 text-success" />
|
||||
) : (
|
||||
<Icon name="x" class="me-1 text-danger" />
|
||||
)}
|
||||
{feature}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<ul class="list-unstyled lh-lg">
|
||||
<li><strong>{users}</strong> Users</li>
|
||||
{
|
||||
featureNames.map((feature, i) => (
|
||||
<li>
|
||||
{availableFeatures[i] === '1' ? <Icon name="check" class="me-1 text-success" /> : <Icon name="x" class="me-1 text-danger" />}
|
||||
{feature}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="#" class={`btn${featuredColor ? ` btn-${featuredColor}` : ''} w-100`}>Choose plan</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4">
|
||||
<a href="#" class={`btn${featuredColor ? ` btn-${featuredColor}` : ''} w-100`}>Choose plan</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
---
|
||||
// TODO: fallback to people[1] when person omitted — unused on card-gradients, so `person` is required here.
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
job_title?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
job_title?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
person: Person;
|
||||
color?: string;
|
||||
person: Person
|
||||
color?: string
|
||||
}
|
||||
|
||||
const { person, color = 'yellow' } = Astro.props;
|
||||
const { person, color = 'yellow' } = Astro.props
|
||||
---
|
||||
|
||||
<div class={`card card-gradient card-gradient-${color}`}>
|
||||
<div class="card-body text-center py-6">
|
||||
<div class="position-absolute top-0 end-0 p-1">
|
||||
<div class="btn btn-action"><Icon name="star" color="yellow" type="filled" /></div>
|
||||
</div>
|
||||
<div>
|
||||
<Avatar size="2xl" person={person} shape="rounded-circle" />
|
||||
</div>
|
||||
<div class="h1 mt-4 mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
<div class="card-body text-center py-6">
|
||||
<div class="position-absolute top-0 end-0 p-1">
|
||||
<div class="btn btn-action"><Icon name="star" color="yellow" type="filled" /></div>
|
||||
</div>
|
||||
<div>
|
||||
<Avatar size="2xl" person={person} shape="rounded-circle" />
|
||||
</div>
|
||||
<div class="h1 mt-4 mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
|
||||
<ButtonList class="justify-content-center mt-3">
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Message" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="message" /></a>
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Phone" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="phone" /></a>
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Email" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="mail" /></a>
|
||||
</ButtonList>
|
||||
</div>
|
||||
<ButtonList class="justify-content-center mt-3">
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Message" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="message" /></a>
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Phone" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="phone" /></a>
|
||||
<a href="#" class="btn btn-icon btn-pill" title="Email" data-bs-toggle="tooltip" data-bs-placement="top"><Icon name="mail" /></a>
|
||||
</ButtonList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +1,48 @@
|
||||
---
|
||||
// NOTE: widgets page passes `value=` but only `percentage` (default 20) drives the bar — `value` is accepted but ignored.
|
||||
import Progress from '@ui/Progress.astro';
|
||||
import AvatarList from '@ui/AvatarList.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Progress from '@ui/Progress.astro'
|
||||
import AvatarList from '@ui/AvatarList.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
badge?: string;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
percentage?: number | string;
|
||||
percentageColor?: string;
|
||||
due?: string;
|
||||
/** unused — see note above */
|
||||
value?: number | string;
|
||||
title?: string
|
||||
badge?: string
|
||||
offset?: number
|
||||
limit?: number
|
||||
percentage?: number | string
|
||||
percentageColor?: string
|
||||
due?: string
|
||||
/** unused — see note above */
|
||||
value?: number | string
|
||||
}
|
||||
|
||||
const {
|
||||
title = 'Task Title',
|
||||
badge,
|
||||
offset = 40,
|
||||
limit = 7,
|
||||
percentage = 20,
|
||||
percentageColor = 'green',
|
||||
due = '2 days',
|
||||
} = Astro.props;
|
||||
const { title = 'Task Title', badge, offset = 40, limit = 7, percentage = 20, percentageColor = 'green', due = '2 days' } = Astro.props
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<Progress value={percentage} class="card-progress" color={percentageColor} />
|
||||
<div class="card-body">
|
||||
<CardTitle>
|
||||
<a href="#">{title}</a>
|
||||
{badge && <Fragment> <span class="badge ms-2">{badge}</span></Fragment>}
|
||||
</CardTitle>
|
||||
<Progress value={percentage} class="card-progress" color={percentageColor} />
|
||||
<div class="card-body">
|
||||
<CardTitle>
|
||||
<a href="#">{title}</a>
|
||||
{
|
||||
badge && (
|
||||
<Fragment>
|
||||
{' '}
|
||||
<span class="badge ms-2">{badge}</span>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
</CardTitle>
|
||||
|
||||
<AvatarList offset={offset} limit={limit} stacked class="mb-3" />
|
||||
<AvatarList offset={offset} limit={limit} stacked class="mb-3" />
|
||||
|
||||
<div class="card-meta d-flex justify-content-between">
|
||||
<div class="d-flex align-items-center">
|
||||
<Icon name="check" class="me-2" />
|
||||
<span>5/10</span>
|
||||
</div>
|
||||
<span>Due {due}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-meta d-flex justify-content-between">
|
||||
<div class="d-flex align-items-center">
|
||||
<Icon name="check" class="me-2" />
|
||||
<span>5/10</span>
|
||||
</div>
|
||||
<span>Due {due}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
---
|
||||
// NOTE: `days-ago` param accepted but unused ("Updated 2 hours ago" is hardcoded).
|
||||
import Progress from '@ui/Progress.astro';
|
||||
import CardDropdown from '@ui/CardDropdown.astro';
|
||||
import projects from '@data/projects.json';
|
||||
import Progress from '@ui/Progress.astro'
|
||||
import CardDropdown from '@ui/CardDropdown.astro'
|
||||
import projects from '@data/projects.json'
|
||||
|
||||
interface Project {
|
||||
title?: string;
|
||||
image?: string;
|
||||
[key: string]: unknown;
|
||||
title?: string
|
||||
image?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
projectId?: number;
|
||||
progress?: number;
|
||||
daysAgo?: number;
|
||||
projectId?: number
|
||||
progress?: number
|
||||
daysAgo?: number
|
||||
}
|
||||
|
||||
const { projectId = 0, progress = 25 } = Astro.props;
|
||||
const project = (projects as Project[])[projectId];
|
||||
const { projectId = 0, progress = 25 } = Astro.props
|
||||
const project = (projects as Project[])[projectId]
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-3">
|
||||
<img src={project.image} alt={project.title} class="rounded" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<h3 class="card-title mb-1">
|
||||
<a href="#" class="text-reset">{project.title}</a>
|
||||
</h3>
|
||||
<div class="text-secondary">
|
||||
Updated 2 hours ago
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-3">
|
||||
<img src={project.image} alt={project.title} class="rounded" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<h3 class="card-title mb-1">
|
||||
<a href="#" class="text-reset">{project.title}</a>
|
||||
</h3>
|
||||
<div class="text-secondary">Updated 2 hours ago</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-auto">
|
||||
{progress}%
|
||||
</div>
|
||||
<div class="col">
|
||||
<Progress value={progress} size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<CardDropdown />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-auto">
|
||||
{progress}%
|
||||
</div>
|
||||
<div class="col">
|
||||
<Progress value={progress} size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<CardDropdown />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
---
|
||||
// Icon class is a literal template string (color token is not interpolated).
|
||||
import CardActions from '@ui/CardActions.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import crmDashboard from '@data/crm-dashboard.json';
|
||||
import CardActions from '@ui/CardActions.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import crmDashboard from '@data/crm-dashboard.json'
|
||||
|
||||
const activity = crmDashboard.recent_activity;
|
||||
const activity = crmDashboard.recent_activity
|
||||
---
|
||||
|
||||
<div class="card" style="height: 28rem">
|
||||
<div class="card-header">
|
||||
<CardTitle>{activity.title}</CardTitle>
|
||||
<CardActions>
|
||||
<a href="#" class="small fw-medium text-primary">{activity.view_all_text}</a>
|
||||
</CardActions>
|
||||
</div>
|
||||
<div class="card-body card-body-scrollable card-body-scrollable-shadow">
|
||||
<div class="divide-y">
|
||||
{
|
||||
activity.items.map((item) => (
|
||||
<div class="py-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<span class={`avatar bg-${item.icon_color}-lt`}>
|
||||
<Icon name={item.icon} class={'icon text-{{ item.icon_color }}'} />
|
||||
</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="fw-medium">{item.text}</div>
|
||||
<div class="small text-secondary">{item.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<CardTitle>{activity.title}</CardTitle>
|
||||
<CardActions>
|
||||
<a href="#" class="small fw-medium text-primary">{activity.view_all_text}</a>
|
||||
</CardActions>
|
||||
</div>
|
||||
<div class="card-body card-body-scrollable card-body-scrollable-shadow">
|
||||
<div class="divide-y">
|
||||
{
|
||||
activity.items.map((item) => (
|
||||
<div class="py-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<span class={`avatar bg-${item.icon_color}-lt`}>
|
||||
<Icon name={item.icon} class={'icon text-{{ item.icon_color }}'} />
|
||||
</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="fw-medium">{item.text}</div>
|
||||
<div class="small text-secondary">{item.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import FormFooter from '@ui/FormFooter.astro'
|
||||
import InputGroup from '@ui/InputGroup.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
---
|
||||
import { formatNumber } from '@shared/lib/string-format';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import { formatNumber } from '@shared/lib/string-format'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
|
||||
const services = 'Instagram:3550,Twitter:1798,Facebook:1245,TikTok:986,Pinterest:854,VK:650,Pinterest:420'
|
||||
.split(',')
|
||||
.map((service) => {
|
||||
const [name, visitors] = service.split(':');
|
||||
return { name, visitors: Number(visitors) };
|
||||
});
|
||||
const services = 'Instagram:3550,Twitter:1798,Facebook:1245,TikTok:986,Pinterest:854,VK:650,Pinterest:420'.split(',').map((service) => {
|
||||
const [name, visitors] = service.split(':')
|
||||
return { name, visitors: Number(visitors) }
|
||||
})
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<CardTitle>Social Media Traffic</CardTitle>
|
||||
</div>
|
||||
<table class="table card-table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
<th colspan="2">Visitors</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{services.map((service) => (
|
||||
<tr>
|
||||
<td>{service.name}</td>
|
||||
<td>{formatNumber(service.visitors)}</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
|
||||
<div class="progress-bar bg-primary" style={`width: ${(service.visitors / 5000.0) * 100}%`}></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-header">
|
||||
<CardTitle>Social Media Traffic</CardTitle>
|
||||
</div>
|
||||
<table class="table card-table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
<th colspan="2">Visitors</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
services.map((service) => (
|
||||
<tr>
|
||||
<td>{service.name}</td>
|
||||
<td>{formatNumber(service.visitors)}</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style={`width: ${(service.visitors / 5000.0) * 100}%`} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,96 +1,107 @@
|
||||
---
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro'
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
icons?: boolean;
|
||||
/** hide-text param; not used by current call sites */
|
||||
hideText?: boolean;
|
||||
reverse?: boolean;
|
||||
justified?: boolean;
|
||||
activity?: boolean;
|
||||
disabled?: boolean;
|
||||
dropdown?: boolean;
|
||||
settings?: boolean;
|
||||
animation?: boolean;
|
||||
id: string
|
||||
icons?: boolean
|
||||
/** hide-text param; not used by current call sites */
|
||||
hideText?: boolean
|
||||
reverse?: boolean
|
||||
justified?: boolean
|
||||
activity?: boolean
|
||||
disabled?: boolean
|
||||
dropdown?: boolean
|
||||
settings?: boolean
|
||||
animation?: boolean
|
||||
}
|
||||
|
||||
const {
|
||||
id,
|
||||
icons,
|
||||
hideText,
|
||||
reverse = false,
|
||||
justified,
|
||||
activity,
|
||||
disabled,
|
||||
dropdown,
|
||||
settings,
|
||||
animation,
|
||||
} = Astro.props;
|
||||
const { id, icons, hideText, reverse = false, justified, activity, disabled, dropdown, settings, animation } = Astro.props
|
||||
|
||||
const iconClass = hideText ? undefined : 'me-2';
|
||||
const navClass = `nav nav-tabs card-header-tabs${reverse ? ' flex-row-reverse' : ''}${justified ? ' nav-fill' : ''}`;
|
||||
const paneClass = `tab-pane${animation ? ' fade' : ''}`;
|
||||
const iconClass = hideText ? undefined : 'me-2'
|
||||
const navClass = `nav nav-tabs card-header-tabs${reverse ? ' flex-row-reverse' : ''}${justified ? ' nav-fill' : ''}`
|
||||
const paneClass = `tab-pane${animation ? ' fade' : ''}`
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<ul class={navClass} data-bs-toggle="tabs">
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-home-${id}`} class="nav-link active" data-bs-toggle="tab">{icons && <Icon name="home" class={iconClass} />}{!hideText && 'Home'}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-profile-${id}`} class="nav-link" data-bs-toggle="tab">{icons && <Icon name="user" class={iconClass} />}{!hideText && 'Profile'}</a>
|
||||
</li>
|
||||
<div class="card-header">
|
||||
<ul class={navClass} data-bs-toggle="tabs">
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-home-${id}`} class="nav-link active" data-bs-toggle="tab">{icons && <Icon name="home" class={iconClass} />}{!hideText && 'Home'}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-profile-${id}`} class="nav-link" data-bs-toggle="tab">{icons && <Icon name="user" class={iconClass} />}{!hideText && 'Profile'}</a>
|
||||
</li>
|
||||
|
||||
{activity && (
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-activity-${id}`} class="nav-link" data-bs-toggle="tab">{icons && <Icon name="activity" class={iconClass} />}{!hideText && 'Activity'}</a>
|
||||
</li>
|
||||
)}
|
||||
{
|
||||
activity && (
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-activity-${id}`} class="nav-link" data-bs-toggle="tab">
|
||||
{icons && <Icon name="activity" class={iconClass} />}
|
||||
{!hideText && 'Activity'}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
{disabled && (
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-activity-${id}`} class="nav-link disabled" data-bs-toggle="tab">{icons && <Icon name="x" class={iconClass} />}{!hideText && 'Disabled'}</a>
|
||||
</li>
|
||||
)}
|
||||
{
|
||||
disabled && (
|
||||
<li class="nav-item">
|
||||
<a href={`#tabs-activity-${id}`} class="nav-link disabled" data-bs-toggle="tab">
|
||||
{icons && <Icon name="x" class={iconClass} />}
|
||||
{!hideText && 'Disabled'}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
{dropdown && (
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<DropdownMenu />
|
||||
</li>
|
||||
)}
|
||||
{settings && (
|
||||
<li class={`nav-item ${reverse ? 'me-auto' : 'ms-auto'}`}>
|
||||
<a href={`#tabs-settings-${id}`} class="nav-link" title="Settings" data-bs-toggle="tab"><Icon name="settings" /></a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class={`${paneClass} active show`} id={`tabs-home-${id}`}>
|
||||
<h4>Home tab</h4>
|
||||
<div>Cursus turpis vestibulum, dui in pharetra vulputate id sed non turpis ultricies fringilla at sed facilisis lacus pellentesque purus nibh</div>
|
||||
</div>
|
||||
<div class={paneClass} id={`tabs-profile-${id}`}>
|
||||
<h4>Profile tab</h4>
|
||||
<div>Fringilla egestas nunc quis tellus diam rhoncus ultricies tristique enim at diam, sem nunc amet, pellentesque id egestas velit sed</div>
|
||||
</div>
|
||||
{settings && (
|
||||
<div class={paneClass} id={`tabs-settings-${id}`}>
|
||||
<h4>Settings tab</h4>
|
||||
<div>Donec ac vitae diam amet vel leo egestas consequat rhoncus in luctus amet, facilisi sit mauris accumsan nibh habitant senectus</div>
|
||||
</div>
|
||||
)}
|
||||
{activity && (
|
||||
<div class={paneClass} id={`tabs-activity-${id}`}>
|
||||
<h4>Activity tab</h4>
|
||||
<div>Donec ac vitae diam amet vel leo egestas consequat rhoncus in luctus amet, facilisi sit mauris accumsan nibh habitant senectus</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
dropdown && (
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
Dropdown
|
||||
</a>
|
||||
<DropdownMenu />
|
||||
</li>
|
||||
)
|
||||
}
|
||||
{
|
||||
settings && (
|
||||
<li class={`nav-item ${reverse ? 'me-auto' : 'ms-auto'}`}>
|
||||
<a href={`#tabs-settings-${id}`} class="nav-link" title="Settings" data-bs-toggle="tab">
|
||||
<Icon name="settings" />
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class={`${paneClass} active show`} id={`tabs-home-${id}`}>
|
||||
<h4>Home tab</h4>
|
||||
<div>Cursus turpis vestibulum, dui in pharetra vulputate id sed non turpis ultricies fringilla at sed facilisis lacus pellentesque purus nibh</div>
|
||||
</div>
|
||||
<div class={paneClass} id={`tabs-profile-${id}`}>
|
||||
<h4>Profile tab</h4>
|
||||
<div>Fringilla egestas nunc quis tellus diam rhoncus ultricies tristique enim at diam, sem nunc amet, pellentesque id egestas velit sed</div>
|
||||
</div>
|
||||
{
|
||||
settings && (
|
||||
<div class={paneClass} id={`tabs-settings-${id}`}>
|
||||
<h4>Settings tab</h4>
|
||||
<div>Donec ac vitae diam amet vel leo egestas consequat rhoncus in luctus amet, facilisi sit mauris accumsan nibh habitant senectus</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
activity && (
|
||||
<div class={paneClass} id={`tabs-activity-${id}`}>
|
||||
<h4>Activity tab</h4>
|
||||
<div>Donec ac vitae diam amet vel leo egestas consequat rhoncus in luctus amet, facilisi sit mauris accumsan nibh habitant senectus</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,41 +2,38 @@
|
||||
// person = people[person-id]; the cover photo is photos[person-id].file.
|
||||
// Base card-cover already carries `card-cover-blurred`; `blurred` adds it a 2nd time
|
||||
// Base card-cover already carries `card-cover-blurred`; `blurred` adds it a second time.
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import people from '@data/people.json';
|
||||
import photos from '@data/photos.json';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import people from '@data/people.json'
|
||||
import photos from '@data/photos.json'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
job_title?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
job_title?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Photo {
|
||||
file?: string;
|
||||
[key: string]: unknown;
|
||||
file?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
personId?: number;
|
||||
blurred?: boolean;
|
||||
personId?: number
|
||||
blurred?: boolean
|
||||
}
|
||||
|
||||
const { personId = 0, blurred } = Astro.props;
|
||||
const person = (people as Person[])[personId];
|
||||
const photo = (photos as Photo[])[personId];
|
||||
const { personId = 0, blurred } = Astro.props
|
||||
const person = (people as Person[])[personId]
|
||||
const photo = (photos as Photo[])[personId]
|
||||
---
|
||||
|
||||
<a class="card card-link" href="#">
|
||||
<div
|
||||
class={`card-cover card-cover-blurred text-center${blurred ? ' card-cover-blurred' : ''}`}
|
||||
style={`background-image: url(./static/photos/${photo.file})`}
|
||||
>
|
||||
<Avatar size="xl" person={person} thumb />
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="card-title mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
</div>
|
||||
<div class={`card-cover card-cover-blurred text-center${blurred ? ' card-cover-blurred' : ''}`} style={`background-image: url(./static/photos/${photo.file})`}>
|
||||
<Avatar size="xl" person={person} thumb />
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="card-title mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
---
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import people from '@data/people.json';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import people from '@data/people.json'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
job_title?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
job_title?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
personId?: number;
|
||||
personId?: number
|
||||
}
|
||||
|
||||
const { personId = 0 } = Astro.props;
|
||||
const person = (people as Person[])[personId];
|
||||
const { personId = 0 } = Astro.props
|
||||
const person = (people as Person[])[personId]
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<Avatar size="xl" person={person} />
|
||||
</div>
|
||||
<div class="card-title mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
</div>
|
||||
<a href="#" class="card-btn">View full profile</a>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<Avatar size="xl" person={person} />
|
||||
</div>
|
||||
<div class="card-title mb-1">{person.full_name}</div>
|
||||
<div class="text-secondary">{person.job_title}</div>
|
||||
</div>
|
||||
<a href="#" class="card-btn">View full profile</a>
|
||||
</div>
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
---
|
||||
// person = people[person-id - 1] (default person-id = 25 → people[24]).
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Flag from '@ui/Flag.astro';
|
||||
import people from '@data/people.json';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Flag from '@ui/Flag.astro'
|
||||
import people from '@data/people.json'
|
||||
|
||||
interface Person {
|
||||
university?: string;
|
||||
company?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
country_code?: string;
|
||||
birth_date?: string;
|
||||
time_zone?: string;
|
||||
[key: string]: unknown;
|
||||
university?: string
|
||||
company?: string
|
||||
city?: string
|
||||
country?: string
|
||||
country_code?: string
|
||||
birth_date?: string
|
||||
time_zone?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
personId?: number;
|
||||
title?: string
|
||||
personId?: number
|
||||
}
|
||||
|
||||
const { title = 'Basic info', personId = 25 } = Astro.props;
|
||||
const person = (people as Person[])[personId - 1];
|
||||
const { title = 'Basic info', personId = 25 } = Astro.props
|
||||
const person = (people as Person[])[personId - 1]
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title">{title}</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="book" class="me-2 text-secondary" />
|
||||
Went to: <strong>{person.university}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="briefcase" class="me-2 text-secondary" />
|
||||
Worked at: <strong>{person.company}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="home" class="me-2 text-secondary" />
|
||||
Lives in: <strong>{person.city}, {person.country}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="map-pin" class="me-2 text-secondary" />
|
||||
From: <strong><Flag size="xs" flag={person.country_code} /> {person.country}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="calendar" class="me-2 text-secondary" />
|
||||
Birth date: <strong>{person.birth_date}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<Icon name="clock" class="me-2 text-secondary" />
|
||||
Time zone: <strong>{person.time_zone}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-title">{title}</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="book" class="me-2 text-secondary" />
|
||||
Went to: <strong>{person.university}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="briefcase" class="me-2 text-secondary" />
|
||||
Worked at: <strong>{person.company}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="home" class="me-2 text-secondary" />
|
||||
Lives in: <strong>{person.city}, {person.country}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="map-pin" class="me-2 text-secondary" />
|
||||
From: <strong><Flag size="xs" flag={person.country_code} /> {person.country}</strong>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<Icon name="calendar" class="me-2 text-secondary" />
|
||||
Birth date: <strong>{person.birth_date}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<Icon name="clock" class="me-2 text-secondary" />
|
||||
Time zone: <strong>{person.time_zone}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,103 +1,97 @@
|
||||
---
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import ListGroup from '@ui/ListGroup.astro';
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import commits from '@data/commits.json';
|
||||
import { randomNumber } from '@shared/lib/pseudo-random';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import ListGroup from '@ui/ListGroup.astro'
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
import commits from '@data/commits.json'
|
||||
import { randomNumber } from '@shared/lib/pseudo-random'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
photo?: string;
|
||||
last_name?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
photo?: string
|
||||
last_name?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
hoverable?: boolean;
|
||||
checkbox?: boolean;
|
||||
/** checked-ids — 1-based indices, e.g. [2, 5, 8] */
|
||||
checkedIds?: number[];
|
||||
title?: string;
|
||||
class?: string;
|
||||
/**
|
||||
* Inert. The lists.html Contacts card passes `hover=true`, but
|
||||
* users-list.html never reads it — only `hoverable` toggles the star column.
|
||||
* Declared for call-site compatibility.
|
||||
*/
|
||||
hover?: boolean;
|
||||
limit?: number
|
||||
offset?: number
|
||||
hoverable?: boolean
|
||||
checkbox?: boolean
|
||||
/** checked-ids — 1-based indices, e.g. [2, 5, 8] */
|
||||
checkedIds?: number[]
|
||||
title?: string
|
||||
class?: string
|
||||
/**
|
||||
* Inert. The lists.html Contacts card passes `hover=true`, but
|
||||
* users-list.html never reads it — only `hoverable` toggles the star column.
|
||||
* Declared for call-site compatibility.
|
||||
*/
|
||||
hover?: boolean
|
||||
}
|
||||
|
||||
const {
|
||||
limit = 8,
|
||||
offset = 0,
|
||||
hoverable = false,
|
||||
checkbox,
|
||||
checkedIds,
|
||||
title = 'Last commits',
|
||||
class: className,
|
||||
} = Astro.props;
|
||||
const { limit = 8, offset = 0, hoverable = false, checkbox, checkedIds, title = 'Last commits', class: className } = Astro.props
|
||||
|
||||
const colors = ['green', 'red', 'yellow', 'x', 'x'];
|
||||
const commitList = commits as { description: string }[];
|
||||
const colors = ['green', 'red', 'yellow', 'x', 'x']
|
||||
const commitList = commits as { description: string }[]
|
||||
|
||||
const rows = (people as Person[]).slice(offset, offset + limit).map((person, idx) => {
|
||||
const index = idx + 1; // forloop.index (1-based)
|
||||
const color = colors[randomNumber(index + 5, 0, colors.length - 1)];
|
||||
const checked = checkedIds?.includes(index) ?? false;
|
||||
const i = index + offset;
|
||||
return {
|
||||
person,
|
||||
color,
|
||||
checked,
|
||||
description: commitList[i]?.description,
|
||||
starColor: checked ? 'text-yellow' : 'text-secondary',
|
||||
};
|
||||
});
|
||||
const index = idx + 1 // forloop.index (1-based)
|
||||
const color = colors[randomNumber(index + 5, 0, colors.length - 1)]
|
||||
const checked = checkedIds?.includes(index) ?? false
|
||||
const i = index + offset
|
||||
return {
|
||||
person,
|
||||
color,
|
||||
checked,
|
||||
description: commitList[i]?.description,
|
||||
starColor: checked ? 'text-yellow' : 'text-secondary',
|
||||
}
|
||||
})
|
||||
---
|
||||
|
||||
<div class={`card${className ? ` ${className}` : ''}`}>
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<ListGroup flush hoverable={hoverable}>
|
||||
{
|
||||
rows.map((row) => (
|
||||
<ListGroupItem active={row.checked}>
|
||||
<div class="row align-items-center">
|
||||
{checkbox ? (
|
||||
<div class="col-auto">
|
||||
<input type="checkbox" class="form-check-input" checked={row.checked} />
|
||||
</div>
|
||||
) : (
|
||||
<div class="col-auto">
|
||||
<span class={`badge${row.color !== 'x' ? ` bg-${row.color}` : ''}`} />
|
||||
</div>
|
||||
)}
|
||||
<div class="col-auto">
|
||||
<a href="#">
|
||||
<Avatar person={row.person} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<ListGroup flush hoverable={hoverable}>
|
||||
{
|
||||
rows.map((row) => (
|
||||
<ListGroupItem active={row.checked}>
|
||||
<div class="row align-items-center">
|
||||
{checkbox ? (
|
||||
<div class="col-auto">
|
||||
<input type="checkbox" class="form-check-input" checked={row.checked} />
|
||||
</div>
|
||||
) : (
|
||||
<div class="col-auto">
|
||||
<span class={`badge${row.color !== 'x' ? ` bg-${row.color}` : ''}`} />
|
||||
</div>
|
||||
)}
|
||||
<div class="col-auto">
|
||||
<a href="#">
|
||||
<Avatar person={row.person} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-reset d-block">{row.person.full_name}</a>
|
||||
<div class="d-block text-secondary text-truncate mt-n1">{row.description}</div>
|
||||
</div>
|
||||
{hoverable && (
|
||||
<div class="col-auto">
|
||||
<a href="#" class={`list-group-item-actions${row.checked ? ' show' : ''}`}>
|
||||
<Icon name="star" class={row.starColor} />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-reset d-block">
|
||||
{row.person.full_name}
|
||||
</a>
|
||||
<div class="d-block text-secondary text-truncate mt-n1">{row.description}</div>
|
||||
</div>
|
||||
{hoverable && (
|
||||
<div class="col-auto">
|
||||
<a href="#" class={`list-group-item-actions${row.checked ? ' show' : ''}`}>
|
||||
<Icon name="star" class={row.starColor} />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
</div>
|
||||
|
||||
@@ -1,57 +1,59 @@
|
||||
---
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import { randomNumber, timeagoLabel } from '@shared/lib/pseudo-random';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
import { randomNumber, timeagoLabel } from '@shared/lib/pseudo-random'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
photo?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
photo?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
title?: string;
|
||||
class?: string;
|
||||
limit?: number
|
||||
offset?: number
|
||||
title?: string
|
||||
class?: string
|
||||
}
|
||||
|
||||
const { limit = 10, offset = 0, title = 'Top users', class: className } = Astro.props;
|
||||
const { limit = 10, offset = 0, title = 'Top users', class: className } = Astro.props
|
||||
|
||||
const colors = 'green,red,yellow,x,x'.split(',');
|
||||
const colors = 'green,red,yellow,x,x'.split(',')
|
||||
|
||||
const rows = (people as Person[]).slice(offset, offset + limit).map((person, idx) => {
|
||||
const index = idx + 1; // forloop.index (1-based)
|
||||
return {
|
||||
person,
|
||||
status: colors[randomNumber(index + 5, 0, colors.length - 1)],
|
||||
timeago: timeagoLabel(index, 6),
|
||||
};
|
||||
});
|
||||
const index = idx + 1 // forloop.index (1-based)
|
||||
return {
|
||||
person,
|
||||
status: colors[randomNumber(index + 5, 0, colors.length - 1)],
|
||||
timeago: timeagoLabel(index, 6),
|
||||
}
|
||||
})
|
||||
---
|
||||
|
||||
<div class={`card${className ? ` ${className}` : ''}`}>
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
{
|
||||
rows.map((row) => (
|
||||
<div class="col-6">
|
||||
<div class="row g-3 align-items-center">
|
||||
<a href="#" class="col-auto">
|
||||
<Avatar person={row.person} status={row.status} />
|
||||
</a>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-reset d-block text-truncate">{row.person.full_name}</a>
|
||||
<div class="text-secondary text-truncate mt-n1">{row.timeago}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
{
|
||||
rows.map((row) => (
|
||||
<div class="col-6">
|
||||
<div class="row g-3 align-items-center">
|
||||
<a href="#" class="col-auto">
|
||||
<Avatar person={row.person} status={row.status} />
|
||||
</a>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-reset d-block text-truncate">
|
||||
{row.person.full_name}
|
||||
</a>
|
||||
<div class="text-secondary text-truncate mt-n1">{row.timeago}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,70 +1,72 @@
|
||||
---
|
||||
import Avatar from '@ui/Avatar.astro';
|
||||
import ListGroup from '@ui/ListGroup.astro';
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro';
|
||||
import ListGroupHeader from '@ui/ListGroupHeader.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import people from '@data/people.json';
|
||||
import commits from '@data/commits.json';
|
||||
import { sortBy } from '@shared/lib/string-format';
|
||||
import Avatar from '@ui/Avatar.astro'
|
||||
import ListGroup from '@ui/ListGroup.astro'
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro'
|
||||
import ListGroupHeader from '@ui/ListGroupHeader.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import people from '@data/people.json'
|
||||
import commits from '@data/commits.json'
|
||||
import { sortBy } from '@shared/lib/string-format'
|
||||
|
||||
interface Person {
|
||||
full_name?: string;
|
||||
photo?: string;
|
||||
last_name?: string;
|
||||
[key: string]: unknown;
|
||||
full_name?: string
|
||||
photo?: string
|
||||
last_name?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
title?: string
|
||||
}
|
||||
|
||||
const { title = 'People' } = Astro.props;
|
||||
const { title = 'People' } = Astro.props
|
||||
|
||||
const commitList = commits as { description: string }[];
|
||||
const commitList = commits as { description: string }[]
|
||||
|
||||
// Sort by last_name (case-sensitive).
|
||||
const sorted = sortBy(people as Person[], (p) => p.last_name ?? '');
|
||||
const sorted = sortBy(people as Person[], (p) => p.last_name ?? '')
|
||||
|
||||
// offset is unset in the include, so `forloop.index | plus: offset` == forloop.index.
|
||||
let prevLetter = '';
|
||||
let prevLetter = ''
|
||||
const rows = sorted.map((person, idx) => {
|
||||
const index = idx + 1; // forloop.index (1-based)
|
||||
const firstLetter = (person.last_name ?? '').slice(0, 1);
|
||||
let header: string | null = null;
|
||||
if (prevLetter !== firstLetter) {
|
||||
prevLetter = firstLetter;
|
||||
header = firstLetter;
|
||||
}
|
||||
return { person, header, description: commitList[index]?.description };
|
||||
});
|
||||
const index = idx + 1 // forloop.index (1-based)
|
||||
const firstLetter = (person.last_name ?? '').slice(0, 1)
|
||||
let header: string | null = null
|
||||
if (prevLetter !== firstLetter) {
|
||||
prevLetter = firstLetter
|
||||
header = firstLetter
|
||||
}
|
||||
return { person, header, description: commitList[index]?.description }
|
||||
})
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</div>
|
||||
|
||||
<ListGroup flush class="overflow-auto" style="max-height: 35rem">
|
||||
{
|
||||
rows.map((row) => (
|
||||
<>
|
||||
{row.header && <ListGroupHeader class="sticky-top">{row.header}</ListGroupHeader>}
|
||||
<ListGroupItem>
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a href="#">
|
||||
<Avatar person={row.person} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-body d-block">{row.person.full_name}</a>
|
||||
<div class="text-secondary text-truncate mt-n1">{row.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
<ListGroup flush class="overflow-auto" style="max-height: 35rem">
|
||||
{
|
||||
rows.map((row) => (
|
||||
<>
|
||||
{row.header && <ListGroupHeader class="sticky-top">{row.header}</ListGroupHeader>}
|
||||
<ListGroupItem>
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a href="#">
|
||||
<Avatar person={row.person} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-body d-block">
|
||||
{row.person.full_name}
|
||||
</a>
|
||||
<div class="text-secondary text-truncate mt-n1">{row.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
// legend=true at call site is inert — Chart reads legend from chart data, not props.
|
||||
import CardTitle from '@ui/CardTitle.astro';
|
||||
import Chart from '@ui/Chart.astro';
|
||||
import DropdownDays from '@ui/DropdownDays.astro';
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import Chart from '@ui/Chart.astro'
|
||||
import DropdownDays from '@ui/DropdownDays.astro'
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex">
|
||||
<CardTitle>Social referrals</CardTitle>
|
||||
<div class="ms-auto">
|
||||
<DropdownDays id="social-referrals" label="Select time range for sales data" />
|
||||
</div>
|
||||
</div>
|
||||
<Chart chartId="social-referrals" />
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex">
|
||||
<CardTitle>Social referrals</CardTitle>
|
||||
<div class="ms-auto">
|
||||
<DropdownDays id="social-referrals" label="Select time range for sales data" />
|
||||
</div>
|
||||
</div>
|
||||
<Chart chartId="social-referrals" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,55 +1,53 @@
|
||||
---
|
||||
// Switch-icon heart variant inlined: icon-b="heart", icon-b-class="icon-filled", icon-a-color="muted", icon-b-color="red".
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro';
|
||||
import ListGroup from '@ui/ListGroup.astro';
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro';
|
||||
import tracks from '@data/tracks.json';
|
||||
import { millisecondsToMinutes } from '@shared/lib/string-format';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import DropdownMenu from '@ui/DropdownMenu.astro'
|
||||
import ListGroup from '@ui/ListGroup.astro'
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro'
|
||||
import tracks from '@data/tracks.json'
|
||||
import { millisecondsToMinutes } from '@shared/lib/string-format'
|
||||
|
||||
const items = tracks.slice(0, 12);
|
||||
const items = tracks.slice(0, 12)
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<ListGroup class="card-list-group">
|
||||
{items.map((track, index) => (
|
||||
<ListGroupItem>
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-auto fs-3">
|
||||
{index + 1}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<img src={`./static/tracks/${track.album.images[1].path}`} class="rounded" alt={track.name} width="40" height="40" />
|
||||
</div>
|
||||
<div class="col">
|
||||
{track.name}
|
||||
<div class="text-secondary">
|
||||
{track.artists.map((artist) => artist.name).join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto text-secondary">
|
||||
{millisecondsToMinutes(track.duration_ms)}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="#" class="link-secondary">
|
||||
<button class="switch-icon" data-bs-toggle="switch-icon">
|
||||
<span class="switch-icon-a text-muted">
|
||||
<Icon name="heart" />
|
||||
</span>
|
||||
<span class="switch-icon-b text-red">
|
||||
<Icon name="heart" class="icon-filled" />
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-auto lh-1">
|
||||
<div class="dropdown">
|
||||
<a href="#" class="link-secondary" data-bs-toggle="dropdown"><Icon name="dots" /></a>
|
||||
<DropdownMenu right />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))}
|
||||
</ListGroup>
|
||||
<ListGroup class="card-list-group">
|
||||
{
|
||||
items.map((track, index) => (
|
||||
<ListGroupItem>
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-auto fs-3">{index + 1}</div>
|
||||
<div class="col-auto">
|
||||
<img src={`./static/tracks/${track.album.images[1].path}`} class="rounded" alt={track.name} width="40" height="40" />
|
||||
</div>
|
||||
<div class="col">
|
||||
{track.name}
|
||||
<div class="text-secondary">{track.artists.map((artist) => artist.name).join(', ')}</div>
|
||||
</div>
|
||||
<div class="col-auto text-secondary">{millisecondsToMinutes(track.duration_ms)}</div>
|
||||
<div class="col-auto">
|
||||
<a href="#" class="link-secondary">
|
||||
<button class="switch-icon" data-bs-toggle="switch-icon">
|
||||
<span class="switch-icon-a text-muted">
|
||||
<Icon name="heart" />
|
||||
</span>
|
||||
<span class="switch-icon-b text-red">
|
||||
<Icon name="heart" class="icon-filled" />
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-auto lh-1">
|
||||
<div class="dropdown">
|
||||
<a href="#" class="link-secondary" data-bs-toggle="dropdown">
|
||||
<Icon name="dots" />
|
||||
</a>
|
||||
<DropdownMenu right />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card placeholder-glow">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
import ListGroup from '@ui/ListGroup.astro';
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro';
|
||||
import ListGroup from '@ui/ListGroup.astro'
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro'
|
||||
|
||||
const items = [1, 2, 3, 4];
|
||||
const items = [1, 2, 3, 4]
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<ListGroup as="ul" flush class="placeholder-glow">
|
||||
{
|
||||
items.map(() => (
|
||||
<ListGroupItem as="li">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="avatar avatar-rounded placeholder" />
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<div class="placeholder placeholder-xs col-9" />
|
||||
<div class="placeholder placeholder-xs col-7" />
|
||||
</div>
|
||||
<div class="col-2 ms-auto text-end">
|
||||
<div class="placeholder placeholder-xs col-8" />
|
||||
<div class="placeholder placeholder-xs col-10" />
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
<ListGroup as="ul" flush class="placeholder-glow">
|
||||
{
|
||||
items.map(() => (
|
||||
<ListGroupItem as="li">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="avatar avatar-rounded placeholder" />
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<div class="placeholder placeholder-xs col-9" />
|
||||
<div class="placeholder placeholder-xs col-7" />
|
||||
</div>
|
||||
<div class="col-2 ms-auto text-end">
|
||||
<div class="placeholder placeholder-xs col-8" />
|
||||
<div class="placeholder placeholder-xs col-10" />
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
}
|
||||
</ListGroup>
|
||||
</div>
|
||||
|
||||
@@ -3,155 +3,147 @@
|
||||
// with dropdowns, icon/loader/separated inputs, and a help-icon input.
|
||||
// TomSelect init for #select-states — FIRST page script, matching the reference
|
||||
// order (select-states precedes the FormElements6 scripts).
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import FormGroup from '@ui/FormGroup.astro';
|
||||
import selects from '@data/selects.json';
|
||||
import CaptureScript from '../CaptureScript.astro';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import FormGroup from '@ui/FormGroup.astro'
|
||||
import selects from '@data/selects.json'
|
||||
import CaptureScript from '../CaptureScript.astro'
|
||||
|
||||
const states = (selects as { states: { options: Record<string, { name: string; selected?: boolean }> } }).states.options;
|
||||
const states = (selects as { states: { options: Record<string, { name: string; selected?: boolean }> } }).states.options
|
||||
---
|
||||
|
||||
<FormGroup label="Static">
|
||||
<div class="form-control-plaintext">Input value</div>
|
||||
<div class="form-control-plaintext">Input value</div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Text">
|
||||
<input type="text" class="form-control" name="example-text-input" placeholder="Input placeholder">
|
||||
<input type="text" class="form-control" name="example-text-input" placeholder="Input placeholder" />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Password">
|
||||
<input type="text" class="form-control" name="example-password-input" placeholder="Input placeholder">
|
||||
<input type="text" class="form-control" name="example-password-input" placeholder="Input placeholder" />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Disabled">
|
||||
<input type="text" class="form-control" name="example-disabled-input" placeholder="Disabled..." value="Well, she turned me into a newt." disabled>
|
||||
<input type="text" class="form-control" name="example-disabled-input" placeholder="Disabled..." value="Well, she turned me into a newt." disabled />
|
||||
</FormGroup>
|
||||
<FormGroup label="Readonly">
|
||||
<input type="text" class="form-control" name="example-disabled-input" placeholder="Readonly..." value="Well, how'd you become king, then?" readonly>
|
||||
<input type="text" class="form-control" name="example-disabled-input" placeholder="Readonly..." value="Well, how'd you become king, then?" readonly />
|
||||
</FormGroup>
|
||||
<FormGroup label="Required" required>
|
||||
<input type="text" class="form-control" name="example-required-input" placeholder="Required...">
|
||||
<input type="text" class="form-control" name="example-required-input" placeholder="Required..." />
|
||||
</FormGroup>
|
||||
<FormGroup label="Textarea" description="56/100">
|
||||
<textarea class="form-control" name="example-textarea-input" rows="6" placeholder="Content..">Oh! Come and see the violence inherent in the system! Help, help, I'm being repressed! We shall say 'Ni' again to you, if you do not appease us. I'm not a witch. I'm not a witch. Camelot!</textarea>
|
||||
<textarea class="form-control" name="example-textarea-input" rows="6" placeholder="Content..">Oh! Come and see the violence inherent in the system! Help, help, I'm being repressed! We shall say 'Ni' again to you, if you do not appease us. I'm not a witch. I'm not a witch. Camelot!</textarea>
|
||||
</FormGroup>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Select</div>
|
||||
<select class="form-select">
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
<div class="form-label">Select</div>
|
||||
<select class="form-select">
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Select multiple</div>
|
||||
<select class="form-select" multiple>
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
<div class="form-label">Select multiple</div>
|
||||
<select class="form-select" multiple>
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Select multiple states</div>
|
||||
<select class="form-select" id="select-states" value="" multiple>
|
||||
{
|
||||
Object.entries(states).map(([code, { name, selected }]) => (
|
||||
<option value={code} selected={selected}>{name}</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
<div class="form-label">Select multiple states</div>
|
||||
<select class="form-select" id="select-states" value="" multiple>
|
||||
{
|
||||
Object.entries(states).map(([code, { name, selected }]) => (
|
||||
<option value={code} selected={selected}>
|
||||
{name}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<FormGroup label="Input group">
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" class="form-control" placeholder="Search for…">
|
||||
<button class="btn" type="button">Go!</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Action
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
<input type="text" class="form-control" aria-label="Text input with dropdown button">
|
||||
</div>
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" class="form-control" placeholder="Search for…" />
|
||||
<button class="btn" type="button">Go!</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Action </button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#"> Action </a>
|
||||
<a class="dropdown-item" href="#"> Another action </a>
|
||||
</div>
|
||||
<input type="text" class="form-control" aria-label="Text input with dropdown button" />
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="Input group buttons">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control">
|
||||
<button type="button" class="btn">Action</button>
|
||||
<button data-bs-toggle="dropdown" type="button" class="btn dropdown-toggle dropdown-toggle-split"></button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" />
|
||||
<button type="button" class="btn">Action</button>
|
||||
<button data-bs-toggle="dropdown" type="button" class="btn dropdown-toggle dropdown-toggle-split"></button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#"> Action </a>
|
||||
<a class="dropdown-item" href="#"> Another action </a>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Icon input">
|
||||
<div class="input-icon mb-3">
|
||||
<input type="text" value="" class="form-control" placeholder="Search…">
|
||||
<span class="input-icon-addon">
|
||||
<Icon name="search" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<span class="input-icon-addon">
|
||||
<Icon name="user" />
|
||||
</span>
|
||||
<input type="text" value="" class="form-control" placeholder="Username">
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<input type="text" value="" class="form-control" placeholder="Search…" />
|
||||
<span class="input-icon-addon">
|
||||
<Icon name="search" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<span class="input-icon-addon">
|
||||
<Icon name="user" />
|
||||
</span>
|
||||
<input type="text" value="" class="form-control" placeholder="Username" />
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Loader input">
|
||||
<div class="input-icon mb-3">
|
||||
<input type="text" value="" class="form-control" placeholder="Loading…">
|
||||
<span class="input-icon-addon">
|
||||
<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<span class="input-icon-addon">
|
||||
<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>
|
||||
</span>
|
||||
<input type="text" value="" class="form-control" placeholder="Loading…">
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<input type="text" value="" class="form-control" placeholder="Loading…" />
|
||||
<span class="input-icon-addon">
|
||||
<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-icon mb-3">
|
||||
<span class="input-icon-addon">
|
||||
<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>
|
||||
</span>
|
||||
<input type="text" value="" class="form-control" placeholder="Loading…" />
|
||||
</div>
|
||||
</FormGroup>
|
||||
<FormGroup label="Separated inputs">
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<input type="text" class="form-control" placeholder="Search for…">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="#" class="btn btn-icon" aria-label="Button">
|
||||
<Icon name="search" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<input type="text" class="form-control" placeholder="Search for…" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="#" class="btn btn-icon" aria-label="Button">
|
||||
<Icon name="search" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label="Input with help icon">
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<input type="text" class="form-control" placeholder="Search for…">
|
||||
</div>
|
||||
<div class="col-auto align-self-center">
|
||||
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>ZIP Code must be US or CDN format. You can use an extended ZIP+4 code to determine address more accurately.</p><p class='mb-0'><a href='#'>USP ZIP codes lookup tools</a></p>" data-bs-html="true">?</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<input type="text" class="form-control" placeholder="Search for…" />
|
||||
</div>
|
||||
<div class="col-auto align-self-center">
|
||||
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>ZIP Code must be US or CDN format. You can use an extended ZIP+4 code to determine address more accurately.</p><p class='mb-0'><a href='#'>USP ZIP codes lookup tools</a></p>" data-bs-html="true">?</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
<!--
|
||||
is:inline: TomSelect loads via a deferred page-lib <script>. A processed (module)
|
||||
@@ -162,31 +154,31 @@ const states = (selects as { states: { options: Record<string, { name: string; s
|
||||
actually delays until after the library has loaded.
|
||||
-->
|
||||
<CaptureScript>
|
||||
<!-- BEGIN SELECT STATES -->
|
||||
<script is:inline>
|
||||
function initSelectStates() {
|
||||
window.tabler_select ??= {};
|
||||
<!-- BEGIN SELECT STATES -->
|
||||
<script is:inline>
|
||||
function initSelectStates() {
|
||||
window.tabler_select ??= {}
|
||||
|
||||
const renderOption = (data, escape) => {
|
||||
if (data.customProperties) {
|
||||
return `<div><span class="dropdown-item-indicator">${data.customProperties}</span>${escape(data.text)}</div>`;
|
||||
}
|
||||
return `<div>${escape(data.text)}</div>`;
|
||||
};
|
||||
const renderOption = (data, escape) => {
|
||||
if (data.customProperties) {
|
||||
return `<div><span class="dropdown-item-indicator">${data.customProperties}</span>${escape(data.text)}</div>`
|
||||
}
|
||||
return `<div>${escape(data.text)}</div>`
|
||||
}
|
||||
|
||||
window.TomSelect &&
|
||||
(window.tabler_select['select-states'] = new TomSelect(document.getElementById('select-states'), {
|
||||
copyClassesToDropdown: false,
|
||||
dropdownParent: 'body',
|
||||
controlInput: '<input>',
|
||||
render: {
|
||||
item: renderOption,
|
||||
option: renderOption,
|
||||
},
|
||||
}));
|
||||
}
|
||||
window.TomSelect &&
|
||||
(window.tabler_select['select-states'] = new TomSelect(document.getElementById('select-states'), {
|
||||
copyClassesToDropdown: false,
|
||||
dropdownParent: 'body',
|
||||
controlInput: '<input>',
|
||||
render: {
|
||||
item: renderOption,
|
||||
option: renderOption,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
document.readyState !== 'loading' ? initSelectStates() : document.addEventListener('DOMContentLoaded', initSelectStates, { once: true });
|
||||
</script>
|
||||
<!-- END SELECT STATES -->
|
||||
document.readyState !== 'loading' ? initSelectStates() : document.addEventListener('DOMContentLoaded', initSelectStates, { once: true })
|
||||
</script>
|
||||
<!-- END SELECT STATES -->
|
||||
</CaptureScript>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import CaptureScript from '../CaptureScript.astro';
|
||||
import CaptureScript from '../CaptureScript.astro'
|
||||
|
||||
interface Props {
|
||||
id?: string
|
||||
@@ -12,18 +12,18 @@ const listId = `table-${id}`
|
||||
---
|
||||
|
||||
<CaptureScript>
|
||||
<!-- BEGIN TABLER LIST -->
|
||||
<script define:vars={{ listId, valueNames }}>
|
||||
function initTablerList() {
|
||||
window.tabler_list ??= {}
|
||||
window.tabler_list[listId] = new List(listId, {
|
||||
sortClass: 'table-sort',
|
||||
listClass: 'table-tbody',
|
||||
valueNames,
|
||||
})
|
||||
}
|
||||
<!-- BEGIN TABLER LIST -->
|
||||
<script define:vars={{ listId, valueNames }}>
|
||||
function initTablerList() {
|
||||
window.tabler_list ??= {}
|
||||
window.tabler_list[listId] = new List(listId, {
|
||||
sortClass: 'table-sort',
|
||||
listClass: 'table-tbody',
|
||||
valueNames,
|
||||
})
|
||||
}
|
||||
|
||||
document.readyState !== 'loading' ? initTablerList() : document.addEventListener('DOMContentLoaded', initTablerList, { once: true })
|
||||
</script>
|
||||
<!-- END TABLER LIST -->
|
||||
document.readyState !== 'loading' ? initTablerList() : document.addEventListener('DOMContentLoaded', initTablerList, { once: true })
|
||||
</script>
|
||||
<!-- END TABLER LIST -->
|
||||
</CaptureScript>
|
||||
|
||||
@@ -1,65 +1,61 @@
|
||||
---
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import { site } from '@shared/lib/site';
|
||||
import { formatUtcTimestamp } from '@shared/lib/date-format';
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { formatUtcTimestamp } from '@shared/lib/date-format'
|
||||
|
||||
// Development build (unminified assets), same as BaseLayout.
|
||||
const environment: string = 'development';
|
||||
const environment: string = 'development'
|
||||
|
||||
const now = new Date();
|
||||
const generatedAt = formatUtcTimestamp(now);
|
||||
const now = new Date()
|
||||
const generatedAt = formatUtcTimestamp(now)
|
||||
---
|
||||
|
||||
<!-- BEGIN FOOTER -->
|
||||
<footer class="footer footer-transparent d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row text-center align-items-center flex-row-reverse">
|
||||
<div class="col-lg-auto ms-lg-auto">
|
||||
<nav aria-label="Footer">
|
||||
<ul class="list-inline list-inline-dots mb-0">
|
||||
<li class="list-inline-item">
|
||||
<a href={site.docsUrl} target="_blank" class="link-secondary" rel="noopener">
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="./license.html" class="link-secondary">License</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href={site.githubUrl} target="_blank" class="link-secondary" rel="noopener">
|
||||
Source code
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href={site.githubSponsorsUrl} target="_blank" class="link-secondary" rel="noopener">
|
||||
{/* filled prop ignored — only type matters, heart renders as outline */}
|
||||
<Icon name="heart" inline color="pink" />
|
||||
Sponsor
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
|
||||
<ul class="list-inline list-inline-dots mb-0">
|
||||
<li class="list-inline-item">
|
||||
Copyright © {now.getUTCFullYear()}
|
||||
<a href="." class="link-secondary">{site.title}</a>. All rights reserved.
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
{
|
||||
environment === 'production' || environment === 'preview' ? (
|
||||
<a href="./changelog.html" class="link-secondary" rel="noopener">
|
||||
v{site.version}
|
||||
</a>
|
||||
) : (
|
||||
`Generated ${generatedAt}`
|
||||
)
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-xl">
|
||||
<div class="row text-center align-items-center flex-row-reverse">
|
||||
<div class="col-lg-auto ms-lg-auto">
|
||||
<nav aria-label="Footer">
|
||||
<ul class="list-inline list-inline-dots mb-0">
|
||||
<li class="list-inline-item">
|
||||
<a href={site.docsUrl} target="_blank" class="link-secondary" rel="noopener"> Documentation </a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="./license.html" class="link-secondary">License</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href={site.githubUrl} target="_blank" class="link-secondary" rel="noopener"> Source code </a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href={site.githubSponsorsUrl} target="_blank" class="link-secondary" rel="noopener">
|
||||
{/* filled prop ignored — only type matters, heart renders as outline */}
|
||||
<Icon name="heart" inline color="pink" />
|
||||
Sponsor
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
|
||||
<ul class="list-inline list-inline-dots mb-0">
|
||||
<li class="list-inline-item">
|
||||
Copyright © {now.getUTCFullYear()}
|
||||
<a href="." class="link-secondary">{site.title}</a>. All rights reserved.
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
{
|
||||
environment === 'production' || environment === 'preview' ? (
|
||||
<a href="./changelog.html" class="link-secondary" rel="noopener">
|
||||
v{site.version}
|
||||
</a>
|
||||
) : (
|
||||
`Generated ${generatedAt}`
|
||||
)
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
// Breadcrumb is always "Tabler > Pages" — page-header append is empty in reference output.
|
||||
import Breadcrumb from '@ui/Breadcrumb.astro';
|
||||
import Breadcrumb from '@ui/Breadcrumb.astro'
|
||||
---
|
||||
|
||||
<!-- BEGIN HEADER ACTIONS -->
|
||||
<div class="d-flex">
|
||||
<Breadcrumb pages={['Tabler', 'Pages']} class="breadcrumb-arrows" />
|
||||
<Breadcrumb pages={['Tabler', 'Pages']} class="breadcrumb-arrows" />
|
||||
</div>
|
||||
<!-- END HEADER ACTIONS -->
|
||||
|
||||
@@ -1,48 +1,37 @@
|
||||
---
|
||||
import Button from '@ui/Button.astro';
|
||||
import ButtonList from '@ui/ButtonList.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import Modal from '../modals/Modal.astro';
|
||||
import CaptureModal from '../CaptureModal.astro';
|
||||
import ReportModalContent from '../modals/ReportModalContent.astro';
|
||||
import Button from '@ui/Button.astro'
|
||||
import ButtonList from '@ui/ButtonList.astro'
|
||||
import Icon from '@ui/Icon.astro'
|
||||
import Modal from '../modals/Modal.astro'
|
||||
import CaptureModal from '../CaptureModal.astro'
|
||||
import ReportModalContent from '../modals/ReportModalContent.astro'
|
||||
|
||||
interface Props {
|
||||
/** layout-navbar-overlap && layout-navbar-dark → the "New view" button is color="dark" */
|
||||
dark?: boolean;
|
||||
/** layout-navbar-overlap && layout-navbar-dark → the "New view" button is color="dark" */
|
||||
dark?: boolean
|
||||
}
|
||||
|
||||
const { dark } = Astro.props;
|
||||
const { dark } = Astro.props
|
||||
---
|
||||
|
||||
<!-- BEGIN HEADER ACTIONS -->
|
||||
<ButtonList>
|
||||
<span class="d-none d-sm-inline">
|
||||
<Button text="New view" color={dark ? 'dark' : undefined} />
|
||||
</span>
|
||||
{/* Button.astro doesn't support modal-id (data-bs-toggle/target). */}
|
||||
<a
|
||||
href="#"
|
||||
class="btn btn-primary d-none d-sm-inline-block"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-report"
|
||||
>
|
||||
<Icon name="plus" />
|
||||
Create new report
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
class="btn btn-primary d-sm-none btn-icon"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-report"
|
||||
aria-label="Create new report"
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
<span class="d-none d-sm-inline">
|
||||
<Button text="New view" color={dark ? 'dark' : undefined} />
|
||||
</span>
|
||||
{/* Button.astro doesn't support modal-id (data-bs-toggle/target). */}
|
||||
<a href="#" class="btn btn-primary d-none d-sm-inline-block" data-bs-toggle="modal" data-bs-target="#modal-report">
|
||||
<Icon name="plus" />
|
||||
Create new report
|
||||
</a>
|
||||
<a href="#" class="btn btn-primary d-sm-none btn-icon" data-bs-toggle="modal" data-bs-target="#modal-report" aria-label="Create new report">
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
</ButtonList>
|
||||
<!-- END HEADER ACTIONS -->
|
||||
|
||||
<CaptureModal>
|
||||
<Modal modalId="report" size="lg" top>
|
||||
<ReportModalContent />
|
||||
</Modal>
|
||||
<Modal modalId="report" size="lg" top>
|
||||
<ReportModalContent />
|
||||
</Modal>
|
||||
</CaptureModal>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user