feat(ui): add inline Icon component for themeable feather icons

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-07-27 19:03:59 -04:00
parent 92552f0d7f
commit 25fddd2afe
4 changed files with 49 additions and 0 deletions

View file

@ -0,0 +1,22 @@
import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import Icon from './Icon.vue';
describe('Icon', () => {
it('renders an inline svg that inherits currentColor', () => {
const wrapper = mount(Icon, { props: { name: 'refresh-cw' } });
expect(wrapper.html()).toContain('<svg');
expect(wrapper.html()).toContain('currentColor');
});
it('applies the size to width and height', () => {
const wrapper = mount(Icon, { props: { name: 'chevron-right', size: 32 } });
expect(wrapper.html()).toContain('width="32"');
expect(wrapper.html()).toContain('height="32"');
});
it('throws for an unknown icon name', () => {
expect(() => mount(Icon, { props: { name: 'not-a-real-icon' } }))
.toThrow(/unknown feather icon/);
});
});

View file

@ -0,0 +1,21 @@
<template>
<span class="inline-flex items-center" v-html="svg"/>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import feather from 'feather-icons';
const props = withDefaults(defineProps<{
name: string;
size?: number;
}>(), {
size: 20,
});
const svg = computed(() => {
const icon = feather.icons[props.name as keyof typeof feather.icons];
if (!icon) throw new Error(`unknown feather icon: ${props.name}`);
return icon.toSvg({ width: props.size, height: props.size });
});
</script>

View file

@ -1,6 +1,9 @@
{
"name": "n-link-core",
"version": "0.0.0",
"dependencies": {
"feather-icons": "^4.29.2"
},
"peerDependencies": {
"element-plus": "^2.14.3",
"vue": "^3.5.40"

3
package-lock.json generated
View file

@ -41,6 +41,9 @@
},
"n-link-core": {
"version": "0.0.0",
"dependencies": {
"feather-icons": "^4.29.2"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.11",
"tailwindcss": "^3.4.19"