rune-lab 0.0.6 โ†’ 0.0.8

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 (35) hide show
  1. package/README.md +38 -27
  2. package/dist/components/api/RLApiInterface.svelte +52 -0
  3. package/dist/components/api/RLApiInterface.svelte.d.ts +14 -0
  4. package/dist/components/api/RLApiOperationModal.svelte +190 -0
  5. package/dist/components/api/RLApiOperationModal.svelte.d.ts +18 -0
  6. package/dist/components/dataview/RLMetadataTable.svelte +137 -0
  7. package/dist/components/dataview/RLMetadataTable.svelte.d.ts +15 -0
  8. package/dist/components/explorer/RLSchemaExplorer.svelte +506 -0
  9. package/dist/components/explorer/RLSchemaExplorer.svelte.d.ts +3 -0
  10. package/dist/components/form/RLFilterForm.svelte +119 -0
  11. package/dist/components/form/RLFilterForm.svelte.d.ts +15 -0
  12. package/dist/components/form/RLFunctionForm.svelte +84 -0
  13. package/dist/components/form/RLFunctionForm.svelte.d.ts +9 -0
  14. package/dist/components/form/RLResourceForm.svelte +164 -0
  15. package/dist/components/form/RLResourceForm.svelte.d.ts +11 -0
  16. package/dist/components/layout/URLDisplay.svelte +7 -6
  17. package/dist/components/stores/api.svelte.d.ts +34 -17
  18. package/dist/components/stores/api.svelte.js +97 -45
  19. package/dist/components/stores/app.svelte.d.ts +15 -0
  20. package/dist/components/stores/app.svelte.js +24 -0
  21. package/dist/components/stores/explorer.svelte.d.ts +82 -0
  22. package/dist/components/stores/explorer.svelte.js +113 -0
  23. package/dist/components/ui/showcase/BasicUI.svelte +1 -1
  24. package/dist/components/ui/showcase/main.svelte +1 -1
  25. package/dist/mod.d.ts +4 -0
  26. package/dist/mod.js +5 -2
  27. package/dist/tools/form-helpers.d.ts +23 -0
  28. package/dist/tools/form-helpers.js +51 -0
  29. package/dist/tools/format.d.ts +20 -0
  30. package/dist/tools/format.js +25 -0
  31. package/dist/tools/pdf.d.ts +1 -0
  32. package/dist/tools/pdf.js +320 -0
  33. package/dist/tools/schema-transformer.d.ts +5 -0
  34. package/dist/tools/schema-transformer.js +130 -0
  35. package/package.json +14 -12
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
- # <div align="center">
2
-
3
1
  <h1 align="center">
4
- <img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/static/rune.png" alt="Rune Lab Icon" width="128" height="128" description="Some rune that represents the Svelte rune system">
2
+ <img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/static/rune.png" alt="Rune Lab Icon" width="128" height="128" description="Icon representing the Svelte Runes system">
5
3
  <div align="center">Rune Lab</div>
6
4
  </h1>
7
5
 
@@ -16,48 +14,61 @@
16
14
 
17
15
  ## Overview
18
16
 
