@chelone/core 0.3.0 → 0.4.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.
@@ -1,3 +1,4 @@
1
+ import type { SourcePath } from "../resource/source-path.js";
1
2
  import { type CalendarRange } from "./calendar-dates.js";
2
3
  export interface CalendarEvent<R> {
3
4
  record: R;
@@ -7,10 +8,10 @@ export interface CalendarEvent<R> {
7
8
  color?: string;
8
9
  }
9
10
  export interface ToCalendarEventsOptions<R> {
10
- startSource: string;
11
- endSource?: string;
12
- titleSource?: string;
13
- colorSource?: string;
11
+ startSource: SourcePath<R>;
12
+ endSource?: SourcePath<R>;
13
+ titleSource?: SourcePath<R>;
14
+ colorSource?: SourcePath<R>;
14
15
  /** Maps the value at `colorSource` to a color. */
15
16
  colorMap?: Record<string, string>;
16
17
  /** The title when `titleSource` is unset or empty: the adapter fills it from the record representation. */
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Pure rules behind a kanban board: which column holds which record, and what
3
+ * a drop writes back. No react, no vue: both `KanbanBoard` adapters build the
4
+ * same columns from the same records, and a drag handler that decides for
5
+ * itself lets the two boards disagree about the same drop.
6
+ *
7
+ * Drag events stay in the adapters, as `CalendarList` keeps its drag glue
8
+ * beside {@link moveCalendarEvent}.
9
+ */
10
+ import type { Identifier } from "../resource/identifier.js";
11
+ import type { ResourceRecord } from "../resource/record.js";
12
+ import type { SourceKey } from "../resource/source-path.js";
13
+ export interface KanbanColumnDef {
14
+ /** Value written to `groupSource` by a drop into this column. */
15
+ id: string;
16
+ label: string;
17
+ className?: string;
18
+ }
19
+ export interface KanbanBucket<R> {
20
+ column: KanbanColumnDef;
21
+ records: R[];
22
+ }
23
+ /**
24
+ * The update a drop asks for, in the shape `update` takes: `data` is the patch
25
+ * and `previousData` the record as it was, so an adapter can undo the drop
26
+ * without a second read.
27
+ */
28
+ export interface KanbanMove<R> {
29
+ id: Identifier;
30
+ data: Record<string, string>;
31
+ previousData: R;
32
+ }
33
+ /**
34
+ * One bucket per column, in column order, empty columns included. A record
35
+ * lands in the column whose id is its group key; a record that names no column
36
+ * is in no bucket, and records keep list order inside a bucket. A repeated
37
+ * column id keeps the first column's records and leaves the later one empty.
38
+ */
39
+ export declare function buildKanbanBuckets<R extends ResourceRecord>(records: readonly R[], groupSource: SourceKey<R>, columns: readonly KanbanColumnDef[]): KanbanBucket<R>[];
40
+ /**
41
+ * The update a drop into `targetColumnId` asks for, or `null` when there is
42
+ * nothing to write: the target is not a column, or the record already sits in
43
+ * it. A drop that writes nothing still fires a data-provider `update` on every
44
+ * board that re-renders on a dropped drag, so the adapters ask here first.
45
+ */
46
+ export declare function planKanbanMove<R extends ResourceRecord>(record: R, groupSource: SourceKey<R>, columns: readonly KanbanColumnDef[], targetColumnId: string): KanbanMove<R> | null;
@@ -0,0 +1,30 @@
1
+ function bucketKey(record, groupSource) {
2
+ const value = record[groupSource];
3
+ return String(value ?? "");
4
+ }
5
+ function buildKanbanBuckets(records, groupSource, columns) {
6
+ const buckets = columns.map((column) => ({
7
+ column,
8
+ records: []
9
+ }));
10
+ const bucketOf = /* @__PURE__ */ new Map();
11
+ columns.forEach((column, index) => {
12
+ if (!bucketOf.has(column.id)) bucketOf.set(column.id, index);
13
+ });
14
+ for (const record of records) {
15
+ const index = bucketOf.get(bucketKey(record, groupSource));
16
+ if (index === void 0) continue;
17
+ buckets[index].records.push(record);
18
+ }
19
+ return buckets;
20
+ }
21
+ function planKanbanMove(record, groupSource, columns, targetColumnId) {
22
+ if (!columns.some((column) => column.id === targetColumnId)) return null;
23
+ if (bucketKey(record, groupSource) === targetColumnId) return null;
24
+ const data = { [groupSource]: targetColumnId };
25
+ return { id: record.id, data, previousData: record };
26
+ }
27
+ export {
28
+ buildKanbanBuckets,
29
+ planKanbanMove
30
+ };
@@ -4,6 +4,7 @@
4
4
  * records, and no record disappears when the data holds a parent cycle.
5
5
  */
6
6
  import type { Identifier } from "../resource/identifier.js";
7
+ import type { SourcePath } from "../resource/source-path.js";
7
8
  export interface RecordTreeNode<R> {
8
9
  record: R;
9
10
  children: RecordTreeNode<R>[];
@@ -17,4 +18,4 @@ export interface RecordTreeNode<R> {
17
18
  */
18
19
  export declare function buildRecordTree<R extends {
19
20
  id: Identifier;
20
- }>(records: readonly R[], parentSource: string): RecordTreeNode<R>[];
21
+ }>(records: readonly R[], parentSource: SourcePath<R>): RecordTreeNode<R>[];
package/dist/index.d.ts CHANGED
@@ -21,6 +21,8 @@ export { arrangeColumns, columnLabelMatches, dataTableColumnKeys, defaultDataTab
21
21
  export { isFilterSelected, toggleFilterValue, } from "./controller/filter-toggle.js";
22
22
  export type { InfiniteListController, InfiniteListControllerState, } from "./controller/infinite-list-controller.js";
23
23
  export { createInfiniteListController } from "./controller/infinite-list-controller.js";
24
+ export type { KanbanBucket, KanbanColumnDef, KanbanMove, } from "./controller/kanban-board.js";
25
+ export { buildKanbanBuckets, planKanbanMove, } from "./controller/kanban-board.js";
24
26
  export type { ListController, ListControllerContext, ListControllerOptions, ListControllerState, ListParamsPatch, } from "./controller/list-controller.js";
25
27
  export { createListController, listControllerActions, } from "./controller/list-controller.js";
26
28
  export type { ListParamsQuery, ListUrlState, } from "./controller/list-params.js";
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { createClientListController } from "./controller/client-list-controller.
10
10
  import { arrangeColumns, columnLabelMatches, dataTableColumnKeys, defaultDataTableStoreKey, getDataTableColumnId, moveColumn, toggleHiddenColumn } from "./controller/data-table-columns.js";
11
11
  import { isFilterSelected, toggleFilterValue } from "./controller/filter-toggle.js";
12
12
  import { createInfiniteListController } from "./controller/infinite-list-controller.js";
13
+ import { buildKanbanBuckets, planKanbanMove } from "./controller/kanban-board.js";
13
14
  import { createListController, listControllerActions } from "./controller/list-controller.js";
14
15
  import { hasCustomParams, listPatchFromSearch, parseListParams, serializeListParams, serializeListState } from "./controller/list-params.js";
15
16
  import { canUseCachedPage, getPrevNextParams, resolvePrevNext } from "./controller/prev-next.js";
@@ -117,6 +118,7 @@ export {
117
118
  authStateQueryOptions,
118
119
  bboxToPolygon,
119
120
  buildApprovalUpdate,
121
+ buildKanbanBuckets,
120
122
  buildOverpassQuery,
121
123
  buildRecordTree,
122
124
  calendarDayKey,
@@ -290,6 +292,7 @@ export {
290
292
  parseLocation,
291
293
  patchedRecord,
292
294
  permissionsQueryOptions,
295
+ planKanbanMove,
293
296
  polygonToBBox,
294
297
  presenceTopic,
295
298
  rasterTileStyle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chelone/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",