runeforge 0.0.1 → 0.0.3
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 +42 -2
- package/dist/components/IconRenderer.svelte +2 -2
- package/dist/components/Modal.svelte +5 -10
- package/dist/components/common/Header.svelte +2 -2
- package/dist/components/crud/Field.svelte +12 -17
- package/dist/components/navigation/Breadcrumbs.svelte +2 -2
- package/dist/icons/sets/bootstrap.js +4 -1
- package/dist/icons/types.d.ts +1 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/package.json +7 -8
- package/dist/icons/bootstrapIconSet.d.ts +0 -1
- package/dist/icons/bootstrapIconSet.js +0 -1
- package/dist/icons/defaultIconSet.d.ts +0 -1
- package/dist/icons/defaultIconSet.js +0 -1
package/README.md
CHANGED
|
@@ -12,6 +12,47 @@ A SvelteKit toolkit that forges forms, tables, actions, and CRUD workflows from
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Runeforge](#runeforge)
|
|
18
|
+
- [Table of Contents](#table-of-contents)
|
|
19
|
+
- [Introduction](#introduction)
|
|
20
|
+
- [Requirements](#requirements)
|
|
21
|
+
- [Key Features](#key-features)
|
|
22
|
+
- [Installation](#installation)
|
|
23
|
+
- [Basic Usage](#basic-usage)
|
|
24
|
+
- [1. Define your interface and metadata](#1-define-your-interface-and-metadata)
|
|
25
|
+
- [2. Create the model](#2-create-the-model)
|
|
26
|
+
- [3. Set up the server](#3-set-up-the-server)
|
|
27
|
+
- [4. Add the page component](#4-add-the-page-component)
|
|
28
|
+
- [Components](#components)
|
|
29
|
+
- [GenericCRUD](#genericcrud)
|
|
30
|
+
- [PaginatedTable](#paginatedtable)
|
|
31
|
+
- [Form Components](#form-components)
|
|
32
|
+
- [Shared Components](#shared-components)
|
|
33
|
+
- [Formatters](#formatters)
|
|
34
|
+
- [`formatBoolean`](#formatboolean)
|
|
35
|
+
- [`formatDatetime`](#formatdatetime)
|
|
36
|
+
- [`formatTruncateTextUpTo`](#formattruncatetextupto)
|
|
37
|
+
- [`formatInstance`](#formatinstance)
|
|
38
|
+
- [Custom Cell Components](#custom-cell-components)
|
|
39
|
+
- [Example: avatar column](#example-avatar-column)
|
|
40
|
+
- [Example: icon column](#example-icon-column)
|
|
41
|
+
- [Internationalization](#internationalization)
|
|
42
|
+
- [Switch to English](#switch-to-english)
|
|
43
|
+
- [Override individual strings](#override-individual-strings)
|
|
44
|
+
- [Full `RuneforgeStrings` reference](#full-runeforgestrings-reference)
|
|
45
|
+
- [Bundled locales](#bundled-locales)
|
|
46
|
+
- [Icon System](#icon-system)
|
|
47
|
+
- [Running Tests](#running-tests)
|
|
48
|
+
- [Unit Tests](#unit-tests)
|
|
49
|
+
- [End-to-End Tests](#end-to-end-tests)
|
|
50
|
+
- [Run All Tests](#run-all-tests)
|
|
51
|
+
- [Development](#development)
|
|
52
|
+
- [License](#license)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
15
56
|
## Introduction
|
|
16
57
|
|
|
17
58
|
Runeforge provides a set of composable, metadata-driven components for building data-heavy interfaces in SvelteKit. It handles the repetitive parts of CRUD UIs — listing records, creating and editing forms, sorting and filtering tables — through a declarative API built on top of [DaisyUI](https://daisyui.com/) and [Tailwind CSS](https://tailwindcss.com/).
|
|
@@ -486,8 +527,7 @@ Runeforge ships with a default icon set. To use Bootstrap Icons instead:
|
|
|
486
527
|
|
|
487
528
|
```svelte
|
|
488
529
|
<script>
|
|
489
|
-
import { setIconSet } from 'runeforge';
|
|
490
|
-
import bootstrapIcons from 'runeforge/icons/bootstrap';
|
|
530
|
+
import { setIconSet, bootstrapIcons } from 'runeforge';
|
|
491
531
|
|
|
492
532
|
setIconSet(bootstrapIcons);
|
|
493
533
|
</script>
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
} = $props();
|
|
13
13
|
|
|
14
14
|
const icons = $derived(getIconSet());
|
|
15
|
-
const IconComponent = $derived(icons?.
|
|
15
|
+
const IconComponent = $derived(icons?.getByName?.(name) ?? null);
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
{#if IconComponent}
|
|
19
|
-
<IconComponent {
|
|
19
|
+
<IconComponent {size} class={className} />
|
|
20
20
|
{:else}
|
|
21
21
|
<span class={className} title={name}></span>
|
|
22
22
|
{/if}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
} = $props();
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
|
-
<
|
|
16
|
+
<dialog class="modal" open>
|
|
17
17
|
<div class="modal-box">
|
|
18
18
|
<div class="flex items-center justify-between gap-4">
|
|
19
19
|
<h3 class="text-lg font-bold">{title}</h3>
|
|
@@ -35,13 +35,8 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
|
|
37
37
|
{#if onClose}
|
|
38
|
-
<div
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
role="button"
|
|
42
|
-
tabindex="0"
|
|
43
|
-
onclick={onClose}
|
|
44
|
-
onkeydown={(e) => e.key === 'Enter' && onClose?.()}
|
|
45
|
-
></div>
|
|
38
|
+
<div class="modal-backdrop">
|
|
39
|
+
<button onclick={onClose} aria-label="Cerrar"></button>
|
|
40
|
+
</div>
|
|
46
41
|
{/if}
|
|
47
|
-
</
|
|
42
|
+
</dialog>
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
</script>
|
|
18
18
|
|
|
19
19
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
20
|
-
<div class="flex flex-col gap-1">
|
|
20
|
+
<div class="flex flex-col gap-1 flex-1">
|
|
21
21
|
<h1>{title}</h1>
|
|
22
22
|
<Breadcrumbs items={breadcrumbs} {admin} />
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
{#if buttons}
|
|
26
|
-
<div class="flex items-center gap-2 pt-1">
|
|
26
|
+
<div class="flex shrink-0 items-center gap-2 sm:pt-1">
|
|
27
27
|
{@render buttons()}
|
|
28
28
|
</div>
|
|
29
29
|
{/if}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<div class="flex flex-col gap-1">
|
|
44
44
|
{#if field.type === 'file'}
|
|
45
45
|
<div class="flex justify-center">
|
|
46
|
-
<Avatar src={preview} text={avatarInitials} alt={labelText} class="w-
|
|
46
|
+
<Avatar src={preview} text={avatarInitials} alt={labelText} class="w-20 rounded-full" textClass="text-xl" />
|
|
47
47
|
</div>
|
|
48
48
|
{/if}
|
|
49
49
|
|
|
@@ -59,25 +59,20 @@
|
|
|
59
59
|
type="checkbox"
|
|
60
60
|
id={field.attribute}
|
|
61
61
|
{name}
|
|
62
|
-
class="toggle"
|
|
62
|
+
class="toggle toggle-primary"
|
|
63
63
|
checked={!!saved}
|
|
64
64
|
disabled={readonly}
|
|
65
65
|
/>
|
|
66
66
|
{:else if field.type === 'file'}
|
|
67
67
|
{#if !readonly}
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/>
|
|
77
|
-
{#if field.placeholder}
|
|
78
|
-
<Label text={field.placeholder} for={field.attribute} class="label" />
|
|
79
|
-
{/if}
|
|
80
|
-
</fieldset>
|
|
68
|
+
<input
|
|
69
|
+
type="file"
|
|
70
|
+
id={field.attribute}
|
|
71
|
+
{name}
|
|
72
|
+
class="file-input file-input-bordered w-full"
|
|
73
|
+
class:file-input-error={!!error}
|
|
74
|
+
onchange={onFileChange}
|
|
75
|
+
/>
|
|
81
76
|
{/if}
|
|
82
77
|
<!-- read-only file value is shown as the avatar above -->
|
|
83
78
|
{:else if field.type === 'select'}
|
|
@@ -121,14 +116,14 @@
|
|
|
121
116
|
{/if}
|
|
122
117
|
{:else if field.type === 'textarea'}
|
|
123
118
|
{#if readonly}
|
|
124
|
-
<textarea id={field.attribute} class="textarea textarea-bordered w-full" value={displayValue} disabled></textarea>
|
|
119
|
+
<textarea id={field.attribute} class="textarea textarea-bordered bg-base-100 w-full" value={displayValue} disabled></textarea>
|
|
125
120
|
{:else}
|
|
126
121
|
<textarea
|
|
127
122
|
id={field.attribute}
|
|
128
123
|
{name}
|
|
129
124
|
placeholder={field.placeholder ?? ''}
|
|
130
125
|
bind:value={record[field.attribute]}
|
|
131
|
-
class="textarea textarea-bordered w-full"
|
|
126
|
+
class="textarea textarea-bordered bg-base-100 w-full"
|
|
132
127
|
class:textarea-error={!!error}
|
|
133
128
|
></textarea>
|
|
134
129
|
{/if}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
{@const ItemIcon = item.icon}
|
|
35
35
|
<ItemIcon class="size-4" />
|
|
36
36
|
{/if}
|
|
37
|
-
<span
|
|
37
|
+
<span>{item.label}</span>
|
|
38
38
|
</a>
|
|
39
39
|
{:else}
|
|
40
40
|
<span class="inline-flex items-center gap-2">
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{@const ItemIcon = item.icon}
|
|
43
43
|
<ItemIcon class="size-4" />
|
|
44
44
|
{/if}
|
|
45
|
-
<span
|
|
45
|
+
<span>{item.label}</span>
|
|
46
46
|
</span>
|
|
47
47
|
{/if}
|
|
48
48
|
</li>
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* import { bootstrapIconSet } from 'runeforge/icons/sets/bootstrap';
|
|
10
10
|
* setIconSet(bootstrapIconSet);
|
|
11
11
|
*/
|
|
12
|
-
import
|
|
12
|
+
import * as Icons from 'svelte-bootstrap-icons';
|
|
13
|
+
const { ChevronExpand, CaretUpFill, CaretDownFill, Funnel, FunnelFill, Plus, Eye, PencilSquare, Trash3, HouseDoor, Folder, EyeSlash, } = Icons;
|
|
13
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
15
|
function asIcon(c) { return c; }
|
|
15
16
|
export const bootstrapIconSet = {
|
|
@@ -26,4 +27,6 @@ export const bootstrapIconSet = {
|
|
|
26
27
|
folder: asIcon(Folder),
|
|
27
28
|
passwordShow: asIcon(Eye),
|
|
28
29
|
passwordHide: asIcon(EyeSlash),
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
getByName: (name) => asIcon(Icons[name]) ?? null,
|
|
29
32
|
};
|
package/dist/icons/types.d.ts
CHANGED
|
@@ -3,11 +3,6 @@ export type IconComponent = Component<{
|
|
|
3
3
|
size?: string | number;
|
|
4
4
|
class?: string;
|
|
5
5
|
}>;
|
|
6
|
-
export type IconByNameComponent = Component<{
|
|
7
|
-
name: string;
|
|
8
|
-
size?: string | number;
|
|
9
|
-
class?: string;
|
|
10
|
-
}>;
|
|
11
6
|
export interface CRUDIconSet {
|
|
12
7
|
sortNone: IconComponent;
|
|
13
8
|
sortAsc: IconComponent;
|
|
@@ -22,5 +17,5 @@ export interface CRUDIconSet {
|
|
|
22
17
|
folder: IconComponent;
|
|
23
18
|
passwordShow: IconComponent;
|
|
24
19
|
passwordHide: IconComponent;
|
|
25
|
-
|
|
20
|
+
getByName?: (name: string) => IconComponent | null;
|
|
26
21
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,10 @@ export { AttributeType } from './types/attribute.js';
|
|
|
3
3
|
export type { AttributeMetadata, InterfaceMetadata, SelectOption, OptionsResolver, FormatterResolver } from './types/attribute.js';
|
|
4
4
|
export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry } from './types/table.js';
|
|
5
5
|
export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, RowAction } from './types/crud.js';
|
|
6
|
-
export type { CRUDIconSet, IconComponent
|
|
6
|
+
export type { CRUDIconSet, IconComponent } from './icons/types.js';
|
|
7
7
|
export { setIconSet, getIconSet } from './icons/context.js';
|
|
8
8
|
export { defaultIconSet } from './icons/sets/default.js';
|
|
9
|
+
export { bootstrapIconSet } from './icons/sets/bootstrap.js';
|
|
9
10
|
export type { RuneforgeStrings } from './i18n/types.js';
|
|
10
11
|
export { setStrings, getStrings } from './i18n/context.js';
|
|
11
12
|
export { es } from './i18n/es.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { AttributeType } from './types/attribute.js';
|
|
2
2
|
export { setIconSet, getIconSet } from './icons/context.js';
|
|
3
3
|
export { defaultIconSet } from './icons/sets/default.js';
|
|
4
|
+
export { bootstrapIconSet } from './icons/sets/bootstrap.js';
|
|
4
5
|
export { setStrings, getStrings } from './i18n/context.js';
|
|
5
6
|
export { es } from './i18n/es.js';
|
|
6
7
|
export { en } from './i18n/en.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runeforge",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "SvelteKit toolkit for building metadata-driven CRUD interfaces with tables, forms, and actions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ezequiel Puerta",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@sveltejs/kit": "^2.0.0",
|
|
51
|
+
"daisyui": "^5.0.0",
|
|
51
52
|
"svelte": "^5.0.0",
|
|
52
|
-
"tailwindcss": "^4.0.0"
|
|
53
|
-
"daisyui": "^5.0.0"
|
|
53
|
+
"tailwindcss": "^4.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@eslint/js": "^10.0.1",
|
|
@@ -59,7 +59,9 @@
|
|
|
59
59
|
"@sveltejs/kit": "^2.63.0",
|
|
60
60
|
"@sveltejs/package": "^2.5.8",
|
|
61
61
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
62
|
+
"@tailwindcss/vite": "^4.3.1",
|
|
62
63
|
"@types/node": "^22",
|
|
64
|
+
"daisyui": "^5.5.23",
|
|
63
65
|
"eslint": "^10.4.1",
|
|
64
66
|
"eslint-config-prettier": "^10.1.8",
|
|
65
67
|
"eslint-plugin-svelte": "^3.19.0",
|
|
@@ -70,13 +72,10 @@
|
|
|
70
72
|
"svelte": "^5.56.1",
|
|
71
73
|
"svelte-bootstrap-icons": "^3.3.0",
|
|
72
74
|
"svelte-check": "^4.6.0",
|
|
75
|
+
"tailwindcss": "^4.3.1",
|
|
73
76
|
"typescript": "^6.0.3",
|
|
74
77
|
"typescript-eslint": "^8.60.1",
|
|
75
78
|
"vite": "^8.0.16",
|
|
76
|
-
"@tailwindcss/vite": "^4.3.1",
|
|
77
|
-
"daisyui": "^5.5.23",
|
|
78
|
-
"tailwindcss": "^4.3.1",
|
|
79
79
|
"vitest": "^4.1.8"
|
|
80
|
-
}
|
|
81
|
-
"dependencies": {}
|
|
80
|
+
}
|
|
82
81
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { bootstrapIconSet } from './sets/bootstrap.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { bootstrapIconSet } from './sets/bootstrap.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { defaultIconSet } from './sets/default.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { defaultIconSet } from './sets/default.js';
|