19
- Rune Lab is a modern component library built with Svelte 5, focused on providing powerful,
20
- reactive UI components using Svelte's runes system. It offers a comprehensive set of components,
21
- theming capabilities, and utilities for building modern web applications.
17
+ **Rune Lab** is your modern toolkit for crafting stunning, reactive web applications with
18
+ **Svelte 5**. Harnessing the power of Svelte's new **Runes** system, Rune Lab offers a suite of
19
+ elegant UI components designed for seamless data handling and beautiful theming.
20
+
21
+ It's built to integrate effortlessly with your data sources, especially shining when connected
22
+ to the [prism-py](https://github.com/Yrrrrrf/prism-py) and
23
+ [prism-ts](https://github.com/Yrrrrrf/prism-ts) ecosystem for end-to-end type-safe API
24
+ interactions.
25
+
26
+ ## Key Features
27
+
28
+ - **โœจ Svelte 5 Runes Core:** Experience fine-grained reactivity and cleaner component logic.
29
+ - **๐ŸŽจ Dynamic Theming:** Powered by DaisyUI & Tailwind CSS for extensive customization and
30
+ out-of-the-box themes.
31
+ - **๐Ÿ”’ TypeScript First:** Robust type-safety for a confident and productive development
32
+ workflow.
33
+ - **๐Ÿ“Š Data-Aware Components:** Tools and components built to handle and visualize complex data.
34
+ - **๐Ÿ›ฐ๏ธ Interactive Schema Explorer:** A standout feature! Visually explore and interact with
35
+ database schemas exposed by `prism-py` APIs directly within your Svelte application. Test CRUD
36
+ operations, execute functions, and understand your data structure like never before.
37
+ - **๐ŸŒ Smart API Integration:** Includes `apiStore` (using `prism-ts`) for easy and type-safe
38
+ connection to backend APIs.
39
+ - **๐Ÿ“ฆ Lightweight Core:** Designed to be lean, with optional integrations.
40
+ - **๐Ÿฆ• Universal Access:** Available on JSR (for Deno) and NPM (for Node.js/Bun/Yarn).
41
+
42
+ ## The Prism Ecosystem Advantage
43
+
44
+ Rune Lab is designed to be a perfect companion to the Prism ecosystem:
22
45
 
23
- ## Features
46
+ - **[prism-py](https://github.com/Yrrrrrf/prism-py):** Automatically generates REST APIs from
47
+ your database schema.
48
+ - **[prism-ts](https://github.com/Yrrrrrf/prism-ts):** A TypeScript client that consumes these
49
+ APIs with full type-safety.
24
50
 
25
- - **๐Ÿงช Svelte 5 Runes**: Built with Svelte 5's powerful reactivity primitives
26
- - **๐ŸŽจ Theme System**: Extensive theming with DaisyUI integration
27
- - **๐Ÿ”„ Type Safety**: Full TypeScript support with strong typing
28
- - **๐Ÿ“Š Data Visualization**: Components for visualizing complex data
29
- - **๐ŸŒ API Integration**: Tools for type-safe API interactions
30
- - **๐Ÿ“ฆ Zero Dependencies**: Lightweight core with optional integrations
31
- - **๐Ÿฆ• Deno & NPM**: Available on both JSR and NPM
51
+ When used together, Rune Lab's API integration tools (like the `apiStore` and
52
+ `RLSchemaExplorer`) provide a remarkably streamlined and type-safe path from your backend data
53
+ to your frontend UI.
32
54
 
33
55
  ## Installation
34
56
 
35
- ### Using Deno / JSR
57
+ ### Using Deno / [JSR](https://jsr.io/@yrrrrrf/rune-lab)
36
58
 
37
59
  ```bash
38
60
  # Add to your Deno project
39
61
  deno add @yrrrrrf/rune-lab
40
62
  ```
41
63
 
42
- ### Using NPM / Bun / Yarn
64
+ ### Using [NPM](https://www.npmjs.com/package/rune-lab) / Bun / Yarn
43
65
 
44
66
  ```bash
45
- # NPM
46
67
  npm install rune-lab
47
-
48
- # Bun
49
68
  bun add rune-lab
50
-
51
- # Yarn
52
69
  yarn add rune-lab
53
70
  ```
54
71
 
55
72
  ## License
56
73
 
57
74
  MIT License - See [LICENSE](LICENSE) for details.
58
-
59
- ---
60
-
61
- <div align="center">
62
- Built with โค๏ธ using Svelte 5 and Deno
63
- </div>
@@ -0,0 +1,52 @@
1
+ <!-- src/lib/components/api/RLApiInterface.svelte -->
2
+ <script lang="ts">
3
+ import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
4
+ type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
5
+
6
+ let {
7
+ schemaName,
8
+ resourceName,
9
+ resourceType,
10
+ columns,
11
+ onOpenModal // <-- ADDED THIS PROP
12
+ } = $props<{
13
+ schemaName: string;
14
+ resourceName: string;
15
+ resourceType: 'table' | 'view' | 'function'; // Added 'function' for future
16
+ columns: ColumnMetadata[]; // Still relevant for tables/views context
17
+ onOpenModal: (params: { operation: APIOperation }) => void; // <-- TYPE FOR THE PROP
18
+ }>();
19
+
20
+ function getAllowedOps(type: 'table' | 'view' | 'function'): APIOperation[] {
21
+ if (type === 'table') return ['GET', 'POST', 'PUT', 'DELETE'];
22
+ if (type === 'view') return ['GET'];
23
+ if (type === 'function') return ['POST']; // Typical for functions
24
+ return [];
25
+ }
26
+
27
+ const operationDetails: Record<APIOperation, { label: string, class: string }> = {
28
+ GET: { label: 'GET', class: 'btn-info' },
29
+ POST: { label: 'POST', class: 'btn-success' },
30
+ PUT: { label: 'PUT', class: 'btn-warning' },
31
+ DELETE: { label: 'DELETE', class: 'btn-error' },
32
+ };
33
+
34
+ function handleOperationClick(operation: APIOperation) {
35
+ onOpenModal({ operation });
36
+ }
37
+
38
+ </script>
39
+
40
+ <div class="flex flex-wrap gap-2 my-2">
41
+ {#each getAllowedOps(resourceType) as operation}
42
+ {@const detail = operationDetails[operation]}
43
+ {#if detail} <!-- Added a check in case an operation is not in details -->
44
+ <button
45
+ class="btn btn-sm {detail.class}"
46
+ onclick={() => handleOperationClick(operation)}
47
+ >
48
+ {detail.label}
49
+ </button>
50
+ {/if}
51
+ {/each}
52
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
2
+ type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
3
+ type $$ComponentProps = {
4
+ schemaName: string;
5
+ resourceName: string;
6
+ resourceType: 'table' | 'view' | 'function';
7
+ columns: ColumnMetadata[];
8
+ onOpenModal: (params: {
9
+ operation: APIOperation;
10
+ }) => void;
11
+ };
12
+ declare const RlApiInterface: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type RlApiInterface = ReturnType<typeof RlApiInterface>;
14
+ export default RlApiInterface;
@@ -0,0 +1,190 @@
1
+ <!-- src/lib/components/api/RLApiOperationModal.svelte -->
2
+ <script lang="ts">
3
+ import type { ColumnMetadata, FunctionMetadata as PrismFunctionMetadata, CrudOperations } from '@yrrrrrf/prism-ts';
4
+ import { apiStore } from '../stores/api.svelte';
5
+ import RLResourceForm from '../form/RLResourceForm.svelte';
6
+ import RLFilterForm from '../form/RLFilterForm.svelte';
7
+ import RLFunctionForm from '../form/RLFunctionForm.svelte';
8
+
9
+ type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
10
+ type ModalResourceType = 'table' | 'view' | 'function'; // Specific for modal context
11
+
12
+ let {
13
+ isOpen,
14
+ onClose,
15
+ schemaName,
16
+ resourceName,
17
+ operation,
18
+ columns = [], // Default to empty array if not a table/view
19
+ functionParams = null, // <-- ACCEPT THIS PROP
20
+ resourceType,
21
+ initialId = null,
22
+ initialDataForPut = {}
23
+ } = $props<{
24
+ isOpen: boolean;
25
+ onClose: () => void;
26
+ schemaName: string;
27
+ resourceName: string;
28
+ operation: APIOperation;
29
+ columns?: ColumnMetadata[]; // Optional as functions won't have it
30
+ functionParams?: PrismFunctionMetadata['parameters'] | null; // <-- TYPE FOR PROP
31
+ resourceType: ModalResourceType;
32
+ initialId?: string | number | null;
33
+ initialDataForPut?: Record<string, any>;
34
+ }>();
35
+
36
+ // ... (rest of your existing script block for RLApiOperationModal) ...
37
+ // No structural changes needed in the rest of the script for this specific error,
38
+ // as the logic for handling resourceType === 'function' and using functionParams
39
+ // was already present. The issue was just the prop declaration.
40
+ let loading = $state(false);
41
+ let error = $state<string | null>(null);
42
+ let apiResponse = $state<any>(null);
43
+ let crudOps = $state<CrudOperations<any> | null>(null);
44
+ let recordIdForAction = $state<string | number | undefined>(initialId ?? undefined);
45
+
46
+ let combinedInitialDataForPut = $derived({
47
+ ...initialDataForPut,
48
+ ...(recordIdForAction && columns && columns.find((c: ColumnMetadata) => c.isPrimaryKey) ? { [columns.find((c: ColumnMetadata) => c.isPrimaryKey)!.name]: recordIdForAction } : {})
49
+ });
50
+
51
+ $effect(() => {
52
+ async function initClient() {
53
+ if (isOpen && apiStore.IS_CONNECTED && apiStore.prism && schemaName && resourceName) {
54
+ if (resourceType === 'table' || resourceType === 'view') {
55
+ crudOps = await apiStore.prism.getTableOperations(schemaName, resourceName);
56
+ } else {
57
+ crudOps = null;
58
+ }
59
+ } else {
60
+ crudOps = null;
61
+ }
62
+ if (isOpen) {
63
+ recordIdForAction = initialId ?? undefined;
64
+ apiResponse = null;
65
+ error = null;
66
+ }
67
+ }
68
+ initClient();
69
+ });
70
+
71
+ async function handleFormSubmit(data: any) {
72
+ if (!apiStore.prism) {
73
+ error = "API client (Prism) not ready.";
74
+ return;
75
+ }
76
+ loading = true; error = null; apiResponse = null;
77
+ try {
78
+ let result;
79
+ if (resourceType === 'table' || resourceType === 'view') {
80
+ if (!crudOps) {
81
+ error = "CRUD operations not initialized.";
82
+ loading = false; return;
83
+ }
84
+ switch (operation) {
85
+ case 'GET': result = await crudOps.findMany(data); break;
86
+ case 'POST': result = await crudOps.create(data); break;
87
+ case 'PUT':
88
+ const pkColumnPut = columns?.find((c: ColumnMetadata) => c.isPrimaryKey); // Added optional chaining
89
+ const idToUpdate = recordIdForAction ?? data[pkColumnPut?.name || 'id'];
90
+ if (!idToUpdate) throw new Error("Primary key value is required for PUT.");
91
+ const payloadPut = { ...data };
92
+ if (pkColumnPut) delete payloadPut[pkColumnPut.name];
93
+ result = await crudOps.update(idToUpdate, payloadPut);
94
+ break;
95
+ case 'DELETE':
96
+ const pkColumnDelete = columns?.find((c: ColumnMetadata) => c.isPrimaryKey); // Added optional chaining
97
+ const idToDelete = recordIdForAction ?? data[pkColumnDelete?.name || 'id'];
98
+ if (!idToDelete) throw new Error("Primary key value is required for DELETE.");
99
+ await crudOps.delete(idToDelete);
100
+ result = { message: `Record ${idToDelete} deleted successfully.` };
101
+ break;
102
+ }
103
+ } else if (resourceType === 'function') {
104
+ if (operation === 'POST') {
105
+ result = await apiStore.prism.executeFunction(schemaName, resourceName, data);
106
+ } else {
107
+ throw new Error(`Operation ${operation} not supported for functions via this modal.`);
108
+ }
109
+ }
110
+ apiResponse = result;
111
+ if (operation !== 'GET' || resourceType === 'function') {
112
+ setTimeout(() => onClose(), 1500);
113
+ }
114
+ } catch (e) {
115
+ console.error("API Operation Error:", e);
116
+ error = e instanceof Error ? e.message : String(e);
117
+ } finally {
118
+ loading = false;
119
+ }
120
+ }
121
+ let primaryKeyName = $derived(columns?.find((c: ColumnMetadata) => c.isPrimaryKey)?.name || 'id'); // Added optional chaining
122
+ </script>
123
+
124
+ {#if isOpen}
125
+ <dialog class="modal modal-open" open>
126
+ <div class="modal-box w-11/12 max-w-2xl">
127
+ <form method="dialog">
128
+ <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" onclick={onClose}>โœ•</button>
129
+ </form>
130
+ <h3 class="font-bold text-lg mb-4">
131
+ {operation}
132
+ <span class="badge badge-neutral badge-sm font-mono align-middle">{resourceType}</span>
133
+ <span class="font-mono badge badge-neutral align-middle">{schemaName}.{resourceName}</span>
134
+ </h3>
135
+
136
+ {#if error} <div role="alert" class="alert alert-error mb-4"> <!-- error display -->
137
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
138
+ <span>Error: {error}</span>
139
+ </div> {/if}
140
+
141
+ {#if resourceType === 'function'}
142
+ {#if operation === 'POST' && functionParams}
143
+ <RLFunctionForm params={functionParams} onSubmit={handleFormSubmit} {loading} />
144
+ {:else}
145
+ <p class="text-error">Configuration error: Operation {operation} not set up for functions or parameters missing.</p>
146
+ {/if}
147
+ {:else if operation === 'GET'}
148
+ <RLFilterForm columns={columns || []} onSubmit={handleFormSubmit} {loading} />
149
+ {:else if operation === 'POST'}
150
+ <RLResourceForm columns={columns || []} {operation} onSubmit={handleFormSubmit} {loading} />
151
+ {:else if operation === 'PUT'}
152
+ {#if !initialId && resourceType !== 'function'}
153
+ <div class="form-control mb-4">
154
+ <label class="label" for="put-record-id"><span class="label-text">Record {primaryKeyName} to Update <span class="text-error">*</span></span></label>
155
+ <input type="text" id="put-record-id" class="input input-bordered" bind:value={recordIdForAction} placeholder="Enter {primaryKeyName}" />
156
+ </div>
157
+ {/if}
158
+ <RLResourceForm columns={columns || []} {operation} onSubmit={handleFormSubmit} {loading} initialData={combinedInitialDataForPut} />
159
+ {:else if operation === 'DELETE'}
160
+ {#if !initialId && resourceType !== 'function'}
161
+ <div class="form-control mb-4">
162
+ <label class="label" for="delete-record-id"><span class="label-text">Record {primaryKeyName} to Delete <span class="text-error">*</span></span></label>
163
+ <input type="text" id="delete-record-id" class="input input-bordered" bind:value={recordIdForAction} placeholder="Enter {primaryKeyName}" />
164
+ </div>
165
+ {/if}
166
+ <p class="my-4">Are you sure you want to delete record with {primaryKeyName}: <strong class="font-mono">{recordIdForAction || ' (Enter ID above)'}</strong>?</p>
167
+ <div class="modal-action">
168
+ <button class="btn" onclick={onClose}>Cancel</button>
169
+ <button class="btn btn-error" onclick={() => handleFormSubmit({ [primaryKeyName]: recordIdForAction })} disabled={loading || !recordIdForAction}>
170
+ {#if loading} <span class="loading loading-spinner loading-xs"></span> {/if} Confirm Delete
171
+ </button>
172
+ </div>
173
+ {/if}
174
+
175
+ {#if apiResponse}
176
+ <div class="mt-6">
177
+ <h4 class="font-semibold">API Response:</h4>
178
+ <div class="mockup-code text-xs max-h-96 overflow-auto">
179
+ <pre data-prefix=">"><code>{JSON.stringify(apiResponse, null, 2)}</code></pre>
180
+ </div>
181
+ </div>
182
+ {/if}
183
+
184
+ {#if (operation !== 'DELETE' || resourceType === 'function' && operation === 'POST')} <!-- Simplified close button logic -->
185
+ <div class="modal-action mt-4"> <button class="btn" onclick={onClose}>Close</button> </div>
186
+ {/if}
187
+ </div>
188
+ <form method="dialog" class="modal-backdrop"> <button onclick={onClose}>close</button> </form>
189
+ </dialog>
190
+ {/if}
@@ -0,0 +1,18 @@
1
+ import type { ColumnMetadata, FunctionMetadata as PrismFunctionMetadata } from '@yrrrrrf/prism-ts';
2
+ type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
3
+ type ModalResourceType = 'table' | 'view' | 'function';
4
+ type $$ComponentProps = {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ schemaName: string;
8
+ resourceName: string;
9
+ operation: APIOperation;
10
+ columns?: ColumnMetadata[];
11
+ functionParams?: PrismFunctionMetadata['parameters'] | null;
12
+ resourceType: ModalResourceType;
13
+ initialId?: string | number | null;
14
+ initialDataForPut?: Record<string, any>;
15
+ };
16
+ declare const RlApiOperationModal: import("svelte").Component<$$ComponentProps, {}, "">;
17
+ type RlApiOperationModal = ReturnType<typeof RlApiOperationModal>;
18
+ export default RlApiOperationModal;
@@ -0,0 +1,137 @@
1
+ <!-- src/lib/components/dataview/RLMetadataTable.svelte -->
2
+ <script lang="ts">
3
+ import type { ColumnMetadata, ColumnReference, EnumMetadata } from '@yrrrrrf/prism-ts';
4
+ import { formatReferenceText } from '../../tools/form-helpers.js';
5
+
6
+ let {
7
+ title,
8
+ itemType,
9
+ columns,
10
+ enumsInSchema = {},
11
+ onFkClick, // Callback prop
12
+ onEnumClick, // Callback prop
13
+ } = $props<{
14
+ title: string;
15
+ itemType: 'table' | 'view';
16
+ columns: ColumnMetadata[];
17
+ enumsInSchema?: Record<string, EnumMetadata>;
18
+ onFkClick?: (ref: ColumnReference) => void; // Make optional if not always needed
19
+ onEnumClick?: (enumData: { name: string; values: string[] }) => void; // Make optional
20
+ }>();
21
+
22
+
23
+ function handleFkButtonClick(ref: ColumnReference | undefined) {
24
+ if (ref && onFkClick) {
25
+ onFkClick(ref);
26
+ }
27
+ }
28
+
29
+ function getEnumForColumn(column: ColumnMetadata): EnumMetadata | undefined {
30
+ if (!column.isEnum || !enumsInSchema || Object.keys(enumsInSchema).length === 0) {
31
+ return undefined;
32
+ }
33
+
34
+ // Heuristic:
35
+ // 1. Exact match: column.name === enumMeta.name
36
+ // 2. Suffix match: enumMeta.name === `${column.name}_enum` (e.g., "status" -> "status_enum")
37
+ // 3. Prefix match: enumMeta.name === `${itemType}_${column.name}` (e.g., "enrollment_status" for table "enrollment", col "status")
38
+ // 4. More general: enumMeta.name includes column.name and "_enum" (e.g. "enrollment_status_enum" for "status")
39
+ // The prism-py output for enrollment.status shows "Enum(enrollment_status)".
40
+ // The actual enum name from `dt-schemas.json` (API response) for student schema is `status_enum`.
41
+ // If `column.name` is 'status' and `isEnum` is true, we need to find `status_enum`.
42
+
43
+ const colNameLower = column.name.toLowerCase();
44
+ for (const enumKey in enumsInSchema) {
45
+ const enumMeta = enumsInSchema[enumKey];
46
+ const enumNameLower = enumMeta.name.toLowerCase();
47
+
48
+ if (enumNameLower === colNameLower) return enumMeta; // Exact name match
49
+ if (enumNameLower === `${colNameLower}_enum`) return enumMeta; // status -> status_enum
50
+ if (enumNameLower === `${title.toLowerCase()}_${colNameLower}`) return enumMeta; // enrollment_status for table enrollment, column status
51
+ // Check if the enum name is specifically related to the column, common pattern is `table_column_enum` or `column_enum`
52
+ if (enumNameLower.includes(colNameLower) && enumNameLower.endsWith("_enum")) return enumMeta;
53
+
54
+
55
+ }
56
+ // If after specific checks nothing, try to find any enum that might be related by column name part
57
+ // This is very broad, use with caution or remove if too many false positives.
58
+ // for (const enumKey in enumsInSchema) {
59
+ // if (enumsInSchema[enumKey].name.toLowerCase().includes(colNameLower)) {
60
+ // return enumsInSchema[enumKey];
61
+ // }
62
+ // }
63
+ return undefined;
64
+ }
65
+
66
+ function handleEnumBadgeClick(enumMeta: EnumMetadata | undefined) {
67
+ if (enumMeta && onEnumClick) {
68
+ onEnumClick({ name: enumMeta.name, values: enumMeta.values });
69
+ }
70
+ }
71
+
72
+ </script>
73
+
74
+ <div class="card bg-base-100 shadow-xl overflow-hidden">
75
+ <div class="card-body p-0">
76
+ {#if columns && columns.length > 0}
77
+ <div class="overflow-x-auto">
78
+ <table class="table table-sm w-full">
79
+ <thead>
80
+ <tr>
81
+ <th class="w-2/5 pl-4">Column</th>
82
+ <th class="w-3/5 pr-4">Details</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody>
86
+ {#each columns as column (column.name)}
87
+ {@const enumInfo = getEnumForColumn(column)}
88
+ <tr class="hover">
89
+ <td class="align-top py-2.5 pl-4">
90
+ <div class="flex items-baseline">
91
+ <span class="font-medium">{column.name}</span>
92
+ {#if !column.nullable}
93
+ <span class="text-error ml-1 select-none" title="Required field">*</span>
94
+ {/if}
95
+ </div>
96
+ <div class="font-mono text-xs text-base-content/60 italic mt-0.5">{column.type}</div>
97
+ </td>
98
+ <td class="align-top py-2.5 pr-4 space-x-1.5">
99
+ {#if column.isPrimaryKey}
100
+ <span class="badge badge-accent badge-sm font-semibold">PK</span>
101
+ {/if}
102
+ {#if column.references}
103
+ <span class="badge badge-secondary badge-sm">FK</span>
104
+ <button
105
+ class="link link-hover text-xs font-mono !text-info normal-case"
106
+ onclick={() => handleFkButtonClick(column.references)}
107
+ title="Navigate to {formatReferenceText(column.references)}"
108
+ >
109
+ {formatReferenceText(column.references)}
110
+ </button>
111
+ {/if}
112
+ {#if enumInfo}
113
+ <button
114
+ class="badge badge-warning badge-sm hover:shadow-md transition-shadow normal-case"
115
+ onclick={() => handleEnumBadgeClick(enumInfo)}
116
+ title="Enum: {enumInfo.name} (Click to see values)"
117
+ >
118
+ <span class="mr-1 opacity-70">Enum:</span>{enumInfo.name}
119
+ </button>
120
+ {:else if column.isEnum}
121
+ <span class="badge badge-ghost badge-sm normal-case" title="This column uses an enumerated type (details not auto-linked).">
122
+ <span class="opacity-70">Enum</span>
123
+ </span>
124
+ {/if}
125
+ </td>
126
+ </tr>
127
+ {/each}
128
+ </tbody>
129
+ </table>
130
+ </div>
131
+ {:else}
132
+ <div class="p-6 text-center text-neutral-content/70">
133
+ No columns defined for this {itemType}.
134
+ </div>
135
+ {/if}
136
+ </div>
137
+ </div>
@@ -0,0 +1,15 @@
1
+ import type { ColumnMetadata, ColumnReference, EnumMetadata } from '@yrrrrrf/prism-ts';
2
+ type $$ComponentProps = {
3
+ title: string;
4
+ itemType: 'table' | 'view';
5
+ columns: ColumnMetadata[];
6
+ enumsInSchema?: Record<string, EnumMetadata>;
7
+ onFkClick?: (ref: ColumnReference) => void;
8
+ onEnumClick?: (enumData: {
9
+ name: string;
10
+ values: string[];
11
+ }) => void;
12
+ };
13
+ declare const RlMetadataTable: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type RlMetadataTable = ReturnType<typeof RlMetadataTable>;
15
+ export default RlMetadataTable;