@adaptive-sm/astro-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +183 -0
- package/lib/button/Button.astro +58 -0
- package/lib/button/buttonCva.ts +196 -0
- package/lib/button/buttonIconCva.ts +52 -0
- package/lib/button/classesButtonClickAnimation.ts +8 -0
- package/lib/button/classesButtonClickAnimationPush.ts +6 -0
- package/lib/button/classesButtonClickAnimationSquish.ts +6 -0
- package/lib/button/classesButtonDisabled.ts +1 -0
- package/lib/card/CardWrapper.astro +15 -0
- package/lib/card/classesCardWrapper.ts +16 -0
- package/lib/details/Details.astro +57 -0
- package/lib/dev/TailwindIndicator.astro +28 -0
- package/lib/form/Fieldset.astro +21 -0
- package/lib/form/classesFieldset.ts +1 -0
- package/lib/generate_ai_rules/generate_agent_rules.bash +9 -0
- package/lib/generate_ai_rules/generate_agent_rules_1_lib.bash +33 -0
- package/lib/generate_ai_rules/generate_agent_rules_2_copy.bash +14 -0
- package/lib/generate_ai_rules/generate_agent_rules_3_combine.bash +31 -0
- package/lib/generate_demo_list/DemoList.astro +31 -0
- package/lib/generate_demo_list/DemoListType.ts +1 -0
- package/lib/generate_demo_list/generateDemoList.ts +55 -0
- package/lib/generate_image_list/generateImageList.ts +101 -0
- package/lib/grid/classesGridCols.ts +86 -0
- package/lib/icon/Icon1.astro +21 -0
- package/lib/img/ImageType.ts +6 -0
- package/lib/img/Img.astro +27 -0
- package/lib/img/TypedImg.astro +26 -0
- package/lib/img/classInvertDiagram.ts +1 -0
- package/lib/img/classesImgZoomInOnHover.ts +1 -0
- package/lib/layouts/MarkdownWrapper.astro +17 -0
- package/lib/layouts/MinimalLayout.astro +67 -0
- package/lib/layouts/parts/ThemeToggle.astro +137 -0
- package/lib/layouts/parts/astroElementId.ts +7 -0
- package/lib/layouts/parts/markdown.css +93 -0
- package/lib/link/LinkButton.astro +48 -0
- package/lib/link/LinkText.astro +23 -0
- package/lib/link/classesTextLink.ts +13 -0
- package/lib/list/BlackBulletPoint.astro +16 -0
- package/lib/list/BlackBulletPoints.astro +23 -0
- package/lib/list/CheckPoint.astro +20 -0
- package/lib/list/CheckPoints.astro +23 -0
- package/lib/list/NumberedList.astro +14 -0
- package/lib/list/Ps.astro +12 -0
- package/lib/list/TextOrLink.astro +19 -0
- package/lib/modal/Modal.astro +54 -0
- package/lib/modal/Modal.module.css +19 -0
- package/lib/modal/ModalButton.astro +41 -0
- package/lib/modal/modal.ts +137 -0
- package/lib/page/PageCentered.astro +14 -0
- package/lib/page/PageCenteredCard.astro +17 -0
- package/lib/page/classesBg.ts +27 -0
- package/lib/page/classesPageCentered.ts +6 -0
- package/lib/popover/Popover1.astro +45 -0
- package/lib/popover/setupPopoverListeners.ts +22 -0
- package/lib/select/Select.astro +45 -0
- package/lib/table/DesktopTableClassses.ts +6 -0
- package/lib/table/MobileTableClassses.ts +7 -0
- package/lib/table/Table.astro +41 -0
- package/lib/table/TableColumnDef.ts +10 -0
- package/lib/table/TableD.astro +55 -0
- package/lib/table/TableM.astro +22 -0
- package/lib/table/TableMEntry.astro +27 -0
- package/lib/table/sharedTableRowClasses.ts +1 -0
- package/lib/table/tableVisibilityClasses.ts +28 -0
- package/lib/text/classesTextGray.ts +1 -0
- package/lib/text/classesTextHeader.ts +1 -0
- package/lib/utils/bun/BunCmd.ts +7 -0
- package/lib/utils/bun/cryAndTryAgainLater.ts +6 -0
- package/lib/utils/bun/logBunCmd.ts +1 -0
- package/lib/utils/bun/runCmdAsync.ts +44 -0
- package/lib/utils/bun/runCmdLocally.ts +13 -0
- package/lib/utils/obj/objectKeys.ts +21 -0
- package/lib/utils/ran/generateId12.ts +7 -0
- package/lib/utils/ran/generateId3.ts +7 -0
- package/lib/utils/ran/generateId4.ts +7 -0
- package/lib/utils/ran/generateId5.ts +7 -0
- package/lib/utils/ran/generateId6.ts +7 -0
- package/lib/utils/ran/generateId7.ts +7 -0
- package/lib/utils/ran/generateReadableId.ts +35 -0
- package/lib/utils/ran/urlAlphabet32.ts +8 -0
- package/lib/utils/ui/classArr.ts +3 -0
- package/lib/utils/ui/classMerge.ts +10 -0
- package/lib/utils/ui/isDevEnv.ts +7 -0
- package/lib/utils/ui/tailwindBreakpoint.ts +83 -0
- package/package.json +56 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -x # Print all executed commands to the terminal
|
|
4
|
+
set -e # Exit immediately if a command exits with a non-zero status
|
|
5
|
+
|
|
6
|
+
scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
|
7
|
+
bash "$scriptDir"/generate_agent_rules_1_lib.bash
|
|
8
|
+
bash "$scriptDir"/generate_agent_rules_2_copy.bash
|
|
9
|
+
bash "$scriptDir"/generate_agent_rules_3_combine.bash
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e # Exit immediately if a command exits with a non-zero status
|
|
3
|
+
set -x # Print all executed commands to the terminal
|
|
4
|
+
|
|
5
|
+
scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
|
6
|
+
# libDir="lib"
|
|
7
|
+
libDir="$scriptDir/../"
|
|
8
|
+
localRulesDir=".roo/rules-code"
|
|
9
|
+
localMdFile=$(readlink -f $localRulesDir/astro_lib.md)
|
|
10
|
+
|
|
11
|
+
mkdir -p "$localRulesDir"
|
|
12
|
+
|
|
13
|
+
if [ ! -d "$libDir" ]; then
|
|
14
|
+
echo "Error: Directory $libDir does not exist." >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
cat > "$localMdFile" << EOF
|
|
19
|
+
# Available Astro UI Components and utility functions
|
|
20
|
+
|
|
21
|
+
These components from @adaptive-sm/astro-ui can be imported using the \`~/\` alias.
|
|
22
|
+
Generated at: $(date +"%Y-%m-%d %H:%M")
|
|
23
|
+
|
|
24
|
+
EOF
|
|
25
|
+
|
|
26
|
+
foundFiles=$(find "$libDir" \( -name "*.astro" -o -name "*.ts" -o -name "*.tsx" \) -printf "%P\n")
|
|
27
|
+
if [ -z "$foundFiles" ]; then
|
|
28
|
+
echo "Error: No .astro, .ts, or .tsx files found in $libDir" >&2
|
|
29
|
+
rm -f "$localMdFile"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
echo "$foundFiles" | sed 's#^#- ~/#' >> "$localMdFile"
|
|
33
|
+
# echo "Found $(echo "$foundFiles" | wc -l) files."
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e # Exit immediately if a command exits with a non-zero status
|
|
3
|
+
set -x # Print all executed commands to the terminal
|
|
4
|
+
|
|
5
|
+
srcDir="node_modules/@adaptive-sm/astro-ui/.roo"
|
|
6
|
+
# srcDir="/home/david/Coding/adaptive-astro-ui/.roo/rules-code"
|
|
7
|
+
dstDir=".roo"
|
|
8
|
+
|
|
9
|
+
# create rules dir if missing
|
|
10
|
+
mkdir -p "$dstDir"
|
|
11
|
+
# copy files
|
|
12
|
+
rsync -av "$srcDir/rules-code/" "$dstDir/rules-code/"
|
|
13
|
+
rsync -av "$srcDir/rules-ask/" "$dstDir/rules-ask/"
|
|
14
|
+
rsync -av "$srcDir/mcp.json" "$dstDir/mcp.json"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -x # Print all executed commands to the terminal
|
|
3
|
+
set -e # Exit immediately if a command exits with a non-zero status
|
|
4
|
+
|
|
5
|
+
shopt -s nullglob # glob returns nothing if no matches
|
|
6
|
+
|
|
7
|
+
srcDir="./.roo/rules-code"
|
|
8
|
+
|
|
9
|
+
# combine md files into AGENTS.md
|
|
10
|
+
outfile="AGENTS.md"
|
|
11
|
+
sep=$'\n\n---\n\n'
|
|
12
|
+
|
|
13
|
+
# Start with a fresh output file
|
|
14
|
+
: > "$outfile"
|
|
15
|
+
|
|
16
|
+
first=true
|
|
17
|
+
for md in $srcDir/*.md; do
|
|
18
|
+
if [[ -f "$md" ]]; then
|
|
19
|
+
# Only prepend the separator *between* files, not before the first one
|
|
20
|
+
if [[ "$first" == true ]]; then
|
|
21
|
+
first=false
|
|
22
|
+
else
|
|
23
|
+
printf %s "$sep" >> "$outfile"
|
|
24
|
+
fi
|
|
25
|
+
cat "$md" >> "$outfile"
|
|
26
|
+
fi
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
# Count and print the total number of lines in the resulting file
|
|
30
|
+
total_lines=$(wc -l < "$outfile")
|
|
31
|
+
echo "$outfile: $total_lines lines"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { classesTextLink } from "~/link/classesTextLink"
|
|
3
|
+
import { classArr } from "~/utils/ui/classArr"
|
|
4
|
+
import { type DemoListType } from "./DemoListType"
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
demos: DemoListType
|
|
8
|
+
class?: string
|
|
9
|
+
}
|
|
10
|
+
const props = Astro.props
|
|
11
|
+
|
|
12
|
+
// Extract categories and their demo pages
|
|
13
|
+
const categories = Object.entries(props.demos)
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
17
|
+
{
|
|
18
|
+
categories.map(([category, pages]) => (
|
|
19
|
+
<section class="bg-white dark:bg-zinc-700 rounded-lg shadow-md p-4 border border-gray-200 dark:border-zinc-600">
|
|
20
|
+
<h2 class="text-xl font-bold mb-2 capitalize">{category.replace(/_/g, " ")}</h2>
|
|
21
|
+
<ul class="list-disc pl-4 space-y-1">
|
|
22
|
+
{pages.map((page) => (
|
|
23
|
+
<li class={classArr(classesTextLink)}>
|
|
24
|
+
<a href={`/demos/${category}/${page}`}>{page.replace(/_/g, " ").replaceAll("demo ", "")}</a>
|
|
25
|
+
</li>
|
|
26
|
+
))}
|
|
27
|
+
</ul>
|
|
28
|
+
</section>
|
|
29
|
+
))
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type DemoListType = Record<string, string[]>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readdir, writeFile } from "node:fs/promises"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { runCmdAsync } from "~/utils/bun/runCmdAsync"
|
|
4
|
+
import type { DemoListType } from "./DemoListType"
|
|
5
|
+
|
|
6
|
+
export async function generateDemoList(demosPath: string, outputPath: string) {
|
|
7
|
+
const categories = await readdir(demosPath, { withFileTypes: true })
|
|
8
|
+
|
|
9
|
+
const demoPageList: DemoListType = {}
|
|
10
|
+
|
|
11
|
+
for (const category of categories) {
|
|
12
|
+
if (category.isDirectory()) {
|
|
13
|
+
const categoryPath = join(demosPath, category.name)
|
|
14
|
+
const demoFiles = await readdir(categoryPath)
|
|
15
|
+
const files = demoFiles.filter(isDemoFile).map((file) => file.replace(/\.astro$/, ""))
|
|
16
|
+
if (files.length > 0) {
|
|
17
|
+
demoPageList[category.name] = files
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
sortDemoPageList(demoPageList)
|
|
22
|
+
|
|
23
|
+
const outputContent = `import type { DemoListType } from "~/generate_demo_list/DemoListType"
|
|
24
|
+
|
|
25
|
+
export const demoList = ${JSON.stringify(demoPageList, null, 2)} satisfies DemoListType;
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
await writeFile(outputPath, outputContent, "utf-8")
|
|
29
|
+
await formatGeneratedCodeFile(outputPath)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isDemoFile(file: string) {
|
|
33
|
+
if (!file.endsWith(".astro")) return false
|
|
34
|
+
if (!file.startsWith("demo")) return false
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sortDemoPageList(demoPageList: DemoListType) {
|
|
39
|
+
for (const list of Object.values(demoPageList)) {
|
|
40
|
+
sortStringsByLengthThenByCharactersInPlace(list)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sortStringsByLengthThenByCharactersInPlace(arr: string[]): string[] {
|
|
45
|
+
return arr.sort((a, b) => a.length - b.length || a.localeCompare(b))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function sortStringsByLengthThenByCharacters(arr: string[]): string[] {
|
|
49
|
+
return sortStringsByLengthThenByCharactersInPlace([...arr])
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function formatGeneratedCodeFile(outputPath: string) {
|
|
53
|
+
const cmd = `bun run biome check --write ${outputPath}`.split(" ")
|
|
54
|
+
runCmdAsync(cmd)
|
|
55
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import imageSize from "image-size"
|
|
2
|
+
import { promises as fs } from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import type { ImageType } from "~/img/ImageType"
|
|
5
|
+
import { runCmdAsync } from "~/utils/bun/runCmdAsync"
|
|
6
|
+
|
|
7
|
+
const IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif", ".tiff", ".svg"])
|
|
8
|
+
|
|
9
|
+
export async function generateImageList(
|
|
10
|
+
imageDirectory: string,
|
|
11
|
+
existingImages: Record<string, ImageType>,
|
|
12
|
+
outputPath: string,
|
|
13
|
+
) {
|
|
14
|
+
// console.log("found", existingImages.length, "existing images")
|
|
15
|
+
const imageMap = await processImageFiles(imageDirectory, existingImages)
|
|
16
|
+
const sorted = sortImageMap(imageMap)
|
|
17
|
+
await writeGeneratedImagesFile(sorted, outputPath)
|
|
18
|
+
await formatGeneratedImagesCodeFile(outputPath)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function writeGeneratedImagesFile(imageMap: Record<string, ImageType>, outputPath: string): Promise<void> {
|
|
22
|
+
const outputContent = `
|
|
23
|
+
import type { ImageType } from "~/img/ImageType"
|
|
24
|
+
// Auto-generated, manual changes will be lost
|
|
25
|
+
export const imageList = ${JSON.stringify(imageMap, null, 2)} as const satisfies Record<string, ImageType>;
|
|
26
|
+
`
|
|
27
|
+
await Bun.write(outputPath, outputContent)
|
|
28
|
+
console.log(`Generated ${Object.keys(imageMap).length} images to ${outputPath}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function processImageFiles(
|
|
32
|
+
directory: string,
|
|
33
|
+
existingImages: Record<string, any>,
|
|
34
|
+
): Promise<Record<string, ImageType>> {
|
|
35
|
+
const imageMap: Record<string, ImageType> = {}
|
|
36
|
+
|
|
37
|
+
for await (const filePath of walkDirectory(directory)) {
|
|
38
|
+
const ext = path.extname(filePath).toLowerCase()
|
|
39
|
+
if (!IMAGE_EXTENSIONS.has(ext)) {
|
|
40
|
+
console.log("ignoring " + ext, filePath)
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const buffer = await fs.readFile(filePath)
|
|
46
|
+
const dimensions = imageSize(buffer)
|
|
47
|
+
if (!dimensions.width || !dimensions.height) continue
|
|
48
|
+
|
|
49
|
+
const relativePath = path.relative(directory, filePath)
|
|
50
|
+
const fileName = path.basename(filePath, ext)
|
|
51
|
+
let key = fileName.replace(/-/g, "_")
|
|
52
|
+
if (/^\d/.test(fileName)) {
|
|
53
|
+
key = "i" + key
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const prevAlt = existingImages[key]?.alt
|
|
57
|
+
const alt = prevAlt || fileName.replace(/[-_]/g, " ")
|
|
58
|
+
|
|
59
|
+
imageMap[key] = {
|
|
60
|
+
path: relativePath,
|
|
61
|
+
width: dimensions.width,
|
|
62
|
+
height: dimensions.height,
|
|
63
|
+
alt,
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(`Error processing ${filePath}:`, error)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return imageMap
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function* walkDirectory(dir: string): AsyncGenerator<string> {
|
|
74
|
+
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
const fullPath = path.join(dir, entry.name)
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
yield* walkDirectory(fullPath)
|
|
79
|
+
} else if (entry.isFile()) {
|
|
80
|
+
yield fullPath
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sortImageMap(m: Record<string, ImageType>): Record<string, ImageType> {
|
|
86
|
+
return Object.keys(m)
|
|
87
|
+
.sort()
|
|
88
|
+
.reduce(
|
|
89
|
+
(sorted, key) => {
|
|
90
|
+
// We know key exists in m since we're iterating its keys
|
|
91
|
+
sorted[key] = m[key]!
|
|
92
|
+
return sorted
|
|
93
|
+
},
|
|
94
|
+
{} as Record<string, ImageType>,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function formatGeneratedImagesCodeFile(outputPath: string) {
|
|
99
|
+
const cmd = `bun run biome check --write ${outputPath}`.split(" ")
|
|
100
|
+
runCmdAsync(cmd)
|
|
101
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { classArr } from "~/utils/ui/classArr"
|
|
2
|
+
|
|
3
|
+
export const classesGridCols2ContentFr = "grid grid-cols-[max-content_1fr]"
|
|
4
|
+
export const classesGridCols3MaxMinFr = "grid grid-cols-[max-content_min-content_1fr]"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ~600 px (37rem, ~60ch) / col
|
|
8
|
+
*/
|
|
9
|
+
export const classesGridCols2xl = classArr("grid grid-cols-1", "xl:grid-cols-2")
|
|
10
|
+
export const classesGridCols2xl3 = classArr(classesGridCols2xl, "2xl:grid-cols-4", "3xl:grid-cols-6")
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ~400 px (25rem, ~40ch) / col
|
|
14
|
+
*/
|
|
15
|
+
export const classesGridCols3xl = classArr("grid grid-cols-1", "md:grid-cols-2", "xl:grid-cols-3")
|
|
16
|
+
export const classesGridCols3xl3 = classArr(classesGridCols3xl, "2xl:grid-cols-6", "3xl:grid-cols-9")
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ~320 px (18rem, ~32ch) / col
|
|
20
|
+
* 4 = 4x1, 2x2
|
|
21
|
+
* xl: 1280/320 = 4
|
|
22
|
+
* lg: 1024/320 = 3.2
|
|
23
|
+
* md: 768/320 = 2.4
|
|
24
|
+
* sm: 640/320 = 2
|
|
25
|
+
* xs: 400/320 = 1.25
|
|
26
|
+
*/
|
|
27
|
+
export const classesGridCols4xl = classArr("grid grid-cols-1", "sm:grid-cols-2", "md:grid-cols-2", "xl:grid-cols-4")
|
|
28
|
+
export const classesGridCols4xl3 = classArr(classesGridCols4xl, "2xl:grid-cols-8", "3xl:grid-cols-12")
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* ~256 px (16rem, ~26ch) / col
|
|
32
|
+
* 5 = 5x1
|
|
33
|
+
* xl: 1280/256 = 5
|
|
34
|
+
* lg: 1024/256 = 4
|
|
35
|
+
* md: 768/256 = 3
|
|
36
|
+
* sm: 640/256 = 2.5
|
|
37
|
+
* xs: 400/256 = 1.56
|
|
38
|
+
*/
|
|
39
|
+
export const classesGridCols5xl = classArr("grid grid-cols-1", "sm:grid-cols-2", "md:grid-cols-3", "lg:grid-cols-4", "xl:grid-cols-5")
|
|
40
|
+
export const classesGridCols5xl3 = classArr(classesGridCols5xl, "2xl:grid-cols-10", "3xl:grid-cols-15")
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* ~200 px (13rem, ~20ch) / col
|
|
45
|
+
* 6 = 6x1, 3x2, 2x3
|
|
46
|
+
* xl: 1280/200 = 6.4
|
|
47
|
+
* lg: 1024/200 = 5.12
|
|
48
|
+
* md: 768/200 = 3.84
|
|
49
|
+
* sm: 640/200 = 3.2
|
|
50
|
+
* xs: 400/200 = 2
|
|
51
|
+
*/
|
|
52
|
+
export const classesGridCols6xl = classArr("grid grid-cols-2", "sm:grid-cols-3", "md:grid-cols-4", "xl:grid-cols-6")
|
|
53
|
+
export const classesGridCols6xl3 = classArr(classesGridCols6xl, "2xl:grid-cols-12", "3xl:grid-cols-18")
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* ~160 px (9rem, ~16ch) / col
|
|
58
|
+
* 8 = 8x1, 4x2, 2x4
|
|
59
|
+
* xl: 1280/160 = 8
|
|
60
|
+
* lg: 1024/160 = 6.4
|
|
61
|
+
* md: 768/160 = 4.8
|
|
62
|
+
* sm: 640/160 = 4
|
|
63
|
+
* xs: 400/160 = 2.5
|
|
64
|
+
*/
|
|
65
|
+
export const classesGridCols8xl = classArr(
|
|
66
|
+
"grid grid-cols-3",
|
|
67
|
+
"sm:grid-cols-4",
|
|
68
|
+
"md:grid-cols-5",
|
|
69
|
+
"lg:grid-cols-6",
|
|
70
|
+
"xl:grid-cols-8",
|
|
71
|
+
)
|
|
72
|
+
export const classesGridCols8xl3 = classArr(classesGridCols8xl, "2xl:grid-cols-16", "3xl:grid-cols-24")
|
|
73
|
+
|
|
74
|
+
export function classesGridColsByCharacterLength(chars: number): string {
|
|
75
|
+
if (chars < 16) return classesGridCols8xl
|
|
76
|
+
if (chars < 20) return classesGridCols6xl
|
|
77
|
+
if (chars < 32) return classesGridCols4xl
|
|
78
|
+
if (chars < 40) return classesGridCols3xl
|
|
79
|
+
return classesGridCols2xl
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function classesGridColsByCharacterLengthMaxLgDialog(chars: number): string {
|
|
83
|
+
if (chars < 20) return "grid " + "grid-cols-2 " + "sm:grid-cols-3 " + "md:grid-cols-4 " + "lg:grid-cols-5 "
|
|
84
|
+
if (chars < 40) return "grid " + "grid-cols-1 " + "md:grid-cols-2 "
|
|
85
|
+
return "grid " + "grid-cols-1 "
|
|
86
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { classMerge } from "~/utils/ui/classMerge"
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
path: string
|
|
6
|
+
class?: string
|
|
7
|
+
title?: string
|
|
8
|
+
ariaHidden?: boolean
|
|
9
|
+
}
|
|
10
|
+
const props = Astro.props
|
|
11
|
+
const ariaHidden = props.ariaHidden ?? !props.title
|
|
12
|
+
// TODO: maybe re-add title={title}
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<svg
|
|
16
|
+
viewBox={"0 0 24 24"}
|
|
17
|
+
aria-hidden={ariaHidden}
|
|
18
|
+
class={classMerge("size-6 align-middle dark:fill-white", props.class)}
|
|
19
|
+
>
|
|
20
|
+
<path d={props.path}></path>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { classesImgZoomInOnHover } from "./classesImgZoomInOnHover"
|
|
3
|
+
import { classMerge } from "~/utils/ui/classMerge"
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
alt: string
|
|
7
|
+
src: string
|
|
8
|
+
zoomIn?: boolean
|
|
9
|
+
class?: string
|
|
10
|
+
size?: number
|
|
11
|
+
width?: number | null | undefined
|
|
12
|
+
height?: number | null | undefined
|
|
13
|
+
loadingLazy?:boolean
|
|
14
|
+
decodeAsync?:boolean
|
|
15
|
+
}
|
|
16
|
+
const p = Astro.props
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<img
|
|
20
|
+
src={p.src}
|
|
21
|
+
alt={p.alt}
|
|
22
|
+
class={classMerge(p.zoomIn && classesImgZoomInOnHover, p.class)}
|
|
23
|
+
loading={p.loadingLazy ?? true ? "lazy" : undefined}
|
|
24
|
+
decoding={p.decodeAsync ?? true ? "async" : undefined}
|
|
25
|
+
width={p.size ?? p.width}
|
|
26
|
+
height={p.size ?? p.height}
|
|
27
|
+
/>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { classArr } from "~/utils/ui/classArr"
|
|
3
|
+
import type { ImageType } from "./ImageType"
|
|
4
|
+
import Img from "./Img.astro"
|
|
5
|
+
import { classInvertDiagram } from "~/img/classInvertDiagram"
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
img: ImageType
|
|
9
|
+
zoomIn?: boolean
|
|
10
|
+
class?: string
|
|
11
|
+
}
|
|
12
|
+
const props = Astro.props
|
|
13
|
+
const isSvg = props.img.path.endsWith(".svg")
|
|
14
|
+
|
|
15
|
+
function imageUrl(p: string) {
|
|
16
|
+
return "/media-b2/" + p
|
|
17
|
+
}
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<Img
|
|
21
|
+
src={imageUrl(props.img.path)}
|
|
22
|
+
alt={props.img.alt}
|
|
23
|
+
class={classArr(isSvg ? classInvertDiagram : "", props.class)}
|
|
24
|
+
width={isSvg ? null : props.img.width}
|
|
25
|
+
height={isSvg ? null : props.img.height}
|
|
26
|
+
/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const classInvertDiagram = "invert-diagram"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const classesImgZoomInOnHover = "transform transition-transform duration-200 -translate-x-3 hover:scale-110"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
import PageCenteredCard from "~/page/PageCenteredCard.astro"
|
|
3
|
+
import { classMerge } from "~/utils/ui/classMerge"
|
|
4
|
+
import { classArr } from "~/utils/ui/classArr"
|
|
5
|
+
|
|
6
|
+
import "./parts/markdown.css"
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
class?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const p = Astro.props
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<PageCenteredCard class={classMerge("max-w-5xl my-12", p.class)} classInner={classArr("markdown-body", p.class)}>
|
|
16
|
+
<slot />
|
|
17
|
+
</PageCenteredCard>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { classArr } from "../utils/ui/classArr"
|
|
3
|
+
import { objectKeys } from "~/utils/obj/objectKeys"
|
|
4
|
+
import TailwindIndicator from "~/dev/TailwindIndicator.astro"
|
|
5
|
+
|
|
6
|
+
export interface Props {
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
meta?: Record<string, string>
|
|
10
|
+
class?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const props = Astro.props
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<!doctype html>
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<head>
|
|
19
|
+
<!-- Base -->
|
|
20
|
+
<meta charset="UTF-8" />
|
|
21
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
22
|
+
<meta name="generator" content={Astro.generator} />
|
|
23
|
+
|
|
24
|
+
<!-- Site -->
|
|
25
|
+
<title>{props.title}</title>
|
|
26
|
+
{props.description && <meta name="description" content={props.description} />}
|
|
27
|
+
|
|
28
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
|
29
|
+
|
|
30
|
+
<!-- Icons -->
|
|
31
|
+
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
32
|
+
<link rel="icon" sizes="32x32" href="/favicon.ico" />
|
|
33
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png" />
|
|
34
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
35
|
+
|
|
36
|
+
<!-- Crawlers -->
|
|
37
|
+
<meta name="robots" content="index, follow" />
|
|
38
|
+
<meta name="googlebot" content="index, follow" />
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
props.meta && (
|
|
42
|
+
<Fragment>
|
|
43
|
+
{objectKeys(props.meta).map((k) => (
|
|
44
|
+
<meta property={k} content={props.meta![k]} />
|
|
45
|
+
))}
|
|
46
|
+
</Fragment>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
</head>
|
|
50
|
+
<body class="min-h-dvh w-full font-sans antialiased bg-background text-foreground">
|
|
51
|
+
<slot name="navbar" />
|
|
52
|
+
<main
|
|
53
|
+
id="main"
|
|
54
|
+
class={classArr(
|
|
55
|
+
"relative min-h-dvh",
|
|
56
|
+
"w-full",
|
|
57
|
+
"flex flex-col items-center",
|
|
58
|
+
props.class,
|
|
59
|
+
//
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
<slot />
|
|
63
|
+
</main>
|
|
64
|
+
<slot name="footer" />
|
|
65
|
+
<TailwindIndicator />
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|