mvc-kit 2.8.0 → 2.9.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.
Files changed (100) hide show
  1. package/README.md +29 -0
  2. package/agent-config/claude-code/skills/guide/anti-patterns.md +3 -3
  3. package/agent-config/claude-code/skills/guide/api-reference.md +138 -1
  4. package/agent-config/claude-code/skills/guide/patterns.md +120 -0
  5. package/agent-config/copilot/copilot-instructions.md +52 -0
  6. package/agent-config/cursor/cursorrules +52 -0
  7. package/dist/Collection.cjs +38 -0
  8. package/dist/Collection.cjs.map +1 -1
  9. package/dist/Collection.d.ts.map +1 -1
  10. package/dist/Collection.js +38 -0
  11. package/dist/Collection.js.map +1 -1
  12. package/dist/Feed.cjs +86 -0
  13. package/dist/Feed.cjs.map +1 -0
  14. package/dist/Feed.d.ts +46 -0
  15. package/dist/Feed.d.ts.map +1 -0
  16. package/dist/Feed.js +86 -0
  17. package/dist/Feed.js.map +1 -0
  18. package/dist/Pagination.cjs +84 -0
  19. package/dist/Pagination.cjs.map +1 -0
  20. package/dist/Pagination.d.ts +39 -0
  21. package/dist/Pagination.d.ts.map +1 -0
  22. package/dist/Pagination.js +84 -0
  23. package/dist/Pagination.js.map +1 -0
  24. package/dist/PersistentCollection.cjs +8 -5
  25. package/dist/PersistentCollection.cjs.map +1 -1
  26. package/dist/PersistentCollection.d.ts +6 -1
  27. package/dist/PersistentCollection.d.ts.map +1 -1
  28. package/dist/PersistentCollection.js +8 -5
  29. package/dist/PersistentCollection.js.map +1 -1
  30. package/dist/Resource.cjs +3 -0
  31. package/dist/Resource.cjs.map +1 -1
  32. package/dist/Resource.d.ts +3 -0
  33. package/dist/Resource.d.ts.map +1 -1
  34. package/dist/Resource.js +3 -0
  35. package/dist/Resource.js.map +1 -1
  36. package/dist/Selection.cjs +99 -0
  37. package/dist/Selection.cjs.map +1 -0
  38. package/dist/Selection.d.ts +36 -0
  39. package/dist/Selection.d.ts.map +1 -0
  40. package/dist/Selection.js +99 -0
  41. package/dist/Selection.js.map +1 -0
  42. package/dist/Sorting.cjs +114 -0
  43. package/dist/Sorting.cjs.map +1 -0
  44. package/dist/Sorting.d.ts +43 -0
  45. package/dist/Sorting.d.ts.map +1 -0
  46. package/dist/Sorting.js +114 -0
  47. package/dist/Sorting.js.map +1 -0
  48. package/dist/index.d.ts +6 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/mvc-kit.cjs +8 -0
  51. package/dist/mvc-kit.cjs.map +1 -1
  52. package/dist/mvc-kit.js +8 -0
  53. package/dist/mvc-kit.js.map +1 -1
  54. package/dist/react/components/CardList.cjs +42 -0
  55. package/dist/react/components/CardList.cjs.map +1 -0
  56. package/dist/react/components/CardList.d.ts +22 -0
  57. package/dist/react/components/CardList.d.ts.map +1 -0
  58. package/dist/react/components/CardList.js +42 -0
  59. package/dist/react/components/CardList.js.map +1 -0
  60. package/dist/react/components/DataTable.cjs +179 -0
  61. package/dist/react/components/DataTable.cjs.map +1 -0
  62. package/dist/react/components/DataTable.d.ts +30 -0
  63. package/dist/react/components/DataTable.d.ts.map +1 -0
  64. package/dist/react/components/DataTable.js +179 -0
  65. package/dist/react/components/DataTable.js.map +1 -0
  66. package/dist/react/components/InfiniteScroll.cjs +44 -0
  67. package/dist/react/components/InfiniteScroll.cjs.map +1 -0
  68. package/dist/react/components/InfiniteScroll.d.ts +21 -0
  69. package/dist/react/components/InfiniteScroll.d.ts.map +1 -0
  70. package/dist/react/components/InfiniteScroll.js +44 -0
  71. package/dist/react/components/InfiniteScroll.js.map +1 -0
  72. package/dist/react/components/types.cjs +15 -0
  73. package/dist/react/components/types.cjs.map +1 -0
  74. package/dist/react/components/types.d.ts +71 -0
  75. package/dist/react/components/types.d.ts.map +1 -0
  76. package/dist/react/components/types.js +15 -0
  77. package/dist/react/components/types.js.map +1 -0
  78. package/dist/react/index.d.ts +7 -0
  79. package/dist/react/index.d.ts.map +1 -1
  80. package/dist/react-native/NativeCollection.cjs +3 -0
  81. package/dist/react-native/NativeCollection.cjs.map +1 -1
  82. package/dist/react-native/NativeCollection.d.ts +3 -0
  83. package/dist/react-native/NativeCollection.d.ts.map +1 -1
  84. package/dist/react-native/NativeCollection.js +3 -0
  85. package/dist/react-native/NativeCollection.js.map +1 -1
  86. package/dist/react.cjs +6 -0
  87. package/dist/react.cjs.map +1 -1
  88. package/dist/react.js +6 -0
  89. package/dist/react.js.map +1 -1
  90. package/dist/web/idb.cjs.map +1 -1
  91. package/dist/web/idb.d.ts +18 -0
  92. package/dist/web/idb.d.ts.map +1 -1
  93. package/dist/web/idb.js.map +1 -1
  94. package/dist/wrapAsyncMethods.cjs +21 -41
  95. package/dist/wrapAsyncMethods.cjs.map +1 -1
  96. package/dist/wrapAsyncMethods.d.ts +2 -0
  97. package/dist/wrapAsyncMethods.d.ts.map +1 -1
  98. package/dist/wrapAsyncMethods.js +21 -41
  99. package/dist/wrapAsyncMethods.js.map +1 -1
  100. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sorting.js","sources":["../src/Sorting.ts"],"sourcesContent":["/** Describes a single sort column with key and direction. */\nexport interface SortDescriptor {\n key: string;\n direction: 'asc' | 'desc';\n}\n\n/**\n * Multi-column sort state manager with a comparator pipeline.\n * Maintains an ordered list of sort descriptors and applies them to arrays.\n * Subscribable — auto-tracked when used as a ViewModel property.\n */\nexport class Sorting<T = any> {\n private _sorts: readonly SortDescriptor[];\n private _listeners = new Set<() => void>();\n\n constructor(options?: { sorts?: SortDescriptor[] }) {\n this._sorts = Object.freeze(options?.sorts?.map(s => ({ ...s })) ?? []);\n }\n\n // ── Readable state ──\n\n /** Current list of active sort descriptors, in priority order. */\n get sorts(): readonly SortDescriptor[] {\n return this._sorts;\n }\n\n /** Primary sort key (first descriptor), or null when empty. */\n get key(): string | null {\n return this._sorts.length > 0 ? this._sorts[0].key : null;\n }\n\n /** Primary sort direction. Defaults to 'asc' when empty. */\n get direction(): 'asc' | 'desc' {\n return this._sorts.length > 0 ? this._sorts[0].direction : 'asc';\n }\n\n // ── Query ──\n\n /** Whether the given key is currently sorted. */\n isSorted(key: string): boolean {\n return this._sorts.some(s => s.key === key);\n }\n\n /** Returns the sort direction for a key, or null if not sorted. */\n directionOf(key: string): 'asc' | 'desc' | null {\n const found = this._sorts.find(s => s.key === key);\n return found ? found.direction : null;\n }\n\n /** Returns the priority index of a sorted key, or -1 if not sorted. */\n indexOf(key: string): number {\n return this._sorts.findIndex(s => s.key === key);\n }\n\n // ── Actions ──\n\n /** 3-click cycle: not sorted → asc → desc → removed. */\n toggle(key: string): void {\n const idx = this.indexOf(key);\n if (idx === -1) {\n // Add as asc\n this._sorts = Object.freeze([...this._sorts, { key, direction: 'asc' as const }]);\n } else if (this._sorts[idx].direction === 'asc') {\n // Flip to desc\n const next = this._sorts.map((s, i) =>\n i === idx ? { key: s.key, direction: 'desc' as const } : s\n );\n this._sorts = Object.freeze(next);\n } else {\n // Remove\n this._sorts = Object.freeze(this._sorts.filter((_, i) => i !== idx));\n }\n this._notify();\n }\n\n /** Replace all with a single sort. */\n setSort(key: string, direction: 'asc' | 'desc'): void {\n this._sorts = Object.freeze([{ key, direction }]);\n this._notify();\n }\n\n /** Replace all sorts. */\n setSorts(sorts: SortDescriptor[]): void {\n this._sorts = Object.freeze(sorts.map(s => ({ ...s })));\n this._notify();\n }\n\n /** Clear all sort descriptors. */\n reset(): void {\n this._sorts = Object.freeze([]);\n this._notify();\n }\n\n // ── Pipeline ──\n\n /** Sort an array using the current descriptors. Returns a new sorted array. */\n apply(\n items: T[],\n compareFn?: (a: T, b: T, key: string, dir: 'asc' | 'desc') => number,\n ): T[] {\n if (this._sorts.length === 0) return items;\n const sorted = items.slice();\n const sorts = this._sorts;\n sorted.sort((a, b) => {\n for (const { key, direction } of sorts) {\n let cmp: number;\n if (compareFn) {\n cmp = compareFn(a, b, key, direction);\n } else {\n cmp = defaultCompare(a, b, key);\n }\n if (direction === 'desc') cmp = -cmp;\n if (cmp !== 0) return cmp;\n }\n return 0;\n });\n return sorted;\n }\n\n // ── Subscribable interface ──\n\n /** Subscribe to sort state changes. Returns an unsubscribe function. */\n subscribe(cb: () => void): () => void {\n this._listeners.add(cb);\n return () => { this._listeners.delete(cb); };\n }\n\n private _notify(): void {\n for (const cb of this._listeners) cb();\n }\n}\n\nfunction defaultCompare(a: any, b: any, key: string): number {\n const aVal = a[key];\n const bVal = b[key];\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return -1;\n if (bVal == null) return 1;\n if (typeof aVal === 'string' && typeof bVal === 'string') {\n return aVal.localeCompare(bVal);\n }\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,6 +11,12 @@ export { Service } from './Service';
11
11
  export { EventBus } from './EventBus';
12
12
  export { Channel } from './Channel';
13
13
  export type { ChannelStatus } from './Channel';
14
+ export { Sorting } from './Sorting';
15
+ export type { SortDescriptor } from './Sorting';
16
+ export { Pagination } from './Pagination';
17
+ export { Selection } from './Selection';
18
+ export { Feed } from './Feed';
19
+ export type { FeedPage } from './Feed';
14
20
  export type { AppError } from './errors';
15
21
  export { HttpError, isAbortError, classifyError } from './errors';
16
22
  export { singleton, hasSingleton, teardown, teardownAll } from './singleton';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGvC,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/mvc-kit.cjs CHANGED
@@ -9,6 +9,10 @@ const Controller = require("./Controller.cjs");
9
9
  const Service = require("./Service.cjs");
10
10
  const EventBus = require("./EventBus.cjs");
11
11
  const Channel = require("./Channel.cjs");
12
+ const Sorting = require("./Sorting.cjs");
13
+ const Pagination = require("./Pagination.cjs");
14
+ const Selection = require("./Selection.cjs");
15
+ const Feed = require("./Feed.cjs");
12
16
  const errors = require("./errors.cjs");
13
17
  const singleton = require("./singleton.cjs");
14
18
  exports.ViewModel = ViewModel.ViewModel;
@@ -20,6 +24,10 @@ exports.Controller = Controller.Controller;
20
24
  exports.Service = Service.Service;
21
25
  exports.EventBus = EventBus.EventBus;
22
26
  exports.Channel = Channel.Channel;
27
+ exports.Sorting = Sorting.Sorting;
28
+ exports.Pagination = Pagination.Pagination;
29
+ exports.Selection = Selection.Selection;
30
+ exports.Feed = Feed.Feed;
23
31
  exports.HttpError = errors.HttpError;
24
32
  exports.classifyError = errors.classifyError;
25
33
  exports.isAbortError = errors.isAbortError;
@@ -1 +1 @@
1
- {"version":3,"file":"mvc-kit.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"mvc-kit.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/mvc-kit.js CHANGED
@@ -7,6 +7,10 @@ import { Controller } from "./Controller.js";
7
7
  import { Service } from "./Service.js";
8
8
  import { EventBus } from "./EventBus.js";
9
9
  import { Channel } from "./Channel.js";
10
+ import { Sorting } from "./Sorting.js";
11
+ import { Pagination } from "./Pagination.js";
12
+ import { Selection } from "./Selection.js";
13
+ import { Feed } from "./Feed.js";
10
14
  import { HttpError, classifyError, isAbortError } from "./errors.js";
11
15
  import { hasSingleton, singleton, teardown, teardownAll } from "./singleton.js";
12
16
  export {
@@ -14,11 +18,15 @@ export {
14
18
  Collection,
15
19
  Controller,
16
20
  EventBus,
21
+ Feed,
17
22
  HttpError,
18
23
  Model,
24
+ Pagination,
19
25
  PersistentCollection,
20
26
  Resource,
27
+ Selection,
21
28
  Service,
29
+ Sorting,
22
30
  ViewModel,
23
31
  classifyError,
24
32
  hasSingleton,
@@ -1 +1 @@
1
- {"version":3,"file":"mvc-kit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;"}
1
+ {"version":3,"file":"mvc-kit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const defaultKeyOf = (item) => item.id;
5
+ function CardList({
6
+ items,
7
+ renderItem,
8
+ keyOf = defaultKeyOf,
9
+ layout = "list",
10
+ columns = 3,
11
+ gap = "1rem",
12
+ loading,
13
+ error,
14
+ renderEmpty,
15
+ renderLoading,
16
+ renderError,
17
+ className,
18
+ "aria-label": ariaLabel
19
+ }) {
20
+ if (loading && renderLoading) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderLoading() });
21
+ if (error && renderError) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderError(error) });
22
+ if (items.length === 0 && renderEmpty) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderEmpty() });
23
+ const style = layout === "grid" ? {
24
+ display: "grid",
25
+ gridTemplateColumns: `repeat(var(--card-list-columns, ${columns}), 1fr)`,
26
+ gap: `var(--card-list-gap, ${gap})`
27
+ } : void 0;
28
+ return /* @__PURE__ */ jsxRuntime.jsx(
29
+ "ul",
30
+ {
31
+ role: "list",
32
+ "data-component": "card-list",
33
+ "data-layout": layout,
34
+ className,
35
+ "aria-label": ariaLabel,
36
+ style,
37
+ children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { "data-index": index, children: renderItem(item, index) }, keyOf(item)))
38
+ }
39
+ );
40
+ }
41
+ exports.CardList = CardList;
42
+ //# sourceMappingURL=CardList.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CardList.cjs","sources":["../../../src/react/components/CardList.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { AsyncStateProps } from './types';\n\n/** Props for the CardList headless component. */\nexport interface CardListProps<T> extends AsyncStateProps {\n items: T[];\n renderItem: (item: T, index: number) => ReactNode;\n keyOf?: (item: T) => string | number;\n layout?: 'list' | 'grid';\n columns?: number;\n gap?: string;\n renderEmpty?: () => ReactNode;\n renderLoading?: () => ReactNode;\n renderError?: (error: string) => ReactNode;\n className?: string;\n 'aria-label'?: string;\n}\n\nconst defaultKeyOf = (item: any) => item.id;\n\n/**\n * Headless list/grid component with render-prop items.\n * Renders a semantic `<ul>` with optional CSS grid layout.\n */\nexport function CardList<T>({\n items,\n renderItem,\n keyOf = defaultKeyOf,\n layout = 'list',\n columns = 3,\n gap = '1rem',\n loading,\n error,\n renderEmpty,\n renderLoading,\n renderError,\n className,\n 'aria-label': ariaLabel,\n}: CardListProps<T>) {\n if (loading && renderLoading) return <>{renderLoading()}</>;\n if (error && renderError) return <>{renderError(error)}</>;\n if (items.length === 0 && renderEmpty) return <>{renderEmpty()}</>;\n\n const style = layout === 'grid'\n ? {\n display: 'grid',\n gridTemplateColumns: `repeat(var(--card-list-columns, ${columns}), 1fr)`,\n gap: `var(--card-list-gap, ${gap})`,\n } as const\n : undefined;\n\n return (\n <ul\n role=\"list\"\n data-component=\"card-list\"\n data-layout={layout}\n className={className}\n aria-label={ariaLabel}\n style={style}\n >\n {items.map((item, index) => (\n <li key={keyOf(item)} data-index={index}>\n {renderItem(item, index)}\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["jsx","Fragment"],"mappings":";;;AAkBA,MAAM,eAAe,CAAC,SAAc,KAAK;AAMlC,SAAS,SAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAqB;AACnB,MAAI,WAAW,cAAe,QAAOA,2BAAAA,IAAAC,WAAAA,UAAA,EAAG,2BAAgB;AACxD,MAAI,SAAS,YAAa,QAAOD,+BAAAC,WAAAA,UAAA,EAAG,UAAA,YAAY,KAAK,GAAE;AACvD,MAAI,MAAM,WAAW,KAAK,YAAa,QAAOD,+BAAAC,WAAAA,UAAA,EAAG,yBAAc;AAE/D,QAAM,QAAQ,WAAW,SACrB;AAAA,IACE,SAAS;AAAA,IACT,qBAAqB,mCAAmC,OAAO;AAAA,IAC/D,KAAK,wBAAwB,GAAG;AAAA,EAAA,IAElC;AAEJ,SACED,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,kBAAe;AAAA,MACf,eAAa;AAAA,MACb;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MAEC,gBAAM,IAAI,CAAC,MAAM,yCACf,MAAA,EAAqB,cAAY,OAC/B,UAAA,WAAW,MAAM,KAAK,KADhB,MAAM,IAAI,CAEnB,CACD;AAAA,IAAA;AAAA,EAAA;AAGP;;"}
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { AsyncStateProps } from './types';
3
+ /** Props for the CardList headless component. */
4
+ export interface CardListProps<T> extends AsyncStateProps {
5
+ items: T[];
6
+ renderItem: (item: T, index: number) => ReactNode;
7
+ keyOf?: (item: T) => string | number;
8
+ layout?: 'list' | 'grid';
9
+ columns?: number;
10
+ gap?: string;
11
+ renderEmpty?: () => ReactNode;
12
+ renderLoading?: () => ReactNode;
13
+ renderError?: (error: string) => ReactNode;
14
+ className?: string;
15
+ 'aria-label'?: string;
16
+ }
17
+ /**
18
+ * Headless list/grid component with render-prop items.
19
+ * Renders a semantic `<ul>` with optional CSS grid layout.
20
+ */
21
+ export declare function CardList<T>({ items, renderItem, keyOf, layout, columns, gap, loading, error, renderEmpty, renderLoading, renderError, className, 'aria-label': ariaLabel, }: CardListProps<T>): import("react/jsx-runtime").JSX.Element;
22
+ //# sourceMappingURL=CardList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CardList.d.ts","sourceRoot":"","sources":["../../../src/react/components/CardList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,iDAAiD;AACjD,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,eAAe;IACvD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAClD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,SAAS,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAC1B,KAAK,EACL,UAAU,EACV,KAAoB,EACpB,MAAe,EACf,OAAW,EACX,GAAY,EACZ,OAAO,EACP,KAAK,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,SAAS,EACT,YAAY,EAAE,SAAS,GACxB,EAAE,aAAa,CAAC,CAAC,CAAC,2CA6BlB"}
@@ -0,0 +1,42 @@
1
+ import { jsx, Fragment } from "react/jsx-runtime";
2
+ const defaultKeyOf = (item) => item.id;
3
+ function CardList({
4
+ items,
5
+ renderItem,
6
+ keyOf = defaultKeyOf,
7
+ layout = "list",
8
+ columns = 3,
9
+ gap = "1rem",
10
+ loading,
11
+ error,
12
+ renderEmpty,
13
+ renderLoading,
14
+ renderError,
15
+ className,
16
+ "aria-label": ariaLabel
17
+ }) {
18
+ if (loading && renderLoading) return /* @__PURE__ */ jsx(Fragment, { children: renderLoading() });
19
+ if (error && renderError) return /* @__PURE__ */ jsx(Fragment, { children: renderError(error) });
20
+ if (items.length === 0 && renderEmpty) return /* @__PURE__ */ jsx(Fragment, { children: renderEmpty() });
21
+ const style = layout === "grid" ? {
22
+ display: "grid",
23
+ gridTemplateColumns: `repeat(var(--card-list-columns, ${columns}), 1fr)`,
24
+ gap: `var(--card-list-gap, ${gap})`
25
+ } : void 0;
26
+ return /* @__PURE__ */ jsx(
27
+ "ul",
28
+ {
29
+ role: "list",
30
+ "data-component": "card-list",
31
+ "data-layout": layout,
32
+ className,
33
+ "aria-label": ariaLabel,
34
+ style,
35
+ children: items.map((item, index) => /* @__PURE__ */ jsx("li", { "data-index": index, children: renderItem(item, index) }, keyOf(item)))
36
+ }
37
+ );
38
+ }
39
+ export {
40
+ CardList
41
+ };
42
+ //# sourceMappingURL=CardList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CardList.js","sources":["../../../src/react/components/CardList.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { AsyncStateProps } from './types';\n\n/** Props for the CardList headless component. */\nexport interface CardListProps<T> extends AsyncStateProps {\n items: T[];\n renderItem: (item: T, index: number) => ReactNode;\n keyOf?: (item: T) => string | number;\n layout?: 'list' | 'grid';\n columns?: number;\n gap?: string;\n renderEmpty?: () => ReactNode;\n renderLoading?: () => ReactNode;\n renderError?: (error: string) => ReactNode;\n className?: string;\n 'aria-label'?: string;\n}\n\nconst defaultKeyOf = (item: any) => item.id;\n\n/**\n * Headless list/grid component with render-prop items.\n * Renders a semantic `<ul>` with optional CSS grid layout.\n */\nexport function CardList<T>({\n items,\n renderItem,\n keyOf = defaultKeyOf,\n layout = 'list',\n columns = 3,\n gap = '1rem',\n loading,\n error,\n renderEmpty,\n renderLoading,\n renderError,\n className,\n 'aria-label': ariaLabel,\n}: CardListProps<T>) {\n if (loading && renderLoading) return <>{renderLoading()}</>;\n if (error && renderError) return <>{renderError(error)}</>;\n if (items.length === 0 && renderEmpty) return <>{renderEmpty()}</>;\n\n const style = layout === 'grid'\n ? {\n display: 'grid',\n gridTemplateColumns: `repeat(var(--card-list-columns, ${columns}), 1fr)`,\n gap: `var(--card-list-gap, ${gap})`,\n } as const\n : undefined;\n\n return (\n <ul\n role=\"list\"\n data-component=\"card-list\"\n data-layout={layout}\n className={className}\n aria-label={ariaLabel}\n style={style}\n >\n {items.map((item, index) => (\n <li key={keyOf(item)} data-index={index}>\n {renderItem(item, index)}\n </li>\n ))}\n </ul>\n );\n}\n"],"names":[],"mappings":";AAkBA,MAAM,eAAe,CAAC,SAAc,KAAK;AAMlC,SAAS,SAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAqB;AACnB,MAAI,WAAW,cAAe,QAAO,oBAAA,UAAA,EAAG,2BAAgB;AACxD,MAAI,SAAS,YAAa,QAAO,oBAAA,UAAA,EAAG,UAAA,YAAY,KAAK,GAAE;AACvD,MAAI,MAAM,WAAW,KAAK,YAAa,QAAO,oBAAA,UAAA,EAAG,yBAAc;AAE/D,QAAM,QAAQ,WAAW,SACrB;AAAA,IACE,SAAS;AAAA,IACT,qBAAqB,mCAAmC,OAAO;AAAA,IAC/D,KAAK,wBAAwB,GAAG;AAAA,EAAA,IAElC;AAEJ,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,kBAAe;AAAA,MACf,eAAa;AAAA,MACb;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MAEC,gBAAM,IAAI,CAAC,MAAM,8BACf,MAAA,EAAqB,cAAY,OAC/B,UAAA,WAAW,MAAM,KAAK,KADhB,MAAM,IAAI,CAEnB,CACD;AAAA,IAAA;AAAA,EAAA;AAGP;"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const types = require("./types.cjs");
5
+ function resolveSelectionProp(selection) {
6
+ if (types.isSelectionHelper(selection)) {
7
+ return {
8
+ selected: selection.selected,
9
+ onToggle: (key) => selection.toggle(key),
10
+ onToggleAll: (allKeys) => selection.toggleAll(allKeys)
11
+ };
12
+ }
13
+ return {
14
+ selected: selection.selected,
15
+ onToggle: selection.onToggle,
16
+ onToggleAll: (allKeys) => selection.onToggleAll(allKeys)
17
+ };
18
+ }
19
+ function resolveSortProp(sort, onSort) {
20
+ if (types.isSortingHelper(sort)) {
21
+ return { sorts: sort.sorts, onSort: (key) => sort.toggle(key) };
22
+ }
23
+ return { sorts: sort, onSort };
24
+ }
25
+ function resolvePaginationProp(pagination, pageSize, paginationTotal) {
26
+ if (types.isPaginationHelper(pagination)) {
27
+ const total2 = paginationTotal ?? 0;
28
+ const ps2 = pagination.pageSize;
29
+ const pageCount2 = Math.max(1, Math.ceil(total2 / ps2));
30
+ const page2 = pagination.page;
31
+ return {
32
+ page: page2,
33
+ pageCount: pageCount2,
34
+ total: total2,
35
+ pageSize: ps2,
36
+ hasPrev: page2 > 1,
37
+ hasNext: page2 < pageCount2,
38
+ goToPage: (p) => pagination.setPage(p),
39
+ goPrev: () => pagination.setPage(page2 - 1),
40
+ goNext: () => pagination.setPage(page2 + 1)
41
+ };
42
+ }
43
+ const total = pagination.total;
44
+ const ps = pageSize ?? 10;
45
+ const pageCount = Math.max(1, Math.ceil(total / ps));
46
+ const page = pagination.page;
47
+ return {
48
+ page,
49
+ pageCount,
50
+ total,
51
+ pageSize: ps,
52
+ hasPrev: page > 1,
53
+ hasNext: page < pageCount,
54
+ goToPage: pagination.onPageChange,
55
+ goPrev: () => pagination.onPageChange(page - 1),
56
+ goNext: () => pagination.onPageChange(page + 1)
57
+ };
58
+ }
59
+ const defaultKeyOf = (item) => item.id;
60
+ function getAriaSortValue(key, sorts) {
61
+ if (!sorts) return "none";
62
+ const desc = sorts.find((s) => s.key === key);
63
+ if (!desc) return "none";
64
+ return desc.direction === "asc" ? "ascending" : "descending";
65
+ }
66
+ function DataTable({
67
+ items,
68
+ columns,
69
+ keyOf = defaultKeyOf,
70
+ pageSize,
71
+ sort,
72
+ onSort,
73
+ selection,
74
+ loading,
75
+ error,
76
+ pagination,
77
+ paginationTotal,
78
+ renderEmpty,
79
+ renderLoading,
80
+ renderError,
81
+ renderSortIndicator,
82
+ renderRow,
83
+ renderPagination,
84
+ className,
85
+ "aria-label": ariaLabel
86
+ }) {
87
+ if (loading && renderLoading) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderLoading() });
88
+ if (error && renderError) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderError(error) });
89
+ if (items.length === 0 && renderEmpty) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderEmpty() });
90
+ const resolvedSelection = selection ? resolveSelectionProp(selection) : void 0;
91
+ let resolvedSorts;
92
+ let resolvedOnSort;
93
+ if (sort) {
94
+ const resolved = resolveSortProp(sort, onSort);
95
+ resolvedSorts = resolved.sorts;
96
+ resolvedOnSort = resolved.onSort;
97
+ }
98
+ let displayItems = items;
99
+ let paginationInfo;
100
+ if (pagination) {
101
+ paginationInfo = resolvePaginationProp(pagination, pageSize, paginationTotal);
102
+ } else if (pageSize && !pagination) {
103
+ displayItems = items.slice(0, pageSize);
104
+ }
105
+ const allKeys = resolvedSelection ? displayItems.map((item) => keyOf(item)) : [];
106
+ const allSelected = resolvedSelection && allKeys.length > 0 && allKeys.every((k) => resolvedSelection.selected.has(k));
107
+ const someSelected = resolvedSelection && allKeys.some((k) => resolvedSelection.selected.has(k));
108
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-component": "data-table", className, children: [
109
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { role: "grid", "aria-label": ariaLabel, children: [
110
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
111
+ resolvedSelection && /* @__PURE__ */ jsxRuntime.jsx("th", { "data-column": "select", children: /* @__PURE__ */ jsxRuntime.jsx(
112
+ "input",
113
+ {
114
+ type: "checkbox",
115
+ checked: !!allSelected,
116
+ ref: (el) => {
117
+ if (el) el.indeterminate = !!someSelected && !allSelected;
118
+ },
119
+ onChange: () => resolvedSelection.onToggleAll(allKeys),
120
+ "aria-label": "Select all"
121
+ }
122
+ ) }),
123
+ columns.map((col) => {
124
+ const isSortable = col.sortable && resolvedOnSort;
125
+ const sortDesc = resolvedSorts?.find((s) => s.key === col.key);
126
+ const isActive = !!sortDesc;
127
+ const sortIndex = resolvedSorts ? resolvedSorts.findIndex((s) => s.key === col.key) : -1;
128
+ return /* @__PURE__ */ jsxRuntime.jsx(
129
+ "th",
130
+ {
131
+ "data-sortable": isSortable ? "" : void 0,
132
+ "data-sorted": isActive ? "" : void 0,
133
+ "data-align": col.align,
134
+ style: col.width ? { width: col.width } : void 0,
135
+ "aria-sort": isSortable ? getAriaSortValue(col.key, resolvedSorts) : void 0,
136
+ children: isSortable ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => resolvedOnSort(col.key), children: [
137
+ col.header,
138
+ renderSortIndicator?.({
139
+ active: isActive,
140
+ direction: sortDesc?.direction ?? "asc",
141
+ index: sortIndex,
142
+ onToggle: () => resolvedOnSort(col.key)
143
+ })
144
+ ] }) : col.header
145
+ },
146
+ col.key
147
+ );
148
+ })
149
+ ] }) }),
150
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: displayItems.map((item, index) => {
151
+ const key = keyOf(item);
152
+ const isSelected = resolvedSelection?.selected.has(key);
153
+ const cells = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
154
+ resolvedSelection && /* @__PURE__ */ jsxRuntime.jsx("td", { "data-column": "select", children: /* @__PURE__ */ jsxRuntime.jsx(
155
+ "input",
156
+ {
157
+ type: "checkbox",
158
+ checked: !!isSelected,
159
+ onChange: () => resolvedSelection.onToggle(key),
160
+ "aria-label": `Select row ${key}`
161
+ }
162
+ ) }),
163
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
164
+ "td",
165
+ {
166
+ "data-align": col.align,
167
+ children: col.render(item, index)
168
+ },
169
+ col.key
170
+ ))
171
+ ] });
172
+ return /* @__PURE__ */ jsxRuntime.jsx("tr", { "data-selected": isSelected ? "" : void 0, children: renderRow ? renderRow(item, index, cells) : cells }, key);
173
+ }) })
174
+ ] }),
175
+ paginationInfo && renderPagination?.(paginationInfo)
176
+ ] });
177
+ }
178
+ exports.DataTable = DataTable;
179
+ //# sourceMappingURL=DataTable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTable.cjs","sources":["../../../src/react/components/DataTable.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type {\n Column,\n SortHeaderProps,\n SelectionState,\n SelectionHelper,\n PaginationState,\n PaginationHelper,\n PaginationInfo,\n SortingHelper,\n AsyncStateProps,\n} from './types';\nimport { isSelectionHelper, isPaginationHelper, isSortingHelper } from './types';\nimport type { SortDescriptor } from '../../Sorting';\n\n// ── Prop resolution helpers ──\n\ninterface ResolvedSelection {\n selected: ReadonlySet<any>;\n onToggle: (key: any) => void;\n onToggleAll: (allKeys: any[]) => void;\n}\n\nfunction resolveSelectionProp(selection: SelectionState | SelectionHelper): ResolvedSelection {\n if (isSelectionHelper(selection)) {\n return {\n selected: selection.selected,\n onToggle: (key) => selection.toggle(key),\n onToggleAll: (allKeys) => selection.toggleAll(allKeys),\n };\n }\n return {\n selected: selection.selected,\n onToggle: selection.onToggle,\n onToggleAll: (allKeys) => selection.onToggleAll(allKeys),\n };\n}\n\nfunction resolveSortProp(\n sort: readonly SortDescriptor[] | SortingHelper,\n onSort?: (key: string) => void,\n): { sorts: readonly SortDescriptor[]; onSort: ((key: string) => void) | undefined } {\n if (isSortingHelper(sort)) {\n return { sorts: sort.sorts, onSort: (key) => sort.toggle(key) };\n }\n return { sorts: sort, onSort };\n}\n\nfunction resolvePaginationProp(\n pagination: PaginationState | PaginationHelper,\n pageSize?: number,\n paginationTotal?: number,\n): PaginationInfo {\n if (isPaginationHelper(pagination)) {\n const total = paginationTotal ?? 0;\n const ps = pagination.pageSize;\n const pageCount = Math.max(1, Math.ceil(total / ps));\n const page = pagination.page;\n return {\n page,\n pageCount,\n total,\n pageSize: ps,\n hasPrev: page > 1,\n hasNext: page < pageCount,\n goToPage: (p) => pagination.setPage(p),\n goPrev: () => pagination.setPage(page - 1),\n goNext: () => pagination.setPage(page + 1),\n };\n }\n const total = pagination.total;\n const ps = pageSize ?? 10;\n const pageCount = Math.max(1, Math.ceil(total / ps));\n const page = pagination.page;\n return {\n page,\n pageCount,\n total,\n pageSize: ps,\n hasPrev: page > 1,\n hasNext: page < pageCount,\n goToPage: pagination.onPageChange,\n goPrev: () => pagination.onPageChange(page - 1),\n goNext: () => pagination.onPageChange(page + 1),\n };\n}\n\n/** Props for the DataTable headless component. */\nexport interface DataTableProps<T> extends AsyncStateProps {\n items: T[];\n columns: Column<T>[];\n keyOf?: (item: T) => string | number;\n pageSize?: number;\n\n // Controlled state — accepts object-literal OR helper instance\n sort?: readonly SortDescriptor[] | SortingHelper;\n onSort?: (key: string) => void;\n selection?: SelectionState | SelectionHelper;\n pagination?: PaginationState | PaginationHelper;\n paginationTotal?: number;\n\n // Render slots\n renderEmpty?: () => ReactNode;\n renderLoading?: () => ReactNode;\n renderError?: (error: string) => ReactNode;\n renderSortIndicator?: (props: SortHeaderProps) => ReactNode;\n renderRow?: (item: T, index: number, defaultCells: ReactNode) => ReactNode;\n renderPagination?: (info: PaginationInfo) => ReactNode;\n\n className?: string;\n 'aria-label'?: string;\n}\n\nconst defaultKeyOf = (item: any) => item.id;\n\nfunction getAriaSortValue(key: string, sorts: readonly SortDescriptor[] | undefined): 'ascending' | 'descending' | 'none' {\n if (!sorts) return 'none';\n const desc = sorts.find(s => s.key === key);\n if (!desc) return 'none';\n return desc.direction === 'asc' ? 'ascending' : 'descending';\n}\n\n/**\n * Headless data table with sort headers, selection checkboxes, and pagination slots.\n * Renders semantic HTML (`<table>`) with data attributes for styling.\n * Accepts Sorting/Selection/Pagination helpers directly via duck-typing.\n */\nexport function DataTable<T>({\n items,\n columns,\n keyOf = defaultKeyOf,\n pageSize,\n sort,\n onSort,\n selection,\n loading,\n error,\n pagination,\n paginationTotal,\n renderEmpty,\n renderLoading,\n renderError,\n renderSortIndicator,\n renderRow,\n renderPagination,\n className,\n 'aria-label': ariaLabel,\n}: DataTableProps<T>) {\n if (loading && renderLoading) return <>{renderLoading()}</>;\n if (error && renderError) return <>{renderError(error)}</>;\n if (items.length === 0 && renderEmpty) return <>{renderEmpty()}</>;\n\n // ── Resolve props ──\n const resolvedSelection = selection ? resolveSelectionProp(selection) : undefined;\n\n let resolvedSorts: readonly SortDescriptor[] | undefined;\n let resolvedOnSort: ((key: string) => void) | undefined;\n if (sort) {\n const resolved = resolveSortProp(sort, onSort);\n resolvedSorts = resolved.sorts;\n resolvedOnSort = resolved.onSort;\n }\n\n let displayItems = items;\n let paginationInfo: PaginationInfo | undefined;\n if (pagination) {\n paginationInfo = resolvePaginationProp(pagination, pageSize, paginationTotal);\n } else if (pageSize && !pagination) {\n displayItems = items.slice(0, pageSize);\n }\n\n // Selection: check indeterminate state\n const allKeys = resolvedSelection ? displayItems.map(item => keyOf(item)) : [];\n const allSelected = resolvedSelection && allKeys.length > 0 && allKeys.every(k => resolvedSelection!.selected.has(k));\n const someSelected = resolvedSelection && allKeys.some(k => resolvedSelection!.selected.has(k));\n\n return (\n <div data-component=\"data-table\" className={className}>\n <table role=\"grid\" aria-label={ariaLabel}>\n <thead>\n <tr>\n {resolvedSelection && (\n <th data-column=\"select\">\n <input\n type=\"checkbox\"\n checked={!!allSelected}\n ref={(el) => {\n if (el) el.indeterminate = !!someSelected && !allSelected;\n }}\n onChange={() => resolvedSelection!.onToggleAll(allKeys)}\n aria-label=\"Select all\"\n />\n </th>\n )}\n {columns.map((col) => {\n const isSortable = col.sortable && resolvedOnSort;\n const sortDesc = resolvedSorts?.find(s => s.key === col.key);\n const isActive = !!sortDesc;\n const sortIndex = resolvedSorts ? resolvedSorts.findIndex(s => s.key === col.key) : -1;\n\n return (\n <th\n key={col.key}\n data-sortable={isSortable ? '' : undefined}\n data-sorted={isActive ? '' : undefined}\n data-align={col.align}\n style={col.width ? { width: col.width } : undefined}\n aria-sort={isSortable ? getAriaSortValue(col.key, resolvedSorts) : undefined}\n >\n {isSortable ? (\n <button type=\"button\" onClick={() => resolvedOnSort!(col.key)}>\n {col.header}\n {renderSortIndicator?.({\n active: isActive,\n direction: sortDesc?.direction ?? 'asc',\n index: sortIndex,\n onToggle: () => resolvedOnSort!(col.key),\n })}\n </button>\n ) : (\n col.header\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody>\n {displayItems.map((item, index) => {\n const key = keyOf(item);\n const isSelected = resolvedSelection?.selected.has(key);\n\n const cells = (\n <>\n {resolvedSelection && (\n <td data-column=\"select\">\n <input\n type=\"checkbox\"\n checked={!!isSelected}\n onChange={() => resolvedSelection!.onToggle(key)}\n aria-label={`Select row ${key}`}\n />\n </td>\n )}\n {columns.map((col) => (\n <td\n key={col.key}\n data-align={col.align}\n >\n {col.render(item, index)}\n </td>\n ))}\n </>\n );\n\n return (\n <tr key={key} data-selected={isSelected ? '' : undefined}>\n {renderRow ? renderRow(item, index, cells) : cells}\n </tr>\n );\n })}\n </tbody>\n </table>\n {paginationInfo && renderPagination?.(paginationInfo)}\n </div>\n );\n}\n"],"names":["isSelectionHelper","isSortingHelper","isPaginationHelper","total","ps","pageCount","page","jsx","Fragment","jsxs"],"mappings":";;;;AAuBA,SAAS,qBAAqB,WAAgE;AAC5F,MAAIA,MAAAA,kBAAkB,SAAS,GAAG;AAChC,WAAO;AAAA,MACL,UAAU,UAAU;AAAA,MACpB,UAAU,CAAC,QAAQ,UAAU,OAAO,GAAG;AAAA,MACvC,aAAa,CAAC,YAAY,UAAU,UAAU,OAAO;AAAA,IAAA;AAAA,EAEzD;AACA,SAAO;AAAA,IACL,UAAU,UAAU;AAAA,IACpB,UAAU,UAAU;AAAA,IACpB,aAAa,CAAC,YAAY,UAAU,YAAY,OAAO;AAAA,EAAA;AAE3D;AAEA,SAAS,gBACP,MACA,QACmF;AACnF,MAAIC,MAAAA,gBAAgB,IAAI,GAAG;AACzB,WAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,CAAC,QAAQ,KAAK,OAAO,GAAG,EAAA;AAAA,EAC9D;AACA,SAAO,EAAE,OAAO,MAAM,OAAA;AACxB;AAEA,SAAS,sBACP,YACA,UACA,iBACgB;AAChB,MAAIC,MAAAA,mBAAmB,UAAU,GAAG;AAClC,UAAMC,SAAQ,mBAAmB;AACjC,UAAMC,MAAK,WAAW;AACtB,UAAMC,aAAY,KAAK,IAAI,GAAG,KAAK,KAAKF,SAAQC,GAAE,CAAC;AACnD,UAAME,QAAO,WAAW;AACxB,WAAO;AAAA,MACL,MAAAA;AAAAA,MACA,WAAAD;AAAAA,MACA,OAAAF;AAAAA,MACA,UAAUC;AAAAA,MACV,SAASE,QAAO;AAAA,MAChB,SAASA,QAAOD;AAAAA,MAChB,UAAU,CAAC,MAAM,WAAW,QAAQ,CAAC;AAAA,MACrC,QAAQ,MAAM,WAAW,QAAQC,QAAO,CAAC;AAAA,MACzC,QAAQ,MAAM,WAAW,QAAQA,QAAO,CAAC;AAAA,IAAA;AAAA,EAE7C;AACA,QAAM,QAAQ,WAAW;AACzB,QAAM,KAAK,YAAY;AACvB,QAAM,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,EAAE,CAAC;AACnD,QAAM,OAAO,WAAW;AACxB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,UAAU,WAAW;AAAA,IACrB,QAAQ,MAAM,WAAW,aAAa,OAAO,CAAC;AAAA,IAC9C,QAAQ,MAAM,WAAW,aAAa,OAAO,CAAC;AAAA,EAAA;AAElD;AA4BA,MAAM,eAAe,CAAC,SAAc,KAAK;AAEzC,SAAS,iBAAiB,KAAa,OAAmF;AACxH,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MAAM,KAAK,CAAA,MAAK,EAAE,QAAQ,GAAG;AAC1C,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,cAAc,QAAQ,cAAc;AAClD;AAOO,SAAS,UAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAsB;AACpB,MAAI,WAAW,cAAe,QAAOC,2BAAAA,IAAAC,WAAAA,UAAA,EAAG,2BAAgB;AACxD,MAAI,SAAS,YAAa,QAAOD,+BAAAC,WAAAA,UAAA,EAAG,UAAA,YAAY,KAAK,GAAE;AACvD,MAAI,MAAM,WAAW,KAAK,YAAa,QAAOD,+BAAAC,WAAAA,UAAA,EAAG,yBAAc;AAG/D,QAAM,oBAAoB,YAAY,qBAAqB,SAAS,IAAI;AAExE,MAAI;AACJ,MAAI;AACJ,MAAI,MAAM;AACR,UAAM,WAAW,gBAAgB,MAAM,MAAM;AAC7C,oBAAgB,SAAS;AACzB,qBAAiB,SAAS;AAAA,EAC5B;AAEA,MAAI,eAAe;AACnB,MAAI;AACJ,MAAI,YAAY;AACd,qBAAiB,sBAAsB,YAAY,UAAU,eAAe;AAAA,EAC9E,WAAW,YAAY,CAAC,YAAY;AAClC,mBAAe,MAAM,MAAM,GAAG,QAAQ;AAAA,EACxC;AAGA,QAAM,UAAU,oBAAoB,aAAa,IAAI,UAAQ,MAAM,IAAI,CAAC,IAAI,CAAA;AAC5E,QAAM,cAAc,qBAAqB,QAAQ,SAAS,KAAK,QAAQ,MAAM,CAAA,MAAK,kBAAmB,SAAS,IAAI,CAAC,CAAC;AACpH,QAAM,eAAe,qBAAqB,QAAQ,KAAK,OAAK,kBAAmB,SAAS,IAAI,CAAC,CAAC;AAE9F,SACEC,2BAAAA,KAAC,OAAA,EAAI,kBAAe,cAAa,WAC/B,UAAA;AAAA,IAAAA,2BAAAA,KAAC,SAAA,EAAM,MAAK,QAAO,cAAY,WAC7B,UAAA;AAAA,MAAAF,2BAAAA,IAAC,SAAA,EACC,0CAAC,MAAA,EACE,UAAA;AAAA,QAAA,qBACCA,2BAAAA,IAAC,MAAA,EAAG,eAAY,UACd,UAAAA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,CAAC,CAAC;AAAA,YACX,KAAK,CAAC,OAAO;AACX,kBAAI,GAAI,IAAG,gBAAgB,CAAC,CAAC,gBAAgB,CAAC;AAAA,YAChD;AAAA,YACA,UAAU,MAAM,kBAAmB,YAAY,OAAO;AAAA,YACtD,cAAW;AAAA,UAAA;AAAA,QAAA,GAEf;AAAA,QAED,QAAQ,IAAI,CAAC,QAAQ;AACpB,gBAAM,aAAa,IAAI,YAAY;AACnC,gBAAM,WAAW,eAAe,KAAK,OAAK,EAAE,QAAQ,IAAI,GAAG;AAC3D,gBAAM,WAAW,CAAC,CAAC;AACnB,gBAAM,YAAY,gBAAgB,cAAc,UAAU,OAAK,EAAE,QAAQ,IAAI,GAAG,IAAI;AAEpF,iBACEA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cAEC,iBAAe,aAAa,KAAK;AAAA,cACjC,eAAa,WAAW,KAAK;AAAA,cAC7B,cAAY,IAAI;AAAA,cAChB,OAAO,IAAI,QAAQ,EAAE,OAAO,IAAI,UAAU;AAAA,cAC1C,aAAW,aAAa,iBAAiB,IAAI,KAAK,aAAa,IAAI;AAAA,cAElE,UAAA,aACCE,2BAAAA,KAAC,UAAA,EAAO,MAAK,UAAS,SAAS,MAAM,eAAgB,IAAI,GAAG,GACzD,UAAA;AAAA,gBAAA,IAAI;AAAA,gBACJ,sBAAsB;AAAA,kBACrB,QAAQ;AAAA,kBACR,WAAW,UAAU,aAAa;AAAA,kBAClC,OAAO;AAAA,kBACP,UAAU,MAAM,eAAgB,IAAI,GAAG;AAAA,gBAAA,CACxC;AAAA,cAAA,EAAA,CACH,IAEA,IAAI;AAAA,YAAA;AAAA,YAlBD,IAAI;AAAA,UAAA;AAAA,QAsBf,CAAC;AAAA,MAAA,EAAA,CACH,EAAA,CACF;AAAA,qCACC,SAAA,EACE,UAAA,aAAa,IAAI,CAAC,MAAM,UAAU;AACjC,cAAM,MAAM,MAAM,IAAI;AACtB,cAAM,aAAa,mBAAmB,SAAS,IAAI,GAAG;AAEtD,cAAM,QACJA,2BAAAA,KAAAD,WAAAA,UAAA,EACG,UAAA;AAAA,UAAA,qBACCD,2BAAAA,IAAC,MAAA,EAAG,eAAY,UACd,UAAAA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,CAAC,CAAC;AAAA,cACX,UAAU,MAAM,kBAAmB,SAAS,GAAG;AAAA,cAC/C,cAAY,cAAc,GAAG;AAAA,YAAA;AAAA,UAAA,GAEjC;AAAA,UAED,QAAQ,IAAI,CAAC,QACZA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cAEC,cAAY,IAAI;AAAA,cAEf,UAAA,IAAI,OAAO,MAAM,KAAK;AAAA,YAAA;AAAA,YAHlB,IAAI;AAAA,UAAA,CAKZ;AAAA,QAAA,GACH;AAGF,eACEA,2BAAAA,IAAC,MAAA,EAAa,iBAAe,aAAa,KAAK,QAC5C,UAAA,YAAY,UAAU,MAAM,OAAO,KAAK,IAAI,SADtC,GAET;AAAA,MAEJ,CAAC,EAAA,CACH;AAAA,IAAA,GACF;AAAA,IACC,kBAAkB,mBAAmB,cAAc;AAAA,EAAA,GACtD;AAEJ;;"}
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Column, SortHeaderProps, SelectionState, SelectionHelper, PaginationState, PaginationHelper, PaginationInfo, SortingHelper, AsyncStateProps } from './types';
3
+ import type { SortDescriptor } from '../../Sorting';
4
+ /** Props for the DataTable headless component. */
5
+ export interface DataTableProps<T> extends AsyncStateProps {
6
+ items: T[];
7
+ columns: Column<T>[];
8
+ keyOf?: (item: T) => string | number;
9
+ pageSize?: number;
10
+ sort?: readonly SortDescriptor[] | SortingHelper;
11
+ onSort?: (key: string) => void;
12
+ selection?: SelectionState | SelectionHelper;
13
+ pagination?: PaginationState | PaginationHelper;
14
+ paginationTotal?: number;
15
+ renderEmpty?: () => ReactNode;
16
+ renderLoading?: () => ReactNode;
17
+ renderError?: (error: string) => ReactNode;
18
+ renderSortIndicator?: (props: SortHeaderProps) => ReactNode;
19
+ renderRow?: (item: T, index: number, defaultCells: ReactNode) => ReactNode;
20
+ renderPagination?: (info: PaginationInfo) => ReactNode;
21
+ className?: string;
22
+ 'aria-label'?: string;
23
+ }
24
+ /**
25
+ * Headless data table with sort headers, selection checkboxes, and pagination slots.
26
+ * Renders semantic HTML (`<table>`) with data attributes for styling.
27
+ * Accepts Sorting/Selection/Pagination helpers directly via duck-typing.
28
+ */
29
+ export declare function DataTable<T>({ items, columns, keyOf, pageSize, sort, onSort, selection, loading, error, pagination, paginationTotal, renderEmpty, renderLoading, renderError, renderSortIndicator, renderRow, renderPagination, className, 'aria-label': ariaLabel, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
30
+ //# sourceMappingURL=DataTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/react/components/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA0EpD,kDAAkD;AAClD,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,eAAe;IACxD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,IAAI,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,aAAa,CAAC;IACjD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,cAAc,GAAG,eAAe,CAAC;IAC7C,UAAU,CAAC,EAAE,eAAe,GAAG,gBAAgB,CAAC;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,WAAW,CAAC,EAAE,MAAM,SAAS,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAC3C,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;IAC5D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,KAAK,SAAS,CAAC;IAC3E,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,SAAS,CAAC;IAEvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAWD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAC3B,KAAK,EACL,OAAO,EACP,KAAoB,EACpB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAS,EACT,OAAO,EACP,KAAK,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,YAAY,EAAE,SAAS,GACxB,EAAE,cAAc,CAAC,CAAC,CAAC,2CAuHnB"}