blob: ff9b33838b3456c0911d3f5c067395851728cddc [file] [log] [blame] [edit]
---
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
interface ReferenceItem {
class: string;
styles?: string | string[] | Record<string, string>;
description?: string;
comment?: string; // Optional manual comment to append
[key: string]: any; // Allow additional properties
}
interface Props {
className?: string;
columns?: Array<{ label: string; key: string }>;
data?: Array<any>;
reference?: Array<ReferenceItem>; // Direct prop for reference data
}
const {
className = "table reference-table",
columns,
data,
reference
} = Astro.props;
// Use explicit reference prop or data prop
const referenceData = reference || data || [];
// Parse CSS variables from _root.scss at build time
function parseCSSVariables(): Record<string, string> {
try {
const projectRoot = process.cwd();
const rootScssPath = join(projectRoot, 'scss/_root.scss');
const scssContent = readFileSync(rootScssPath, 'utf-8');
const cssVarValues: Record<string, string> = {};
// Match CSS variable declarations: --#{$prefix}variable-name: value;
// This regex captures the variable name and its value
const varRegex = /--#\{\$prefix\}([a-z0-9-]+):\s*([^;]+);/gi;
let match;
while ((match = varRegex.exec(scssContent)) !== null) {
const varName = `--bs-${match[1]}`;
let value = match[2].trim();
// Clean up SCSS interpolation syntax (e.g., #{$variable})
value = value.replace(/#\{[^}]+\}/g, '').trim();
// Remove inline comments
value = value.replace(/\/\/.*$/gm, '').trim();
// Only store if we have a clean value (not empty after removing interpolations)
if (value) {
cssVarValues[varName] = value;
}
}
return cssVarValues;
} catch (error) {
console.warn('Could not parse CSS variables from _root.scss:', error);
return {};
}
}
const cssVarValues = parseCSSVariables();
// Function to add CSS variable value comments
function addVarComments(cssValue: string): string {
const comments: string[] = [];
// Collect resolved values for all CSS variables
cssValue.replace(/var\((--[a-z0-9-]+)\)/gi, (match, varName) => {
const resolvedValue = cssVarValues[varName];
if (resolvedValue) {
comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`);
}
return match;
});
// Append comments after the last semicolon or at the end
if (comments.length > 0) {
const hasSemicolon = cssValue.trimEnd().endsWith(';');
return `${cssValue}${hasSemicolon ? '' : ';'} ${comments.join(' ')}`;
}
return cssValue;
}
// If no explicit columns provided, infer from the first data item
const inferredColumns = columns || (() => {
if (referenceData.length === 0) {
return [
{ label: 'Class', key: 'class' },
{ label: 'Styles', key: 'styles' }
];
}
const firstItem = referenceData[0];
return Object.keys(firstItem)
.filter(key => key !== 'comment') // Exclude comment field from columns
.map(key => ({
label: key.charAt(0).toUpperCase() + key.slice(1),
key: key
}));
})();
// Transform frontmatter format to table format
const tableData = referenceData.map((item: ReferenceItem) => {
const transformedItem: Record<string, any> = {};
inferredColumns.forEach(column => {
const key = column.key;
let value = item[key];
if (key === 'class' && typeof value === 'string' && !value.startsWith('.')) {
value = `.${value}`;
}
if (key === 'styles') {
let processedStyles = '';
if (typeof value === 'string') {
processedStyles = addVarComments(value);
} else if (typeof value === 'object' && !Array.isArray(value)) {
// Handle object syntax: { prop: value, prop2: value2 }
processedStyles = Object.entries(value)
.map(([prop, val]) => {
const cssLine = `${prop}: ${val};`;
return addVarComments(cssLine);
})
.join('<br/>');
} else if (Array.isArray(value)) {
processedStyles = value.map((style: any) => {
if (typeof style === 'string') {
const formattedStyle = style.includes(':') ? style + (style.endsWith(';') ? '' : ';') : style;
return addVarComments(formattedStyle);
}
if (typeof style === 'object') {
const cssLine = Object.entries(style).map(([prop, val]) => `${prop}: ${val};`).join(' ');
return addVarComments(cssLine);
}
return style;
}).join('<br/>');
} else {
processedStyles = value || '';
}
// Append manual comment if provided in frontmatter
if (item.comment) {
processedStyles += `<br/><span class="color-3">/* ${item.comment} */</span>`;
}
transformedItem[key] = processedStyles;
} else {
transformedItem[key] = value;
}
});
return transformedItem;
});
---
<div class="table-responsive bd-reference-table">
<table class={className}>
<thead>
<tr>
{inferredColumns.map(column => (
<th scope="col">{column.label}</th>
))}
</tr>
</thead>
<tbody>
{tableData.map((row: any) => (
<tr>
{inferredColumns.map(column => (
<td>
{column.key === 'styles' ? (
<Fragment set:html={row[column.key]} />
) : (
row[column.key]
)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>