runeforge 0.0.56 → 0.0.57
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/README.md +18 -5
- package/dist/components/IconRenderer.svelte +56 -5
- package/dist/components/IconRenderer.svelte.d.ts +2 -0
- package/dist/icons/context.d.ts +5 -0
- package/dist/icons/context.js +10 -0
- package/dist/icons/sets/bootstrap.js +1 -4
- package/dist/icons/types.d.ts +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1011,7 +1011,7 @@ Individual form primitives styled with DaisyUI:
|
|
|
1011
1011
|
- `Avatar` — user avatar display
|
|
1012
1012
|
- `Modal` — DaisyUI modal wrapper. Size it with Tailwind utility classes via `class` (e.g. `class="max-w-4xl"`), or with explicit `width`/`maxWidth`/`height`/`maxHeight` CSS lengths, which are applied as inline styles and take priority over `class`
|
|
1013
1013
|
- `Breadcrumbs` — navigation breadcrumb trail
|
|
1014
|
-
- `IconRenderer` — renders
|
|
1014
|
+
- `IconRenderer` — renders a named SVG file fetched from `setIconAssetsPath` (see [Icon System](#icon-system))
|
|
1015
1015
|
|
|
1016
1016
|
---
|
|
1017
1017
|
|
|
@@ -1152,7 +1152,16 @@ export const userMeta = {
|
|
|
1152
1152
|
|
|
1153
1153
|
### Example: icon column
|
|
1154
1154
|
|
|
1155
|
-
A simpler case — render
|
|
1155
|
+
A simpler case — render an SVG file by name, stored as a plain string (e.g. `bar-chart.svg`). Drop the curated set of icons your app actually uses under `static/icons/` (or any path), point `IconRenderer`/`IconCell` at it once with `setIconAssetsPath`, and the field just stores the filename:
|
|
1156
|
+
|
|
1157
|
+
```ts
|
|
1158
|
+
<!-- routes/+layout.svelte -->
|
|
1159
|
+
<script lang="ts">
|
|
1160
|
+
import { setIconAssetsPath } from 'runeforge';
|
|
1161
|
+
|
|
1162
|
+
setIconAssetsPath('/icons');
|
|
1163
|
+
</script>
|
|
1164
|
+
```
|
|
1156
1165
|
|
|
1157
1166
|
```ts
|
|
1158
1167
|
<!-- components/IconCell.svelte -->
|
|
@@ -1174,6 +1183,8 @@ icon: {
|
|
|
1174
1183
|
},
|
|
1175
1184
|
```
|
|
1176
1185
|
|
|
1186
|
+
Only the SVG files a row actually references are ever fetched — nothing is bundled or preloaded up front, unlike importing an entire icon package to look up a component by name.
|
|
1187
|
+
|
|
1177
1188
|
> [!TIP]
|
|
1178
1189
|
> Both `AvatarCell` and `IconCell` are included in the package and ready to use — you don't need to build them from scratch:
|
|
1179
1190
|
>
|
|
@@ -1274,18 +1285,20 @@ All UI strings default to **Spanish** (Argentina). To switch to another language
|
|
|
1274
1285
|
|
|
1275
1286
|
## Icon System
|
|
1276
1287
|
|
|
1277
|
-
Runeforge ships with a default icon set. To use Bootstrap Icons instead:
|
|
1288
|
+
Runeforge ships with a default icon set for the fixed set of CRUD action icons (sort, filter, create, edit, delete, ...). To use Bootstrap Icons instead:
|
|
1278
1289
|
|
|
1279
1290
|
```ts
|
|
1280
1291
|
<script>
|
|
1281
|
-
import { setIconSet,
|
|
1292
|
+
import { setIconSet, bootstrapIconSet } from 'runeforge';
|
|
1282
1293
|
|
|
1283
|
-
setIconSet(
|
|
1294
|
+
setIconSet(bootstrapIconSet);
|
|
1284
1295
|
</script>
|
|
1285
1296
|
```
|
|
1286
1297
|
|
|
1287
1298
|
You can also provide a fully custom icon set by passing an object that satisfies the icon set interface.
|
|
1288
1299
|
|
|
1300
|
+
For per-row/per-entity icons chosen dynamically by name (e.g. a "pick an icon" field on a model), don't use `CRUDIconSet` — see [Example: icon column](#example-icon-column) for `IconRenderer`/`setIconAssetsPath` instead. That path fetches one SVG file per name from a static folder in your app, rather than importing an entire icon package to resolve a component by name.
|
|
1301
|
+
|
|
1289
1302
|
---
|
|
1290
1303
|
|
|
1291
1304
|
## Running Tests
|
|
@@ -1,22 +1,73 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// Module-scoped so every IconRenderer instance shares one fetch/cache per
|
|
3
|
+
// URL instead of re-requesting the same SVG file.
|
|
4
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
5
|
+
const cache = new Map<string, Promise<string | null>>();
|
|
6
|
+
|
|
7
|
+
async function loadSvg(url: string): Promise<string | null> {
|
|
8
|
+
let pending = cache.get(url);
|
|
9
|
+
if (!pending) {
|
|
10
|
+
pending = fetch(url)
|
|
11
|
+
.then((res) => (res.ok ? res.text() : null))
|
|
12
|
+
.catch(() => null);
|
|
13
|
+
cache.set(url, pending);
|
|
14
|
+
}
|
|
15
|
+
const svg = await pending;
|
|
16
|
+
if (svg === null) cache.delete(url);
|
|
17
|
+
return svg;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Stamps `class`/width/height onto the fetched markup's root `<svg>` tag
|
|
21
|
+
* so Tailwind sizing and `currentColor` theming apply the same way they
|
|
22
|
+
* would to a hand-written inline SVG. */
|
|
23
|
+
function styleSvg(svg: string, className: string, size: string): string {
|
|
24
|
+
return svg.replace(
|
|
25
|
+
'<svg',
|
|
26
|
+
`<svg class="${className}" width="${size}" height="${size}"`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
1
31
|
<script lang="ts">
|
|
2
|
-
import {
|
|
32
|
+
import { getIconAssetsPath } from '../icons/context.js';
|
|
3
33
|
|
|
4
34
|
let {
|
|
5
35
|
name,
|
|
36
|
+
basePath,
|
|
6
37
|
size = '1em',
|
|
7
38
|
class: className = '',
|
|
8
39
|
}: {
|
|
9
40
|
name: string;
|
|
41
|
+
/** Overrides the `setIconAssetsPath` context value for this instance. */
|
|
42
|
+
basePath?: string;
|
|
10
43
|
size?: string | number;
|
|
11
44
|
class?: string;
|
|
12
45
|
} = $props();
|
|
13
46
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
47
|
+
const resolvedBasePath = $derived(basePath ?? getIconAssetsPath());
|
|
48
|
+
const dimension = $derived(typeof size === 'number' ? `${size}px` : size);
|
|
49
|
+
const src = $derived(name ? `${resolvedBasePath}/${name}` : null);
|
|
50
|
+
|
|
51
|
+
let markup = $state<string | null>(null);
|
|
52
|
+
|
|
53
|
+
$effect(() => {
|
|
54
|
+
if (!src) {
|
|
55
|
+
markup = null;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let cancelled = false;
|
|
59
|
+
loadSvg(src).then((svg) => {
|
|
60
|
+
if (!cancelled) markup = svg;
|
|
61
|
+
});
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true;
|
|
64
|
+
};
|
|
65
|
+
});
|
|
16
66
|
</script>
|
|
17
67
|
|
|
18
|
-
{#if
|
|
19
|
-
|
|
68
|
+
{#if markup}
|
|
69
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
70
|
+
{@html styleSvg(markup, className, dimension)}
|
|
20
71
|
{:else}
|
|
21
72
|
<span class={className} title={name}></span>
|
|
22
73
|
{/if}
|
package/dist/icons/context.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { CRUDIconSet } from './types.js';
|
|
2
2
|
export declare function setIconSet(icons: Partial<CRUDIconSet>): void;
|
|
3
3
|
export declare function getIconSet(): CRUDIconSet | undefined;
|
|
4
|
+
/** Base path `IconRenderer`/`IconCell` fetch named SVG files from, e.g.
|
|
5
|
+
* `setIconAssetsPath('/icons/chapters')` for files served out of
|
|
6
|
+
* `static/icons/chapters/*.svg`. Defaults to `/icons`. */
|
|
7
|
+
export declare function setIconAssetsPath(path: string): void;
|
|
8
|
+
export declare function getIconAssetsPath(): string;
|
package/dist/icons/context.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
const KEY = Symbol('runeforge-icons');
|
|
3
|
+
const ASSETS_KEY = Symbol('runeforge-icon-assets-path');
|
|
3
4
|
export function setIconSet(icons) {
|
|
4
5
|
const existing = getContext(KEY);
|
|
5
6
|
setContext(KEY, { ...existing, ...icons });
|
|
@@ -7,3 +8,12 @@ export function setIconSet(icons) {
|
|
|
7
8
|
export function getIconSet() {
|
|
8
9
|
return getContext(KEY);
|
|
9
10
|
}
|
|
11
|
+
/** Base path `IconRenderer`/`IconCell` fetch named SVG files from, e.g.
|
|
12
|
+
* `setIconAssetsPath('/icons/chapters')` for files served out of
|
|
13
|
+
* `static/icons/chapters/*.svg`. Defaults to `/icons`. */
|
|
14
|
+
export function setIconAssetsPath(path) {
|
|
15
|
+
setContext(ASSETS_KEY, path);
|
|
16
|
+
}
|
|
17
|
+
export function getIconAssetsPath() {
|
|
18
|
+
return getContext(ASSETS_KEY) ?? '/icons';
|
|
19
|
+
}
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* import { bootstrapIconSet } from 'runeforge/icons/sets/bootstrap';
|
|
10
10
|
* setIconSet(bootstrapIconSet);
|
|
11
11
|
*/
|
|
12
|
-
import
|
|
13
|
-
const { ChevronExpand, CaretUpFill, CaretDownFill, Funnel, FunnelFill, Plus, Eye, PencilSquare, Trash3, HouseDoor, Folder, EyeSlash, X, Download, GripVertical, } = Icons;
|
|
12
|
+
import { ChevronExpand, CaretUpFill, CaretDownFill, Funnel, FunnelFill, Plus, Eye, PencilSquare, Trash3, HouseDoor, Folder, EyeSlash, X, Download, GripVertical, } from 'svelte-bootstrap-icons';
|
|
14
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
14
|
function asIcon(c) { return c; }
|
|
16
15
|
export const bootstrapIconSet = {
|
|
@@ -30,6 +29,4 @@ export const bootstrapIconSet = {
|
|
|
30
29
|
passwordHide: asIcon(EyeSlash),
|
|
31
30
|
download: asIcon(Download),
|
|
32
31
|
grip: asIcon(GripVertical),
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
getByName: (name) => asIcon(Icons[name]) ?? null,
|
|
35
32
|
};
|
package/dist/icons/types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomActi
|
|
|
6
6
|
export type { RuneforgeConfig } from './config/context.js';
|
|
7
7
|
export { setConfig, getConfig } from './config/context.js';
|
|
8
8
|
export type { CRUDIconSet, IconComponent } from './icons/types.js';
|
|
9
|
-
export { setIconSet, getIconSet } from './icons/context.js';
|
|
9
|
+
export { setIconSet, getIconSet, setIconAssetsPath, getIconAssetsPath } from './icons/context.js';
|
|
10
10
|
export { defaultIconSet } from './icons/sets/default.js';
|
|
11
11
|
export { bootstrapIconSet } from './icons/sets/bootstrap.js';
|
|
12
12
|
export type { RuneforgeStrings } from './i18n/types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AttributeType } from './types/attribute.js';
|
|
2
2
|
export { setConfig, getConfig } from './config/context.js';
|
|
3
|
-
export { setIconSet, getIconSet } from './icons/context.js';
|
|
3
|
+
export { setIconSet, getIconSet, setIconAssetsPath, getIconAssetsPath } from './icons/context.js';
|
|
4
4
|
export { defaultIconSet } from './icons/sets/default.js';
|
|
5
5
|
export { bootstrapIconSet } from './icons/sets/bootstrap.js';
|
|
6
6
|
export { setStrings, getStrings } from './i18n/context.js';
|