@warkypublic/svelix 0.1.39 → 0.1.41

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 (52) hide show
  1. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +41 -24
  2. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte.d.ts +1 -1
  3. package/dist/components/BetterMenu/types.d.ts +2 -1
  4. package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +2 -3
  5. package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +2 -3
  6. package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +2 -3
  7. package/dist/components/Former/FormerButtonArea.svelte +3 -2
  8. package/dist/components/Former/FormerDrawer.svelte +130 -124
  9. package/dist/components/Former/FormerDrawer.svelte.d.ts +1 -1
  10. package/dist/components/FormerControllers/InlineWrapper.svelte +4 -34
  11. package/dist/components/Gridler/adapters/GridlerResolveSpecAdapter.d.ts +1 -0
  12. package/dist/components/Gridler/adapters/GridlerResolveSpecAdapter.js +6 -3
  13. package/dist/components/Gridler/adapters/GridlerRestHeaderSpecAdapter.d.ts +1 -0
  14. package/dist/components/Gridler/adapters/GridlerRestHeaderSpecAdapter.js +6 -3
  15. package/dist/components/Gridler/components/GridlerFull.svelte +2 -0
  16. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +340 -0
  17. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte.d.ts +3 -0
  18. package/dist/components/Gridler/components/GridlerSearch.svelte +2 -4
  19. package/dist/components/Gridler/components/GridlerSearchToggle.svelte +2 -4
  20. package/dist/components/Gridler/types.d.ts +19 -0
  21. package/dist/components/Icons/IconAdd.svelte +8 -0
  22. package/dist/components/Icons/IconAdd.svelte.d.ts +7 -0
  23. package/dist/components/Icons/IconAlertCircle.svelte +9 -0
  24. package/dist/components/Icons/IconAlertCircle.svelte.d.ts +7 -0
  25. package/dist/components/Icons/IconAlertTriangle.svelte +9 -0
  26. package/dist/components/Icons/IconAlertTriangle.svelte.d.ts +7 -0
  27. package/dist/components/Icons/IconCamera.svelte +8 -0
  28. package/dist/components/Icons/IconCamera.svelte.d.ts +7 -0
  29. package/dist/components/Icons/IconClose.svelte +7 -0
  30. package/dist/components/Icons/IconClose.svelte.d.ts +7 -0
  31. package/dist/components/Icons/IconEdit.svelte +8 -0
  32. package/dist/components/Icons/IconEdit.svelte.d.ts +7 -0
  33. package/dist/components/Icons/IconFilter.svelte +7 -0
  34. package/dist/components/Icons/IconFilter.svelte.d.ts +7 -0
  35. package/dist/components/Icons/IconSearch.svelte +8 -0
  36. package/dist/components/Icons/IconSearch.svelte.d.ts +7 -0
  37. package/dist/components/Icons/IconSort.svelte +11 -0
  38. package/dist/components/Icons/IconSort.svelte.d.ts +8 -0
  39. package/dist/components/Icons/IconTrash.svelte +10 -0
  40. package/dist/components/Icons/IconTrash.svelte.d.ts +7 -0
  41. package/dist/components/Icons/index.d.ts +11 -0
  42. package/dist/components/Icons/index.js +11 -0
  43. package/dist/components/Icons/strings.d.ts +11 -0
  44. package/dist/components/Icons/strings.js +15 -0
  45. package/dist/components/Screenshot/Screenshot.svelte +2 -15
  46. package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +4 -13
  47. package/dist/components/Types/generic_grid.d.ts +18 -0
  48. package/dist/components/index.d.ts +1 -0
  49. package/dist/components/index.js +1 -0
  50. package/llm/COMPONENT_GUIDE.md +98 -0
  51. package/llm/README.md +93 -0
  52. package/package.json +46 -47
