Add Svelte 5 (Runes) icon package (#1434)
Introduced the @tabler/icons-svelte-runes package for Svelte 5+ with runes reactivity, including source, build, tests, and documentation. Updated issue templates, labeler, and README to reference the new package and distinguish between Svelte 4 and Svelte 5 usage. Adjusted build scripts and lockfile to support the new package.
This commit is contained in:
parent
48280d7eb6
commit
0678fad12c
|
|
@ -31,6 +31,7 @@ body:
|
||||||
- label: "@tabler/icons-react-native"
|
- label: "@tabler/icons-react-native"
|
||||||
- label: "@tabler/icons-solid"
|
- label: "@tabler/icons-solid"
|
||||||
- label: "@tabler/icons-svelte"
|
- label: "@tabler/icons-svelte"
|
||||||
|
- label: "@tabler/icons-svelte-runes"
|
||||||
- label: "@tabler/icons-vue"
|
- label: "@tabler/icons-vue"
|
||||||
- label: Figma plugin
|
- label: Figma plugin
|
||||||
- label: source/main
|
- label: source/main
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,18 @@
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- 'packages/icons-preact/*'
|
- 'packages/icons-preact/*'
|
||||||
|
|
||||||
# For Svelte package
|
# For Svelte 4 and below package
|
||||||
🔗 svelte package:
|
🔗 svelte package:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- 'packages/icons-svelte/*'
|
- 'packages/icons-svelte/*'
|
||||||
|
|
||||||
|
# For Svelte 5 package
|
||||||
|
🔗 svelte-runes package:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/icons-svelte-runes/*'
|
||||||
|
|
||||||
# For SolidJS package
|
# For SolidJS package
|
||||||
🔗 solid package:
|
🔗 solid package:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -232,7 +232,7 @@ After importing the _IconsModule_ in your feature or shared module, use the icon
|
||||||
|
|
||||||
For more usage documentation refer to [the official documentation](https://github.com/pierreavn/angular-tabler-icons).
|
For more usage documentation refer to [the official documentation](https://github.com/pierreavn/angular-tabler-icons).
|
||||||
|
|
||||||
### Svelte
|
### Svelte 4 and below
|
||||||
|
|
||||||
Svelte components available through [`@tabler/icons-svelte`](https://github.com/tabler/tabler-icons/tree/master/packages/icons-svelte) package.
|
Svelte components available through [`@tabler/icons-svelte`](https://github.com/tabler/tabler-icons/tree/master/packages/icons-svelte) package.
|
||||||
|
|
||||||
|
|
@ -248,6 +248,21 @@ Svelte components available through [`@tabler/icons-svelte`](https://github.com/
|
||||||
</main>
|
</main>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Svelte 5
|
||||||
|
Svelte 5 components available through [`@tabler/icons-svelte-runes`](https://www.npmjs.com/package/@tabler/icons-svelte-runes) package.
|
||||||
|
|
||||||
|
```js
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<IconHeart size={48} stroke={1} />
|
||||||
|
<IconHeart size="32" stroke={1.5} />
|
||||||
|
<IconHeart color="crimson" class="p-1" size="96" stroke="2" />
|
||||||
|
</main>
|
||||||
|
```
|
||||||
|
|
||||||
## CDN
|
## CDN
|
||||||
|
|
||||||
All files included in `@tabler/icons` npm package are available over a CDN.
|
All files included in `@tabler/icons` npm package are available over a CDN.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
title: Tabler Icons for Svelte 5
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<TabsPackage name="@tabler/icons-svelte-runes" />
|
||||||
|
|
||||||
|
or just [download from Github](https://github.com/tabler/tabler-icons/releases).
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
It's build with ESmodules so it's completely tree-shakable. Each icon can be imported as a component.
|
||||||
|
|
||||||
|
```sveltehtml
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<IconHeart />
|
||||||
|
</main>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can pass additional props to adjust the icon.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<IconHeart size={48} stroke={1} />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| name | type | default |
|
||||||
|
| ------------- | -------- | ------------ |
|
||||||
|
| `size` | _Number_ | 24 |
|
||||||
|
| `color` | _String_ | currentColor |
|
||||||
|
| `stroke` | _Number_ | 2 |
|
||||||
|
| `class` | _String_ | |
|
||||||
|
|
@ -33,13 +33,14 @@
|
||||||
"validate": "node ./.build/validate-icons.mjs",
|
"validate": "node ./.build/validate-icons.mjs",
|
||||||
"release": "git pull && release-it --verbose",
|
"release": "git pull && release-it --verbose",
|
||||||
"build:copy": "rm -rf ./icons && mkdir ./icons && cp ./_site/tags.json tags.json && cp ./_site/icons/* ./icons/ && rm -rf ./_site/",
|
"build:copy": "rm -rf ./icons && mkdir ./icons && cp ./_site/tags.json tags.json && cp ./_site/icons/* ./icons/ && rm -rf ./_site/",
|
||||||
"build:packages": "pnpm run build:icons && pnpm run build:sprite && pnpm run build:react && pnpm run build:react-native && pnpm run build:preact && pnpm run build:solidjs && pnpm run build:svelte && pnpm run build:vue && pnpm run build:png && pnpm run build:pdf && pnpm run build:esp && pnpm run build:webfont",
|
"build:packages": "pnpm run build:icons && pnpm run build:sprite && pnpm run build:react && pnpm run build:react-native && pnpm run build:preact && pnpm run build:solidjs && pnpm run build:svelte && pnpm run build:svelte-runes && pnpm run build:vue && pnpm run build:png && pnpm run build:pdf && pnpm run build:esp && pnpm run build:webfont",
|
||||||
"build:icons": "pnpm --filter @tabler/icons build",
|
"build:icons": "pnpm --filter @tabler/icons build",
|
||||||
"build:react": "pnpm --filter @tabler/icons-react build",
|
"build:react": "pnpm --filter @tabler/icons-react build",
|
||||||
"build:react-native": "pnpm --filter @tabler/icons-react-native build",
|
"build:react-native": "pnpm --filter @tabler/icons-react-native build",
|
||||||
"build:preact": "pnpm --filter @tabler/icons-preact build",
|
"build:preact": "pnpm --filter @tabler/icons-preact build",
|
||||||
"build:solidjs": "pnpm --filter @tabler/icons-solidjs build",
|
"build:solidjs": "pnpm --filter @tabler/icons-solidjs build",
|
||||||
"build:svelte": "pnpm --filter @tabler/icons-svelte build",
|
"build:svelte": "pnpm --filter @tabler/icons-svelte build",
|
||||||
|
"build:svelte-runes": "pnpm --filter @tabler/icons-svelte-runes build",
|
||||||
"build:vue": "pnpm --filter @tabler/icons-vue build",
|
"build:vue": "pnpm --filter @tabler/icons-vue build",
|
||||||
"build:png": "pnpm --filter @tabler/icons-png build",
|
"build:png": "pnpm --filter @tabler/icons-png build",
|
||||||
"build:pdf": "pnpm --filter @tabler/icons-pdf build",
|
"build:pdf": "pnpm --filter @tabler/icons-pdf build",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
src/icons/*.svelte
|
||||||
|
.svelte-kit
|
||||||
|
src/aliases.ts
|
||||||
|
src/icons-list.ts
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
# Tabler Icons for Svelte (Runes)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/tabler/tabler-icons/main/.github/packages/og-package-svelte.png" alt="Tabler Icons" width="838">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Implementation of the Tabler Icons library for Svelte 5+ using the new runes reactivity system.
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://tabler-icons.io/"><strong>Browse all icons at tabler-icons.io →</strong></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/tabler/tabler-icons/releases"><img src="https://img.shields.io/npm/v/@tabler/icons-svelte-runes" alt="Latest Release"></a>
|
||||||
|
<a href="https://github.com/tabler/tabler-icons/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@tabler/icons.svg" alt="License"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Why This Package?
|
||||||
|
|
||||||
|
This package is specifically built for **Svelte 5+** using the new **runes** reactivity system (`$props()`, `$derived`, etc.).
|
||||||
|
|
||||||
|
- **For Svelte 5+ projects:** Use this package (`@tabler/icons-svelte-runes`)
|
||||||
|
- **For Svelte 3/4 projects:** Use [`@tabler/icons-svelte`](https://www.npmjs.com/package/@tabler/icons-svelte)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add @tabler/icons-svelte-runes
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @tabler/icons-svelte-runes
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add @tabler/icons-svelte-runes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **Svelte 5.0+** with runes enabled
|
||||||
|
- For older Svelte versions, use `@tabler/icons-svelte` instead
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
It's built with ES modules so it's completely tree-shakable. Each icon can be imported as a component.
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<IconHeart />
|
||||||
|
```
|
||||||
|
|
||||||
|
You can pass additional props to adjust the icon:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<IconHeart size={48} stroke={1} color="red" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| name | type | default |
|
||||||
|
| -------- | ------------------ | ------------ |
|
||||||
|
| `size` | `number \| string` | 24 |
|
||||||
|
| `color` | `string` | currentColor |
|
||||||
|
| `stroke` | `number \| string` | 2 |
|
||||||
|
| `class` | `string` | '' |
|
||||||
|
|
||||||
|
All other HTML attributes are forwarded to the SVG element.
|
||||||
|
|
||||||
|
## TypeScript Support
|
||||||
|
|
||||||
|
The package includes full TypeScript definitions. Icons are typed as Svelte 5 `Component<IconProps>`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Icon } from '@tabler/icons-svelte-runes';
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
|
||||||
|
// Icon is compatible with Svelte 5's Component type
|
||||||
|
const MyIcon: Icon = IconHeart;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Icons in Props
|
||||||
|
|
||||||
|
When passing icons as props, use the `Component` type or `any` for maximum compatibility:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Component } from 'svelte';
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon: Component<any>;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { icon: Icon, label }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button>
|
||||||
|
<Icon size={20} />
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Basic Icon Usage
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart, IconStar, IconHome } from '@tabler/icons-svelte-runes';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<IconHeart />
|
||||||
|
<IconStar size={32} color="gold" />
|
||||||
|
<IconHome stroke={1.5} class="my-icon" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic Icons with Runes
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart, IconStar, IconCircle } from '@tabler/icons-svelte-runes';
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
heart: IconHeart,
|
||||||
|
star: IconStar,
|
||||||
|
circle: IconCircle
|
||||||
|
};
|
||||||
|
|
||||||
|
let selected = $state('heart');
|
||||||
|
let DynamicIcon = $derived(icons[selected]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DynamicIcon size={32} />
|
||||||
|
|
||||||
|
<button onclick={() => selected = 'heart'}>Heart</button>
|
||||||
|
<button onclick={() => selected = 'star'}>Star</button>
|
||||||
|
<button onclick={() => selected = 'circle'}>Circle</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reactive Size with $derived
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
|
||||||
|
let isLarge = $state(false);
|
||||||
|
let iconSize = $derived(isLarge ? 48 : 24);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<IconHeart size={iconSize} />
|
||||||
|
<button onclick={() => isLarge = !isLarge}>Toggle Size</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migrating from @tabler/icons-svelte
|
||||||
|
|
||||||
|
The API is identical, just change the package name:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- import { IconHeart } from '@tabler/icons-svelte';
|
||||||
|
+ import { IconHeart } from '@tabler/icons-svelte-runes';
|
||||||
|
```
|
||||||
|
|
||||||
|
No other changes needed! Your existing props and usage remain the same.
|
||||||
|
|
||||||
|
## What's Different from @tabler/icons-svelte?
|
||||||
|
|
||||||
|
Internally, this package uses Svelte 5's new features:
|
||||||
|
|
||||||
|
- `$props()` instead of `export let`
|
||||||
|
- `$derived` for computed values
|
||||||
|
- Modern TypeScript `Component` type
|
||||||
|
- Optimized for Svelte 5's fine-grained reactivity
|
||||||
|
|
||||||
|
**Result:** Better performance and smaller bundle sizes in Svelte 5 projects!
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
**If you want to support this project, you can [become a sponsor on GitHub](https://github.com/sponsors/codecalm) or just [donate on PayPal](https://paypal.me/codecalm) :)**
|
||||||
|
|
||||||
|
<a href="https://github.com/sponsors/codecalm">
|
||||||
|
<img src="https://cdn.jsdelivr.net/gh/tabler/sponsors@latest/sponsors.svg" alt="Tabler sponsors">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
For more info on how to contribute please see the [contribution guidelines](https://github.com/tabler/tabler-icons/blob/main/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/tabler/tabler-icons/blob/main/packages/icons-svelte-runes/README.md)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Tabler Icons is licensed under the [MIT License](https://github.com/tabler/tabler-icons/blob/master/LICENSE).
|
||||||
|
|
||||||
|
## Sponsor Tabler
|
||||||
|
|
||||||
|
<a href="https://github.com/sponsors/codecalm" target="_blank"><img src="https://github.com/tabler/tabler/raw/dev/src/static/sponsor-banner-readme.png?raw=true" alt="Sponsor Tabler" /></a>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { buildJsIcons, buildIconsList } from '../../.build/build-icons.mjs';
|
||||||
|
|
||||||
|
const componentTemplate = ({ type, name, children, stringify }) => {
|
||||||
|
return `\
|
||||||
|
<script lang="ts">
|
||||||
|
import Icon from '../Icon.svelte';
|
||||||
|
import type { IconNode, IconProps } from '../types.js';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
|
interface Props extends IconProps {
|
||||||
|
children?: Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children, ...props }: Props = $props();
|
||||||
|
|
||||||
|
const iconNode: IconNode = ${JSON.stringify(children)};
|
||||||
|
</script>
|
||||||
|
<Icon type="${type}" name="${name}" {...props} iconNode={iconNode} {children} />
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const aliasTemplate = ({ fromPascal, to }) =>
|
||||||
|
`export { default as Icon${fromPascal} } from './icons/${to}.svelte';\n`;
|
||||||
|
|
||||||
|
const indexItemTemplate = ({ name, namePascal }) =>
|
||||||
|
`export { default as Icon${namePascal} } from './${name}.svelte';`;
|
||||||
|
|
||||||
|
buildJsIcons({
|
||||||
|
name: 'icons-svelte-runes',
|
||||||
|
componentTemplate,
|
||||||
|
indexItemTemplate,
|
||||||
|
aliasTemplate,
|
||||||
|
extension: 'svelte',
|
||||||
|
key: false,
|
||||||
|
indexFile: 'index.ts',
|
||||||
|
pascalName: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
buildIconsList('icons-svelte-runes');
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
{
|
||||||
|
"name": "@tabler/icons-svelte-runes",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "codecalm",
|
||||||
|
"description": "A set of free MIT-licensed high-quality SVG icons for Svelte 5+ using runes",
|
||||||
|
"homepage": "https://tabler-icons.io",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/tabler/tabler-icons/issues"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/codecalm"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/tabler/tabler-icons.git",
|
||||||
|
"directory": "packages/icons-svelte-runes"
|
||||||
|
},
|
||||||
|
"svelte": "./dist/tabler-icons-svelte-runes.js",
|
||||||
|
"types": "./dist/tabler-icons-svelte-runes.d.ts",
|
||||||
|
"type": "module",
|
||||||
|
"amdName": "TablerIconsSvelteRunes",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/tabler-icons-svelte-runes.d.ts",
|
||||||
|
"svelte": "./dist/tabler-icons-svelte-runes.js",
|
||||||
|
"default": "./dist/tabler-icons-svelte-runes.js"
|
||||||
|
},
|
||||||
|
"./icons": {
|
||||||
|
"types": "./dist/tabler-icons-svelte-runes.d.ts",
|
||||||
|
"svelte": "./dist/tabler-icons-svelte-runes.js"
|
||||||
|
},
|
||||||
|
"./icons/*": {
|
||||||
|
"types": "./dist/icons/*.svelte.d.ts",
|
||||||
|
"svelte": "./dist/icons/*.svelte"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sideEffects": false,
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"svelte",
|
||||||
|
"svelte5",
|
||||||
|
"runes",
|
||||||
|
"icons",
|
||||||
|
"svg",
|
||||||
|
"tabler",
|
||||||
|
"tabler-icons",
|
||||||
|
"svelte-runes",
|
||||||
|
"ui"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm run clean && pnpm run copy:license && pnpm run build:icons && pnpm run build:package",
|
||||||
|
"build:icons": "node build.mjs",
|
||||||
|
"build:package": "svelte-package --input ./src",
|
||||||
|
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||||
|
"clean": "rm -rf dist && find . ! -name '.gitkeep' -path '*/src/icons/*' -exec rm -rf {} +",
|
||||||
|
"test": "vitest run",
|
||||||
|
"imports-check": "attw $(npm pack)"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tabler/icons": "3.35.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/package": "^2.3.7",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/svelte": "^5.2.9",
|
||||||
|
"@tsconfig/svelte": "^5.0.4",
|
||||||
|
"jest-serializer-html": "^7.1.0",
|
||||||
|
"jsdom": "^25.0.1",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.3.4",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vite": "^7.2.7",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { expect } from 'vitest';
|
||||||
|
import * as matchers from '@testing-library/jest-dom/matchers';
|
||||||
|
import htmlSerializer from 'jest-serializer-html';
|
||||||
|
|
||||||
|
// Extend Vitest's expect with jest-dom matchers
|
||||||
|
expect.extend(matchers);
|
||||||
|
|
||||||
|
// Add HTML serializer for snapshots
|
||||||
|
expect.addSnapshotSerializer(htmlSerializer);
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import defaultAttributes from './defaultAttributes';
|
||||||
|
import type { IconNode } from './types';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
type: 'outline' | 'filled';
|
||||||
|
name: string;
|
||||||
|
color?: string;
|
||||||
|
size?: number | string;
|
||||||
|
stroke?: number | string;
|
||||||
|
iconNode: IconNode;
|
||||||
|
class?: string;
|
||||||
|
children?: Snippet;
|
||||||
|
[key: string]: any; // For rest props
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
color = 'currentColor',
|
||||||
|
size = 24,
|
||||||
|
stroke = 2,
|
||||||
|
iconNode,
|
||||||
|
class: className = '',
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
// Derive the fill/stroke attributes based on type
|
||||||
|
const typeAttributes = $derived(
|
||||||
|
type === 'filled' ? { fill: color } : { 'stroke-width': stroke, stroke: color },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Derive the full class name
|
||||||
|
const fullClassName = $derived(`tabler-icon tabler-icon-${name} ${className}`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
{...defaultAttributes[type]}
|
||||||
|
{...restProps}
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
class={fullClassName}
|
||||||
|
{...typeAttributes}
|
||||||
|
>
|
||||||
|
{#each iconNode as [tag, attrs]}
|
||||||
|
<svelte:element this={tag} {...attrs} />
|
||||||
|
{/each}
|
||||||
|
{#if children}
|
||||||
|
{@render children()}
|
||||||
|
{/if}
|
||||||
|
</svg>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { Attrs } from "./types.js";
|
||||||
|
|
||||||
|
const defaultAttributes: Record<"outline" | "filled", Attrs> = {
|
||||||
|
outline: {
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'currentColor',
|
||||||
|
'stroke-width': 2,
|
||||||
|
'stroke-linecap': 'round',
|
||||||
|
'stroke-linejoin': 'round',
|
||||||
|
},
|
||||||
|
filled: {
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
fill: 'currentColor',
|
||||||
|
stroke: 'none'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defaultAttributes;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export * from './icons/index.js';
|
||||||
|
export * as icons from './icons/index.js';
|
||||||
|
export * as iconsList from './icons-list.js';
|
||||||
|
export * from './aliases.js';
|
||||||
|
export { default as defaultAttributes } from './defaultAttributes.js';
|
||||||
|
|
||||||
|
export type { Icon } from './types.js';
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import type { Component, Snippet } from 'svelte';
|
||||||
|
import type { SVGAttributes, SvelteHTMLElements } from 'svelte/elements';
|
||||||
|
|
||||||
|
export type Attrs = SVGAttributes<SVGSVGElement>;
|
||||||
|
|
||||||
|
export type IconNode = [elementName: keyof SvelteHTMLElements, attrs: Attrs][];
|
||||||
|
|
||||||
|
export interface IconProps extends Omit<Attrs, 'color' | 'stroke'> {
|
||||||
|
color?: string;
|
||||||
|
size?: number | string;
|
||||||
|
stroke?: number | string;
|
||||||
|
class?: string;
|
||||||
|
children?: Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Svelte 5 compatible icon type
|
||||||
|
export type Icon = Component<IconProps>;
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
compilerOptions: {
|
||||||
|
runes: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
import { describe, it, expect, afterEach } from 'vitest';
|
||||||
|
import { render, cleanup } from '@testing-library/svelte';
|
||||||
|
import { IconAccessible, IconAccessibleFilled } from './src/tabler-icons-svelte-runes.js';
|
||||||
|
|
||||||
|
describe('Svelte Runes Icon component', () => {
|
||||||
|
afterEach(() => cleanup());
|
||||||
|
|
||||||
|
it('should render icon component', () => {
|
||||||
|
const { container } = render(IconAccessible);
|
||||||
|
expect(container.getElementsByTagName('svg').length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a class to the element', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
class: 'test-class',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg).toHaveClass('test-class');
|
||||||
|
expect(svg).toHaveClass('tabler-icon');
|
||||||
|
expect(svg).toHaveClass('tabler-icon-accessible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a style attribute to the element', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
style: 'color: red',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg).toHaveStyle('color: rgb(255, 0, 0)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update svg attributes when there are props passed to the component', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
size: 48,
|
||||||
|
color: 'red',
|
||||||
|
stroke: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg.getAttribute('width')).toBe('48');
|
||||||
|
expect(svg.getAttribute('stroke')).toBe('red');
|
||||||
|
expect(svg.getAttribute('stroke-width')).toBe('4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept stroke as a number', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
stroke: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg.getAttribute('stroke-width')).toBe('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept size as a string', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
size: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg.getAttribute('width')).toBe('100%');
|
||||||
|
expect(svg.getAttribute('height')).toBe('100%');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update svg attributes when there are props passed to the filled version of component', () => {
|
||||||
|
const { container } = render(IconAccessibleFilled, {
|
||||||
|
props: {
|
||||||
|
size: 48,
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
|
||||||
|
expect(svg.getAttribute('width')).toBe('48');
|
||||||
|
expect(svg.getAttribute('fill')).toBe('red');
|
||||||
|
expect(svg.getAttribute('stroke')).toBe('none');
|
||||||
|
expect(svg.getAttribute('stroke-width')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render children content using snippets', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {},
|
||||||
|
// Test that children can be passed (even though we can't easily test snippet content in this setup)
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName('svg')[0];
|
||||||
|
expect(svg).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match snapshot', () => {
|
||||||
|
const { container } = render(IconAccessible);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewbox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="tabler-icon tabler-icon-accessible "
|
||||||
|
>
|
||||||
|
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0">
|
||||||
|
</path>
|
||||||
|
<path d="M10 16.5l2 -3l2 3m-2 -3v-2l3 -1m-6 0l3 1">
|
||||||
|
</path>
|
||||||
|
<circle cx="12"
|
||||||
|
cy="7.5"
|
||||||
|
r=".5"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"types": ["vitest/globals", "@testing-library/jest-dom"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.svelte",
|
||||||
|
"tests/**/*.ts",
|
||||||
|
"*.spec.js",
|
||||||
|
"vitest-setup.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// <reference types="vitest" />
|
||||||
|
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
|
||||||
|
|
||||||
|
declare module 'vitest' {
|
||||||
|
interface Assertion<T = any> extends jest.Matchers<void, T>, TestingLibraryMatchers<T, void> {}
|
||||||
|
interface AsymmetricMatchersContaining extends TestingLibraryMatchers<any, void> {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [svelte()],
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
setupFiles: './setupVitest.ts',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
conditions: ['browser'],
|
||||||
|
},
|
||||||
|
});
|
||||||
830
pnpm-lock.yaml
830
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue