@toolbox-web/grid 1.26.1 → 1.27.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/all.js +2 -2
- package/all.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +64 -1
- package/lib/core/internal/diagnostics.d.ts +4 -3
- package/lib/core/internal/row-manager.d.ts +3 -1
- package/lib/core/plugin/base-plugin.d.ts +2 -2
- package/lib/core/types.d.ts +59 -3
- package/lib/features/registry.js.map +1 -1
- package/lib/plugins/clipboard/ClipboardPlugin.d.ts +1 -1
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +24 -5
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/EditingPlugin.d.ts +1 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/ExportPlugin.d.ts +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.d.ts +2 -2
- package/lib/plugins/filtering/index.js +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +2 -2
- package/lib/plugins/grouping-columns/grouping-columns.d.ts +18 -3
- package/lib/plugins/grouping-columns/index.d.ts +0 -1
- package/lib/plugins/grouping-columns/index.js +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-columns/types.d.ts +1 -7
- package/lib/plugins/grouping-rows/index.d.ts +2 -1
- package/lib/plugins/grouping-rows/index.js +1 -1
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +2 -0
- package/lib/plugins/master-detail/index.js +1 -1
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/master-detail/types.d.ts +20 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +8 -1
- package/lib/plugins/pinned-columns/index.js +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-columns/pinned-columns.d.ts +11 -1
- package/lib/plugins/pinned-rows/index.d.ts +1 -1
- package/lib/plugins/pinned-rows/index.js +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pinned-rows/types.d.ts +10 -3
- package/lib/plugins/pivot/PivotPlugin.d.ts +107 -1
- package/lib/plugins/pivot/index.d.ts +2 -1
- package/lib/plugins/pivot/index.js +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/print/types.d.ts +0 -3
- package/lib/plugins/reorder-columns/ReorderPlugin.d.ts +19 -2
- package/lib/plugins/reorder-columns/index.js +1 -1
- package/lib/plugins/reorder-columns/index.js.map +1 -1
- package/lib/plugins/reorder-rows/index.d.ts +1 -1
- package/lib/plugins/reorder-rows/index.js.map +1 -1
- package/lib/plugins/responsive/ResponsivePlugin.d.ts +1 -1
- package/lib/plugins/responsive/index.js +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/selection/SelectionPlugin.d.ts +1 -1
- package/lib/plugins/selection/index.js +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/selection/types.d.ts +3 -3
- package/lib/plugins/server-side/ServerSidePlugin.d.ts +6 -1
- package/lib/plugins/server-side/index.js +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/TreePlugin.d.ts +116 -0
- package/lib/plugins/tree/index.d.ts +1 -1
- package/lib/plugins/tree/index.js +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/tree/types.d.ts +16 -1
- package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/undo-redo/types.d.ts +15 -3
- package/lib/plugins/visibility/VisibilityPlugin.d.ts +18 -5
- package/lib/plugins/visibility/index.js +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/public.d.ts +2 -4
- package/themes/dg-theme-material.css +16 -4
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/context-menu.umd.js.map +1 -1
- package/umd/plugins/editing.umd.js.map +1 -1
- package/umd/plugins/export.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +1 -1
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-columns.umd.js +1 -1
- package/umd/plugins/grouping-columns.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +1 -1
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js +1 -1
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/pinned-columns.umd.js +1 -1
- package/umd/plugins/pinned-columns.umd.js.map +1 -1
- package/umd/plugins/pinned-rows.umd.js +1 -1
- package/umd/plugins/pinned-rows.umd.js.map +1 -1
- package/umd/plugins/pivot.umd.js +1 -1
- package/umd/plugins/pivot.umd.js.map +1 -1
- package/umd/plugins/reorder-columns.umd.js +1 -1
- package/umd/plugins/reorder-columns.umd.js.map +1 -1
- package/umd/plugins/responsive.umd.js +1 -1
- package/umd/plugins/responsive.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +1 -1
- package/umd/plugins/selection.umd.js.map +1 -1
- package/umd/plugins/server-side.umd.js +1 -1
- package/umd/plugins/server-side.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
- package/umd/plugins/undo-redo.umd.js.map +1 -1
- package/umd/plugins/visibility.umd.js +1 -1
- package/umd/plugins/visibility.umd.js.map +1 -1
package/lib/core/grid.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ShellState } from './internal/shell';
|
|
|
2
2
|
import { AfterCellRenderContext, AfterRowRenderContext, CellMouseEvent } from './plugin';
|
|
3
3
|
import { BaseGridPlugin, PluginQuery } from './plugin/base-plugin';
|
|
4
4
|
import { PluginManager } from './plugin/plugin-manager';
|
|
5
|
-
import { ColumnConfig, ColumnConfigMap, ColumnInternal, DataGridEventMap, FitMode, FrameworkAdapter, GridColumnState, GridConfig, HeaderContentDefinition, IconValue, InternalGrid, PluginNameMap, ResizeController, RowAnimationType, RowElementInternal, ScrollToRowOptions, ToolbarContentDefinition, ToolPanelDefinition, UpdateSource, VirtualState } from './types';
|
|
5
|
+
import { ColumnConfig, ColumnConfigMap, ColumnInternal, DataGridEventMap, FitMode, FrameworkAdapter, GridColumnState, GridConfig, HeaderContentDefinition, IconValue, InternalGrid, PluginNameMap, ResizeController, RowAnimationType, RowElementInternal, RowTransaction, ScrollToRowOptions, ToolbarContentDefinition, ToolPanelDefinition, TransactionResult, UpdateSource, VirtualState } from './types';
|
|
6
6
|
export declare class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {
|
|
7
7
|
#private;
|
|
8
8
|
static readonly tagName = "tbw-grid";
|
|
@@ -949,6 +949,69 @@ export declare class DataGridElement<T = any> extends HTMLElement implements Int
|
|
|
949
949
|
* ```
|
|
950
950
|
*/
|
|
951
951
|
removeRow(index: number, animate?: boolean): Promise<T | undefined>;
|
|
952
|
+
/**
|
|
953
|
+
* Apply a batch of row mutations (add, update, remove) in a single render cycle.
|
|
954
|
+
*
|
|
955
|
+
* This is the most efficient way to apply multiple row changes at once — ideal
|
|
956
|
+
* for streaming data from WebSocket, SSE, or any real-time source.
|
|
957
|
+
*
|
|
958
|
+
* Operations are applied in order: removes → updates → adds. This ensures
|
|
959
|
+
* updates don't target rows about to be removed, and adds don't collide
|
|
960
|
+
* with existing rows.
|
|
961
|
+
*
|
|
962
|
+
* @group Data Management
|
|
963
|
+
* @param transaction - The mutations to apply
|
|
964
|
+
* @param animate - Whether to animate the changes (default: `true`)
|
|
965
|
+
* @returns Result with the actual row objects affected
|
|
966
|
+
*
|
|
967
|
+
* @example
|
|
968
|
+
* ```typescript
|
|
969
|
+
* // Apply a mixed transaction
|
|
970
|
+
* const result = await grid.applyTransaction({
|
|
971
|
+
* add: [{ id: 'new-1', name: 'Alice' }],
|
|
972
|
+
* update: [{ id: 'emp-5', changes: { status: 'Inactive' } }],
|
|
973
|
+
* remove: [{ id: 'emp-3' }],
|
|
974
|
+
* });
|
|
975
|
+
*
|
|
976
|
+
* // Wire up a WebSocket stream
|
|
977
|
+
* ws.onmessage = (e) => {
|
|
978
|
+
* const event = JSON.parse(e.data);
|
|
979
|
+
* grid.applyTransaction({
|
|
980
|
+
* [event.type]: event.type === 'update'
|
|
981
|
+
* ? [{ id: event.rowId, changes: event.changes }]
|
|
982
|
+
* : event.type === 'add'
|
|
983
|
+
* ? [event.row]
|
|
984
|
+
* : [{ id: event.rowId }],
|
|
985
|
+
* });
|
|
986
|
+
* };
|
|
987
|
+
* ```
|
|
988
|
+
*/
|
|
989
|
+
applyTransaction(transaction: RowTransaction<T>, animate?: boolean): Promise<TransactionResult<T>>;
|
|
990
|
+
/**
|
|
991
|
+
* Apply a transaction asynchronously, batching rapid calls into a single render.
|
|
992
|
+
*
|
|
993
|
+
* Ideal for high-frequency streaming where many small updates arrive faster
|
|
994
|
+
* than the browser can render. All transactions queued within the same
|
|
995
|
+
* animation frame are merged and applied together.
|
|
996
|
+
*
|
|
997
|
+
* Animations are disabled for batched transactions to avoid visual overload.
|
|
998
|
+
*
|
|
999
|
+
* @group Data Management
|
|
1000
|
+
* @param transaction - The mutations to apply
|
|
1001
|
+
* @returns Result with the actual row objects affected (after batching)
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```typescript
|
|
1005
|
+
* // High-frequency WebSocket — updates batched into single renders
|
|
1006
|
+
* ws.onmessage = (e) => {
|
|
1007
|
+
* const event = JSON.parse(e.data);
|
|
1008
|
+
* grid.applyTransactionAsync({
|
|
1009
|
+
* update: [{ id: event.id, changes: event.changes }],
|
|
1010
|
+
* });
|
|
1011
|
+
* };
|
|
1012
|
+
* ```
|
|
1013
|
+
*/
|
|
1014
|
+
applyTransactionAsync(transaction: RowTransaction<T>): Promise<TransactionResult<T>>;
|
|
952
1015
|
/**
|
|
953
1016
|
* Suspend row processing for the next rows update.
|
|
954
1017
|
*
|
|
@@ -153,10 +153,11 @@ export declare function throwDiagnostic(code: DiagnosticCode, message: string, g
|
|
|
153
153
|
*/
|
|
154
154
|
export declare function warnDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void;
|
|
155
155
|
/**
|
|
156
|
-
* Log
|
|
157
|
-
* Use for optional/soft dependency notifications
|
|
156
|
+
* Log a debug message with a diagnostic code and docs link.
|
|
157
|
+
* Use for optional/soft dependency notifications — visible only when
|
|
158
|
+
* the browser DevTools "Verbose" log level is enabled.
|
|
158
159
|
*/
|
|
159
|
-
export declare function
|
|
160
|
+
export declare function debugDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void;
|
|
160
161
|
/**
|
|
161
162
|
* Log an error with a diagnostic code and docs link.
|
|
162
163
|
* Use for non-throwing errors (e.g., failed async operations).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GridHost, UpdateSource } from '../types';
|
|
1
|
+
import { GridHost, RowTransaction, TransactionResult, UpdateSource } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Try to resolve the ID for a row using a configured getRowId or fallback.
|
|
4
4
|
* Returns undefined if no ID can be determined (non-throwing).
|
|
@@ -28,4 +28,6 @@ export declare class RowManager<T = any> {
|
|
|
28
28
|
}>, source?: UpdateSource): void;
|
|
29
29
|
insertRow(index: number, row: T, animate?: boolean): Promise<void>;
|
|
30
30
|
removeRow(index: number, animate?: boolean): Promise<T | undefined>;
|
|
31
|
+
applyTransaction(transaction: RowTransaction<T>, animate?: boolean): Promise<TransactionResult<T>>;
|
|
32
|
+
applyTransactionAsync(transaction: RowTransaction<T>): Promise<TransactionResult<T>>;
|
|
31
33
|
}
|
|
@@ -81,7 +81,7 @@ export interface PluginIncompatibility {
|
|
|
81
81
|
/**
|
|
82
82
|
* Human-readable reason for the incompatibility.
|
|
83
83
|
* Should explain why the plugins conflict and any workarounds.
|
|
84
|
-
* @example "
|
|
84
|
+
* @example "Both transform the entire row model in different ways"
|
|
85
85
|
*/
|
|
86
86
|
reason: string;
|
|
87
87
|
}
|
|
@@ -245,7 +245,7 @@ export interface PluginManifest<TConfig = unknown> {
|
|
|
245
245
|
* @example
|
|
246
246
|
* ```typescript
|
|
247
247
|
* incompatibleWith: [
|
|
248
|
-
* { name: '
|
|
248
|
+
* { name: 'tree', reason: 'Both transform the entire row model in different ways' },
|
|
249
249
|
* ],
|
|
250
250
|
* ```
|
|
251
251
|
*/
|
package/lib/core/types.d.ts
CHANGED
|
@@ -105,6 +105,10 @@ export interface PublicGrid<T = any> {
|
|
|
105
105
|
insertRow?(index: number, row: T, animate?: boolean): Promise<void>;
|
|
106
106
|
/** Remove a row at a visible index, bypassing the sort/filter pipeline. Auto-animates by default. */
|
|
107
107
|
removeRow?(index: number, animate?: boolean): Promise<T | undefined>;
|
|
108
|
+
/** Apply a batch of add/update/remove mutations in a single render cycle. */
|
|
109
|
+
applyTransaction?(transaction: RowTransaction<T>, animate?: boolean): Promise<TransactionResult<T>>;
|
|
110
|
+
/** Batch-friendly version — merges rapid calls within a single animation frame. */
|
|
111
|
+
applyTransactionAsync?(transaction: RowTransaction<T>): Promise<TransactionResult<T>>;
|
|
108
112
|
/** Resolves once the component has finished initial work (layout, inference). */
|
|
109
113
|
ready?: () => Promise<void>;
|
|
110
114
|
/** Force a layout / measurement pass (e.g. after container resize). */
|
|
@@ -151,7 +155,7 @@ export interface PublicGrid<T = any> {
|
|
|
151
155
|
/**
|
|
152
156
|
* Get a plugin instance by its name.
|
|
153
157
|
*
|
|
154
|
-
* When a plugin augments the
|
|
158
|
+
* When a plugin augments the `PluginNameMap` interface, the return
|
|
155
159
|
* type is narrowed automatically:
|
|
156
160
|
*
|
|
157
161
|
* ```typescript
|
|
@@ -2492,7 +2496,7 @@ export type UpdateSource = 'user' | 'cascade' | 'api';
|
|
|
2492
2496
|
* ```
|
|
2493
2497
|
*
|
|
2494
2498
|
* @see {@link UpdateSource} for understanding change origins
|
|
2495
|
-
* @see
|
|
2499
|
+
* @see CellCommitDetail for the commit event (editing lifecycle)
|
|
2496
2500
|
* @category Events
|
|
2497
2501
|
*/
|
|
2498
2502
|
export interface CellChangeDetail<TRow = unknown> {
|
|
@@ -2541,6 +2545,58 @@ export interface RowUpdate<TRow = unknown> {
|
|
|
2541
2545
|
/** Fields to update */
|
|
2542
2546
|
changes: Partial<TRow>;
|
|
2543
2547
|
}
|
|
2548
|
+
/**
|
|
2549
|
+
* A batch of row mutations to apply atomically in a single render cycle.
|
|
2550
|
+
*
|
|
2551
|
+
* All adds, updates, and removes are processed together with one re-render,
|
|
2552
|
+
* making this far more efficient than calling `insertRow`, `updateRow`, and
|
|
2553
|
+
* `removeRow` individually — especially for high-frequency streaming data.
|
|
2554
|
+
*
|
|
2555
|
+
* Row identification for `update` and `remove` uses the grid's configured
|
|
2556
|
+
* {@link GridConfig.getRowId | getRowId} function.
|
|
2557
|
+
*
|
|
2558
|
+
* @example
|
|
2559
|
+
* ```typescript
|
|
2560
|
+
* // Apply a mixed transaction from a WebSocket message
|
|
2561
|
+
* const result = await grid.applyTransaction({
|
|
2562
|
+
* add: [{ id: 'new-1', name: 'Alice', status: 'Active' }],
|
|
2563
|
+
* update: [{ id: 'emp-5', changes: { status: 'Inactive' } }],
|
|
2564
|
+
* remove: [{ id: 'emp-3' }],
|
|
2565
|
+
* });
|
|
2566
|
+
*
|
|
2567
|
+
* console.log(`Added: ${result.added.length}, Updated: ${result.updated.length}, Removed: ${result.removed.length}`);
|
|
2568
|
+
* ```
|
|
2569
|
+
*
|
|
2570
|
+
* @see {@link TransactionResult} for the result structure
|
|
2571
|
+
* @category Data Management
|
|
2572
|
+
*/
|
|
2573
|
+
export interface RowTransaction<TRow = unknown> {
|
|
2574
|
+
/** Rows to insert. Appended at the end of the current view. */
|
|
2575
|
+
add?: TRow[];
|
|
2576
|
+
/** Rows to update in-place by ID. */
|
|
2577
|
+
update?: RowUpdate<TRow>[];
|
|
2578
|
+
/** Rows to remove by ID. */
|
|
2579
|
+
remove?: Array<{
|
|
2580
|
+
id: string;
|
|
2581
|
+
}>;
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Result of a {@link RowTransaction} applied via `applyTransaction`.
|
|
2585
|
+
*
|
|
2586
|
+
* Contains the actual row objects that were affected, useful for
|
|
2587
|
+
* post-processing or logging.
|
|
2588
|
+
*
|
|
2589
|
+
* @see {@link RowTransaction} for the input structure
|
|
2590
|
+
* @category Data Management
|
|
2591
|
+
*/
|
|
2592
|
+
export interface TransactionResult<TRow = unknown> {
|
|
2593
|
+
/** Rows that were successfully added. */
|
|
2594
|
+
added: TRow[];
|
|
2595
|
+
/** Rows that were successfully updated (references to the mutated row objects). */
|
|
2596
|
+
updated: TRow[];
|
|
2597
|
+
/** Rows that were successfully removed. */
|
|
2598
|
+
removed: TRow[];
|
|
2599
|
+
}
|
|
2544
2600
|
/**
|
|
2545
2601
|
* Animation behavior mode.
|
|
2546
2602
|
* - `true` or `'on'`: Animations always enabled
|
|
@@ -3564,7 +3620,7 @@ export type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow
|
|
|
3564
3620
|
*
|
|
3565
3621
|
* @see {@link DataGridElement.on} for the recommended subscription API
|
|
3566
3622
|
* @see {@link DataGridEventMap} for all event types
|
|
3567
|
-
* @see
|
|
3623
|
+
* @see `DataGridEventDetail` for extracting detail type only
|
|
3568
3624
|
* @category Events
|
|
3569
3625
|
*/
|
|
3570
3626
|
export type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<DataGridEventMap<TRow>[K]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sources":["../../../../../libs/grid/src/lib/core/internal/diagnostics.ts","../../../../../libs/grid/src/lib/features/registry.ts"],"sourcesContent":["/**\n * Centralized diagnostic messages for @toolbox-web/grid.\n *\n * Every user-facing warning, error, or info message in the grid has a unique\n * diagnostic code (e.g. `TBW001`). Each code maps to a section on the online\n * troubleshooting page, giving developers a direct link to resolution steps.\n *\n * ## Usage\n *\n * ```ts\n * import { MISSING_BREAKPOINT, warnDiagnostic, throwDiagnostic } from './diagnostics';\n *\n * // Warn with a code\n * warnDiagnostic(MISSING_BREAKPOINT, 'Set a breakpoint...', gridId);\n *\n * // Throw with a code\n * throwDiagnostic(MISSING_ROW_ID, 'Configure getRowId...', gridId);\n * ```\n *\n * Plugins should prefer `this.warn(MISSING_BREAKPOINT, message)` via BaseGridPlugin\n * instead of importing this module directly.\n *\n * @internal\n */\n\n// #region Grid Prefix\n\n/**\n * Build the `[tbw-grid]` or `[tbw-grid#my-id]` log prefix.\n * Pass `pluginName` for a scoped prefix like `[tbw-grid:SelectionPlugin]`.\n */\nexport function gridPrefix(gridId?: string, pluginName?: string): string {\n const id = gridId ? `#${gridId}` : '';\n const plugin = pluginName ? `:${pluginName}` : '';\n return `[tbw-grid${id}${plugin}]`;\n}\n\n// #endregion\n\n// #region Diagnostic Codes\n\n/**\n * Diagnostic codes used across the grid library.\n *\n * Each code is an individual export so that unused codes are tree-shaken\n * from bundles that don't reference them (esbuild can't tree-shake\n * properties from a single object).\n *\n * Naming: TBW + 3-digit number.\n * Ranges:\n * 001–019 Configuration validation (missing plugins, bad config)\n * 020–029 Plugin lifecycle (dependencies, incompatibilities, deprecation)\n * 030–039 Feature registry\n * 040–049 Row operations (row ID, row mutations)\n * 050–059 Column operations (width, template)\n * 060–069 Rendering (callbacks, formatters, external views)\n * 070–079 Shell (tool panels, header/toolbar content)\n * 080–089 Editing & editors\n * 090–099 Print\n * 100–109 Clipboard\n * 110–119 Plugin-specific (responsive, undo-redo, grouping-columns)\n * 120–129 Style injection\n * 130–139 Attribute parsing\n */\n\n// --- Config validation (001–019) ---\n/** Column uses a plugin-owned property but the plugin is not loaded. */\nexport const MISSING_PLUGIN = 'TBW001' as const;\n/** Grid config uses a plugin-owned property but the plugin is not loaded. */\nexport const MISSING_PLUGIN_CONFIG = 'TBW002' as const;\n/** Plugin config rule violation (error severity). */\nexport const CONFIG_RULE_ERROR = 'TBW003' as const;\n/** Plugin config rule violation (warning severity). */\nexport const CONFIG_RULE_WARN = 'TBW004' as const;\n\n// --- Plugin lifecycle (020–029) ---\n/** Required plugin dependency is missing. */\nexport const MISSING_DEPENDENCY = 'TBW020' as const;\n/** Optional plugin dependency is missing. */\nexport const OPTIONAL_DEPENDENCY = 'TBW021' as const;\n/** Two loaded plugins are incompatible. */\nexport const INCOMPATIBLE_PLUGINS = 'TBW022' as const;\n/** Plugin uses deprecated hooks. */\nexport const DEPRECATED_HOOK = 'TBW023' as const;\n/** Error thrown inside a plugin event handler. */\nexport const PLUGIN_EVENT_ERROR = 'TBW024' as const;\n\n// --- Feature registry (030–039) ---\n/** Feature was re-registered (overwritten). */\nexport const FEATURE_REREGISTERED = 'TBW030' as const;\n/** Feature configured but not imported. */\nexport const FEATURE_NOT_IMPORTED = 'TBW031' as const;\n/** Feature depends on another feature that is not enabled. */\nexport const FEATURE_MISSING_DEP = 'TBW032' as const;\n\n// --- Row operations (040–049) ---\n/** Cannot determine row ID (no getRowId and no id property). */\nexport const MISSING_ROW_ID = 'TBW040' as const;\n/** Row with given ID not found. */\nexport const ROW_NOT_FOUND = 'TBW041' as const;\n\n// --- Column operations (050–059) ---\n/** Column has an invalid CSS width value. */\nexport const INVALID_COLUMN_WIDTH = 'TBW050' as const;\n\n// --- Rendering callbacks (060–069) ---\n/** rowClass callback threw an error. */\nexport const ROW_CLASS_ERROR = 'TBW060' as const;\n/** cellClass callback threw an error. */\nexport const CELL_CLASS_ERROR = 'TBW061' as const;\n/** Column format function threw an error. */\nexport const FORMAT_ERROR = 'TBW062' as const;\n/** External view mount() threw an error. */\nexport const VIEW_MOUNT_ERROR = 'TBW063' as const;\n/** External view event dispatch error. */\nexport const VIEW_DISPATCH_ERROR = 'TBW064' as const;\n\n// --- Shell (070–079) ---\n/** Tool panel missing required id or title. */\nexport const TOOL_PANEL_MISSING_ATTR = 'TBW070' as const;\n/** No tool panels registered. */\nexport const NO_TOOL_PANELS = 'TBW071' as const;\n/** Tool panel section not found. */\nexport const TOOL_PANEL_NOT_FOUND = 'TBW072' as const;\n/** Tool panel already registered. */\nexport const TOOL_PANEL_DUPLICATE = 'TBW073' as const;\n/** Header content already registered. */\nexport const HEADER_CONTENT_DUPLICATE = 'TBW074' as const;\n/** Toolbar content already registered. */\nexport const TOOLBAR_CONTENT_DUPLICATE = 'TBW075' as const;\n\n// --- Editing & editors (080–089) ---\n/** External editor mount() threw an error. */\nexport const EDITOR_MOUNT_ERROR = 'TBW080' as const;\n\n// --- Print (090–099) ---\n/** Print already in progress. */\nexport const PRINT_IN_PROGRESS = 'TBW090' as const;\n/** Grid not available for printing. */\nexport const PRINT_NO_GRID = 'TBW091' as const;\n/** Print operation failed. */\nexport const PRINT_FAILED = 'TBW092' as const;\n/** Multiple elements share the same grid ID (print isolation issue). */\nexport const PRINT_DUPLICATE_ID = 'TBW093' as const;\n\n// --- Clipboard (100–109) ---\n/** Clipboard API write failed. */\nexport const CLIPBOARD_FAILED = 'TBW100' as const;\n\n// --- Plugin-specific (110–119) ---\n/** ResponsivePlugin: no breakpoint configured. */\nexport const MISSING_BREAKPOINT = 'TBW110' as const;\n/** UndoRedoPlugin: transaction already in progress. */\nexport const TRANSACTION_IN_PROGRESS = 'TBW111' as const;\n/** UndoRedoPlugin: no transaction in progress. */\nexport const NO_TRANSACTION = 'TBW112' as const;\n/** GroupingColumnsPlugin: missing id or header on column group definition. */\nexport const COLUMN_GROUP_NO_ID = 'TBW113' as const;\n/** GroupingColumnsPlugin: conflicting columnGroups sources. */\nexport const COLUMN_GROUPS_CONFLICT = 'TBW114' as const;\n\n// --- Style injection (120–129) ---\n/** Failed to extract grid.css from document stylesheets. */\nexport const STYLE_EXTRACT_FAILED = 'TBW120' as const;\n/** Could not find grid.css in document.styleSheets. */\nexport const STYLE_NOT_FOUND = 'TBW121' as const;\n\n// --- Attribute parsing (130–139) ---\n/** Invalid JSON in an HTML attribute. */\nexport const INVALID_ATTRIBUTE_JSON = 'TBW130' as const;\n\nexport type DiagnosticCode =\n | typeof MISSING_PLUGIN\n | typeof MISSING_PLUGIN_CONFIG\n | typeof CONFIG_RULE_ERROR\n | typeof CONFIG_RULE_WARN\n | typeof MISSING_DEPENDENCY\n | typeof OPTIONAL_DEPENDENCY\n | typeof INCOMPATIBLE_PLUGINS\n | typeof DEPRECATED_HOOK\n | typeof PLUGIN_EVENT_ERROR\n | typeof FEATURE_REREGISTERED\n | typeof FEATURE_NOT_IMPORTED\n | typeof FEATURE_MISSING_DEP\n | typeof MISSING_ROW_ID\n | typeof ROW_NOT_FOUND\n | typeof INVALID_COLUMN_WIDTH\n | typeof ROW_CLASS_ERROR\n | typeof CELL_CLASS_ERROR\n | typeof FORMAT_ERROR\n | typeof VIEW_MOUNT_ERROR\n | typeof VIEW_DISPATCH_ERROR\n | typeof TOOL_PANEL_MISSING_ATTR\n | typeof NO_TOOL_PANELS\n | typeof TOOL_PANEL_NOT_FOUND\n | typeof TOOL_PANEL_DUPLICATE\n | typeof HEADER_CONTENT_DUPLICATE\n | typeof TOOLBAR_CONTENT_DUPLICATE\n | typeof EDITOR_MOUNT_ERROR\n | typeof PRINT_IN_PROGRESS\n | typeof PRINT_NO_GRID\n | typeof PRINT_FAILED\n | typeof PRINT_DUPLICATE_ID\n | typeof CLIPBOARD_FAILED\n | typeof MISSING_BREAKPOINT\n | typeof TRANSACTION_IN_PROGRESS\n | typeof NO_TRANSACTION\n | typeof COLUMN_GROUP_NO_ID\n | typeof COLUMN_GROUPS_CONFLICT\n | typeof STYLE_EXTRACT_FAILED\n | typeof STYLE_NOT_FOUND\n | typeof INVALID_ATTRIBUTE_JSON;\n\n// #endregion\n\n// #region Docs URL\n\nconst DOCS_BASE = 'https://toolboxjs.com/grid/errors';\n\n/** Build a direct link to the troubleshooting section for a code. */\nfunction docsUrl(code: DiagnosticCode): string {\n return `${DOCS_BASE}#${code.toLowerCase()}`;\n}\n\n// #endregion\n\n// #region Formatting\n\n/**\n * Format a diagnostic message with prefix, code, and docs link.\n *\n * Output format:\n * ```\n * [tbw-grid#my-id] TBW001: Your message here.\n *\n * → More info: https://toolboxjs.com/grid/errors#tbw001\n * ```\n */\nexport function formatDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): string {\n const prefix = gridPrefix(gridId, pluginName);\n return `${prefix} ${code}: ${message}\\n\\n → More info: ${docsUrl(code)}`;\n}\n\n// #endregion\n\n// #region Public API\n\n/**\n * Throw an error with a diagnostic code and docs link.\n * Use for configuration errors and API misuse that should halt execution.\n */\nexport function throwDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): never {\n throw new Error(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log a warning with a diagnostic code and docs link.\n * Use for recoverable issues the developer should fix.\n */\nexport function warnDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.warn(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log an info message with a diagnostic code and docs link.\n * Use for optional/soft dependency notifications.\n */\nexport function infoDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.info(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log an error with a diagnostic code and docs link.\n * Use for non-throwing errors (e.g., failed async operations).\n */\nexport function errorDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.error(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n// #endregion\n","/**\n * Core Feature Registry for @toolbox-web/grid\n *\n * This module provides a framework-agnostic registry for plugin factories.\n * Features are registered via side-effect imports, enabling tree-shaking\n * while maintaining a clean declarative API.\n *\n * @example\n * ```typescript\n * // Import features you need (side-effect imports)\n * import '@toolbox-web/grid/features/selection';\n * import '@toolbox-web/grid/features/filtering';\n *\n * // Configure grid declaratively\n * grid.gridConfig = {\n * features: {\n * selection: 'range',\n * filtering: { debounceMs: 200 },\n * },\n * };\n * ```\n *\n * @packageDocumentation\n * @module Features\n */\n\nimport {\n FEATURE_MISSING_DEP,\n FEATURE_NOT_IMPORTED,\n FEATURE_REREGISTERED,\n warnDiagnostic,\n} from '../core/internal/diagnostics';\nimport { setFeatureResolver } from '../core/internal/feature-hook';\nimport type { FeatureConfig, GridPlugin } from '../core/types';\n\n// #region Types\n\n/** Feature name — keys of the augmented FeatureConfig interface. */\nexport type FeatureName = keyof FeatureConfig;\n\n/** Factory function that creates a plugin from a feature config value. */\nexport type PluginFactory<TConfig = unknown> = (config: TConfig) => GridPlugin;\n\ninterface RegistryEntry {\n factory: PluginFactory;\n name: string;\n}\n\n// #endregion\n\n// #region Registry State\n\nconst featureRegistry = new Map<string, RegistryEntry>();\nconst warnedFeatures = new Set<string>();\n\n// #endregion\n\n// #region Registration API\n\n/** Runtime dev-mode check (localhost or 127.0.0.1). */\nconst isDev = (): boolean =>\n typeof window !== 'undefined' &&\n (window.location?.hostname === 'localhost' || window.location?.hostname === '127.0.0.1');\n\n/**\n * Register a feature's plugin factory.\n * Called by side-effect feature imports (e.g., `import '@toolbox-web/grid/features/selection'`).\n *\n * @param name - The feature name (matches a key on FeatureConfig)\n * @param factory - Function that creates a plugin instance from config\n */\nexport function registerFeature<K extends FeatureName>(name: K, factory: PluginFactory<FeatureConfig[K]>): void;\nexport function registerFeature(name: string, factory: PluginFactory): void;\nexport function registerFeature(name: string, factory: PluginFactory): void {\n if (isDev() && featureRegistry.has(name)) {\n warnDiagnostic(FEATURE_REREGISTERED, `Feature \"${name}\" was re-registered. Previous registration overwritten.`);\n }\n featureRegistry.set(name, { factory, name });\n}\n\n/**\n * Check if a feature has been registered.\n */\nexport function isFeatureRegistered(name: string): boolean {\n return featureRegistry.has(name);\n}\n\n/**\n * Get a registered feature's factory. Returns undefined if not registered.\n */\nexport function getFeatureFactory(name: string): PluginFactory | undefined {\n return featureRegistry.get(name)?.factory;\n}\n\n/**\n * Get all registered feature names.\n */\nexport function getRegisteredFeatures(): string[] {\n return Array.from(featureRegistry.keys());\n}\n\n// #endregion\n\n// #region Plugin Creation\n\n/**\n * Plugin dependency declarations.\n * Some plugins require others to be loaded first.\n */\nconst PLUGIN_DEPENDENCIES: Record<string, string[]> = {\n undoRedo: ['editing'],\n clipboard: ['selection'],\n};\n\n/**\n * Deprecated alias mappings.\n * When both alias and primary are set, primary takes precedence.\n */\nconst FEATURE_ALIASES: Record<string, string> = {\n sorting: 'multiSort',\n reorder: 'reorderColumns',\n rowReorder: 'reorderRows',\n};\n\n/**\n * Create a plugin instance for a single feature.\n * Shows a warning if the feature is not registered.\n */\nexport function createPluginFromFeature(name: string, config: unknown): GridPlugin | undefined {\n const entry = featureRegistry.get(name);\n\n if (!entry) {\n if (isDev() && !warnedFeatures.has(name)) {\n warnedFeatures.add(name);\n const kebab = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n warnDiagnostic(\n FEATURE_NOT_IMPORTED,\n `Feature \"${name}\" is configured but not registered.\\n` +\n `Add this import to enable it:\\n\\n` +\n ` import '@toolbox-web/grid/features/${kebab}';\\n`,\n );\n }\n return undefined;\n }\n\n return entry.factory(config);\n}\n\n/**\n * Validate feature dependencies and log warnings for missing ones.\n */\nfunction validateDependencies(featureNames: string[]): void {\n const featureSet = new Set(featureNames);\n\n for (const feature of featureNames) {\n const deps = PLUGIN_DEPENDENCIES[feature];\n if (!deps) continue;\n\n for (const dep of deps) {\n if (!featureSet.has(dep)) {\n if (isDev()) {\n warnDiagnostic(\n FEATURE_MISSING_DEP,\n `Feature \"${feature}\" requires \"${dep}\" to be enabled. ` + `Add \"${dep}\" to your features configuration.`,\n );\n }\n }\n }\n }\n}\n\n/**\n * Create plugin instances from a features configuration object.\n *\n * Handles:\n * - Deprecated alias resolution (sorting → multiSort, etc.)\n * - Dependency validation (clipboard needs selection)\n * - Dependency ordering (selection before clipboard)\n * - Skipping false/undefined values\n *\n * @param features - Partial FeatureConfig object\n * @returns Array of plugin instances ready for gridConfig.plugins\n */\nexport function createPluginsFromFeatures(features: Record<string, unknown>): GridPlugin[] {\n const plugins: GridPlugin[] = [];\n const enabledFeatures: string[] = [];\n\n // Resolve deprecated aliases: use primary name, skip alias if primary is set\n const effective: Record<string, unknown> = { ...features };\n for (const [alias, primary] of Object.entries(FEATURE_ALIASES)) {\n if (effective[alias] !== undefined && effective[primary] === undefined) {\n effective[primary] = effective[alias];\n }\n delete effective[alias];\n }\n\n // Collect enabled feature names\n for (const [key, value] of Object.entries(effective)) {\n if (value === undefined || value === false) continue;\n enabledFeatures.push(key);\n }\n\n // Validate dependencies\n validateDependencies(enabledFeatures);\n\n // Create plugins in dependency order: dep-targets first, then the rest\n const dependencyOrder: string[] = [\n 'selection',\n 'editing',\n ...enabledFeatures.filter((f) => f !== 'selection' && f !== 'editing'),\n ];\n const orderedFeatures = [...new Set(dependencyOrder)].filter((f) => enabledFeatures.includes(f));\n\n for (const featureName of orderedFeatures) {\n const config = effective[featureName];\n if (config === undefined || config === false) continue;\n\n const plugin = createPluginFromFeature(featureName, config);\n if (plugin) {\n plugins.push(plugin);\n }\n }\n\n return plugins;\n}\n\n// #endregion\n\n// #region Auto-Registration\n\n// Wire feature resolver into grid core so `gridConfig.features` is handled automatically.\n// This runs when any feature module is imported (they all import this registry).\nsetFeatureResolver(createPluginsFromFeatures as (features: Record<string, unknown>) => GridPlugin[]);\n\n// #endregion\n\n// #region Testing Utilities\n\n/**\n * Clear the registry. For testing only.\n * @internal\n */\nexport function clearFeatureRegistry(): void {\n featureRegistry.clear();\n warnedFeatures.clear();\n}\n\n// #endregion\n"],"names":["warnDiagnostic","code","message","gridId","pluginName","console","warn","toLowerCase","docsUrl","formatDiagnostic","featureRegistry","Map","warnedFeatures","Set","isDev","window","location","hostname","registerFeature","name","factory","has","set","isFeatureRegistered","getFeatureFactory","get","getRegisteredFeatures","Array","from","keys","PLUGIN_DEPENDENCIES","undoRedo","clipboard","FEATURE_ALIASES","sorting","reorder","rowReorder","createPluginFromFeature","config","entry","add","kebab","replace","createPluginsFromFeatures","features","plugins","enabledFeatures","effective","alias","primary","Object","entries","key","value","push","featureNames","featureSet","feature","deps","dep","validateDependencies","dependencyOrder","filter","f","orderedFeatures","includes","featureName","plugin","clearFeatureRegistry","clear","setFeatureResolver"],"mappings":"uDAmQO,SAASA,EAAeC,EAAsBC,EAAiBC,EAAiBC,GACrFC,QAAQC,KAtBH,SAA0BL,EAAsBC,GAErD,MAAO,cAAaD,MAASC,uBApB/B,SAAiBD,GACf,MAAO,qCAAgBA,EAAKM,eAC9B,CAkB4DC,CAAQP,IACpE,CAmBeQ,CAAiBR,EAAMC,GACtC,CCjNA,MAAMQ,qBAAsBC,IACtBC,qBAAqBC,IAOrBC,EAAQ,IACM,oBAAXC,SACwB,cAA9BA,OAAOC,UAAUC,UAA0D,cAA9BF,OAAOC,UAAUC,UAW1D,SAASC,EAAgBC,EAAcC,GACxCN,KAAWJ,EAAgBW,IAAIF,IACjCnB,EDcgC,SCdK,YAAYmB,4DAEnDT,EAAgBY,IAAIH,EAAM,CAAEC,UAASD,QACvC,CAKO,SAASI,EAAoBJ,GAClC,OAAOT,EAAgBW,IAAIF,EAC7B,CAKO,SAASK,EAAkBL,GAChC,OAAOT,EAAgBe,IAAIN,IAAOC,OACpC,CAKO,SAASM,IACd,OAAOC,MAAMC,KAAKlB,EAAgBmB,OACpC,CAUA,MAAMC,EAAgD,CACpDC,SAAU,CAAC,WACXC,UAAW,CAAC,cAORC,EAA0C,CAC9CC,QAAS,YACTC,QAAS,iBACTC,WAAY,eAOP,SAASC,EAAwBlB,EAAcmB,GACpD,MAAMC,EAAQ7B,EAAgBe,IAAIN,GAElC,GAAKoB,EAcL,OAAOA,EAAMnB,QAAQkB,GAbnB,GAAIxB,MAAYF,EAAeS,IAAIF,GAAO,CACxCP,EAAe4B,IAAIrB,GACnB,MAAMsB,EAAQtB,EAAKuB,QAAQ,kBAAmB,SAASnC,cACvDP,ED5C8B,SC8C5B,YAAYmB,+GAE8BsB,QAE9C,CAKJ,CAqCO,SAASE,EAA0BC,GACxC,MAAMC,EAAwB,GACxBC,EAA4B,GAG5BC,EAAqC,IAAKH,GAChD,IAAA,MAAYI,EAAOC,KAAYC,OAAOC,QAAQlB,QACnB,IAArBc,EAAUC,SAA+C,IAAvBD,EAAUE,KAC9CF,EAAUE,GAAWF,EAAUC,WAE1BD,EAAUC,GAInB,IAAA,MAAYI,EAAKC,KAAUH,OAAOC,QAAQJ,QAC1B,IAAVM,IAAiC,IAAVA,GAC3BP,EAAgBQ,KAAKF,IAhDzB,SAA8BG,GAC5B,MAAMC,EAAa,IAAI3C,IAAI0C,GAE3B,IAAA,MAAWE,KAAWF,EAAc,CAClC,MAAMG,EAAO5B,EAAoB2B,GACjC,GAAKC,EAEL,IAAA,MAAWC,KAAOD,EACXF,EAAWnC,IAAIsC,IACd7C,KACFd,EDpEyB,SCsEvB,YAAYyD,gBAAsBE,0BAAiCA,qCAK7E,CACF,CAkCEC,CAAqBd,GAGrB,MAAMe,EAA4B,CAChC,YACA,aACGf,EAAgBgB,OAAQC,GAAY,cAANA,GAA2B,YAANA,IAElDC,EAAkB,IAAI,IAAInD,IAAIgD,IAAkBC,OAAQC,GAAMjB,EAAgBmB,SAASF,IAE7F,IAAA,MAAWG,KAAeF,EAAiB,CACzC,MAAM1B,EAASS,EAAUmB,GACzB,QAAe,IAAX5B,IAAmC,IAAXA,EAAkB,SAE9C,MAAM6B,EAAS9B,EAAwB6B,EAAa5B,GAChD6B,GACFtB,EAAQS,KAAKa,EAEjB,CAEA,OAAOtB,CACT,CAkBO,SAASuB,IACd1D,EAAgB2D,QAChBzD,EAAeyD,OACjB,CAbAC,EAAmB3B"}
|
|
1
|
+
{"version":3,"file":"registry.js","sources":["../../../../../libs/grid/src/lib/core/internal/diagnostics.ts","../../../../../libs/grid/src/lib/features/registry.ts"],"sourcesContent":["/**\n * Centralized diagnostic messages for @toolbox-web/grid.\n *\n * Every user-facing warning, error, or info message in the grid has a unique\n * diagnostic code (e.g. `TBW001`). Each code maps to a section on the online\n * troubleshooting page, giving developers a direct link to resolution steps.\n *\n * ## Usage\n *\n * ```ts\n * import { MISSING_BREAKPOINT, warnDiagnostic, throwDiagnostic } from './diagnostics';\n *\n * // Warn with a code\n * warnDiagnostic(MISSING_BREAKPOINT, 'Set a breakpoint...', gridId);\n *\n * // Throw with a code\n * throwDiagnostic(MISSING_ROW_ID, 'Configure getRowId...', gridId);\n * ```\n *\n * Plugins should prefer `this.warn(MISSING_BREAKPOINT, message)` via BaseGridPlugin\n * instead of importing this module directly.\n *\n * @internal\n */\n\n// #region Grid Prefix\n\n/**\n * Build the `[tbw-grid]` or `[tbw-grid#my-id]` log prefix.\n * Pass `pluginName` for a scoped prefix like `[tbw-grid:SelectionPlugin]`.\n */\nexport function gridPrefix(gridId?: string, pluginName?: string): string {\n const id = gridId ? `#${gridId}` : '';\n const plugin = pluginName ? `:${pluginName}` : '';\n return `[tbw-grid${id}${plugin}]`;\n}\n\n// #endregion\n\n// #region Diagnostic Codes\n\n/**\n * Diagnostic codes used across the grid library.\n *\n * Each code is an individual export so that unused codes are tree-shaken\n * from bundles that don't reference them (esbuild can't tree-shake\n * properties from a single object).\n *\n * Naming: TBW + 3-digit number.\n * Ranges:\n * 001–019 Configuration validation (missing plugins, bad config)\n * 020–029 Plugin lifecycle (dependencies, incompatibilities, deprecation)\n * 030–039 Feature registry\n * 040–049 Row operations (row ID, row mutations)\n * 050–059 Column operations (width, template)\n * 060–069 Rendering (callbacks, formatters, external views)\n * 070–079 Shell (tool panels, header/toolbar content)\n * 080–089 Editing & editors\n * 090–099 Print\n * 100–109 Clipboard\n * 110–119 Plugin-specific (responsive, undo-redo, grouping-columns)\n * 120–129 Style injection\n * 130–139 Attribute parsing\n */\n\n// --- Config validation (001–019) ---\n/** Column uses a plugin-owned property but the plugin is not loaded. */\nexport const MISSING_PLUGIN = 'TBW001' as const;\n/** Grid config uses a plugin-owned property but the plugin is not loaded. */\nexport const MISSING_PLUGIN_CONFIG = 'TBW002' as const;\n/** Plugin config rule violation (error severity). */\nexport const CONFIG_RULE_ERROR = 'TBW003' as const;\n/** Plugin config rule violation (warning severity). */\nexport const CONFIG_RULE_WARN = 'TBW004' as const;\n\n// --- Plugin lifecycle (020–029) ---\n/** Required plugin dependency is missing. */\nexport const MISSING_DEPENDENCY = 'TBW020' as const;\n/** Optional plugin dependency is missing. */\nexport const OPTIONAL_DEPENDENCY = 'TBW021' as const;\n/** Two loaded plugins are incompatible. */\nexport const INCOMPATIBLE_PLUGINS = 'TBW022' as const;\n/** Plugin uses deprecated hooks. */\nexport const DEPRECATED_HOOK = 'TBW023' as const;\n/** Error thrown inside a plugin event handler. */\nexport const PLUGIN_EVENT_ERROR = 'TBW024' as const;\n\n// --- Feature registry (030–039) ---\n/** Feature was re-registered (overwritten). */\nexport const FEATURE_REREGISTERED = 'TBW030' as const;\n/** Feature configured but not imported. */\nexport const FEATURE_NOT_IMPORTED = 'TBW031' as const;\n/** Feature depends on another feature that is not enabled. */\nexport const FEATURE_MISSING_DEP = 'TBW032' as const;\n\n// --- Row operations (040–049) ---\n/** Cannot determine row ID (no getRowId and no id property). */\nexport const MISSING_ROW_ID = 'TBW040' as const;\n/** Row with given ID not found. */\nexport const ROW_NOT_FOUND = 'TBW041' as const;\n\n// --- Column operations (050–059) ---\n/** Column has an invalid CSS width value. */\nexport const INVALID_COLUMN_WIDTH = 'TBW050' as const;\n\n// --- Rendering callbacks (060–069) ---\n/** rowClass callback threw an error. */\nexport const ROW_CLASS_ERROR = 'TBW060' as const;\n/** cellClass callback threw an error. */\nexport const CELL_CLASS_ERROR = 'TBW061' as const;\n/** Column format function threw an error. */\nexport const FORMAT_ERROR = 'TBW062' as const;\n/** External view mount() threw an error. */\nexport const VIEW_MOUNT_ERROR = 'TBW063' as const;\n/** External view event dispatch error. */\nexport const VIEW_DISPATCH_ERROR = 'TBW064' as const;\n\n// --- Shell (070–079) ---\n/** Tool panel missing required id or title. */\nexport const TOOL_PANEL_MISSING_ATTR = 'TBW070' as const;\n/** No tool panels registered. */\nexport const NO_TOOL_PANELS = 'TBW071' as const;\n/** Tool panel section not found. */\nexport const TOOL_PANEL_NOT_FOUND = 'TBW072' as const;\n/** Tool panel already registered. */\nexport const TOOL_PANEL_DUPLICATE = 'TBW073' as const;\n/** Header content already registered. */\nexport const HEADER_CONTENT_DUPLICATE = 'TBW074' as const;\n/** Toolbar content already registered. */\nexport const TOOLBAR_CONTENT_DUPLICATE = 'TBW075' as const;\n\n// --- Editing & editors (080–089) ---\n/** External editor mount() threw an error. */\nexport const EDITOR_MOUNT_ERROR = 'TBW080' as const;\n\n// --- Print (090–099) ---\n/** Print already in progress. */\nexport const PRINT_IN_PROGRESS = 'TBW090' as const;\n/** Grid not available for printing. */\nexport const PRINT_NO_GRID = 'TBW091' as const;\n/** Print operation failed. */\nexport const PRINT_FAILED = 'TBW092' as const;\n/** Multiple elements share the same grid ID (print isolation issue). */\nexport const PRINT_DUPLICATE_ID = 'TBW093' as const;\n\n// --- Clipboard (100–109) ---\n/** Clipboard API write failed. */\nexport const CLIPBOARD_FAILED = 'TBW100' as const;\n\n// --- Plugin-specific (110–119) ---\n/** ResponsivePlugin: no breakpoint configured. */\nexport const MISSING_BREAKPOINT = 'TBW110' as const;\n/** UndoRedoPlugin: transaction already in progress. */\nexport const TRANSACTION_IN_PROGRESS = 'TBW111' as const;\n/** UndoRedoPlugin: no transaction in progress. */\nexport const NO_TRANSACTION = 'TBW112' as const;\n/** GroupingColumnsPlugin: missing id or header on column group definition. */\nexport const COLUMN_GROUP_NO_ID = 'TBW113' as const;\n/** GroupingColumnsPlugin: conflicting columnGroups sources. */\nexport const COLUMN_GROUPS_CONFLICT = 'TBW114' as const;\n\n// --- Style injection (120–129) ---\n/** Failed to extract grid.css from document stylesheets. */\nexport const STYLE_EXTRACT_FAILED = 'TBW120' as const;\n/** Could not find grid.css in document.styleSheets. */\nexport const STYLE_NOT_FOUND = 'TBW121' as const;\n\n// --- Attribute parsing (130–139) ---\n/** Invalid JSON in an HTML attribute. */\nexport const INVALID_ATTRIBUTE_JSON = 'TBW130' as const;\n\nexport type DiagnosticCode =\n | typeof MISSING_PLUGIN\n | typeof MISSING_PLUGIN_CONFIG\n | typeof CONFIG_RULE_ERROR\n | typeof CONFIG_RULE_WARN\n | typeof MISSING_DEPENDENCY\n | typeof OPTIONAL_DEPENDENCY\n | typeof INCOMPATIBLE_PLUGINS\n | typeof DEPRECATED_HOOK\n | typeof PLUGIN_EVENT_ERROR\n | typeof FEATURE_REREGISTERED\n | typeof FEATURE_NOT_IMPORTED\n | typeof FEATURE_MISSING_DEP\n | typeof MISSING_ROW_ID\n | typeof ROW_NOT_FOUND\n | typeof INVALID_COLUMN_WIDTH\n | typeof ROW_CLASS_ERROR\n | typeof CELL_CLASS_ERROR\n | typeof FORMAT_ERROR\n | typeof VIEW_MOUNT_ERROR\n | typeof VIEW_DISPATCH_ERROR\n | typeof TOOL_PANEL_MISSING_ATTR\n | typeof NO_TOOL_PANELS\n | typeof TOOL_PANEL_NOT_FOUND\n | typeof TOOL_PANEL_DUPLICATE\n | typeof HEADER_CONTENT_DUPLICATE\n | typeof TOOLBAR_CONTENT_DUPLICATE\n | typeof EDITOR_MOUNT_ERROR\n | typeof PRINT_IN_PROGRESS\n | typeof PRINT_NO_GRID\n | typeof PRINT_FAILED\n | typeof PRINT_DUPLICATE_ID\n | typeof CLIPBOARD_FAILED\n | typeof MISSING_BREAKPOINT\n | typeof TRANSACTION_IN_PROGRESS\n | typeof NO_TRANSACTION\n | typeof COLUMN_GROUP_NO_ID\n | typeof COLUMN_GROUPS_CONFLICT\n | typeof STYLE_EXTRACT_FAILED\n | typeof STYLE_NOT_FOUND\n | typeof INVALID_ATTRIBUTE_JSON;\n\n// #endregion\n\n// #region Docs URL\n\nconst DOCS_BASE = 'https://toolboxjs.com/grid/errors';\n\n/** Build a direct link to the troubleshooting section for a code. */\nfunction docsUrl(code: DiagnosticCode): string {\n return `${DOCS_BASE}#${code.toLowerCase()}`;\n}\n\n// #endregion\n\n// #region Formatting\n\n/**\n * Format a diagnostic message with prefix, code, and docs link.\n *\n * Output format:\n * ```\n * [tbw-grid#my-id] TBW001: Your message here.\n *\n * → More info: https://toolboxjs.com/grid/errors#tbw001\n * ```\n */\nexport function formatDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): string {\n const prefix = gridPrefix(gridId, pluginName);\n return `${prefix} ${code}: ${message}\\n\\n → More info: ${docsUrl(code)}`;\n}\n\n// #endregion\n\n// #region Public API\n\n/**\n * Throw an error with a diagnostic code and docs link.\n * Use for configuration errors and API misuse that should halt execution.\n */\nexport function throwDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): never {\n throw new Error(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log a warning with a diagnostic code and docs link.\n * Use for recoverable issues the developer should fix.\n */\nexport function warnDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.warn(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log a debug message with a diagnostic code and docs link.\n * Use for optional/soft dependency notifications — visible only when\n * the browser DevTools \"Verbose\" log level is enabled.\n */\nexport function debugDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.debug(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n/**\n * Log an error with a diagnostic code and docs link.\n * Use for non-throwing errors (e.g., failed async operations).\n */\nexport function errorDiagnostic(code: DiagnosticCode, message: string, gridId?: string, pluginName?: string): void {\n console.error(formatDiagnostic(code, message, gridId, pluginName));\n}\n\n// #endregion\n","/**\n * Core Feature Registry for @toolbox-web/grid\n *\n * This module provides a framework-agnostic registry for plugin factories.\n * Features are registered via side-effect imports, enabling tree-shaking\n * while maintaining a clean declarative API.\n *\n * @example\n * ```typescript\n * // Import features you need (side-effect imports)\n * import '@toolbox-web/grid/features/selection';\n * import '@toolbox-web/grid/features/filtering';\n *\n * // Configure grid declaratively\n * grid.gridConfig = {\n * features: {\n * selection: 'range',\n * filtering: { debounceMs: 200 },\n * },\n * };\n * ```\n *\n * @packageDocumentation\n * @module Features\n */\n\nimport {\n FEATURE_MISSING_DEP,\n FEATURE_NOT_IMPORTED,\n FEATURE_REREGISTERED,\n warnDiagnostic,\n} from '../core/internal/diagnostics';\nimport { setFeatureResolver } from '../core/internal/feature-hook';\nimport type { FeatureConfig, GridPlugin } from '../core/types';\n\n// #region Types\n\n/** Feature name — keys of the augmented FeatureConfig interface. */\nexport type FeatureName = keyof FeatureConfig;\n\n/** Factory function that creates a plugin from a feature config value. */\nexport type PluginFactory<TConfig = unknown> = (config: TConfig) => GridPlugin;\n\ninterface RegistryEntry {\n factory: PluginFactory;\n name: string;\n}\n\n// #endregion\n\n// #region Registry State\n\nconst featureRegistry = new Map<string, RegistryEntry>();\nconst warnedFeatures = new Set<string>();\n\n// #endregion\n\n// #region Registration API\n\n/** Runtime dev-mode check (localhost or 127.0.0.1). */\nconst isDev = (): boolean =>\n typeof window !== 'undefined' &&\n (window.location?.hostname === 'localhost' || window.location?.hostname === '127.0.0.1');\n\n/**\n * Register a feature's plugin factory.\n * Called by side-effect feature imports (e.g., `import '@toolbox-web/grid/features/selection'`).\n *\n * @param name - The feature name (matches a key on FeatureConfig)\n * @param factory - Function that creates a plugin instance from config\n */\nexport function registerFeature<K extends FeatureName>(name: K, factory: PluginFactory<FeatureConfig[K]>): void;\nexport function registerFeature(name: string, factory: PluginFactory): void;\nexport function registerFeature(name: string, factory: PluginFactory): void {\n if (isDev() && featureRegistry.has(name)) {\n warnDiagnostic(FEATURE_REREGISTERED, `Feature \"${name}\" was re-registered. Previous registration overwritten.`);\n }\n featureRegistry.set(name, { factory, name });\n}\n\n/**\n * Check if a feature has been registered.\n */\nexport function isFeatureRegistered(name: string): boolean {\n return featureRegistry.has(name);\n}\n\n/**\n * Get a registered feature's factory. Returns undefined if not registered.\n */\nexport function getFeatureFactory(name: string): PluginFactory | undefined {\n return featureRegistry.get(name)?.factory;\n}\n\n/**\n * Get all registered feature names.\n */\nexport function getRegisteredFeatures(): string[] {\n return Array.from(featureRegistry.keys());\n}\n\n// #endregion\n\n// #region Plugin Creation\n\n/**\n * Plugin dependency declarations.\n * Some plugins require others to be loaded first.\n */\nconst PLUGIN_DEPENDENCIES: Record<string, string[]> = {\n undoRedo: ['editing'],\n clipboard: ['selection'],\n};\n\n/**\n * Deprecated alias mappings.\n * When both alias and primary are set, primary takes precedence.\n */\nconst FEATURE_ALIASES: Record<string, string> = {\n sorting: 'multiSort',\n reorder: 'reorderColumns',\n rowReorder: 'reorderRows',\n};\n\n/**\n * Create a plugin instance for a single feature.\n * Shows a warning if the feature is not registered.\n */\nexport function createPluginFromFeature(name: string, config: unknown): GridPlugin | undefined {\n const entry = featureRegistry.get(name);\n\n if (!entry) {\n if (isDev() && !warnedFeatures.has(name)) {\n warnedFeatures.add(name);\n const kebab = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n warnDiagnostic(\n FEATURE_NOT_IMPORTED,\n `Feature \"${name}\" is configured but not registered.\\n` +\n `Add this import to enable it:\\n\\n` +\n ` import '@toolbox-web/grid/features/${kebab}';\\n`,\n );\n }\n return undefined;\n }\n\n return entry.factory(config);\n}\n\n/**\n * Validate feature dependencies and log warnings for missing ones.\n */\nfunction validateDependencies(featureNames: string[]): void {\n const featureSet = new Set(featureNames);\n\n for (const feature of featureNames) {\n const deps = PLUGIN_DEPENDENCIES[feature];\n if (!deps) continue;\n\n for (const dep of deps) {\n if (!featureSet.has(dep)) {\n if (isDev()) {\n warnDiagnostic(\n FEATURE_MISSING_DEP,\n `Feature \"${feature}\" requires \"${dep}\" to be enabled. ` + `Add \"${dep}\" to your features configuration.`,\n );\n }\n }\n }\n }\n}\n\n/**\n * Create plugin instances from a features configuration object.\n *\n * Handles:\n * - Deprecated alias resolution (sorting → multiSort, etc.)\n * - Dependency validation (clipboard needs selection)\n * - Dependency ordering (selection before clipboard)\n * - Skipping false/undefined values\n *\n * @param features - Partial FeatureConfig object\n * @returns Array of plugin instances ready for gridConfig.plugins\n */\nexport function createPluginsFromFeatures(features: Record<string, unknown>): GridPlugin[] {\n const plugins: GridPlugin[] = [];\n const enabledFeatures: string[] = [];\n\n // Resolve deprecated aliases: use primary name, skip alias if primary is set\n const effective: Record<string, unknown> = { ...features };\n for (const [alias, primary] of Object.entries(FEATURE_ALIASES)) {\n if (effective[alias] !== undefined && effective[primary] === undefined) {\n effective[primary] = effective[alias];\n }\n delete effective[alias];\n }\n\n // Collect enabled feature names\n for (const [key, value] of Object.entries(effective)) {\n if (value === undefined || value === false) continue;\n enabledFeatures.push(key);\n }\n\n // Validate dependencies\n validateDependencies(enabledFeatures);\n\n // Create plugins in dependency order: dep-targets first, then the rest\n const dependencyOrder: string[] = [\n 'selection',\n 'editing',\n ...enabledFeatures.filter((f) => f !== 'selection' && f !== 'editing'),\n ];\n const orderedFeatures = [...new Set(dependencyOrder)].filter((f) => enabledFeatures.includes(f));\n\n for (const featureName of orderedFeatures) {\n const config = effective[featureName];\n if (config === undefined || config === false) continue;\n\n const plugin = createPluginFromFeature(featureName, config);\n if (plugin) {\n plugins.push(plugin);\n }\n }\n\n return plugins;\n}\n\n// #endregion\n\n// #region Auto-Registration\n\n// Wire feature resolver into grid core so `gridConfig.features` is handled automatically.\n// This runs when any feature module is imported (they all import this registry).\nsetFeatureResolver(createPluginsFromFeatures as (features: Record<string, unknown>) => GridPlugin[]);\n\n// #endregion\n\n// #region Testing Utilities\n\n/**\n * Clear the registry. For testing only.\n * @internal\n */\nexport function clearFeatureRegistry(): void {\n featureRegistry.clear();\n warnedFeatures.clear();\n}\n\n// #endregion\n"],"names":["warnDiagnostic","code","message","gridId","pluginName","console","warn","toLowerCase","docsUrl","formatDiagnostic","featureRegistry","Map","warnedFeatures","Set","isDev","window","location","hostname","registerFeature","name","factory","has","set","isFeatureRegistered","getFeatureFactory","get","getRegisteredFeatures","Array","from","keys","PLUGIN_DEPENDENCIES","undoRedo","clipboard","FEATURE_ALIASES","sorting","reorder","rowReorder","createPluginFromFeature","config","entry","add","kebab","replace","createPluginsFromFeatures","features","plugins","enabledFeatures","effective","alias","primary","Object","entries","key","value","push","featureNames","featureSet","feature","deps","dep","validateDependencies","dependencyOrder","filter","f","orderedFeatures","includes","featureName","plugin","clearFeatureRegistry","clear","setFeatureResolver"],"mappings":"uDAmQO,SAASA,EAAeC,EAAsBC,EAAiBC,EAAiBC,GACrFC,QAAQC,KAtBH,SAA0BL,EAAsBC,GAErD,MAAO,cAAaD,MAASC,uBApB/B,SAAiBD,GACf,MAAO,qCAAgBA,EAAKM,eAC9B,CAkB4DC,CAAQP,IACpE,CAmBeQ,CAAiBR,EAAMC,GACtC,CCjNA,MAAMQ,qBAAsBC,IACtBC,qBAAqBC,IAOrBC,EAAQ,IACM,oBAAXC,SACwB,cAA9BA,OAAOC,UAAUC,UAA0D,cAA9BF,OAAOC,UAAUC,UAW1D,SAASC,EAAgBC,EAAcC,GACxCN,KAAWJ,EAAgBW,IAAIF,IACjCnB,EDcgC,SCdK,YAAYmB,4DAEnDT,EAAgBY,IAAIH,EAAM,CAAEC,UAASD,QACvC,CAKO,SAASI,EAAoBJ,GAClC,OAAOT,EAAgBW,IAAIF,EAC7B,CAKO,SAASK,EAAkBL,GAChC,OAAOT,EAAgBe,IAAIN,IAAOC,OACpC,CAKO,SAASM,IACd,OAAOC,MAAMC,KAAKlB,EAAgBmB,OACpC,CAUA,MAAMC,EAAgD,CACpDC,SAAU,CAAC,WACXC,UAAW,CAAC,cAORC,EAA0C,CAC9CC,QAAS,YACTC,QAAS,iBACTC,WAAY,eAOP,SAASC,EAAwBlB,EAAcmB,GACpD,MAAMC,EAAQ7B,EAAgBe,IAAIN,GAElC,GAAKoB,EAcL,OAAOA,EAAMnB,QAAQkB,GAbnB,GAAIxB,MAAYF,EAAeS,IAAIF,GAAO,CACxCP,EAAe4B,IAAIrB,GACnB,MAAMsB,EAAQtB,EAAKuB,QAAQ,kBAAmB,SAASnC,cACvDP,ED5C8B,SC8C5B,YAAYmB,+GAE8BsB,QAE9C,CAKJ,CAqCO,SAASE,EAA0BC,GACxC,MAAMC,EAAwB,GACxBC,EAA4B,GAG5BC,EAAqC,IAAKH,GAChD,IAAA,MAAYI,EAAOC,KAAYC,OAAOC,QAAQlB,YACxCc,EAAUC,SAA+C,IAAvBD,EAAUE,KAC9CF,EAAUE,GAAWF,EAAUC,WAE1BD,EAAUC,GAInB,IAAA,MAAYI,EAAKC,KAAUH,OAAOC,QAAQJ,QAC1B,IAAVM,IAAiC,IAAVA,GAC3BP,EAAgBQ,KAAKF,IAhDzB,SAA8BG,GAC5B,MAAMC,EAAa,IAAI3C,IAAI0C,GAE3B,IAAA,MAAWE,KAAWF,EAAc,CAClC,MAAMG,EAAO5B,EAAoB2B,GACjC,GAAKC,EAEL,IAAA,MAAWC,KAAOD,EACXF,EAAWnC,IAAIsC,IACd7C,KACFd,EDpEyB,SCsEvB,YAAYyD,gBAAsBE,0BAAiCA,qCAK7E,CACF,CAkCEC,CAAqBd,GAGrB,MAAMe,EAA4B,CAChC,YACA,aACGf,EAAgBgB,OAAQC,GAAY,cAANA,GAA2B,YAANA,IAElDC,EAAkB,IAAI,IAAInD,IAAIgD,IAAkBC,OAAQC,GAAMjB,EAAgBmB,SAASF,IAE7F,IAAA,MAAWG,KAAeF,EAAiB,CACzC,MAAM1B,EAASS,EAAUmB,GACzB,QAAe,IAAX5B,IAAmC,IAAXA,EAAkB,SAE9C,MAAM6B,EAAS9B,EAAwB6B,EAAa5B,GAChD6B,GACFtB,EAAQS,KAAKa,EAEjB,CAEA,OAAOtB,CACT,CAkBO,SAASuB,IACd1D,EAAgB2D,QAChBzD,EAAeyD,OACjB,CAbAC,EAAmB3B"}
|
|
@@ -87,7 +87,7 @@ import { ClipboardConfig, CopyOptions } from './types';
|
|
|
87
87
|
* ```
|
|
88
88
|
*
|
|
89
89
|
* @see {@link ClipboardConfig} for all configuration options
|
|
90
|
-
* @see
|
|
90
|
+
* @see SelectionPlugin for enhanced copy/paste with selection
|
|
91
91
|
*
|
|
92
92
|
* @internal Extends BaseGridPlugin
|
|
93
93
|
*/
|