@@ -0,0 +1,11 @@
1
+ export declare const iconAddSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 8v8M8 12h8\"/></svg>";
2
+ export declare const iconEditSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>";
3
+ export declare const iconTrashSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M3 6h18\"/><path d=\"M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/><path d=\"M10 11v6M14 11v6\"/></svg>";
4
+ export declare const iconSearchSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m21 21-4.35-4.35\"/></svg>";
5
+ export declare const iconCloseSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>";
6
+ export declare const iconAlertCircleSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>";
7
+ export declare const iconAlertTriangleSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"m10.29 3.86-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.71-3.14l-8-14a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>";
8
+ export declare const iconFilterSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M3 5a1 1 0 0 1 1-1h16a1 1 0 0 1 .8 1.6L14 13.5V19a1 1 0 0 1-1.45.9l-3-1.5A1 1 0 0 1 9 17.5v-4L3.2 5.6A1 1 0 0 1 3 5Z\"/></svg>";
9
+ export declare const iconCameraSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z\"/><circle cx=\"12\" cy=\"13\" r=\"3\"/></svg>";
10
+ export declare const iconSortAscSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M7 14l5-5 5 5H7z\"/></svg>";
11
+ export declare const iconSortDescSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M7 10l5 5 5-5H7z\"/></svg>";
@@ -0,0 +1,15 @@
1
+ // SVG HTML strings for use with {@html} or the `icon?: string` prop on menu items.
2
+ // Each string renders at 16×16 px and inherits colour via currentColor.
3
+ const S = `width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0" aria-hidden="true"`;
4
+ const F = `width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="flex-shrink:0" aria-hidden="true"`;
5
+ export const iconAddSvg = `<svg ${S}><circle cx="12" cy="12" r="9"/><path d="M12 8v8M8 12h8"/></svg>`;
6
+ export const iconEditSvg = `<svg ${S}><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`;
7
+ export const iconTrashSvg = `<svg ${S}><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>`;
8
+ export const iconSearchSvg = `<svg ${S}><circle cx="11" cy="11" r="7"/><path d="m21 21-4.35-4.35"/></svg>`;
9
+ export const iconCloseSvg = `<svg ${S}><path d="M18 6 6 18M6 6l12 12"/></svg>`;
10
+ export const iconAlertCircleSvg = `<svg ${S}><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`;
11
+ export const iconAlertTriangleSvg = `<svg ${S}><path d="m10.29 3.86-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.71-3.14l-8-14a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`;
12
+ export const iconFilterSvg = `<svg ${F}><path d="M3 5a1 1 0 0 1 1-1h16a1 1 0 0 1 .8 1.6L14 13.5V19a1 1 0 0 1-1.45.9l-3-1.5A1 1 0 0 1 9 17.5v-4L3.2 5.6A1 1 0 0 1 3 5Z"/></svg>`;
13
+ export const iconCameraSvg = `<svg ${S}><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>`;
14
+ export const iconSortAscSvg = `<svg ${F}><path d="M7 14l5-5 5 5H7z"/></svg>`;
15
+ export const iconSortDescSvg = `<svg ${F}><path d="M7 10l5 5 5-5H7z"/></svg>`;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { CaptureScreen } from "./Screenshot.util";
3
+ import IconCamera from '../Icons/IconCamera.svelte';
3
4
 
4
5
  let screenshot = $state("");
5
6
 
@@ -17,21 +18,7 @@
17
18
  onclick={capture}
18
19
  class="flex items-center gap-2 rounded bg-blue-500 px-4 py-2 text-white transition-colors hover:bg-blue-600"
19
20
  >
20
- <svg
21
- xmlns="http://www.w3.org/2000/svg"
22
- width="20"
23
- height="20"
24
- viewBox="0 0 24 24"
25
- fill="none"
26
- stroke="currentColor"
27
- stroke-width="2"
28
- stroke-linecap="round"
29
- stroke-linejoin="round"
30
- aria-hidden="true"
31
- >
32
- <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" />
33
- <circle cx="12" cy="13" r="3" />
34
- </svg>
21
+ <IconCamera size={20} />
35
22
  Capture Screenshot
36
23
  </button>
37
24
 
@@ -4,6 +4,8 @@
4
4
  import type { BetterMenuStore } from '../../BetterMenu/store';
5
5
  import type { GridColumnFilters } from '../../Types/generic_grid';
6
6
  import SvarkColumnFilterForm from './SvarkColumnFilterForm.svelte';
7
+ import IconSort from '../../Icons/IconSort.svelte';
8
+ import IconFilter from '../../Icons/IconFilter.svelte';
7
9
 
8
10
  type IApi = any;
9
11
  type IColumn = any;
