runeforge 0.0.55 → 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.
Files changed (61) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1355 -1342
  3. package/dist/components/Avatar.svelte +31 -31
  4. package/dist/components/IconRenderer.svelte +73 -22
  5. package/dist/components/IconRenderer.svelte.d.ts +2 -0
  6. package/dist/components/Modal.svelte +75 -75
  7. package/dist/components/common/Header.svelte +40 -40
  8. package/dist/components/crud/EmbeddedField.svelte +175 -175
  9. package/dist/components/crud/Field.svelte +469 -376
  10. package/dist/components/crud/GenericCRUD.svelte +426 -426
  11. package/dist/components/crud/SearchInput.svelte +53 -53
  12. package/dist/components/crud/columns/Avatar.svelte +15 -15
  13. package/dist/components/crud/columns/Icon.svelte +8 -8
  14. package/dist/components/crud/views/Create.svelte +232 -232
  15. package/dist/components/crud/views/Read.svelte +124 -124
  16. package/dist/components/crud/views/Update.svelte +208 -208
  17. package/dist/components/crud/views/list/List.svelte +291 -291
  18. package/dist/components/crud/views/list/Modals.svelte +56 -56
  19. package/dist/components/crud/views/list/Table.svelte +176 -176
  20. package/dist/components/crud/views/list/Toolbar.svelte +341 -341
  21. package/dist/components/form/Button.svelte +27 -27
  22. package/dist/components/form/Label.svelte +37 -37
  23. package/dist/components/form/MultiSelect.svelte +248 -248
  24. package/dist/components/form/PasswordInput.svelte +68 -68
  25. package/dist/components/form/Required.svelte +1 -1
  26. package/dist/components/form/Select.svelte +209 -209
  27. package/dist/components/form/Tree.svelte +62 -62
  28. package/dist/components/form/TreeNode.svelte +66 -66
  29. package/dist/components/navigation/Breadcrumbs.svelte +111 -111
  30. package/dist/components/table/ColumnFilter.svelte +168 -168
  31. package/dist/components/table/PaginatedTable.svelte +536 -536
  32. package/dist/components/table/Paginator.svelte +113 -113
  33. package/dist/components/table/SortHeader.svelte +43 -43
  34. package/dist/components/table/TableBody.svelte +150 -150
  35. package/dist/components/table/TableHeader.svelte +88 -88
  36. package/dist/i18n/en.js +1 -0
  37. package/dist/i18n/es.js +1 -0
  38. package/dist/i18n/types.d.ts +1 -0
  39. package/dist/icons/context.d.ts +5 -0
  40. package/dist/icons/context.js +10 -0
  41. package/dist/icons/defaults/Clear.svelte +6 -6
  42. package/dist/icons/defaults/Create.svelte +6 -6
  43. package/dist/icons/defaults/Delete.svelte +6 -6
  44. package/dist/icons/defaults/Download.svelte +7 -7
  45. package/dist/icons/defaults/Edit.svelte +7 -7
  46. package/dist/icons/defaults/Filter.svelte +6 -6
  47. package/dist/icons/defaults/FilterActive.svelte +6 -6
  48. package/dist/icons/defaults/Folder.svelte +6 -6
  49. package/dist/icons/defaults/Grip.svelte +7 -7
  50. package/dist/icons/defaults/Home.svelte +6 -6
  51. package/dist/icons/defaults/PasswordHide.svelte +9 -9
  52. package/dist/icons/defaults/PasswordShow.svelte +7 -7
  53. package/dist/icons/defaults/SortAsc.svelte +6 -6
  54. package/dist/icons/defaults/SortDesc.svelte +6 -6
  55. package/dist/icons/defaults/SortNone.svelte +6 -6
  56. package/dist/icons/defaults/View.svelte +7 -7
  57. package/dist/icons/sets/bootstrap.js +1 -4
  58. package/dist/icons/types.d.ts +0 -1
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.js +1 -1
  61. package/package.json +1 -1
@@ -1,31 +1,31 @@
1
- <script lang="ts">
2
- let {
3
- src = null,
4
- text = '',
5
- alt = '',
6
- class: className = 'w-10 rounded-full',
7
- textClass = '',
8
- }: {
9
- src?: string | null;
10
- text?: string;
11
- alt?: string;
12
- class?: string;
13
- textClass?: string;
14
- } = $props();
15
-
16
- const placeholder = $derived(text.trim().slice(0, 2).toUpperCase() || '?');
17
- </script>
18
-
19
- {#if src}
20
- <div class="avatar">
21
- <div class={className}>
22
- <img {src} {alt} />
23
- </div>
24
- </div>
25
- {:else}
26
- <div class="avatar avatar-placeholder">
27
- <div class="bg-neutral text-neutral-content {className}">
28
- <span class={textClass}>{placeholder}</span>
29
- </div>
30
- </div>
31
- {/if}
1
+ <script lang="ts">
2
+ let {
3
+ src = null,
4
+ text = '',
5
+ alt = '',
6
+ class: className = 'w-10 rounded-full',
7
+ textClass = '',
8
+ }: {
9
+ src?: string | null;
10
+ text?: string;
11
+ alt?: string;
12
+ class?: string;
13
+ textClass?: string;
14
+ } = $props();
15
+
16
+ const placeholder = $derived(text.trim().slice(0, 2).toUpperCase() || '?');
17
+ </script>
18
+
19
+ {#if src}
20
+ <div class="avatar">
21
+ <div class={className}>
22
+ <img {src} {alt} />
23
+ </div>
24
+ </div>
25
+ {:else}
26
+ <div class="avatar avatar-placeholder">
27
+ <div class="bg-neutral text-neutral-content {className}">
28
+ <span class={textClass}>{placeholder}</span>
29
+ </div>
30
+ </div>
31
+ {/if}
@@ -1,22 +1,73 @@
1
- <script lang="ts">
2
- import { getIconSet } from '../icons/context.js';
3
-
4
- let {
5
- name,
6
- size = '1em',
7
- class: className = '',
8
- }: {
9
- name: string;
10
- size?: string | number;
11
- class?: string;
12
- } = $props();
13
-
14
- const icons = $derived(getIconSet());
15
- const IconComponent = $derived(icons?.getByName?.(name) ?? null);
16
- </script>
17
-
18
- {#if IconComponent}
19
- <IconComponent {size} class={className} />
20
- {:else}
21
- <span class={className} title={name}></span>
22
- {/if}
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
+
31
+ <script lang="ts">
32
+ import { getIconAssetsPath } from '../icons/context.js';
33
+
34
+ let {
35
+ name,
36
+ basePath,
37
+ size = '1em',
38
+ class: className = '',
39
+ }: {
40
+ name: string;
41
+ /** Overrides the `setIconAssetsPath` context value for this instance. */
42
+ basePath?: string;
43
+ size?: string | number;
44
+ class?: string;
45
+ } = $props();
46
+
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
+ });
66
+ </script>
67
+
68
+ {#if markup}
69
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
70
+ {@html styleSvg(markup, className, dimension)}
71
+ {:else}
72
+ <span class={className} title={name}></span>
73
+ {/if}
@@ -1,5 +1,7 @@
1
1
  type $$ComponentProps = {
2
2
  name: string;
3
+ /** Overrides the `setIconAssetsPath` context value for this instance. */
4
+ basePath?: string;
3
5
  size?: string | number;
4
6
  class?: string;
5
7
  };
@@ -1,75 +1,75 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import Button from './form/Button.svelte';
4
-
5
- let {
6
- title = '',
7
- onClose,
8
- closeOnClickOutside = true,
9
- onClickOutside,
10
- children,
11
- class: additionalClass,
12
- width,
13
- maxWidth,
14
- height,
15
- maxHeight,
16
- }: {
17
- title?: string;
18
- onClose?: () => void;
19
- /** Whether clicking the backdrop (outside the modal box) closes the modal. */
20
- closeOnClickOutside?: boolean;
21
- /** Callback invoked when clicking outside the modal. Defaults to `onClose`. */
22
- onClickOutside?: () => void;
23
- children: Snippet;
24
- /** Extra classes merged onto the modal box, e.g. Tailwind size utilities
25
- * like `max-w-4xl` or `w-11/12`. */
26
- class?: string;
27
- /** Explicit size overrides (any valid CSS length, e.g. '600px', '90vw').
28
- * Applied as inline styles, so they take priority over `class` utilities. */
29
- width?: string;
30
- maxWidth?: string;
31
- height?: string;
32
- maxHeight?: string;
33
- } = $props();
34
-
35
- const handleClickOutside = $derived(onClickOutside ?? onClose);
36
-
37
- const boxStyle = $derived(
38
- [
39
- width ? `width:${width}` : '',
40
- maxWidth ? `max-width:${maxWidth}` : '',
41
- height ? `height:${height}` : '',
42
- maxHeight ? `max-height:${maxHeight}` : '',
43
- ]
44
- .filter(Boolean)
45
- .join(';')
46
- );
47
- </script>
48
-
49
- <dialog class="modal" open>
50
- <div class={['modal-box', additionalClass]} style={boxStyle}>
51
- <div class="flex items-center justify-between gap-4">
52
- <h3 class="text-lg font-bold">{title}</h3>
53
- {#if onClose}
54
- <Button
55
- variant="ghost"
56
- class="btn-circle"
57
- aria-label="Cerrar"
58
- onclick={onClose}
59
- >
60
-
61
- </Button>
62
- {/if}
63
- </div>
64
-
65
- <div class="divider my-2"></div>
66
-
67
- {@render children()}
68
- </div>
69
-
70
- {#if closeOnClickOutside && handleClickOutside}
71
- <div class="modal-backdrop">
72
- <button onclick={handleClickOutside} aria-label="Cerrar"></button>
73
- </div>
74
- {/if}
75
- </dialog>
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Button from './form/Button.svelte';
4
+
5
+ let {
6
+ title = '',
7
+ onClose,
8
+ closeOnClickOutside = true,
9
+ onClickOutside,
10
+ children,
11
+ class: additionalClass,
12
+ width,
13
+ maxWidth,
14
+ height,
15
+ maxHeight,
16
+ }: {
17
+ title?: string;
18
+ onClose?: () => void;
19
+ /** Whether clicking the backdrop (outside the modal box) closes the modal. */
20
+ closeOnClickOutside?: boolean;
21
+ /** Callback invoked when clicking outside the modal. Defaults to `onClose`. */
22
+ onClickOutside?: () => void;
23
+ children: Snippet;
24
+ /** Extra classes merged onto the modal box, e.g. Tailwind size utilities
25
+ * like `max-w-4xl` or `w-11/12`. */
26
+ class?: string;
27
+ /** Explicit size overrides (any valid CSS length, e.g. '600px', '90vw').
28
+ * Applied as inline styles, so they take priority over `class` utilities. */
29
+ width?: string;
30
+ maxWidth?: string;
31
+ height?: string;
32
+ maxHeight?: string;
33
+ } = $props();
34
+
35
+ const handleClickOutside = $derived(onClickOutside ?? onClose);
36
+
37
+ const boxStyle = $derived(
38
+ [
39
+ width ? `width:${width}` : '',
40
+ maxWidth ? `max-width:${maxWidth}` : '',
41
+ height ? `height:${height}` : '',
42
+ maxHeight ? `max-height:${maxHeight}` : '',
43
+ ]
44
+ .filter(Boolean)
45
+ .join(';')
46
+ );
47
+ </script>
48
+
49
+ <dialog class="modal" open>
50
+ <div class={['modal-box', additionalClass]} style={boxStyle}>
51
+ <div class="flex items-center justify-between gap-4">
52
+ <h3 class="text-lg font-bold">{title}</h3>
53
+ {#if onClose}
54
+ <Button
55
+ variant="ghost"
56
+ class="btn-circle"
57
+ aria-label="Cerrar"
58
+ onclick={onClose}
59
+ >
60
+
61
+ </Button>
62
+ {/if}
63
+ </div>
64
+
65
+ <div class="divider my-2"></div>
66
+
67
+ {@render children()}
68
+ </div>
69
+
70
+ {#if closeOnClickOutside && handleClickOutside}
71
+ <div class="modal-backdrop">
72
+ <button onclick={handleClickOutside} aria-label="Cerrar"></button>
73
+ </div>
74
+ {/if}
75
+ </dialog>
@@ -1,40 +1,40 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import Breadcrumbs from '../navigation/Breadcrumbs.svelte';
4
- import type { BreadcrumbItem } from '../../types/breadcrumb.js';
5
-
6
- let {
7
- title,
8
- breadcrumbs = [],
9
- buttons,
10
- }: {
11
- title: string;
12
- breadcrumbs?: BreadcrumbItem[];
13
- buttons?: Snippet;
14
- } = $props();
15
- </script>
16
-
17
- <div class="header-root flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
18
- <div class="flex flex-col gap-1 flex-1">
19
- <h1>{title}</h1>
20
- <Breadcrumbs items={breadcrumbs} />
21
- </div>
22
-
23
- {#if buttons}
24
- <div class="flex min-w-0 items-center gap-2 sm:pt-1">
25
- {@render buttons()}
26
- </div>
27
- {/if}
28
- </div>
29
-
30
- <style>
31
- h1 {
32
- font-size: var(--runeforge-crud-title-size, 1.875rem);
33
- }
34
-
35
- .header-root {
36
- width: 100%;
37
- max-width: var(--runeforge-crud-max-width);
38
- margin-inline: auto;
39
- }
40
- </style>
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Breadcrumbs from '../navigation/Breadcrumbs.svelte';
4
+ import type { BreadcrumbItem } from '../../types/breadcrumb.js';
5
+
6
+ let {
7
+ title,
8
+ breadcrumbs = [],
9
+ buttons,
10
+ }: {
11
+ title: string;
12
+ breadcrumbs?: BreadcrumbItem[];
13
+ buttons?: Snippet;
14
+ } = $props();
15
+ </script>
16
+
17
+ <div class="header-root flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
18
+ <div class="flex flex-col gap-1 flex-1">
19
+ <h1>{title}</h1>
20
+ <Breadcrumbs items={breadcrumbs} />
21
+ </div>
22
+
23
+ {#if buttons}
24
+ <div class="flex min-w-0 items-center gap-2 sm:pt-1">
25
+ {@render buttons()}
26
+ </div>
27
+ {/if}
28
+ </div>
29
+
30
+ <style>
31
+ h1 {
32
+ font-size: var(--runeforge-crud-title-size, 1.875rem);
33
+ }
34
+
35
+ .header-root {
36
+ width: 100%;
37
+ max-width: var(--runeforge-crud-max-width);
38
+ margin-inline: auto;
39
+ }
40
+ </style>