@open-grid/svelte-ui 0.1.0 → 0.2.0

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 CHANGED
@@ -8,6 +8,11 @@ Use this package for the reference Svelte grid UI, including sorting, filtering,
8
8
 
9
9
  Use Svelte's `class` prop, `getRowClassName(row)`, `getHeaderClassName(context)`, and `getCellClassName(context)` to attach product-owned root, row, header, and cell classes for status, risk, or workflow styling without replacing the renderer. Header contexts include the grid, column, and header; cell contexts also include the row and current value.
10
10
 
11
+ Use `createSvelteDataGridRenderer(Component)` with `renderHeader`, `renderCell`,
12
+ `renderToolbar`, or the state render callbacks for component-based content. See the
13
+ [custom rendering](https://goatshave.github.io/open-grid/custom-rendering) and
14
+ [composition](https://goatshave.github.io/open-grid/composition) guides.
15
+
11
16
  ## Install
12
17
 
13
18
  ```bash
@@ -21,10 +21,9 @@
21
21
  addPointerMoveUpListeners,
22
22
  applyResizeObserverMeasuredSizes,
23
23
  createClickSuppressionController,
24
+ createGridLocalization,
24
25
  createResizeObserver,
25
26
  disconnectResizeObserver,
26
- GROUPING_PANEL_EMPTY_MESSAGE,
27
- HEADER_ACTION_MENU_TRIGGER_TEXT,
28
27
  getCellEditorProps,
29
28
  getCellEditorKeyboardAction,
30
29
  getCellEditorOptionProps,
@@ -214,6 +213,8 @@
214
213
  GRID_DENSITIES,
215
214
  type ColumnVirtualizationPrimitiveOptions,
216
215
  type GridDensity,
216
+ type GridLocalization,
217
+ type GridLocalizationOverrides,
217
218
  type ResolvedColumnVirtualizationOptions,
218
219
  type ResolvedRowVirtualizationOptions,
219
220
  type RowVirtualizationPrimitiveOptions,
@@ -222,11 +223,36 @@
222
223
  import { createGridStore } from "@open-grid/svelte";
223
224
  import { createMeasuredSizeCache, createMeasuredSizeResolver, getColumnCellRenderItems, getColumnLayoutMeasurementSignature, getColumnRenderItems, getHeaderRenderItemKey, getHeaderRenderItems, getInitialScrollFrame, getMeasuredColumnLayoutFromCache, getScrollFrame, getSizedColumnLayout, getVirtualRowRange, getVirtualRowItems, isHeaderRenderSpacerItem, syncMeasuredColumnLayoutCache, type ColumnCellRenderItem, type ColumnRenderItem, type VirtualItem, type VirtualRange } from "@open-grid/virtual";
224
225
  import { onDestroy, onMount, tick } from "svelte";
226
+ import RenderValue from "./RenderValue.svelte";
225
227
 
226
228
  export type RowVirtualizationOptions = RowVirtualizationPrimitiveOptions;
227
229
 
228
230
  export type ColumnVirtualizationOptions = ColumnVirtualizationPrimitiveOptions;
229
231
 
232
+ export interface DataGridRenderContext<TData = unknown> {
233
+ grid: Grid<TData>;
234
+ rows: readonly Row<TData>[];
235
+ visibleColumns: readonly Column<TData, unknown>[];
236
+ }
237
+
238
+ export interface DataGridErrorRenderContext<TData = unknown> extends DataGridRenderContext<TData> {
239
+ retry: (() => void) | undefined;
240
+ }
241
+
242
+ export interface SvelteDataGridRenderer<TContext = unknown> {
243
+ type: "open-grid:svelte-renderer";
244
+ component: unknown;
245
+ context: TContext;
246
+ props?: Record<string, unknown>;
247
+ }
248
+
249
+ export type SvelteDataGridRenderValue<TContext = unknown> = string | number | boolean | null | undefined | SvelteDataGridRenderer<TContext>;
250
+
251
+ const isSvelteDataGridRenderer = (value: unknown): value is SvelteDataGridRenderer<unknown> =>
252
+ typeof value === "object"
253
+ && value !== null
254
+ && (value as { type?: unknown }).type === "open-grid:svelte-renderer";
255
+
230
256
  export interface HeaderActionMenuActionItem<TData = unknown> {
231
257
  type?: "action";
232
258
  id: string;
@@ -276,14 +302,21 @@
276
302
 
277
303
  export type HeaderActionMenuItems<TData = unknown> = (context: HeaderActionMenuContext<TData>) => Array<HeaderActionMenuItem<TData> | null | false | undefined>;
278
304
 
279
- export let ariaLabel = "Data grid";
305
+ export let ariaLabel: string | undefined = undefined;
306
+ export let localization: GridLocalizationOverrides | undefined = undefined;
280
307
  export let options: GridOptions<unknown>;
281
- export let emptyState: unknown = "No rows";
308
+ export let emptyState: unknown = undefined;
282
309
  export let error = false;
283
- export let errorState: unknown = getGridErrorText();
310
+ export let errorState: unknown = undefined;
284
311
  export let onRetry: (() => void) | undefined = undefined;
285
312
  export let loading = false;
286
- export let loadingState: unknown = getGridLoadingText();
313
+ export let loadingState: unknown = undefined;
314
+ export let renderToolbar: ((context: DataGridRenderContext<unknown>) => SvelteDataGridRenderValue<DataGridRenderContext<unknown>>) | undefined = undefined;
315
+ export let renderEmptyState: ((context: DataGridRenderContext<unknown>) => SvelteDataGridRenderValue<DataGridRenderContext<unknown>>) | undefined = undefined;
316
+ export let renderLoadingState: ((context: DataGridRenderContext<unknown>) => SvelteDataGridRenderValue<DataGridRenderContext<unknown>>) | undefined = undefined;
317
+ export let renderErrorState: ((context: DataGridErrorRenderContext<unknown>) => SvelteDataGridRenderValue<DataGridErrorRenderContext<unknown>>) | undefined = undefined;
318
+ export let renderHeader: ((context: HeaderContext<unknown, unknown>) => SvelteDataGridRenderValue<HeaderContext<unknown, unknown>>) | undefined = undefined;
319
+ export let renderCell: ((context: CellContext<unknown, unknown>) => SvelteDataGridRenderValue<CellContext<unknown, unknown>>) | undefined = undefined;
287
320
  export let onGridReady: ((grid: Grid<unknown>) => void | (() => void)) | undefined = undefined;
288
321
  export let getRowClassName: ((row: Row<unknown>) => string | undefined) | undefined = undefined;
289
322
  export let getHeaderClassName: ((context: HeaderContext<unknown, unknown>) => string | undefined) | undefined = undefined;
@@ -381,6 +414,12 @@
381
414
  let gridProps = getGridProps(grid, { ariaLabel, error, loading });
382
415
  let resolvedPageSizeOptions = getPaginationPageSizeOptions(pageSizeOptions, grid.getState().pagination.pageSize);
383
416
  let paginationPageText = getPaginationPageText(grid);
417
+ let resolvedLocalization: GridLocalization = createGridLocalization(localization);
418
+ let resolvedAriaLabel = resolvedLocalization.dataGridLabel;
419
+ let resolvedEmptyState: unknown = resolvedLocalization.noRows;
420
+ let resolvedErrorState: unknown = resolvedLocalization.gridError;
421
+ let resolvedLoadingState: unknown = resolvedLocalization.loadingRows;
422
+ let renderContext: DataGridRenderContext<unknown> = { grid, rows: [], visibleColumns: [] };
384
423
  let canPreviousPage = grid.getCanPreviousPage();
385
424
  let canNextPage = grid.getCanNextPage();
386
425
  let allPageRowsSelected = rowSelectionControls ? grid.getIsAllPageRowsSelected() : false;
@@ -389,6 +428,11 @@
389
428
  let selectionCheckboxElement: HTMLInputElement | null = null;
390
429
 
391
430
  $: gridStore.setOptions(options, { notify: false });
431
+ $: resolvedLocalization = createGridLocalization(localization);
432
+ $: resolvedAriaLabel = ariaLabel ?? resolvedLocalization.dataGridLabel;
433
+ $: resolvedEmptyState = emptyState ?? resolvedLocalization.noRows;
434
+ $: resolvedErrorState = errorState ?? getGridErrorText(resolvedLocalization);
435
+ $: resolvedLoadingState = loadingState ?? getGridLoadingText(resolvedLocalization);
392
436
  $: allRows = ($state, grid.getRowModel().rows);
393
437
  $: additionalHeaderRowCount = columnFilterControls ? 1 : 0;
394
438
  $: resolvedDensity = density ?? uncontrolledDensity;
@@ -402,12 +446,12 @@
402
446
  }
403
447
  }
404
448
  }
405
- $: gridProps = ($state, additionalHeaderRowCount, ariaLabel, error, loading, resolvedDensity, {
406
- ...getGridProps(grid, { additionalHeaderRowCount, ariaLabel, error, loading }),
449
+ $: gridProps = ($state, additionalHeaderRowCount, resolvedAriaLabel, error, loading, resolvedDensity, {
450
+ ...getGridProps(grid, { additionalHeaderRowCount, ariaLabel: resolvedAriaLabel, error, loading }),
407
451
  ...(densitySizingEnabled ? getGridDensityProps(resolvedDensity) : {}),
408
452
  });
409
453
  $: resolvedPageSizeOptions = ($state.pagination.pageSize, pageSizeOptions, getPaginationPageSizeOptions(pageSizeOptions, $state.pagination.pageSize));
410
- $: paginationPageText = ($state.pagination, getPaginationPageText(grid));
454
+ $: paginationPageText = ($state.pagination, resolvedLocalization, getPaginationPageText(grid, resolvedLocalization));
411
455
  $: canPreviousPage = ($state.pagination.pageIndex, grid.getCanPreviousPage());
412
456
  $: canNextPage = ($state.pagination, grid.getCanNextPage());
413
457
  $: allPageRowsSelected = ($state.rowSelection, allRows, rowSelectionControls ? grid.getIsAllPageRowsSelected() : false);
@@ -417,6 +461,7 @@
417
461
  $: bodyRowIndexOffset = ($state, additionalHeaderRowCount, getGridBodyRowIndexOffset(grid, { additionalHeaderRowCount }));
418
462
  $: allColumns = ($state, grid.getAllLeafColumns());
419
463
  $: visibleColumns = ($state, grid.getVisibleLeafColumns());
464
+ $: renderContext = { grid, rows: allRows, visibleColumns };
420
465
  $: filteredVisibilityColumns = getFilteredColumnVisibilityColumns(allColumns, columnVisibilityQuery);
421
466
  $: groupingColumns = $state.grouping.flatMap((columnId) => {
422
467
  const column = getColumnById(columnId, grid.getAllLeafColumns());
@@ -486,6 +531,8 @@
486
531
  });
487
532
  $: simpleCellRendering = !$state.editingCell
488
533
  && (!$state.cellSelectionRange || deferDetailedCellRendering)
534
+ && !renderCell
535
+ && !visibleColumns.some((column) => typeof column.columnDef.cell === "function")
489
536
  && !visibleRowItems.some(({ row }) =>
490
537
  row.getIsGrouped() || row.getIsGroupFooter() || row.depth > 0 || row.getCanExpand(),
491
538
  );
@@ -616,10 +663,14 @@
616
663
  ) =>
617
664
  getColumnResizeHandleProps(column, {
618
665
  valueNow: layout?.size ?? grid.getColumnSize(column.id) ?? column.getSize(),
619
- });
620
-
621
- const getHeaderLabel = (header: Header<unknown>) => {
622
- return getColumnHeaderText(header.column);
666
+ }, resolvedLocalization);
667
+
668
+ const getHeaderContent = (header: Header<unknown>): unknown => {
669
+ const context = { grid, column: header.column, header } as HeaderContext<unknown, unknown>;
670
+ const value = renderHeader?.(context) ?? (typeof header.column.columnDef.header === "function"
671
+ ? header.column.columnDef.header(context)
672
+ : header.column.columnDef.header);
673
+ return value ?? getColumnHeaderText(header.column);
623
674
  };
624
675
  const getCanUseCompactHeaderRendering = (
625
676
  headerGroup: HeaderGroup<unknown>,
@@ -677,8 +728,12 @@
677
728
  const value = row.getValue(column.id);
678
729
  return getCellDisplayText(value);
679
730
  };
731
+ const getCellContent = (row: Row<unknown>, column: Column<unknown, unknown>): unknown => {
732
+ const context = { grid, row, column, value: row.getValue(column.id) } as CellContext<unknown, unknown>;
733
+ return renderCell?.(context) ?? column.columnDef.cell?.(context) ?? getCellValue(row, column);
734
+ };
680
735
  const getGroupLabel = (row: Row<unknown>, column: Column<unknown, unknown>) =>
681
- getGroupRowLabel(row, column, { fallback: getCellValue(row, column) });
736
+ getGroupRowLabel(row, column, { fallback: getCellValue(row, column) }, resolvedLocalization);
682
737
  const getColumnStyle = (
683
738
  size: number,
684
739
  layout: ColumnLayout | undefined,
@@ -1352,7 +1407,7 @@
1352
1407
  canGroup: column.getCanGroup(),
1353
1408
  canMoveLeft,
1354
1409
  canMoveRight,
1355
- }).map((descriptor): HeaderActionMenuActionItem<unknown> => {
1410
+ }, resolvedLocalization).map((descriptor): HeaderActionMenuActionItem<unknown> => {
1356
1411
  if (descriptor.id === "sort-asc") {
1357
1412
  return { ...descriptor, onSelect: () => grid.toggleColumnSorting(column.id, false) };
1358
1413
  }
@@ -1440,37 +1495,47 @@
1440
1495
 
1441
1496
  </script>
1442
1497
 
1498
+ {#if renderToolbar}
1499
+ {@const toolbarContent = renderToolbar(renderContext)}
1500
+ <div class="og-grid__toolbar-slot">
1501
+ {#if isSvelteDataGridRenderer(toolbarContent)}
1502
+ <RenderValue value={toolbarContent} />
1503
+ {:else}
1504
+ {String(toolbarContent ?? "")}
1505
+ {/if}
1506
+ </div>
1507
+ {/if}
1443
1508
  {#if quickFilterControl || rowSelectionControls || columnVisibilityControls || densityControl}
1444
1509
  <div class="og-grid__controls">
1445
1510
  {#if rowSelectionControls}
1446
- <div {...getRowSelectionControlsProps()} class="og-grid__selection-controls">
1511
+ <div {...getRowSelectionControlsProps(resolvedLocalization)} class="og-grid__selection-controls">
1447
1512
  <label class="og-grid__selection-toggle">
1448
1513
  <input
1449
- {...getRowSelectionCheckboxProps({ allSelected: allPageRowsSelected, someSelected: somePageRowsSelected, disabled: allRows.length === 0 })}
1514
+ {...getRowSelectionCheckboxProps({ allSelected: allPageRowsSelected, someSelected: somePageRowsSelected, disabled: allRows.length === 0 }, resolvedLocalization)}
1450
1515
  bind:this={selectionCheckboxElement}
1451
1516
  on:change={() => grid.toggleAllPageRowsSelected(!allPageRowsSelected)}
1452
1517
  />
1453
- <span>{getRowSelectionCheckboxText()}</span>
1518
+ <span>{getRowSelectionCheckboxText(resolvedLocalization)}</span>
1454
1519
  </label>
1455
- <span {...getRowSelectionStatusProps()} class="og-grid__selection-status">{getRowSelectionStatusText(selectedRowCount)}</span>
1456
- <button {...getRowSelectionClearButtonProps(selectedRowCount === 0)} class="og-grid__selection-clear" on:click={() => grid.resetRowSelection()}>{getRowSelectionClearButtonText()}</button>
1520
+ <span {...getRowSelectionStatusProps()} class="og-grid__selection-status">{getRowSelectionStatusText(selectedRowCount, resolvedLocalization)}</span>
1521
+ <button {...getRowSelectionClearButtonProps(selectedRowCount === 0, resolvedLocalization)} class="og-grid__selection-clear" on:click={() => grid.resetRowSelection()}>{getRowSelectionClearButtonText(resolvedLocalization)}</button>
1457
1522
  </div>
1458
1523
  {/if}
1459
1524
  {#if columnVisibilityControls}
1460
- <details {...getColumnVisibilityControlsProps()} class="og-grid__column-visibility">
1461
- <summary {...getColumnVisibilitySummaryProps(visibleColumns.length, allColumns.length)} class="og-grid__column-visibility-summary">
1462
- {getColumnVisibilitySummaryText(visibleColumns.length, allColumns.length)}
1525
+ <details {...getColumnVisibilityControlsProps(resolvedLocalization)} class="og-grid__column-visibility">
1526
+ <summary {...getColumnVisibilitySummaryProps(visibleColumns.length, allColumns.length, resolvedLocalization)} class="og-grid__column-visibility-summary">
1527
+ {getColumnVisibilitySummaryText(visibleColumns.length, allColumns.length, resolvedLocalization)}
1463
1528
  </summary>
1464
1529
  <div class="og-grid__column-visibility-panel">
1465
1530
  <input
1466
- {...getColumnVisibilitySearchInputProps(columnVisibilityQuery)}
1531
+ {...getColumnVisibilitySearchInputProps(columnVisibilityQuery, resolvedLocalization)}
1467
1532
  class="og-grid__column-visibility-search"
1468
1533
  on:input={(event) => (columnVisibilityQuery = event.currentTarget.value)}
1469
1534
  />
1470
1535
  <span {...getColumnVisibilityStatusProps()} class="og-grid__column-visibility-status">
1471
- {getColumnVisibilityStatusText(visibleColumns.length, allColumns.length)}
1536
+ {getColumnVisibilityStatusText(visibleColumns.length, allColumns.length, resolvedLocalization)}
1472
1537
  </span>
1473
- <div {...getColumnVisibilityListProps()} class="og-grid__column-visibility-list">
1538
+ <div {...getColumnVisibilityListProps(resolvedLocalization)} class="og-grid__column-visibility-list">
1474
1539
  {#if filteredVisibilityColumns.length > 0}
1475
1540
  {#each filteredVisibilityColumns as column (column.id)}
1476
1541
  {@const label = getColumnHeaderText(column)}
@@ -1489,32 +1554,32 @@
1489
1554
  </label>
1490
1555
  {/each}
1491
1556
  {:else}
1492
- <span class="og-grid__column-visibility-empty">{getColumnVisibilityEmptyText()}</span>
1557
+ <span class="og-grid__column-visibility-empty">{getColumnVisibilityEmptyText(resolvedLocalization)}</span>
1493
1558
  {/if}
1494
1559
  </div>
1495
1560
  <button
1496
- {...getColumnVisibilityResetButtonProps(allColumns.length - visibleColumns.length)}
1561
+ {...getColumnVisibilityResetButtonProps(allColumns.length - visibleColumns.length, resolvedLocalization)}
1497
1562
  class="og-grid__column-visibility-reset"
1498
1563
  on:click={() => grid.resetColumnVisibility()}
1499
- >{getColumnVisibilityResetButtonText()}</button>
1564
+ >{getColumnVisibilityResetButtonText(resolvedLocalization)}</button>
1500
1565
  </div>
1501
1566
  </details>
1502
1567
  {/if}
1503
1568
  {#if densityControl}
1504
- <div {...getDensityControlsProps()} class="og-grid__density-controls">
1569
+ <div {...getDensityControlsProps(resolvedLocalization)} class="og-grid__density-controls">
1505
1570
  {#each GRID_DENSITIES as densityOption (densityOption)}
1506
1571
  <button
1507
- {...getDensityButtonProps(densityOption, resolvedDensity)}
1572
+ {...getDensityButtonProps(densityOption, resolvedDensity, resolvedLocalization)}
1508
1573
  class="og-grid__density-button"
1509
1574
  on:click={() => setDensity(densityOption)}
1510
- >{getDensityButtonText(densityOption)}</button>
1575
+ >{getDensityButtonText(densityOption, resolvedLocalization)}</button>
1511
1576
  {/each}
1512
1577
  </div>
1513
1578
  {/if}
1514
1579
  {#if quickFilterControl}
1515
- <div {...getQuickFilterProps()} class="og-grid__quick-filter">
1516
- <input {...getQuickFilterInputProps({ value: $state.globalFilter })} class="og-grid__quick-filter-input" on:input={handleQuickFilterInput} />
1517
- <button {...getQuickFilterClearButtonProps($state.globalFilter)} class="og-grid__quick-filter-clear" on:click={() => setQuickFilter("")}>{getQuickFilterClearButtonText()}</button>
1580
+ <div {...getQuickFilterProps(resolvedLocalization)} class="og-grid__quick-filter">
1581
+ <input {...getQuickFilterInputProps({ value: $state.globalFilter }, resolvedLocalization)} class="og-grid__quick-filter-input" on:input={handleQuickFilterInput} />
1582
+ <button {...getQuickFilterClearButtonProps($state.globalFilter, resolvedLocalization)} class="og-grid__quick-filter-clear" on:click={() => setQuickFilter("")}>{getQuickFilterClearButtonText(resolvedLocalization)}</button>
1518
1583
  </div>
1519
1584
  {/if}
1520
1585
  </div>
@@ -1527,31 +1592,31 @@
1527
1592
  {#if groupingPanel}
1528
1593
  <div
1529
1594
  bind:this={groupingPanelElement}
1530
- {...getGroupingPanelProps({ empty: groupingColumns.length === 0 })}
1595
+ {...getGroupingPanelProps({ empty: groupingColumns.length === 0 }, resolvedLocalization)}
1531
1596
  class="og-grid__grouping-panel"
1532
1597
  >
1533
1598
  {#if groupingColumns.length === 0}
1534
- <span {...getGroupingPanelPlaceholderProps()} class="og-grid__grouping-placeholder">{GROUPING_PANEL_EMPTY_MESSAGE}</span>
1599
+ <span {...getGroupingPanelPlaceholderProps()} class="og-grid__grouping-placeholder">{resolvedLocalization.groupingPanelEmpty}</span>
1535
1600
  {:else}
1536
1601
  {#each groupingColumns as column, index (column.id)}
1537
1602
  <span {...getGroupingPanelChipProps(column)} class="og-grid__grouping-chip">
1538
1603
  <span class="og-grid__grouping-chip-label">{getColumnLabel(column)}</span>
1539
1604
  <button
1540
- {...getGroupingPanelMoveButtonProps(column, { direction: "left", disabled: index === 0 })}
1605
+ {...getGroupingPanelMoveButtonProps(column, { direction: "left", disabled: index === 0 }, resolvedLocalization)}
1541
1606
  class="og-grid__grouping-move"
1542
1607
  on:click={() => moveGroupedColumn(grid, groupingColumns, column.id, "left")}
1543
1608
  >
1544
1609
  {"<"}
1545
1610
  </button>
1546
1611
  <button
1547
- {...getGroupingPanelMoveButtonProps(column, { direction: "right", disabled: index === groupingColumns.length - 1 })}
1612
+ {...getGroupingPanelMoveButtonProps(column, { direction: "right", disabled: index === groupingColumns.length - 1 }, resolvedLocalization)}
1548
1613
  class="og-grid__grouping-move"
1549
1614
  on:click={() => moveGroupedColumn(grid, groupingColumns, column.id, "right")}
1550
1615
  >
1551
1616
  {">"}
1552
1617
  </button>
1553
1618
  <button
1554
- {...getGroupingPanelRemoveButtonProps(column)}
1619
+ {...getGroupingPanelRemoveButtonProps(column, resolvedLocalization)}
1555
1620
  class="og-grid__grouping-remove"
1556
1621
  on:click={() => grid.toggleColumnGrouping(column.id, false)}
1557
1622
  >
@@ -1562,7 +1627,7 @@
1562
1627
  {/if}
1563
1628
  </div>
1564
1629
  {/if}
1565
- <div {...gridProps} class="og-grid__scroller" role="grid" bind:this={scrollerElement} on:keydown={handleGridKeyDown} on:scroll={syncFrame}>
1630
+ <div {...gridProps} class="og-grid__scroller" role="grid" tabindex="0" bind:this={scrollerElement} on:keydown={handleGridKeyDown} on:scroll={syncFrame}>
1566
1631
  <div class="og-grid__canvas" style={getInlineSizeStyleText(totalMeasuredWidth)}>
1567
1632
  <div {...getGridHeaderProps()} class="og-grid__header" bind:this={headerElement}>
1568
1633
  {#each grid.getHeaderGroups() as headerGroup, headerRowIndex (headerGroup.id)}
@@ -1582,6 +1647,7 @@
1582
1647
  {@const layout = layoutById.get(firstLeafId)}
1583
1648
  {@const canSort = header.column.getCanSort()}
1584
1649
  {@const sortDirection = grid.getColumnSortDirection(header.column.id)}
1650
+ {@const headerContent = getHeaderContent(header)}
1585
1651
  <div
1586
1652
  {...getHeaderCellProps(grid, header.column, Math.max(0, columnIndex), { pinned: layout?.pinned ?? false })}
1587
1653
  {...getHeaderCellLayoutProps({
@@ -1602,9 +1668,11 @@
1602
1668
  class="og-grid__header-button"
1603
1669
  on:click={(event) => handleHeaderClick(header.column, canSort, event)}
1604
1670
  on:keydown={(event) => handleHeaderButtonKeyDown(header.column, event)}
1605
- ><span class="og-grid__header-label">{getHeaderLabel(header)}</span><span {...getHeaderSortIndicatorProps()} class="og-grid__sort-indicator">{getHeaderSortIndicatorText(sortDirection, { visible: true })}</span></button><span
1671
+ ><span class="og-grid__header-label">{#if isSvelteDataGridRenderer(headerContent)}<RenderValue value={headerContent} />{:else}{String(headerContent ?? "")}{/if}</span><span {...getHeaderSortIndicatorProps()} class="og-grid__sort-indicator">{getHeaderSortIndicatorText(sortDirection, { visible: true })}</span></button><!-- svelte-ignore a11y-no-noninteractive-tabindex a11y-no-noninteractive-element-interactions --><span
1606
1672
  {...getResizeHandleProps(header.column, layout, $state.columnSizing)}
1607
1673
  class="og-grid__resize-handle"
1674
+ role="separator"
1675
+ tabindex="0"
1608
1676
  on:keydown={(event) => handleResizeKeyDown(header.column, Math.max(0, columnIndex), event)}
1609
1677
  on:pointerdown={(event) => handleResizePointerDown(header.column, Math.max(0, columnIndex), event)}
1610
1678
  ></span></div>
@@ -1629,6 +1697,7 @@
1629
1697
  {@const menuId = getHeaderMenuId(header.column.id)}
1630
1698
  {@const menuTriggerId = getHeaderActionMenuTriggerId(header.column.id)}
1631
1699
  {@const headerMenu = getHeaderActionMenu(header.column, sortDirection, pinningPosition, isGrouped, canMoveLeft, canMoveRight)}
1700
+ {@const headerContent = getHeaderContent(header)}
1632
1701
  <div
1633
1702
  {...(header.isPlaceholder
1634
1703
  ? getHeaderPlaceholderCellProps(header.column)
@@ -1662,15 +1731,21 @@
1662
1731
  on:click={(event) => handleHeaderClick(header.column, canSort, event)}
1663
1732
  on:keydown={(event) => handleHeaderButtonKeyDown(header.column, event)}
1664
1733
  >
1665
- <span class="og-grid__header-label">{getHeaderLabel(header)}</span>
1734
+ <span class="og-grid__header-label">
1735
+ {#if isSvelteDataGridRenderer(headerContent)}
1736
+ <RenderValue value={headerContent} />
1737
+ {:else}
1738
+ {String(headerContent ?? "")}
1739
+ {/if}
1740
+ </span>
1666
1741
  <span {...getHeaderSortIndicatorProps()} class="og-grid__sort-indicator">
1667
1742
  {getHeaderSortIndicatorText(sortDirection, { visible: canInteract })}
1668
1743
  </span>
1669
1744
  </button>
1670
1745
  {#if canInteract && columnPinningControls}
1671
- <span {...getColumnPinningControlsProps(header.column)} class="og-grid__pinning-controls">
1746
+ <span {...getColumnPinningControlsProps(header.column, resolvedLocalization)} class="og-grid__pinning-controls">
1672
1747
  <button
1673
- {...getColumnPinningButtonProps(header.column, { position: "left", active: pinningPosition === "left" })}
1748
+ {...getColumnPinningButtonProps(header.column, { position: "left", active: pinningPosition === "left" }, resolvedLocalization)}
1674
1749
  class="og-grid__pinning-button"
1675
1750
  on:pointerdown|stopPropagation={() => undefined}
1676
1751
  on:click|stopPropagation={() => grid.pinColumn(header.column.id, "left")}
@@ -1678,7 +1753,7 @@
1678
1753
  {getColumnPinningButtonText("left")}
1679
1754
  </button>
1680
1755
  <button
1681
- {...getColumnPinningButtonProps(header.column, { position: false, active: pinningPosition === false })}
1756
+ {...getColumnPinningButtonProps(header.column, { position: false, active: pinningPosition === false }, resolvedLocalization)}
1682
1757
  class="og-grid__pinning-button"
1683
1758
  on:pointerdown|stopPropagation={() => undefined}
1684
1759
  on:click|stopPropagation={() => grid.pinColumn(header.column.id, false)}
@@ -1686,7 +1761,7 @@
1686
1761
  {getColumnPinningButtonText(false)}
1687
1762
  </button>
1688
1763
  <button
1689
- {...getColumnPinningButtonProps(header.column, { position: "right", active: pinningPosition === "right" })}
1764
+ {...getColumnPinningButtonProps(header.column, { position: "right", active: pinningPosition === "right" }, resolvedLocalization)}
1690
1765
  class="og-grid__pinning-button"
1691
1766
  on:pointerdown|stopPropagation={() => undefined}
1692
1767
  on:click|stopPropagation={() => grid.pinColumn(header.column.id, "right")}
@@ -1698,7 +1773,7 @@
1698
1773
  {#if canInteract && headerActionMenu}
1699
1774
  <span class="og-grid__header-menu">
1700
1775
  <button
1701
- {...getHeaderActionMenuTriggerProps(header.column, { expanded: headerMenuColumnId === header.column.id, controls: menuId })}
1776
+ {...getHeaderActionMenuTriggerProps(header.column, { expanded: headerMenuColumnId === header.column.id, controls: menuId }, resolvedLocalization)}
1702
1777
  id={menuTriggerId}
1703
1778
  class="og-grid__header-menu-trigger"
1704
1779
  on:pointerdown|stopPropagation={() => undefined}
@@ -1717,13 +1792,15 @@
1717
1792
  void tick().then(() => focusHeaderActionMenuItemById(document, menuId, focusPosition));
1718
1793
  }}
1719
1794
  >
1720
- {HEADER_ACTION_MENU_TRIGGER_TEXT}
1795
+ {resolvedLocalization.headerActionMenuTrigger}
1721
1796
  </button>
1722
1797
  {#if headerMenuColumnId === header.column.id}
1723
1798
  <div
1724
- {...getHeaderActionMenuProps(header.column)}
1799
+ {...getHeaderActionMenuProps(header.column, resolvedLocalization)}
1725
1800
  class="og-grid__header-menu-popover"
1726
1801
  id={menuId}
1802
+ role="menu"
1803
+ tabindex="-1"
1727
1804
  on:pointerdown|stopPropagation={() => undefined}
1728
1805
  on:keydown={(event) => {
1729
1806
  const menuAction = getHeaderActionMenuKeyboardAction(event);
@@ -1770,9 +1847,12 @@
1770
1847
  </span>
1771
1848
  {/if}
1772
1849
  {#if canInteract}
1850
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex a11y-no-noninteractive-element-interactions -->
1773
1851
  <span
1774
1852
  {...getResizeHandleProps(header.column, layout, $state.columnSizing)}
1775
1853
  class="og-grid__resize-handle"
1854
+ role="separator"
1855
+ tabindex="0"
1776
1856
  on:keydown={(event) => handleResizeKeyDown(header.column, Math.max(0, columnIndex), event)}
1777
1857
  on:pointerdown={(event) => handleResizePointerDown(header.column, Math.max(0, columnIndex), event)}
1778
1858
  ></span>
@@ -1806,7 +1886,7 @@
1806
1886
  {...getColumnFilterInputProps(column, {
1807
1887
  label: getColumnHeaderText(column),
1808
1888
  value: getColumnFilterText($state.columnFilters, column.id),
1809
- })}
1889
+ }, resolvedLocalization)}
1810
1890
  class="og-grid__filter-input"
1811
1891
  on:input={(event) => handleColumnFilterInput(column, event)}
1812
1892
  />
@@ -1817,9 +1897,11 @@
1817
1897
  {/if}
1818
1898
  </div>
1819
1899
 
1900
+ <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
1820
1901
  <div
1821
1902
  {...getGridBodyProps({ virtualized: rowVirtualOptions.enabled, columnOrder: $state.columnOrder })}
1822
1903
  class="og-grid__body"
1904
+ role="rowgroup"
1823
1905
  bind:this={bodyElement}
1824
1906
  style={getVirtualBodyStyleText(virtualRange)}
1825
1907
  on:focusin={handleSimpleBodyFocusIn}
@@ -1830,8 +1912,15 @@
1830
1912
  on:dblclick={handleSimpleBodyDoubleClick}
1831
1913
  >
1832
1914
  {#if allRows.length === 0}
1915
+ {@const emptyContent = renderEmptyState?.(renderContext) ?? resolvedEmptyState}
1833
1916
  <div {...getGridEmptyRowProps({ rowIndexOffset: bodyRowIndexOffset })} class="og-grid__empty">
1834
- <div {...getGridEmptyCellProps({ rowIndexOffset: bodyRowIndexOffset, columnCount: visibleColumns.length })} class="og-grid__empty-cell" style={getInlineSizeStyleText(totalMeasuredWidth)}>{String(emptyState ?? "")}</div>
1917
+ <div {...getGridEmptyCellProps({ rowIndexOffset: bodyRowIndexOffset, columnCount: visibleColumns.length })} class="og-grid__empty-cell" style={getInlineSizeStyleText(totalMeasuredWidth)}>
1918
+ {#if isSvelteDataGridRenderer(emptyContent)}
1919
+ <RenderValue value={emptyContent} />
1920
+ {:else}
1921
+ {String(emptyContent ?? "")}
1922
+ {/if}
1923
+ </div>
1835
1924
  </div>
1836
1925
  {:else if simpleCellRendering}
1837
1926
  {#each visibleRowItems as { row, rowIndex, virtualItem } (row.id)}
@@ -1843,12 +1932,14 @@
1843
1932
  columnVirtualized: columnVirtualOptions.enabled,
1844
1933
  })}
1845
1934
  class={["og-grid__row", getRowClassName?.(row)].filter(Boolean).join(" ")}
1935
+ role="row"
1846
1936
  style={getVirtualRowStyleText(virtualItem)}
1847
1937
  use:measureVirtualRow={row.id}
1848
1938
  >{@html getSimpleCellsHtml(row, rowIndex, columnCellRenderItems, $state.columnSizing, bodyRowIndexOffset, getCellClassName)}</div>
1849
1939
  {/each}
1850
1940
  {:else}
1851
1941
  {#each visibleRowItems as { row, rowIndex, virtualItem } (row.id)}
1942
+ <!-- svelte-ignore a11y-interactive-supports-focus -->
1852
1943
  <div
1853
1944
  {...getRowProps(row, rowIndex, { selected: grid.getIsRowSelected(row.id), rowIndexOffset: bodyRowIndexOffset })}
1854
1945
  {...getRowLayoutProps(row, {
@@ -1857,6 +1948,7 @@
1857
1948
  columnVirtualized: columnVirtualOptions.enabled,
1858
1949
  })}
1859
1950
  class={["og-grid__row", getRowClassName?.(row)].filter(Boolean).join(" ")}
1951
+ role="row"
1860
1952
  style={getVirtualRowStyleText(virtualItem)}
1861
1953
  use:measureVirtualRow={row.id}
1862
1954
  on:click={(event) => handleRowClick(row, rowIndex, event)}
@@ -1870,6 +1962,8 @@
1870
1962
  {...getCellProps(row, column, rowIndex, columnIndex, { focusedCell: $state.focusedCell, editing, rangeSelected, rowIndexOffset: bodyRowIndexOffset })}
1871
1963
  {...getCellLayoutProps({ invalid: Boolean(editing && editValidationMessage), pinnedEdge: layout.pinnedEdge })}
1872
1964
  class={["og-grid__cell", getCellClassName?.({ grid, row, column, value: row.getValue(column.id) })].filter(Boolean).join(" ")}
1965
+ role="gridcell"
1966
+ tabindex={isCellCoordinateEqual($state.focusedCell, { rowId: row.id, columnId: column.id }) ? 0 : -1}
1873
1967
  style={getColumnStyle(layout.size, layout, $state.columnSizing, beforeSpacerSize, afterSpacerSize)}
1874
1968
  on:focus={() => grid.setFocusedCell({ rowId: row.id, columnId: column.id })}
1875
1969
  on:pointerdown={(event) => handleCellPointerDown({ rowId: row.id, columnId: column.id }, event)}
@@ -1888,7 +1982,7 @@
1888
1982
  {#if editing}
1889
1983
  {#if column.columnDef.editOptions}
1890
1984
  <select
1891
- {...getCellEditorProps(column, { invalid: Boolean(editValidationMessage) })}
1985
+ {...getCellEditorProps(column, { invalid: Boolean(editValidationMessage) }, resolvedLocalization)}
1892
1986
  use:focusEditor
1893
1987
  class="og-grid__cell-editor"
1894
1988
  bind:value={editDraft}
@@ -1928,7 +2022,7 @@
1928
2022
  </select>
1929
2023
  {:else}
1930
2024
  <input
1931
- {...getCellEditorProps(column, { invalid: Boolean(editValidationMessage) })}
2025
+ {...getCellEditorProps(column, { invalid: Boolean(editValidationMessage) }, resolvedLocalization)}
1932
2026
  use:focusEditor
1933
2027
  class="og-grid__cell-editor"
1934
2028
  bind:value={editDraft}
@@ -1971,7 +2065,7 @@
1971
2065
  <span class="og-grid__group-cell" style={getGroupCellIndentStyleText(row)}>
1972
2066
  {#if row.getCanExpand()}
1973
2067
  <button
1974
- {...getRowExpansionToggleProps(row, { expanded: grid.getIsRowExpanded(row.id), label: String(getGroupLabel(row, column)) })}
2068
+ {...getRowExpansionToggleProps(row, { expanded: grid.getIsRowExpanded(row.id), label: String(getGroupLabel(row, column)) }, resolvedLocalization)}
1975
2069
  class="og-grid__group-toggle"
1976
2070
  on:pointerdown|stopPropagation
1977
2071
  on:click={(event) => {
@@ -1990,7 +2084,12 @@
1990
2084
  {/if}
1991
2085
  </span>
1992
2086
  {:else}
1993
- {getCellValue(row, column)}
2087
+ {@const cellContent = getCellContent(row, column)}
2088
+ {#if isSvelteDataGridRenderer(cellContent)}
2089
+ <RenderValue value={cellContent} />
2090
+ {:else}
2091
+ {String(cellContent ?? "")}
2092
+ {/if}
1994
2093
  {/if}
1995
2094
  </div>
1996
2095
  {/each}
@@ -2001,31 +2100,45 @@
2001
2100
  </div>
2002
2101
  </div>
2003
2102
  {#if error}
2004
- <div {...getGridErrorOverlayProps()} class="og-grid__status-overlay og-grid__error-overlay">
2005
- <span class="og-grid__status-text">{String(errorState ?? "")}</span>
2103
+ {@const errorContent = renderErrorState?.({ ...renderContext, retry: onRetry }) ?? resolvedErrorState}
2104
+ <div {...getGridErrorOverlayProps(resolvedLocalization)} class="og-grid__status-overlay og-grid__error-overlay">
2105
+ <span class="og-grid__status-text">
2106
+ {#if isSvelteDataGridRenderer(errorContent)}
2107
+ <RenderValue value={errorContent} />
2108
+ {:else}
2109
+ {String(errorContent ?? "")}
2110
+ {/if}
2111
+ </span>
2006
2112
  {#if onRetry}
2007
- <button {...getGridErrorRetryButtonProps()} class="og-grid__retry-button" on:click={onRetry}>{getGridErrorRetryButtonText()}</button>
2113
+ <button {...getGridErrorRetryButtonProps(resolvedLocalization)} class="og-grid__retry-button" on:click={onRetry}>{getGridErrorRetryButtonText(resolvedLocalization)}</button>
2008
2114
  {/if}
2009
2115
  </div>
2010
2116
  {:else if loading}
2011
- <div {...getGridLoadingOverlayProps()} class="og-grid__status-overlay og-grid__loading-overlay">
2117
+ {@const loadingContent = renderLoadingState?.(renderContext) ?? resolvedLoadingState}
2118
+ <div {...getGridLoadingOverlayProps(resolvedLocalization)} class="og-grid__status-overlay og-grid__loading-overlay">
2012
2119
  <span class="og-grid__loading-spinner" aria-hidden="true"></span>
2013
- <span class="og-grid__status-text">{String(loadingState ?? "")}</span>
2120
+ <span class="og-grid__status-text">
2121
+ {#if isSvelteDataGridRenderer(loadingContent)}
2122
+ <RenderValue value={loadingContent} />
2123
+ {:else}
2124
+ {String(loadingContent ?? "")}
2125
+ {/if}
2126
+ </span>
2014
2127
  </div>
2015
2128
  {/if}
2016
2129
  </div>
2017
2130
  {#if paginationControls}
2018
- <nav {...getPaginationProps()} class="og-grid__pagination">
2131
+ <nav {...getPaginationProps(resolvedLocalization)} class="og-grid__pagination">
2019
2132
  <div class="og-grid__pagination-buttons">
2020
- <button {...getPaginationButtonProps({ action: "first", disabled: !canPreviousPage })} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.firstPage())}>{getPaginationButtonText("first")}</button>
2021
- <button {...getPaginationButtonProps({ action: "previous", disabled: !canPreviousPage })} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.previousPage())}>{getPaginationButtonText("previous")}</button>
2133
+ <button {...getPaginationButtonProps({ action: "first", disabled: !canPreviousPage }, resolvedLocalization)} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.firstPage())}>{getPaginationButtonText("first")}</button>
2134
+ <button {...getPaginationButtonProps({ action: "previous", disabled: !canPreviousPage }, resolvedLocalization)} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.previousPage())}>{getPaginationButtonText("previous")}</button>
2022
2135
  <span {...getPaginationStatusProps()} class="og-grid__pagination-status">{paginationPageText}</span>
2023
- <button {...getPaginationButtonProps({ action: "next", disabled: !canNextPage })} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.nextPage())}>{getPaginationButtonText("next")}</button>
2024
- <button {...getPaginationButtonProps({ action: "last", disabled: !canNextPage })} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.lastPage())}>{getPaginationButtonText("last")}</button>
2136
+ <button {...getPaginationButtonProps({ action: "next", disabled: !canNextPage }, resolvedLocalization)} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.nextPage())}>{getPaginationButtonText("next")}</button>
2137
+ <button {...getPaginationButtonProps({ action: "last", disabled: !canNextPage }, resolvedLocalization)} class="og-grid__pagination-button" on:click={() => runPaginationAction(() => grid.lastPage())}>{getPaginationButtonText("last")}</button>
2025
2138
  </div>
2026
- <select {...getPaginationPageSizeSelectProps($state.pagination.pageSize)} class="og-grid__pagination-size" on:change={handlePageSizeChange}>
2139
+ <select {...getPaginationPageSizeSelectProps($state.pagination.pageSize, resolvedLocalization)} class="og-grid__pagination-size" on:change={handlePageSizeChange}>
2027
2140
  {#each resolvedPageSizeOptions as pageSize (pageSize)}
2028
- <option value={pageSize}>{getPaginationPageSizeOptionText(pageSize)}</option>
2141
+ <option value={pageSize}>{getPaginationPageSizeOptionText(pageSize, resolvedLocalization)}</option>
2029
2142
  {/each}
2030
2143
  </select>
2031
2144
  </nav>
@@ -1,9 +1,10 @@
1
1
  import type { CellContext, CellFillOptions, ClipboardPasteOptions, ClipboardPasteResult, Column, Grid, GridOptions, HeaderContext, Row } from "@open-grid/core";
2
- import type { ColumnVirtualizationPrimitiveOptions, GridDensity, RowVirtualizationPrimitiveOptions } from "@open-grid/primitives";
2
+ import type { ColumnVirtualizationPrimitiveOptions, GridDensity, GridLocalizationOverrides, RowVirtualizationPrimitiveOptions } from "@open-grid/primitives";
3
3
  import type { SvelteComponentTyped } from "svelte";
4
4
 
5
5
  export interface DataGridProps<TData = unknown> {
6
6
  ariaLabel?: string;
7
+ localization?: GridLocalizationOverrides;
7
8
  options: GridOptions<TData>;
8
9
  emptyState?: unknown;
9
10
  error?: boolean;
@@ -11,6 +12,12 @@ export interface DataGridProps<TData = unknown> {
11
12
  onRetry?: () => void;
12
13
  loading?: boolean;
13
14
  loadingState?: unknown;
15
+ renderToolbar?: (context: DataGridRenderContext<TData>) => SvelteDataGridRenderValue<DataGridRenderContext<TData>>;
16
+ renderEmptyState?: (context: DataGridRenderContext<TData>) => SvelteDataGridRenderValue<DataGridRenderContext<TData>>;
17
+ renderLoadingState?: (context: DataGridRenderContext<TData>) => SvelteDataGridRenderValue<DataGridRenderContext<TData>>;
18
+ renderErrorState?: (context: DataGridErrorRenderContext<TData>) => SvelteDataGridRenderValue<DataGridErrorRenderContext<TData>>;
19
+ renderHeader?: (context: HeaderContext<TData, unknown>) => SvelteDataGridRenderValue<HeaderContext<TData, unknown>>;
20
+ renderCell?: (context: CellContext<TData, unknown>) => SvelteDataGridRenderValue<CellContext<TData, unknown>>;
14
21
  onGridReady?: GridReadyHandler<TData>;
15
22
  getRowClassName?: (row: Row<TData>) => string | undefined;
16
23
  getHeaderClassName?: (context: HeaderContext<TData, unknown>) => string | undefined;
@@ -38,6 +45,25 @@ export interface DataGridProps<TData = unknown> {
38
45
  style?: string;
39
46
  }
40
47
 
48
+ export interface DataGridRenderContext<TData = unknown> {
49
+ grid: Grid<TData>;
50
+ rows: readonly Row<TData>[];
51
+ visibleColumns: readonly Column<TData, unknown>[];
52
+ }
53
+
54
+ export interface DataGridErrorRenderContext<TData = unknown> extends DataGridRenderContext<TData> {
55
+ retry: (() => void) | undefined;
56
+ }
57
+
58
+ export interface SvelteDataGridRenderer<TContext = unknown> {
59
+ type: "open-grid:svelte-renderer";
60
+ component: unknown;
61
+ context: TContext;
62
+ props?: Record<string, unknown>;
63
+ }
64
+
65
+ export type SvelteDataGridRenderValue<TContext = unknown> = string | number | boolean | null | undefined | SvelteDataGridRenderer<TContext>;
66
+
41
67
  export type GridReadyHandler<TData = unknown> = (grid: Grid<TData>) => void | (() => void);
42
68
 
43
69
  export type RowVirtualizationOptions = RowVirtualizationPrimitiveOptions;
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ interface RendererDescriptor {
3
+ type: "open-grid:svelte-renderer";
4
+ component: unknown;
5
+ context: unknown;
6
+ props?: Record<string, unknown>;
7
+ }
8
+
9
+ type RendererComponent = any;
10
+
11
+ export let value: unknown;
12
+
13
+ const isRendererDescriptor = (candidate: unknown): candidate is RendererDescriptor =>
14
+ typeof candidate === "object"
15
+ && candidate !== null
16
+ && (candidate as { type?: unknown }).type === "open-grid:svelte-renderer"
17
+ && "component" in candidate;
18
+
19
+ $: descriptor = isRendererDescriptor(value) ? value : null;
20
+ $: component = descriptor?.component as RendererComponent;
21
+ </script>
22
+
23
+ {#if descriptor && component}
24
+ <svelte:component this={component} {...descriptor.props} context={descriptor.context} />
25
+ {:else}
26
+ {String(value ?? "")}
27
+ {/if}
package/dist/index.css CHANGED
@@ -115,6 +115,11 @@
115
115
  outline-offset: -2px;
116
116
  }
117
117
 
118
+ .og-grid__toolbar-slot {
119
+ width: 100%;
120
+ margin-bottom: 8px;
121
+ }
122
+
118
123
  .og-grid__controls {
119
124
  display: flex;
120
125
  align-items: center;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import { ExportFile } from '@open-grid/core';
2
2
  export { AccessorColumnOptions, AccessorFnColumnDef, AccessorKey, AccessorKeyColumnDef, AnyColumnDef, CellContext, CellCoordinate, CellEditEvent, CellEditEventParams, CellEditHistoryAction, CellEditHistoryState, CellEditOption, CellEditParserContext, CellEditPhase, CellEditValidationContext, CellEditValidationResult, CellEditValidationState, CellEditingState, CellFillOptions, CellInteractionEvent, CellInteractionEventParams, CellRange, CellRangeSelectionState, ClipboardCellContext, ClipboardCopyOptions, ClipboardPasteCellContext, ClipboardPasteCommittedCell, ClipboardPasteOptions, ClipboardPasteResult, ClipboardPasteSkippedCell, ClipboardPasteSkippedReason, ClipboardPasteValidationError, Column, ColumnDef, ColumnFilter, ColumnFiltersState, ColumnHelper, ColumnId, ColumnMovePosition, ColumnOrderState, ColumnPinningPosition, ColumnPinningState, ColumnResizeEvent, ColumnResizeEventParams, ColumnResizePhase, ColumnSizingState, ColumnVisibilityState, DisplayColumnDef, ExpandedState, ExportFile, ExportFileOptions, FilterFn, FitColumnsToWidthOptions, Grid, GridCacheDiagnostics, GridCacheDiagnosticsEntry, GridCacheKey, GridOptions, GridState, GroupColumnDef, GroupingState, Header, HeaderContext, HeaderGroup, MoveFocusOptions, PaginationState, Row, RowId, RowInteractionEvent, RowInteractionEventParams, RowModel, RowSelectionCleanupScope, RowSelectionState, SortFn, SortingRule, SortingState, Updater } from '@open-grid/core';
3
- export { ColumnVirtualizationOptions, default as DataGrid, DataGridProps, GridReadyHandler, HeaderActionMenuActionItem, HeaderActionMenuContext, HeaderActionMenuCustomItem, HeaderActionMenuItem, HeaderActionMenuItems, HeaderActionMenuLabelItem, HeaderActionMenuSeparatorItem, RowVirtualizationOptions } from './DataGrid.svelte';
3
+ export { ColumnVirtualizationOptions, default as DataGrid, DataGridErrorRenderContext, DataGridProps, DataGridRenderContext, GridReadyHandler, HeaderActionMenuActionItem, HeaderActionMenuContext, HeaderActionMenuCustomItem, HeaderActionMenuItem, HeaderActionMenuItems, HeaderActionMenuLabelItem, HeaderActionMenuSeparatorItem, RowVirtualizationOptions, SvelteDataGridRenderValue } from './DataGrid.svelte';
4
4
  export { createColumnHelper, createGridStore, fitColumnsToWidth } from '@open-grid/svelte';
5
- export { GRID_PREFERENCES_VERSION, GridDensity, GridPreferences, GridPreferencesOptions, GridPreferencesState, GridPreferencesStorageEnvironmentLike, GridPreferencesStorageLike, createGridPreferences, getBrowserGridPreferencesStorage, parseGridPreferences, readGridPreferences, removeGridPreferences, serializeGridPreferences, writeGridPreferences } from '@open-grid/primitives';
5
+ export { DEFAULT_GRID_LOCALIZATION, GRID_PREFERENCES_VERSION, GridDensity, GridLocalization, GridLocalizationOverrides, GridPreferences, GridPreferencesMigration, GridPreferencesOptions, GridPreferencesParseOptions, GridPreferencesState, GridPreferencesStorageEnvironmentLike, GridPreferencesStorageLike, createGridLocalization, createGridPreferences, getBrowserGridPreferencesStorage, migrateGridPreferences, parseGridPreferences, readGridPreferences, removeGridPreferences, serializeGridPreferences, writeGridPreferences } from '@open-grid/primitives';
6
+
7
+ interface SvelteDataGridRenderer<TContext> {
8
+ type: "open-grid:svelte-renderer";
9
+ component: unknown;
10
+ context: TContext;
11
+ props?: Record<string, unknown>;
12
+ }
13
+ type SvelteDataGridRendererFactory<TContext> = (context: TContext) => SvelteDataGridRenderer<TContext>;
14
+ declare function createSvelteDataGridRenderer<TContext>(component: unknown, props?: Record<string, unknown>): SvelteDataGridRendererFactory<TContext>;
6
15
 
7
16
  declare function downloadExportFile(file: ExportFile): boolean;
8
17
 
9
- export { downloadExportFile };
18
+ export { type SvelteDataGridRenderer, type SvelteDataGridRendererFactory, createSvelteDataGridRenderer, downloadExportFile };
package/dist/index.js CHANGED
@@ -3,27 +3,42 @@ import { downloadBrowserExportFile } from "@open-grid/primitives";
3
3
  import { default as default2 } from "./DataGrid.svelte";
4
4
  import { createColumnHelper, createGridStore, fitColumnsToWidth } from "@open-grid/svelte";
5
5
  import {
6
+ DEFAULT_GRID_LOCALIZATION,
6
7
  GRID_PREFERENCES_VERSION,
8
+ createGridLocalization,
7
9
  createGridPreferences,
8
10
  getBrowserGridPreferencesStorage,
11
+ migrateGridPreferences,
9
12
  parseGridPreferences,
10
13
  readGridPreferences,
11
14
  removeGridPreferences,
12
15
  serializeGridPreferences,
13
16
  writeGridPreferences
14
17
  } from "@open-grid/primitives";
18
+ function createSvelteDataGridRenderer(component, props) {
19
+ return (context) => ({
20
+ type: "open-grid:svelte-renderer",
21
+ component,
22
+ context,
23
+ ...props ? { props } : {}
24
+ });
25
+ }
15
26
  function downloadExportFile(file) {
16
27
  return downloadBrowserExportFile(file);
17
28
  }
18
29
  export {
30
+ DEFAULT_GRID_LOCALIZATION,
19
31
  default2 as DataGrid,
20
32
  GRID_PREFERENCES_VERSION,
21
33
  createColumnHelper,
34
+ createGridLocalization,
22
35
  createGridPreferences,
23
36
  createGridStore,
37
+ createSvelteDataGridRenderer,
24
38
  downloadExportFile,
25
39
  fitColumnsToWidth,
26
40
  getBrowserGridPreferencesStorage,
41
+ migrateGridPreferences,
27
42
  parseGridPreferences,
28
43
  readGridPreferences,
29
44
  removeGridPreferences,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-grid/svelte-ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Optional styled Svelte data grid for Open Grid.",
5
5
  "keywords": [
6
6
  "data-grid",
@@ -39,22 +39,22 @@
39
39
  "LICENSE"
40
40
  ],
41
41
  "scripts": {
42
- "build": "tsup src/index.ts --format esm --dts --clean --external ./DataGrid.svelte && mkdir -p dist && cp src/DataGrid.svelte dist/DataGrid.svelte && cp src/DataGrid.svelte.d.ts dist/DataGrid.svelte.d.ts && cp ../react-ui/src/index.css dist/index.css",
42
+ "build": "tsup src/index.ts --format esm --dts --clean --external ./DataGrid.svelte && mkdir -p dist && cp src/DataGrid.svelte src/RenderValue.svelte dist/ && cp src/DataGrid.svelte.d.ts dist/DataGrid.svelte.d.ts && cp ../react-ui/src/index.css dist/index.css",
43
43
  "check": "tsc --noEmit",
44
44
  "test": "vitest run --passWithNoTests",
45
45
  "lint": "tsc --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@open-grid/core": "0.1.0",
49
- "@open-grid/primitives": "0.1.0",
50
- "@open-grid/svelte": "0.1.0",
51
- "@open-grid/virtual": "0.1.0"
48
+ "@open-grid/core": "0.2.0",
49
+ "@open-grid/primitives": "0.2.0",
50
+ "@open-grid/svelte": "0.2.0",
51
+ "@open-grid/virtual": "0.2.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "svelte": "^4.2.20 || ^5.0.0"
55
55
  },
56
56
  "devDependencies": {
57
- "svelte": "^5.15.0",
57
+ "svelte": "^5.57.0",
58
58
  "tsup": "^8.3.5",
59
59
  "typescript": "^5.7.2",
60
60
  "vitest": "^2.1.8"