@@ -96,13 +98,7 @@
96
98
  {#if typeof sortMark.index !== 'undefined'}
97
99
  <span class="svark-sort-index">{sortMark.index + 1}</span>
98
100
  {/if}
99
- <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
100
- {#if sortMark.order === 'asc'}
101
- <path fill="currentColor" d="M7 14l5-5 5 5H7z" />
102
- {:else}
103
- <path fill="currentColor" d="M7 10l5 5 5-5H7z" />
104
- {/if}
105
- </svg>
101
+ <IconSort direction={sortMark.order} size={14} />
106
102
  </div>
107
103
  {/if}
108
104
 
@@ -117,12 +113,7 @@
117
113
  onclick={onIconClick}
118
114
  data-testid={`svarkgrid-filter-${columnId}`}
119
115
  >
120
- <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
121
- <path
122
- fill="currentColor"
123
- d="M3 5a1 1 0 0 1 1-1h16a1 1 0 0 1 .8 1.6L14 13.5V19a1 1 0 0 1-1.45.9l-3-1.5A1 1 0 0 1 9 17.5v-4L3.2 5.6A1 1 0 0 1 3 5Z"
124
- />
125
- </svg>
116
+ <IconFilter size={14} />
126
117
  </button>
127
118
  {/if}
128
119
  </div>
@@ -1,3 +1,4 @@
1
+ import type { Options as ResolveSpecOptions } from '@warkypublic/resolvespec-js';
1
2
  export type GridColumnFormat = "text" | "number" | "currency" | "percentage" | "date" | "datetime" | "markdown" | "image" | "uri" | "bubble" | "drilldown";
2
3
  /**
3
4
  * GridEventType represents the various events that can occur on the grid level, such as clicking, hovering, loading, scrolling, resizing, and changes to sorting, filtering, searching, selection, column arrangement, data, and settings. These events can be used to trigger callbacks for handling user interactions and updates to the grid.
@@ -72,6 +73,23 @@ export interface GridCommonProps<RowDataType = unknown, CellType = unknown> {
72
73
  uniqueID?: string;
73
74
  headers?: Record<string, string>;
74
75
  hotfields?: string[];
76
+ /**
77
+ * Default ResolveSpec options sent with every request. Grid-controlled sort, filters, limit,
78
+ * cursor, and columns always take precedence over values set here.
79
+ *
80
+ * Key fields:
81
+ * - `preload` — eager-load relations. Each entry specifies `relation`, optional `columns`,
82
+ * `filters`, `sort`, `limit`, `recursive`, `sql_joins`, etc.
83
+ * - `omit_columns` — columns to exclude from the response.
84
+ * - `computedColumns` — server-side computed expressions: `{ name, expression }`.
85
+ * - `customOperators` — raw SQL WHERE fragments (AND-combined): `{ name, sql }`.
86
+ * - `parameters` — named query parameters passed to the server: `{ name, value, sequence? }`.
87
+ * - `filters` — default server-side filters applied before any grid filters.
88
+ * - `sort` — default sort order, overridden when the user sorts a column.
89
+ * - `fetch_row_number` — column name whose row number is returned in metadata.
90
+ * - `offset` — static row offset (cursor pagination is used by the adapter; avoid mixing).
91
+ */
92
+ extraOptions?: Partial<ResolveSpecOptions>;
75
93
  };
76
94
  data?: RowDataType[] | (() => Promise<RowDataType[]>);
77
95
  onDataChange?: (data: RowDataType[]) => Promise<void>;
@@ -1,4 +1,5 @@
1
1
  export { default as Button } from "./Button.svelte";
2
+ export * from "./Icons/index";
2
3
  export * from "./ErrorBoundary/index";
3
4
  export * from "./BetterMenu/index";
4
5
  export * from "./FormerControllers/index";
@@ -1,4 +1,5 @@
1
1
  export { default as Button } from "./Button.svelte";
2
+ export * from "./Icons/index";
2
3
  export * from "./ErrorBoundary/index";
3
4
  export * from "./BetterMenu/index";
4
5
  export * from "./FormerControllers/index";
@@ -478,6 +478,104 @@ Story behavior worth preserving:
478
478
  - search highlighting and fixed columns are first-class usage patterns
479
479
  - theme overrides are an expected integration point, not a niche feature
480
480
 
481
+ ### GridlerFull: server-driven Gridler with adapters
482
+
483
+ `GridlerFull` wraps `Gridler` and handles paging, sorting, filtering, and search against a
484
+ ResolveSpec or HeaderSpec API. Configure via `dataSource` + `dataSourceOptions`.
485
+
486
+ ```svelte
487
+ <script lang="ts">
488
+ import { GridlerFull } from '@warkypublic/svelix';
489
+ import type { GridlerColumn } from '@warkypublic/svelix';
490
+
491
+ const columns: GridlerColumn[] = [
492
+ { id: 'id', title: 'ID', width: 60 },
493
+ { id: 'name', title: 'Name', width: 180 },
494
+ { id: 'email', title: 'Email', width: 220 },
495
+ ];
496
+ </script>
497
+
498
+ <GridlerFull
499
+ {columns}
500
+ dataSource="resolvespec"
501
+ dataSourceOptions={{
502
+ url: 'https://api.example.com',
503
+ authToken: 'Bearer …',
504
+ schema: 'public',
505
+ entity: 'users',
506
+ uniqueID: 'id',
507
+ hotfields: ['role'],
508
+ extraOptions: {
509
+ preload: [
510
+ {
511
+ relation: 'department',
512
+ columns: ['id', 'name'],
513
+ },
514
+ ],
515
+ filters: [{ column: 'active', operator: 'eq', value: true }],
516
+ sort: [{ column: 'created_at', direction: 'desc' }],
517
+ computedColumns: [{ name: 'full_name', expression: "first_name || ' ' || last_name" }],
518
+ customOperators: [{ name: 'tenant', sql: "tenant_id = '123'" }],
519
+ parameters: [{ name: 'p_role', value: 'admin' }],
520
+ },
521
+ }}
522
+ height={480}
523
+ pageSize={50}
524
+ />
525
+ ```
526
+
527
+ #### `dataSource` values
528
+
529
+ | Value | Adapter class | Transport |
530
+ |----------------|----------------------------------|--------------------|
531
+ | `resolvespec` | `GridlerResolveSpecAdapter` | JSON request body |
532
+ | `headerspec` | `GridlerRestHeaderSpecAdapter` | HTTP headers |
533
+
534
+ #### `dataSourceOptions.extraOptions` — all fields
535
+
536
+ These are `Partial<Options>` from `@warkypublic/resolvespec-js`. Grid-controlled values
537
+ (sort, filters, limit, cursor, columns) always win over anything set here.
538
+
539
+ | Field | Type | Purpose |
540
+ |-------------------|-----------------------|------------------------------------------------------------------|
541
+ | `preload` | `PreloadOption[]` | Eager-load related entities. See `PreloadOption` fields below. |
542
+ | `omit_columns` | `string[]` | Exclude these columns from every response. |
543
+ | `computedColumns` | `ComputedColumn[]` | Server-side SQL expressions: `{ name, expression }`. |
544
+ | `customOperators` | `CustomOperator[]` | Raw SQL WHERE fragments (AND-combined): `{ name, sql }`. |
545
+ | `parameters` | `Parameter[]` | Named query params: `{ name, value, sequence? }`. |
546
+ | `filters` | `FilterOption[]` | Default filters prepended before any grid column filters. |
547
+ | `sort` | `SortOption[]` | Default sort, overridden when the user clicks a column header. |
548
+ | `fetch_row_number`| `string` | Column whose absolute row number is returned in metadata. |
549
+ | `offset` | `number` | Static row offset — avoid mixing with cursor pagination. |
550
+
551
+ ##### `PreloadOption` key fields
552
+
553
+ | Field | Purpose |
554
+ |----------------------|------------------------------------------------------|
555
+ | `relation` | Relation name (required). |
556
+ | `table_name` | Override the table resolved from the relation. |
557
+ | `columns` | Columns to select on the related table. |
558
+ | `omit_columns` | Columns to exclude on the related table. |
559
+ | `filters` | Filters applied to the related table. |
560
+ | `sort` | Sort order for the related rows. |
561
+ | `limit` | Max related rows per parent row. |
562
+ | `recursive` | Self-referential recursive load. |
563
+ | `primary_key` | Override primary key used for the join. |
564
+ | `related_key` | Foreign key on the related table. |
565
+ | `foreign_key` | Foreign key on the parent table. |
566
+ | `recursive_child_key`| Key used for the recursive child join. |
567
+ | `sql_joins` | Raw SQL JOIN clauses. |
568
+ | `join_aliases` | Aliases for the SQL joins. |
569
+ | `computed_ql` | Map of computed column name → SQL expression. |
570
+ | `where` | Raw SQL WHERE fragment for the relation. |
571
+ | `updatable` | Marks the preloaded relation as updatable. |
572
+
573
+ Key behaviors:
574
+ - `extraOptions` is stored on the adapter at construction time; changing `dataSourceOptions` recreates the adapter and triggers a full refetch.
575
+ - Call-time `extraOptions` passed directly to `readPage` override the config-level defaults; specific params (sort, filters, limit, cursor, columns) override both.
576
+ - `hotfields` are fetched alongside column fields but not rendered — use them for fields needed by filters or computed logic.
577
+ - Use `headerspec` when the API endpoint only accepts query options via HTTP headers instead of a JSON body.
578
+
481
579
  ### SvarkGrid: adapter-backed tabular data
482
580
 
483
581
  Based on:
package/llm/README.md CHANGED
@@ -18,6 +18,99 @@ This folder contains AI-readable documentation that ships with the package.
18
18
  - [plans/canvasgrid.md](./plans/canvasgrid.md)
19
19
  - [gridler-events.md](./gridler-events.md)
20
20
 
21
+ ## Implementation Patterns
22
+
23
+ ### Standard Entity Page
24
+
25
+ GridlerFull and FormerDrawer are siblings. GridlerFull handles display/filtering; its `menuItems` and row events (`onRowDblClick`, `onRowContextMenu`) open a `FormerDrawer`. The drawer receives `request` (`insert` | `update` | `delete`), `bind:values`, and an `onAPICall` handler. Field controls go inside a `{#snippet children(state)}` block.
26
+
27
+ ```svelte
28
+ <script lang="ts">
29
+ import GridlerFull from '@warkypublic/svelix/GridlerFull';
30
+ import FormerDrawer from '@warkypublic/svelix/FormerDrawer';
31
+ import TextInputCtrl from '@warkypublic/svelix/TextInputCtrl';
32
+ import NativeSelectCtrl from '@warkypublic/svelix/NativeSelectCtrl';
33
+ import type { FormRequestType } from '@warkypublic/svelix/Former';
34
+ import { iconAddSvg, iconEditSvg, iconTrashSvg } from '@warkypublic/svelix/Icons';
35
+
36
+ let drawerOpen = $state(false);
37
+ let formMode = $state<FormRequestType>('update');
38
+ let formValues = $state<MyRow | undefined>(undefined);
39
+ let selectedItems = $state<Record<string, unknown>[]>([]);
40
+
41
+ function openForm(mode: FormRequestType, row?: MyRow) {
42
+ formMode = mode;
43
+ formValues = row ? { ...row } : { id: 0, name: '' };
44
+ drawerOpen = true;
45
+ }
46
+
47
+ const menuItems = [
48
+ { id: 'add', label: 'Add', icon: iconAddSvg, kind: 'item', onselect: () => openForm('insert') },
49
+ { id: 'sep', label: '', kind: 'separator' },
50
+ { id: 'edit', label: 'Edit', icon: iconEditSvg, kind: 'item', onselect: () => openForm('update', selectedItems[0] as MyRow) },
51
+ { id: 'delete', label: 'Delete', icon: iconTrashSvg, kind: 'item', onselect: () => openForm('delete', selectedItems[0] as MyRow) },
52
+ ];
53
+ </script>
54
+
55
+ <GridlerFull
56
+ {columns}
57
+ {data}
58
+ uniqueID="id"
59
+ {menuItems}
60
+ onRowDblClick={(_row, rowData) => openForm('update', rowData as MyRow)}
61
+ onSelectedItemsChange={(items) => (selectedItems = items)}
62
+ />
63
+
64
+ <FormerDrawer
65
+ bind:opened={drawerOpen}
66
+ bind:values={formValues}
67
+ request={formMode}
68
+ uniqueKeyField="id"
69
+ layout={{ title: 'Employee', buttonArea: 'bottom' }}
70
+ onAPICall={async (_mode, _req, data) => { /* call backend */ return data; }}
71
+ afterSave={(data) => { /* update local rows state */ }}
72
+ onClose={() => { drawerOpen = false; }}
73
+ >
74
+ {#snippet children(state)}
75
+ {@const isReadonly = state.request === 'delete'}
76
+ <TextInputCtrl
77
+ label="Name" name="name"
78
+ value={state.values?.name ?? ''}
79
+ disabled={isReadonly}
80
+ onchange={(v) => state.setState('values', { ...state.values, name: v })}
81
+ />
82
+ <NativeSelectCtrl
83
+ label="Status" name="status"
84
+ value={state.values?.status ?? ''}
85
+ {options}
86
+ disabled={isReadonly}
87
+ onchange={(v) => state.setState('values', { ...state.values, status: v })}
88
+ />
89
+ {/snippet}
90
+ </FormerDrawer>
91
+ ```
92
+
93
+ ### Component roles
94
+
95
+ - **GridlerFull** — data grid with burger menu (`menuItems`), row events (`onRowDblClick`, `onRowContextMenu`, `onSelectedItemsChange`), filtering and pagination
96
+ - **FormerDrawer** — slide-out drawer form for Add/Edit/Delete; `request` controls mode; fields go in `{#snippet children(state)}`; `state.setState('values', ...)` updates field values; `onAPICall` is the save handler; `afterSave` updates local state
97
+ - **FormerControllers** — `TextInputCtrl`, `NumberInputCtrl`, `NativeSelectCtrl`, etc.; use `disabled` for delete readonly mode
98
+ - **ErrorBoundary** — wraps async components to catch render and fetch errors
99
+ - **ContentEditor** — edit JSON, Markdown, code, or documents as a FormerDrawer field
100
+ - **VTree** — hierarchical/tree data display
101
+ - **BetterMenu** — context menus or nav menus
102
+ - **Boxer** — single or multi-value lookup control backed by a data source; used as a FormerDrawer field
103
+ - **GlobalStateStore** — central store for all app state and API data
104
+ - **resolvespec-js** — connects components to the backend via specs passed to GridlerFull, FormerDrawer, Boxer, etc.
105
+
106
+ ### Data flow
107
+
108
+ ```text
109
+ GlobalStateStore ↔ resolvespec-js ↔ Backend API
110
+
111
+ GridlerFull / FormerDrawer / Boxer
112
+ ```
113
+
21
114
  ## Installed package paths
22
115
 
23
116
  When `@warkypublic/svelix` is installed, this documentation is available at:
package/package.json CHANGED
@@ -1,26 +1,8 @@
1
1
  {
2
2
  "name": "@warkypublic/svelix",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Svelte 5 component library with Skeleton UI and Tailwind CSS",
5
5
  "license": "Apache-2.0",
6
- "scripts": {
7
- "dev": "vite dev",
8
- "build": "vite build && pnpm run package",
9
- "preview": "vite preview",
10
- "package": "svelte-kit sync && svelte-package && mkdir -p dist/css && cp src/lib/css/tailwind-source.css dist/css/tailwind-source.css && publint",
11
- "prepublishOnly": "npm run package",
12
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
13
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
14
- "storybook": "storybook dev -p 6006",
15
- "build-storybook": "storybook build",
16
- "lint": "eslint .",
17
- "lint:fix": "eslint . --fix",
18
- "test": "vitest run --project unit",
19
- "test:unit": "vitest run --project unit",
20
- "test:watch": "vitest --project unit",
21
- "test:e2e": "playwright test",
22
- "test:e2e:ui": "playwright test --ui"
23
- },
24
6
  "exports": {
25
7
  ".": {
26
8
  "types": "./dist/index.d.ts",
@@ -44,43 +26,43 @@
44
26
  },
45
27
  "devDependencies": {
46
28
  "@changesets/cli": "^2.31.0",
47
- "@chromatic-com/storybook": "^5.1.2",
48
- "@eslint/compat": "^2.0.5",
29
+ "@chromatic-com/storybook": "^5.2.1",
30
+ "@eslint/compat": "^2.1.0",
49
31
  "@eslint/js": "^10.0.1",
50
- "@playwright/test": "^1.59.1",
51
- "@sentry/svelte": "^10.49.0",
32
+ "@playwright/test": "^1.60.0",
33
+ "@sentry/svelte": "^10.53.1",
52
34
  "@skeletonlabs/skeleton": "^4.15.2",
53
35
  "@skeletonlabs/skeleton-svelte": "^4.15.2",
54
- "@storybook/addon-a11y": "^10.3.5",
55
- "@storybook/addon-docs": "^10.3.5",
36
+ "@storybook/addon-a11y": "^10.4.0",
37
+ "@storybook/addon-docs": "^10.4.0",
56
38
  "@storybook/addon-svelte-csf": "^5.1.2",
57
- "@storybook/addon-vitest": "^10.3.5",
58
- "@storybook/sveltekit": "^10.3.5",
39
+ "@storybook/addon-vitest": "^10.4.0",
40
+ "@storybook/sveltekit": "^10.4.0",
59
41
  "@storybook/test": "^8.6.15",
60
42
  "@sveltejs/adapter-auto": "^7.0.1",
61
- "@sveltejs/kit": "^2.57.1",
43
+ "@sveltejs/kit": "^2.60.1",
62
44
  "@sveltejs/package": "^2.5.7",
63
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
64
- "@tailwindcss/vite": "^4.2.4",
45
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
46
+ "@tailwindcss/vite": "^4.3.0",
65
47
  "@tanstack/svelte-virtual": "^3.13.24",
66
- "@types/node": "^25.6.0",
67
- "@vitest/browser-playwright": "^4.1.5",
68
- "@vitest/coverage-v8": "^4.1.5",
69
- "eslint": "^10.2.1",
48
+ "@types/node": "^25.8.0",
49
+ "@vitest/browser-playwright": "^4.1.6",
50
+ "@vitest/coverage-v8": "^4.1.6",
51
+ "eslint": "^10.4.0",
70
52
  "eslint-plugin-svelte": "^3.17.1",
71
- "globals": "^17.5.0",
72
- "playwright": "^1.59.1",
73
- "publint": "^0.3.18",
74
- "storybook": "^10.3.5",
75
- "svelte": "^5.55.4",
76
- "svelte-check": "^4.4.6",
77
- "tailwindcss": "^4.2.4",
53
+ "globals": "^17.6.0",
54
+ "playwright": "^1.60.0",
55
+ "publint": "^0.3.21",
56
+ "storybook": "^10.4.0",
57
+ "svelte": "^5.55.7",
58
+ "svelte-check": "^4.4.8",
59
+ "tailwindcss": "^4.3.0",
78
60
  "tslib": "^2.8.1",
79
61
  "typescript": "^6.0.3",
80
- "typescript-eslint": "^8.59.0",
81
- "vite": "^8.0.9",
62
+ "typescript-eslint": "^8.59.3",
63
+ "vite": "^8.0.13",
82
64
  "vite-plugin-monaco-editor": "^1.1.0",
83
- "vitest": "^4.1.5"
65
+ "vitest": "^4.1.6"
84
66
  },
85
67
  "svelte": "./dist/index.js",
86
68
  "types": "./dist/index.d.ts",
@@ -99,8 +81,25 @@
99
81
  "@warkypublic/artemis-kit": "^1.0.10",
100
82
  "carta-md": "^4.11.2",
101
83
  "github-markdown-css": "^5.9.0",
102
- "isomorphic-dompurify": "^3.9.0",
103
- "katex": "^0.16.45",
84
+ "isomorphic-dompurify": "^3.13.0",
85
+ "katex": "^0.16.47",
104
86
  "monaco-editor": "^0.55.1"
87
+ },
88
+ "scripts": {
89
+ "dev": "vite dev",
90
+ "build": "vite build && pnpm run package",
91
+ "preview": "vite preview",
92
+ "package": "svelte-kit sync && svelte-package && mkdir -p dist/css && cp src/lib/css/tailwind-source.css dist/css/tailwind-source.css && publint",
93
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
94
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
95
+ "storybook": "storybook dev -p 6006",
96
+ "build-storybook": "storybook build",
97
+ "lint": "eslint .",
98
+ "lint:fix": "eslint . --fix",
99
+ "test": "vitest run --project unit",
100
+ "test:unit": "vitest run --project unit",
101
+ "test:watch": "vitest --project unit",
102
+ "test:e2e": "playwright test",
103
+ "test:e2e:ui": "playwright test --ui"
105
104
  }
106
- }
105
+ }