mirror of
https://codeberg.org/HPCesia/AstralHalo.git
synced 2025-04-08 17:34:27 +08:00
feat(user components): icon
This commit is contained in:
parent
597ab70be5
commit
f0f9457f9e
34
src/components/user/Icon.astro
Normal file
34
src/components/user/Icon.astro
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
import { Icon as AstroIcon } from 'astro-icon/components';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
size?:
|
||||
| string
|
||||
| {
|
||||
width: string;
|
||||
height: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { name, size } = Astro.props;
|
||||
let width = '1.25em';
|
||||
let height = '1.25em';
|
||||
if (size) {
|
||||
if (typeof size === 'string') {
|
||||
width = size;
|
||||
height = size;
|
||||
} else {
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<AstroIcon
|
||||
name={name}
|
||||
class="inline align-text-bottom"
|
||||
width={width}
|
||||
height={height}
|
||||
is:inline
|
||||
/>
|
@ -1,4 +1,5 @@
|
||||
export { default as Collapse } from './Collapse.astro';
|
||||
export { default as Icon } from './Icon.astro';
|
||||
export { default as LinkCard } from './LinkCard.astro';
|
||||
export { default as Repl } from './Repl.astro';
|
||||
export { default as RepoCard } from './RepoCard.astro';
|
||||
|
@ -9,7 +9,7 @@ tags:
|
||||
published: 2025-02-10T21:23:23+08:00
|
||||
---
|
||||
|
||||
import { Collapse, LinkCard, Repl, RepoCard, TabItem, Tabs, Tooltip } from '@components/user';
|
||||
import { Collapse, Icon, LinkCard, Repl, RepoCard, TabItem, Tabs, Tooltip } from '@components/user';
|
||||
|
||||
Components let you easily reuse a piece of UI or styling consistently. You can use them not just in `.astro` files, but also in `.mdx` files.
|
||||
|
||||
@ -145,6 +145,7 @@ Components let you easily reuse a piece of UI or styling consistently. You can u
|
||||
## Inline Containers
|
||||
|
||||
### Tooltip
|
||||
|
||||
<Repl>
|
||||
<div class="flex flex-col gap-2 w-fit mx-auto">
|
||||
<Tooltip tip="I'm here!" position="top">
|
||||
@ -190,6 +191,34 @@ Components let you easily reuse a piece of UI or styling consistently. You can u
|
||||
</Fragment>
|
||||
</Repl>
|
||||
|
||||
### Icon
|
||||
|
||||
<Repl>
|
||||
<div class="flex flex-col gap-2 w-fit mx-auto">
|
||||
<span>You can get this template in <Icon name="mdi:github" />[GitHub](https://github.com/HPCesia/astral-halo)</span>
|
||||
<span>This is an open source project <Icon name="mdi:open-source-initiative" /></span>
|
||||
<span>A huge icon is here: <Icon name="mdi:alert-octagon" size="5em" /></span>
|
||||
</div>
|
||||
<Fragment slot="desc">
|
||||
<Tabs>
|
||||
<TabItem label="mdx" active>
|
||||
```jsx
|
||||
<span>You can get this template in <Icon name="mdi:github" />[GitHub](https://github.com/HPCesia/astral-halo)</span>
|
||||
<span>This is an open source project <Icon name="mdi:open-source-initiative" /></span>
|
||||
<span>A huge icon is here: <Icon name="mdi:alert-octagon" size="5em" /></span>
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="md">
|
||||
```md
|
||||
:icon{name="mdi:github"}
|
||||
:icon{name="mdi:open-source-initiative"}
|
||||
:icon{name="mdi:alert-octagon" size="5em"}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
</Fragment>
|
||||
</Repl>
|
||||
|
||||
## Web Contents
|
||||
|
||||
### RepoCard
|
||||
|
@ -1,10 +1,52 @@
|
||||
/**
|
||||
* All components in this file should sync with the components in `src/components/user`
|
||||
*/
|
||||
import { icons as MaterialSymbols } from '@iconify-json/material-symbols';
|
||||
import { getIconData, iconToHTML, iconToSVG } from '@iconify/utils';
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import { getIconData, iconToHTML, iconToSVG, stringToIcon } from '@iconify/utils';
|
||||
import { h } from 'hastscript';
|
||||
import type { Child } from 'hastscript';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
async function detectInstalledCollections(root: string) {
|
||||
try {
|
||||
const packages = [];
|
||||
const text = await readFile(path.resolve(root, './package.json'), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const { dependencies = {}, devDependencies = {} } = JSON.parse(text);
|
||||
packages.push(...Object.keys(dependencies));
|
||||
packages.push(...Object.keys(devDependencies));
|
||||
const collections = packages
|
||||
.filter((name) => name.startsWith('@iconify-json/'))
|
||||
.map((name) => name.replace('@iconify-json/', ''));
|
||||
return collections;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const iconSets = await detectInstalledCollections(process.cwd());
|
||||
|
||||
async function loadCollection(name: string) {
|
||||
if (!iconSets.find((it) => it === name)) return;
|
||||
const icons: IconifyJSON = JSON.parse(
|
||||
await readFile(
|
||||
path.resolve(process.cwd(), `./node_modules/@iconify-json/${name}/icons.json`),
|
||||
{
|
||||
encoding: 'utf8',
|
||||
}
|
||||
)
|
||||
);
|
||||
return icons;
|
||||
}
|
||||
|
||||
const collections: Record<string, IconifyJSON> = {};
|
||||
iconSets.forEach(async (set) => {
|
||||
const icons = await loadCollection(set);
|
||||
if (icons) collections[set] = icons;
|
||||
});
|
||||
|
||||
const Collapse = function (
|
||||
props: {
|
||||
@ -28,6 +70,54 @@ const Collapse = function (
|
||||
return h('div', { class: wrapperClassName }, [inputNode, titleNode, contentNode]);
|
||||
};
|
||||
|
||||
const Icon = function (props: {
|
||||
name: string;
|
||||
size?:
|
||||
| string
|
||||
| {
|
||||
width: string;
|
||||
height: string;
|
||||
};
|
||||
}) {
|
||||
const { name, size } = props;
|
||||
let width = '1.25em';
|
||||
let height = '1.25em';
|
||||
if (size) {
|
||||
if (typeof size === 'string') {
|
||||
width = size;
|
||||
height = size;
|
||||
} else {
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
}
|
||||
}
|
||||
const className = 'inline align-middle';
|
||||
|
||||
const { prefix, name: iconName } = stringToIcon(name, true)!;
|
||||
const collection = collections[prefix];
|
||||
if (!collection) {
|
||||
console.error(`'Icon set not found: '${prefix}'`);
|
||||
return h('span', `'Icon set not found: '${prefix}'`);
|
||||
}
|
||||
const iconData = getIconData(collection, iconName);
|
||||
if (!iconData) {
|
||||
console.error(`Icon "${iconName}" not found in icon set '${prefix}'`);
|
||||
return h('span', `Icon "${iconName}" not found in icon set '${prefix}'`);
|
||||
}
|
||||
const { attributes, body } = iconToSVG(iconData);
|
||||
attributes.width = width;
|
||||
attributes.height = height;
|
||||
const iconHtml = iconToHTML(body, attributes);
|
||||
return h(
|
||||
'span',
|
||||
{ class: className },
|
||||
{
|
||||
type: 'raw',
|
||||
value: iconHtml,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const LinkCard = function (props: { title: string; description: string; url: string }) {
|
||||
const { title, description, url } = props;
|
||||
const wrapperClassName = 'card border-base-content/25 my-4 overflow-hidden border';
|
||||
@ -39,7 +129,12 @@ const LinkCard = function (props: { title: string; description: string; url: str
|
||||
const descNode = h('div', { class: descClassName }, description);
|
||||
const contentNode = h('div', null, [titleNode, descNode]);
|
||||
|
||||
const iconData = getIconData(MaterialSymbols, 'arrow-right-alt-rounded');
|
||||
const collection = collections['material-symbols'];
|
||||
if (!collection) {
|
||||
console.error('LinkCard icon set found: material-symbols');
|
||||
return h('a', { class: wrapperClassName, href: url, title }, 'Link card error');
|
||||
}
|
||||
const iconData = getIconData(collection, 'arrow-right-alt-rounded');
|
||||
if (!iconData) {
|
||||
console.error('LinkCard icon not found: material-symbols:arrow-right-alt-rounded');
|
||||
return h('a', { class: wrapperClassName, href: url, title }, 'Link card error');
|
||||
@ -75,6 +170,7 @@ const Tooltip = function (
|
||||
|
||||
export const rehypeComponentsList = {
|
||||
collapse: Collapse,
|
||||
icon: Icon,
|
||||
linkcard: LinkCard,
|
||||
tooltip: Tooltip,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user