@alfakit/core 0.1.1 → 0.1.6

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 (78) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components.css +347 -0
  3. package/dist/actions.d.ts +9 -0
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +31 -5
  6. package/dist/actions.js.map +1 -1
  7. package/dist/advanced-fields.js +2 -2
  8. package/dist/advanced-fields.js.map +1 -1
  9. package/dist/assets.d.ts.map +1 -1
  10. package/dist/assets.js +2 -1
  11. package/dist/assets.js.map +1 -1
  12. package/dist/cms-live-preview.d.ts.map +1 -1
  13. package/dist/cms-live-preview.js +7 -1
  14. package/dist/cms-live-preview.js.map +1 -1
  15. package/dist/csp.d.ts +11 -0
  16. package/dist/csp.d.ts.map +1 -0
  17. package/dist/csp.js +16 -0
  18. package/dist/csp.js.map +1 -0
  19. package/dist/file-size.js +1 -1
  20. package/dist/file-size.js.map +1 -1
  21. package/dist/form.d.ts +53 -1
  22. package/dist/form.d.ts.map +1 -1
  23. package/dist/form.js +96 -4
  24. package/dist/form.js.map +1 -1
  25. package/dist/icons.d.ts +6 -0
  26. package/dist/icons.d.ts.map +1 -1
  27. package/dist/icons.js +9 -1
  28. package/dist/icons.js.map +1 -1
  29. package/dist/image-editor.d.ts +2 -2
  30. package/dist/image-editor.js +2 -2
  31. package/dist/image-files.d.ts +8 -0
  32. package/dist/image-files.d.ts.map +1 -1
  33. package/dist/image-files.js +11 -1
  34. package/dist/image-files.js.map +1 -1
  35. package/dist/index.d.ts +12 -11
  36. package/dist/index.js +10 -9
  37. package/dist/kanban.d.ts.map +1 -1
  38. package/dist/kanban.js +1 -1
  39. package/dist/kanban.js.map +1 -1
  40. package/dist/page-actions.d.ts +10 -2
  41. package/dist/page-actions.d.ts.map +1 -1
  42. package/dist/page-actions.js +15 -5
  43. package/dist/page-actions.js.map +1 -1
  44. package/dist/schema/advanced-fields.d.ts +5 -0
  45. package/dist/schema/advanced-fields.d.ts.map +1 -1
  46. package/dist/schema/cms.d.ts +4 -0
  47. package/dist/schema/cms.d.ts.map +1 -1
  48. package/dist/schema/components.d.ts +5 -0
  49. package/dist/schema/components.d.ts.map +1 -1
  50. package/dist/schema/components.js.map +1 -1
  51. package/dist/schema/index.d.ts +3 -3
  52. package/dist/schema/panel.d.ts +31 -0
  53. package/dist/schema/panel.d.ts.map +1 -1
  54. package/dist/schema/panel.js.map +1 -1
  55. package/dist/schema/table-grouping.d.ts +5 -1
  56. package/dist/schema/table-grouping.d.ts.map +1 -1
  57. package/dist/standalone.d.ts +10 -3
  58. package/dist/standalone.d.ts.map +1 -1
  59. package/dist/standalone.js +12 -2
  60. package/dist/standalone.js.map +1 -1
  61. package/dist/starter-kit-widgets.d.ts +40 -7
  62. package/dist/starter-kit-widgets.d.ts.map +1 -1
  63. package/dist/starter-kit-widgets.js +71 -7
  64. package/dist/starter-kit-widgets.js.map +1 -1
  65. package/dist/starter-kit.d.ts +24 -1
  66. package/dist/starter-kit.d.ts.map +1 -1
  67. package/dist/starter-kit.js +33 -3
  68. package/dist/starter-kit.js.map +1 -1
  69. package/dist/utils/debounce.d.ts +2 -0
  70. package/dist/utils/debounce.d.ts.map +1 -1
  71. package/dist/utils/debounce.js +1 -0
  72. package/dist/utils/debounce.js.map +1 -1
  73. package/dist/utils/path.d.ts +11 -1
  74. package/dist/utils/path.d.ts.map +1 -1
  75. package/dist/utils/path.js +30 -3
  76. package/dist/utils/path.js.map +1 -1
  77. package/package.json +5 -3
  78. package/themes.css +8 -335
@@ -1 +1 @@
1
- {"version":3,"file":"kanban.js","names":[],"sources":["../src/kanban.ts"],"sourcesContent":["import type { ActionContext, ActionRunner } from './actions'\nimport { FormStore } from './form'\nimport { HttpError } from './http'\nimport type { Translator } from './i18n'\nimport { PollScheduler } from './poll'\nimport type { ActionSchema } from './schema/actions'\nimport type {\n KanbanCardSchema,\n KanbanColumnSchema,\n KanbanFetchResult,\n KanbanLaneSchema,\n KanbanMoveResult,\n KanbanSchema,\n} from './schema/kanban'\nimport type { NotificationSchema } from './schema/notifications'\nimport { createStore, type Store } from './store'\n\n/**\n * The framework-agnostic half of the Kanban board (alfasix/alfakit-kanban): pure\n * operations on the columns, a `KanbanController` holding the working copy, the picked-up\n * card, the search and the requests, and `attachKanbanPointer()`, which drives mouse, pen\n * and touch dragging over the DOM every renderer emits. Renderers only draw the state.\n *\n * Moves are optimistic: the card moves at once and returns when the server refuses. The\n * server checks every move again (record, policy, column, transition, WIP limit, lane,\n * tenant); the checks here only keep the UI from offering moves it would refuse.\n */\n\n/** The registry key of the board component. */\nexport const KANBAN_REGISTRY_KEY = 'kanban'\n\n/** The lane key of cards without a swimlane value (`Swimlane::NONE`). */\nexport const KANBAN_NO_LANE = '__none'\n\n/** Where a card is, or should go: a column, a lane (null on boards without lanes) and a position in that lane. */\nexport interface KanbanTarget {\n column: string\n lane: string | null\n /** Position among the lane's cards in the column, from 0. */\n index: number\n}\n\n/** Why a move is not offered. */\nexport type KanbanRefusal = 'locked' | 'transition' | 'wip' | 'lane'\n\nexport interface KanbanLocation {\n column: KanbanColumnSchema\n columnIndex: number\n card: KanbanCardSchema\n /** Position in the column's card list. */\n index: number\n lane: string | null\n /** Position among the lane's cards in the column. */\n laneIndex: number\n laneCount: number\n}\n\n/** The cards of one lane of a column (every card when `lane` is null). */\nexport function kanbanCardsIn(column: KanbanColumnSchema, lane: string | null): KanbanCardSchema[] {\n return lane === null\n ? column.cards\n : column.cards.filter((card) => (card.lane ?? KANBAN_NO_LANE) === lane)\n}\n\nexport function locateKanbanCard(\n columns: KanbanColumnSchema[],\n key: string,\n): KanbanLocation | null {\n for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {\n const column = columns[columnIndex]!\n const index = column.cards.findIndex((card) => card.key === key)\n\n if (index >= 0) {\n const card = column.cards[index]!\n const lane = card.lane ?? null\n const laneCards = kanbanCardsIn(column, lane)\n\n return {\n column,\n columnIndex,\n card,\n index,\n lane,\n laneIndex: laneCards.indexOf(card),\n laneCount: laneCards.length,\n }\n }\n }\n\n return null\n}\n\n/** Where a card is now. */\nexport function kanbanCardPosition(\n columns: KanbanColumnSchema[],\n key: string,\n): KanbanTarget | null {\n const found = locateKanbanCard(columns, key)\n\n return found ? { column: found.column.key, lane: found.lane, index: found.laneIndex } : null\n}\n\nfunction sameTarget(a: KanbanTarget, b: KanbanTarget): boolean {\n return a.column === b.column && a.lane === b.lane && a.index === b.index\n}\n\n/**\n * The columns after moving a card to `target` (counts follow the card), or null when the\n * card or column doesn't exist.\n */\nexport function moveKanbanCard(\n columns: KanbanColumnSchema[],\n key: string,\n target: KanbanTarget,\n): KanbanColumnSchema[] | null {\n const from = locateKanbanCard(columns, key)\n const toIndex = columns.findIndex((column) => column.key === target.column)\n\n if (!from || toIndex < 0) {\n return null\n }\n\n const next = columns.map((column) => ({ ...column, cards: [...column.cards] }))\n const card: KanbanCardSchema =\n target.lane === null || target.lane === from.lane\n ? from.card\n : { ...from.card, lane: target.lane }\n\n next[from.columnIndex]!.cards.splice(from.index, 1)\n\n const destination = next[toIndex]!\n const laneCards = kanbanCardsIn(destination, target.lane)\n let at: number\n\n if (laneCards.length === 0) {\n at = target.lane === null ? 0 : destination.cards.length\n } else if (target.index >= laneCards.length) {\n at = destination.cards.indexOf(laneCards[laneCards.length - 1]!) + 1\n } else {\n at = destination.cards.indexOf(laneCards[Math.max(0, target.index)]!)\n }\n\n destination.cards.splice(at, 0, card)\n\n if (from.columnIndex !== toIndex) {\n const source = next[from.columnIndex]!\n source.count = Math.max(0, source.count - 1)\n source.total = Math.max(0, source.total - 1)\n destination.count += 1\n destination.total += 1\n }\n\n return next\n}\n\n/**\n * Why the board would refuse moving a card from `origin` (where it was picked up) to\n * `target`, or null when the move is offered. `columns` is the working copy, which may\n * already show the card elsewhere.\n */\nexport function kanbanRefusal(\n board: Pick<KanbanSchema, 'movable' | 'transitions' | 'laneMovable'>,\n columns: KanbanColumnSchema[],\n key: string,\n origin: KanbanTarget,\n target: KanbanTarget,\n): KanbanRefusal | null {\n const found = locateKanbanCard(columns, key)\n\n if (!board.movable || !found || found.card.movable === false) {\n return 'locked'\n }\n\n if (target.column !== origin.column) {\n const allowed = board.transitions?.[origin.column]\n\n if (allowed && !allowed.includes(target.column)) {\n return 'transition'\n }\n\n const column = columns.find((candidate) => candidate.key === target.column)\n\n if (column?.wipStrict && column.wipLimit !== undefined) {\n const held = column.count - (found.column.key === column.key ? 1 : 0)\n\n if (held >= column.wipLimit) {\n return 'wip'\n }\n }\n }\n\n if (target.lane !== origin.lane && !board.laneMovable) {\n return 'lane'\n }\n\n return null\n}\n\n/**\n * The move as the server's move action reads it: the target column and lane, and the\n * neighbour the card follows (or precedes) in its column.\n */\nexport function kanbanMovePayload(\n board: Pick<KanbanSchema, 'orderable' | 'lanes'>,\n columns: KanbanColumnSchema[],\n key: string,\n): Record<string, unknown> {\n const found = locateKanbanCard(columns, key)\n\n if (!found) {\n return {}\n }\n\n const payload: Record<string, unknown> = { column: found.column.key }\n\n if (board.lanes && found.lane !== null) {\n payload.lane = found.lane\n }\n\n if (!board.orderable) {\n return payload\n }\n\n const after = found.column.cards[found.index - 1]\n const before = found.column.cards[found.index + 1]\n\n if (after) payload.after = after.key\n else if (before) payload.before = before.key\n else payload.position = 'end'\n\n return payload\n}\n\n/** The initials of a name, for avatars without an image. */\nexport function kanbanInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n const letters = parts.length > 1 ? [parts[0]!, parts[parts.length - 1]!] : parts.slice(0, 1)\n\n return letters\n .map((part) => Array.from(part)[0] ?? '')\n .join('')\n .toUpperCase()\n}\n\n/** `count` against `wipLimit`: `over`, `full` or null. */\nexport function kanbanWipState(column: KanbanColumnSchema): 'over' | 'full' | null {\n if (column.wipLimit === undefined) return null\n if (column.count > column.wipLimit) return 'over'\n\n return column.count === column.wipLimit ? 'full' : null\n}\n\n/** How many filters of a filters form value hold something. */\nexport function kanbanActiveFilters(values: Record<string, unknown> | null | undefined): number {\n const filled = (value: unknown): boolean =>\n Array.isArray(value)\n ? value.length > 0\n : value !== null && value !== undefined && value !== '' && value !== false\n\n return Object.values(values ?? {}).filter(\n (state) => state !== null && typeof state === 'object' && Object.values(state).some(filled),\n ).length\n}\n\n/**\n * The form store of the board's filters (null without filters). Searchable selects load\n * their options from the schema the server names (pages only).\n */\nexport function createKanbanFiltersStore(\n board: Pick<KanbanSchema, 'filters' | 'actionComponent'>,\n token: string,\n optionsUrl?: string,\n): FormStore | null {\n if (!board.filters) return null\n\n return new FormStore(\n {\n components: board.filters.components,\n state: board.filters.state,\n columns: board.filters.columns,\n },\n {\n token,\n component: board.filters.component ?? `${board.actionComponent}.kanbanFilters`,\n refreshUrl: undefined,\n optionsUrl: board.filters.component ? optionsUrl : undefined,\n },\n )\n}\n\nexport interface KanbanPicked {\n key: string\n /** Where the card was picked up (it returns there on cancel or a refused move). */\n origin: KanbanTarget\n /** Where it is shown now. */\n target: KanbanTarget\n via: 'keyboard' | 'pointer'\n}\n\nexport interface KanbanState {\n /** The working copy of the columns. */\n columns: KanbanColumnSchema[]\n collapsed: ReadonlySet<string>\n collapsedLanes: ReadonlySet<string>\n picked: KanbanPicked | null\n /** The column the picked card may not enter, and why (for styling a refused target). */\n refused: { column: string; reason: KanbanRefusal } | null\n /** Cards whose move is being saved. */\n saving: ReadonlySet<string>\n search: string\n /** The filters form values the board was last asked for; null for the defaults. */\n filters: Record<string, unknown> | null\n loading: boolean\n /** For a polite live region. */\n announcement: string\n}\n\nexport interface KanbanEnvironment {\n runner: Pick<ActionRunner, 'executeJson' | 'trigger'>\n token: string\n t: Translator['t']\n notify(notifications: NotificationSchema[]): void\n error(message: string): void\n /** Follow a card's URL (an Inertia visit). */\n visit?(url: string): void\n}\n\nexport interface KanbanKeyResult {\n handled: boolean\n /** The card to focus next. */\n focus?: string\n /** Open the card (Enter). */\n open?: boolean\n}\n\ntype Direction = 'up' | 'down' | 'previous' | 'next'\n\n/**\n * The state of one rendered board. `setBoard()` takes a fresh schema from the server\n * (after an action ran or a poll); collapsed columns and the search are kept.\n */\nexport class KanbanController {\n public readonly store: Store<KanbanState>\n\n private searchTimer: ReturnType<typeof setTimeout> | null = null\n private pendingBoard: KanbanSchema | null = null\n private refreshAfterMove = false\n private fetchId = 0\n\n constructor(\n private board: KanbanSchema,\n private env: KanbanEnvironment,\n ) {\n this.store = createStore<KanbanState>({\n columns: board.columns,\n collapsed: new Set(board.columns.filter((column) => column.collapsed).map((c) => c.key)),\n collapsedLanes: new Set(\n (board.lanes ?? []).filter((lane) => lane.collapsed).map((l) => l.key),\n ),\n picked: null,\n refused: null,\n saving: new Set(),\n search: board.search ?? '',\n filters: null,\n loading: false,\n announcement: '',\n })\n }\n\n get state(): KanbanState {\n return this.store.get()\n }\n\n get schema(): KanbanSchema {\n return this.board\n }\n\n /** The renderer's latest runner, token and translator. */\n setEnvironment(env: KanbanEnvironment): void {\n this.env = env\n }\n\n /** The rows to draw: the board's lanes, or one row (null) without swimlanes. */\n lanes(): Array<KanbanLaneSchema | null> {\n return this.board.lanes && this.board.lanes.length > 0 ? this.board.lanes : [null]\n }\n\n /** The schema changed on the server (an action ran): show it, keeping the user's view. */\n setBoard(board: KanbanSchema): void {\n if (this.state.picked || this.state.saving.size > 0) {\n this.pendingBoard = board\n return\n }\n\n // The server draws the default view; ask again for the user's search and filters.\n if (this.isQueried()) {\n this.board = { ...board, columns: this.board.columns, lanes: this.board.lanes }\n void this.refresh()\n return\n }\n\n this.applyBoard(board)\n }\n\n private isQueried(): boolean {\n return this.state.search !== (this.board.search ?? '') || this.state.filters !== null\n }\n\n private applyBoard(board: KanbanSchema): void {\n const known = new Set(this.board.columns.map((column) => column.key))\n const collapsed = new Set(\n [...this.state.collapsed].filter((key) => board.columns.some((c) => c.key === key)),\n )\n\n for (const column of board.columns) {\n if (!known.has(column.key) && column.collapsed) collapsed.add(column.key)\n }\n\n this.board = board\n this.store.update({ columns: board.columns, collapsed })\n }\n\n // ------------------------------------------------------------- Columns and lanes\n\n isCollapsed(column: string): boolean {\n return this.state.collapsed.has(column)\n }\n\n toggleColumn(column: string, collapsed = !this.isCollapsed(column)): void {\n const next = new Set(this.state.collapsed)\n\n if (collapsed) next.add(column)\n else next.delete(column)\n\n this.store.update({ collapsed: next })\n }\n\n toggleLane(lane: string, collapsed = !this.state.collapsedLanes.has(lane)): void {\n const next = new Set(this.state.collapsedLanes)\n\n if (collapsed) next.add(lane)\n else next.delete(lane)\n\n this.store.update({ collapsedLanes: next })\n }\n\n // ------------------------------------------------------------- Moving\n\n canPick(key: string): boolean {\n const found = locateKanbanCard(this.state.columns, key)\n\n return Boolean(this.board.movable && found && found.card.movable !== false)\n }\n\n /** Pick a card up (Space, or the start of a drag). */\n pick(key: string, via: KanbanPicked['via'] = 'keyboard'): boolean {\n const origin = kanbanCardPosition(this.state.columns, key)\n\n if (!origin || !this.canPick(key) || this.state.saving.has(key)) {\n return false\n }\n\n this.store.update({\n picked: { key, origin, target: origin, via },\n refused: null,\n announcement: this.describe(\n 'kanban.picked_up',\n key,\n ':title picked up in :column, position :position of :count.',\n ),\n })\n\n return true\n }\n\n /** Why the picked card may not go to `target`, or null. */\n refusal(target: KanbanTarget): KanbanRefusal | null {\n const picked = this.state.picked\n\n if (!picked) return 'locked'\n\n return kanbanRefusal(this.board, this.state.columns, picked.key, picked.origin, target)\n }\n\n /** Show the picked card at `target` (a preview until it is dropped). */\n hover(target: KanbanTarget): boolean {\n const picked = this.state.picked\n\n if (!picked) return false\n if (sameTarget(picked.target, target)) return true\n\n const reason = this.refusal(target)\n\n if (reason) {\n this.store.update({\n refused: { column: target.column, reason },\n announcement:\n picked.via === 'keyboard'\n ? this.refusedMessage(picked.key, target.column)\n : this.state.announcement,\n })\n\n return false\n }\n\n const columns = moveKanbanCard(this.state.columns, picked.key, target)\n\n if (!columns) return false\n\n const position = kanbanCardPosition(columns, picked.key) ?? target\n\n this.store.update({\n columns,\n picked: { ...picked, target: position },\n refused: null,\n })\n\n if (picked.via === 'keyboard') {\n this.store.update({\n announcement: this.describe(\n 'kanban.moved',\n picked.key,\n ':title is in :column, position :position of :count.',\n ),\n })\n }\n\n return true\n }\n\n /** Put the picked card back where it was picked up. */\n cancel(): void {\n const picked = this.state.picked\n\n if (!picked) return\n\n const columns =\n moveKanbanCard(this.state.columns, picked.key, picked.origin) ?? this.state.columns\n const column = columns.find((candidate) => candidate.key === picked.origin.column)\n\n this.store.update({\n columns,\n picked: null,\n refused: null,\n announcement: this.env.t(\n 'kanban.cancelled',\n { title: this.title(picked.key), column: column?.label ?? '' },\n 'Move cancelled. :title is back in :column.',\n ),\n })\n this.flushPending()\n }\n\n /** Drop the picked card where it is shown and save the move. */\n async drop(): Promise<boolean> {\n const picked = this.state.picked\n\n if (!picked) return false\n\n if (sameTarget(picked.origin, picked.target)) {\n this.store.update({\n picked: null,\n refused: null,\n announcement: this.describe(\n 'kanban.dropped',\n picked.key,\n ':title dropped in :column, position :position of :count.',\n ),\n })\n this.flushPending()\n\n return false\n }\n\n const saving = new Set(this.state.saving).add(picked.key)\n\n this.store.update({\n picked: null,\n refused: null,\n saving,\n announcement: this.describe(\n 'kanban.dropped',\n picked.key,\n ':title dropped in :column, position :position of :count.',\n ),\n })\n\n return this.save(picked.key, picked.origin)\n }\n\n /** Move a card in one step (the keyboard, or a drop the renderer worked out itself). */\n async moveTo(key: string, target: KanbanTarget): Promise<boolean> {\n if (!this.pick(key, 'pointer')) return false\n if (!this.hover(target)) {\n this.cancel()\n return false\n }\n\n return this.drop()\n }\n\n private async save(key: string, origin: KanbanTarget): Promise<boolean> {\n const action: ActionSchema = { type: 'Action', name: this.board.moveAction, hasAction: true }\n const context: ActionContext = {\n token: this.env.token,\n component: this.board.actionComponent,\n record: key,\n arguments: {\n [this.board.argumentsKey]: kanbanMovePayload(this.board, this.state.columns, key),\n },\n }\n\n try {\n const result = await this.env.runner.executeJson<KanbanMoveResult>(action, context)\n\n if (result && result.ok === false) {\n throw new Error('refused')\n }\n\n this.applySaved(key, result)\n this.env.notify(result?.notifications ?? [])\n\n return true\n } catch (error) {\n const columns = moveKanbanCard(this.state.columns, key, origin) ?? this.state.columns\n const message =\n error instanceof HttpError && error.status === 422\n ? error.errors[this.board.argumentsKey]?.[0]\n : error instanceof HttpError && error.status === 403\n ? this.env.t('kanban.forbidden', {}, 'You are not allowed to move this card.')\n : undefined\n const text = message ?? this.env.t('kanban.move_failed', {}, 'The card could not be moved.')\n\n this.store.update({ columns, announcement: text })\n this.env.error(text)\n\n // The board changed under us: show the server's version.\n if (error instanceof HttpError && error.status === 422) this.refreshAfterMove = true\n\n return false\n } finally {\n const saving = new Set(this.state.saving)\n saving.delete(key)\n this.store.update({ saving })\n this.flushPending()\n }\n }\n\n private applySaved(key: string, result: KanbanMoveResult | null | undefined): void {\n const fresh = result?.card\n const counts = result?.counts ?? {}\n const columns = this.state.columns.map((column) => {\n const count = counts[column.key]\n const index = fresh ? column.cards.findIndex((card) => card.key === key) : -1\n\n if (count === undefined && index < 0) return column\n\n const cards =\n index >= 0 && fresh\n ? column.cards.map((card, at) =>\n at === index ? { ...fresh, lane: fresh.lane ?? card.lane } : card,\n )\n : column.cards\n\n return { ...column, cards, ...(count !== undefined ? { count } : {}) }\n })\n\n this.store.update({ columns })\n }\n\n private flushPending(): void {\n if (this.state.picked || this.state.saving.size > 0) return\n\n const board = this.pendingBoard\n this.pendingBoard = null\n\n if (board) {\n this.setBoard(board)\n } else if (this.refreshAfterMove) {\n this.refreshAfterMove = false\n void this.refresh()\n }\n }\n\n /** Move the picked card one step: within its lane, to the next lane or column. */\n step(direction: Direction): boolean {\n const picked = this.state.picked\n\n if (!picked) return false\n\n const { column, lane, index } = picked.target\n const columns = this.state.columns\n const current = columns.find((candidate) => candidate.key === column)\n const count = current ? kanbanCardsIn(current, lane).length : 0\n const lanes = this.lanes().map((row) => row?.key ?? null)\n const laneIndex = lanes.indexOf(lane)\n\n if (direction === 'up' || direction === 'down') {\n const within = direction === 'up' ? index - 1 : index + 1\n\n if (this.board.orderable && within >= 0 && within < count) {\n return this.hover({ column, lane, index: within })\n }\n\n const otherLane = lanes[laneIndex + (direction === 'up' ? -1 : 1)]\n\n if (otherLane === undefined || laneIndex < 0) return false\n\n const target = columns.find((candidate) => candidate.key === column)!\n const size = kanbanCardsIn(target, otherLane).length\n\n return this.hover({ column, lane: otherLane, index: direction === 'up' ? size : 0 })\n }\n\n const start = columns.findIndex((candidate) => candidate.key === column)\n const delta = direction === 'previous' ? -1 : 1\n let first: string | null = null\n\n for (let at = start + delta; at >= 0 && at < columns.length; at += delta) {\n const candidate = columns[at]!\n\n if (this.isCollapsed(candidate.key)) continue\n\n first ??= candidate.key\n\n const size = kanbanCardsIn(candidate, lane).length\n const target = { column: candidate.key, lane, index: Math.min(index, size) }\n\n if (!this.refusal(target)) {\n return this.hover(target)\n }\n }\n\n if (first) {\n this.store.update({ announcement: this.refusedMessage(picked.key, first) })\n }\n\n return false\n }\n\n /**\n * Keyboard handling for a focused card. Space picks a card up and drops it, arrow keys\n * move the focus or the picked card (mirrored right to left), Escape cancels, Enter\n * opens the card.\n */\n handleKey(\n key: string,\n event: { key: string },\n direction: 'ltr' | 'rtl' = 'ltr',\n ): KanbanKeyResult {\n const next = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight'\n const previous = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft'\n const picked = this.state.picked\n\n if (picked && picked.key === key) {\n switch (event.key) {\n case ' ':\n case 'Spacebar':\n case 'Enter':\n void this.drop()\n return { handled: true, focus: key }\n case 'Escape':\n this.cancel()\n return { handled: true, focus: key }\n case 'ArrowUp':\n this.step('up')\n return { handled: true, focus: key }\n case 'ArrowDown':\n this.step('down')\n return { handled: true, focus: key }\n case next:\n this.step('next')\n return { handled: true, focus: key }\n case previous:\n this.step('previous')\n return { handled: true, focus: key }\n case 'Tab':\n this.cancel()\n return { handled: false }\n default:\n return { handled: true, focus: key }\n }\n }\n\n switch (event.key) {\n case ' ':\n case 'Spacebar':\n if (!this.pick(key)) {\n this.store.update({\n announcement: this.env.t(\n 'kanban.not_allowed',\n {\n title: this.title(key),\n column: locateKanbanCard(this.state.columns, key)?.column.label ?? '',\n },\n ':title cannot be moved to :column.',\n ),\n })\n }\n return { handled: true, focus: key }\n case 'Enter':\n return { handled: true, open: true }\n case 'ArrowUp':\n case 'ArrowDown':\n case 'Home':\n case 'End':\n return { handled: true, focus: this.focusWithin(key, event.key) }\n case next:\n case previous:\n return { handled: true, focus: this.focusAcross(key, event.key === next ? 1 : -1) }\n default:\n return { handled: false }\n }\n }\n\n private focusWithin(key: string, keyName: string): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return key\n\n const cards = kanbanCardsIn(found.column, found.lane)\n\n switch (keyName) {\n case 'Home':\n return cards[0]?.key ?? key\n case 'End':\n return cards[cards.length - 1]?.key ?? key\n case 'ArrowUp':\n return cards[found.laneIndex - 1]?.key ?? key\n default:\n return cards[found.laneIndex + 1]?.key ?? key\n }\n }\n\n private focusAcross(key: string, delta: number): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return key\n\n const columns = this.state.columns\n\n for (let at = found.columnIndex + delta; at >= 0 && at < columns.length; at += delta) {\n const column = columns[at]!\n\n if (this.isCollapsed(column.key)) continue\n\n const cards = kanbanCardsIn(column, found.lane)\n\n if (cards.length > 0) {\n return cards[Math.min(found.laneIndex, cards.length - 1)]!.key\n }\n }\n\n return key\n }\n\n // ------------------------------------------------------------- Actions\n\n /** Open a card: its URL, else the card action with its record. */\n open(key: string): void {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found || found.card.clickable === false) return\n\n if (found.card.url) {\n this.env.visit?.(found.card.url)\n return\n }\n\n if (this.board.cardAction) {\n void this.env.runner.trigger(this.board.cardAction, {\n token: this.env.token,\n component: this.board.actionComponent,\n record: key,\n })\n }\n }\n\n /** Open the quick-create modal for a column (and lane). */\n create(column: string, lane: string | null = null): void {\n if (!this.board.createAction) return\n\n void this.env.runner.trigger(this.board.createAction, {\n token: this.env.token,\n component: this.board.actionComponent,\n arguments: lane === null ? { column } : { column, lane },\n })\n }\n\n // ------------------------------------------------------------- Search, filters, polling\n\n setSearch(search: string, delay = 300): void {\n this.store.update({ search })\n this.schedule(delay)\n }\n\n setFilters(filters: Record<string, unknown> | null, delay = 300): void {\n this.store.update({ filters })\n this.schedule(delay)\n }\n\n private schedule(delay: number): void {\n if (this.searchTimer !== null) clearTimeout(this.searchTimer)\n\n this.searchTimer = setTimeout(() => {\n this.searchTimer = null\n void this.refresh()\n }, delay)\n }\n\n /** Fetch the board again with the search and filters (skipped while a card moves). */\n async refresh(): Promise<void> {\n if (this.state.picked || this.state.saving.size > 0) {\n this.refreshAfterMove = true\n return\n }\n\n const id = ++this.fetchId\n const action: ActionSchema = { type: 'Action', name: this.board.fetchAction, hasAction: true }\n const state = this.state\n\n this.store.update({ loading: true })\n\n try {\n const result = await this.env.runner.executeJson<KanbanFetchResult>(action, {\n token: this.env.token,\n component: this.board.actionComponent,\n arguments: {\n [this.board.argumentsKey]: {\n search: state.search,\n ...(state.filters ? { filters: state.filters } : {}),\n },\n },\n })\n\n // A newer request or a move started meanwhile: its answer wins.\n if (id !== this.fetchId || !result?.board) return\n\n if (this.state.picked || this.state.saving.size > 0) {\n this.pendingBoard = null\n this.refreshAfterMove = true\n return\n }\n\n this.applyBoard({ ...result.board, search: this.state.search })\n } catch {\n if (id === this.fetchId) {\n this.env.error(this.env.t('kanban.load_failed', {}, 'The board could not be loaded.'))\n }\n } finally {\n if (id === this.fetchId) this.store.update({ loading: false })\n }\n }\n\n /** Poll the board every `pollingInterval`; returns a function that stops. */\n startPolling(): () => void {\n if (!this.board.pollingInterval) return () => {}\n\n const scheduler = new PollScheduler(() => this.refresh())\n\n return scheduler.subscribe(this.board.actionComponent, this.board.pollingInterval)\n }\n\n destroy(): void {\n if (this.searchTimer !== null) clearTimeout(this.searchTimer)\n this.searchTimer = null\n }\n\n // ------------------------------------------------------------- Announcements\n\n private title(key: string): string {\n return locateKanbanCard(this.state.columns, key)?.card.title ?? ''\n }\n\n private describe(translationKey: string, key: string, fallback: string): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return ''\n\n return this.env.t(\n translationKey,\n {\n title: found.card.title,\n column: found.column.label,\n position: found.laneIndex + 1,\n count: found.laneCount,\n },\n fallback,\n )\n }\n\n private refusedMessage(key: string, column: string): string {\n const target = this.state.columns.find((candidate) => candidate.key === column)\n\n return this.env.t(\n 'kanban.not_allowed',\n { title: this.title(key), column: target?.label ?? column },\n ':title cannot be moved to :column.',\n )\n }\n}\n\n// ----------------------------------------------------------------- Pointer dragging\n\nexport interface KanbanPointerOptions {\n /** `document.elementFromPoint` (injectable for tests). */\n elementFromPoint?: (x: number, y: number) => Element | null\n /** Milliseconds a touch must rest on a card before it drags (shorter moves scroll). */\n touchDelay?: number\n}\n\nconst CARD = '[data-slot=\"kanban-card\"]'\nconst DROPZONE = '[data-slot=\"kanban-dropzone\"]'\nconst SCROLLER = '[data-slot=\"kanban-scroller\"]'\nconst NO_DRAG =\n 'button, a, input, textarea, select, label, [contenteditable], [data-kanban-no-drag]'\n\n/**\n * Drag cards with a mouse, pen or finger. The board root delegates `pointerdown`; cards\n * are `[data-slot=\"kanban-card\"][data-key]` (not `[data-movable=\"false\"]`) inside\n * `[data-slot=\"kanban-dropzone\"][data-column][data-lane]` lists. A touch drags after a\n * short press (a quick swipe scrolls); a mouse after a few pixels. While dragging, a copy\n * of the card follows the pointer, the card itself moves to where it would land, the board\n * scrolls near its edges and Escape cancels. Returns a function that detaches everything.\n */\nexport function attachKanbanPointer(\n root: HTMLElement,\n controller: KanbanController,\n options: KanbanPointerOptions = {},\n): () => void {\n const doc = root.ownerDocument\n const view = doc.defaultView ?? window\n const fromPoint =\n options.elementFromPoint ?? ((x: number, y: number) => doc.elementFromPoint(x, y))\n const touchDelay = options.touchDelay ?? 220\n\n let pending: {\n id: number\n key: string\n element: HTMLElement\n x: number\n y: number\n touch: boolean\n timer: ReturnType<typeof setTimeout> | null\n } | null = null\n let drag: {\n id: number\n key: string\n ghost: HTMLElement\n offsetX: number\n offsetY: number\n x: number\n y: number\n frame: number | null\n } | null = null\n\n const preventTouch = (event: Event) => {\n if (drag) event.preventDefault()\n }\n\n const suppressClick = (event: Event) => {\n event.preventDefault()\n event.stopPropagation()\n }\n\n const begin = () => {\n if (!pending) return\n\n const { element, key, x, y, id, touch } = pending\n pending.timer = null\n\n if (!controller.pick(key, 'pointer')) {\n pending = null\n return\n }\n\n const box = element.getBoundingClientRect()\n const ghost = element.cloneNode(true) as HTMLElement\n\n ghost.removeAttribute('id')\n ghost.setAttribute('data-slot', 'kanban-ghost')\n ghost.removeAttribute('data-picked')\n ghost.setAttribute('aria-hidden', 'true')\n ghost.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'))\n Object.assign(ghost.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: `${box.width}px`,\n margin: '0',\n pointerEvents: 'none',\n zIndex: '60',\n transform: `translate(${box.left}px, ${box.top}px)`,\n })\n doc.body.appendChild(ghost)\n doc.body.setAttribute('data-kanban-dragging', '')\n\n drag = { id, key, ghost, offsetX: x - box.left, offsetY: y - box.top, x, y, frame: null }\n pending = null\n\n if (touch) (view.navigator as Navigator | undefined)?.vibrate?.(10)\n\n autoScroll()\n }\n\n const targetAt = (x: number, y: number) => {\n const zone = fromPoint(x, y)?.closest<HTMLElement>(DROPZONE)\n\n if (!zone || !root.contains(zone)) return null\n\n const column = zone.dataset.column\n\n if (!column) return null\n\n const lane = zone.dataset.lane ?? null\n const cards = Array.from(zone.querySelectorAll<HTMLElement>(CARD)).filter(\n (card) => card.dataset.key !== drag?.key,\n )\n let index = 0\n\n for (const card of cards) {\n const box = card.getBoundingClientRect()\n\n if (y > box.top + box.height / 2) index++\n }\n\n return { column, lane: lane === '' ? null : lane, index }\n }\n\n const position = (x: number, y: number) => {\n if (!drag) return\n\n drag.x = x\n drag.y = y\n drag.ghost.style.transform = `translate(${x - drag.offsetX}px, ${y - drag.offsetY}px) rotate(2deg)`\n\n const target = targetAt(x, y)\n\n if (target) controller.hover(target)\n }\n\n // Scroll the board (and the window) while the pointer rests near an edge.\n const autoScroll = () => {\n if (!drag) return\n\n const edge = 48\n const speed = 14\n const scroller = root.querySelector<HTMLElement>(SCROLLER)\n\n if (scroller) {\n const box = scroller.getBoundingClientRect()\n\n if (drag.x < box.left + edge) scroller.scrollLeft -= speed\n else if (drag.x > box.right - edge) scroller.scrollLeft += speed\n }\n\n const zone = fromPoint(drag.x, drag.y)?.closest<HTMLElement>(DROPZONE)\n\n if (zone && zone.scrollHeight > zone.clientHeight) {\n const box = zone.getBoundingClientRect()\n\n if (drag.y < box.top + edge) zone.scrollTop -= speed\n else if (drag.y > box.bottom - edge) zone.scrollTop += speed\n }\n\n if (drag.y < edge) view.scrollBy?.(0, -speed)\n else if (drag.y > view.innerHeight - edge) view.scrollBy?.(0, speed)\n\n drag.frame = view.requestAnimationFrame?.(autoScroll) ?? null\n }\n\n const finish = (cancelled: boolean) => {\n if (pending?.timer) clearTimeout(pending.timer)\n pending = null\n\n if (drag) {\n if (drag.frame !== null) view.cancelAnimationFrame?.(drag.frame)\n drag.ghost.remove()\n doc.body.removeAttribute('data-kanban-dragging')\n drag = null\n\n if (cancelled) {\n controller.cancel()\n } else {\n void controller.drop()\n // The release would also click the card: swallow that one click.\n view.addEventListener('click', suppressClick, { capture: true, once: true })\n setTimeout(() => view.removeEventListener('click', suppressClick, { capture: true }), 0)\n }\n }\n\n doc.removeEventListener('pointermove', onMove)\n doc.removeEventListener('pointerup', onUp)\n doc.removeEventListener('pointercancel', onCancel)\n doc.removeEventListener('keydown', onKey, true)\n doc.removeEventListener('touchmove', preventTouch)\n }\n\n const onMove = (event: PointerEvent) => {\n if (drag && event.pointerId === drag.id) {\n event.preventDefault()\n position(event.clientX, event.clientY)\n return\n }\n\n if (!pending || event.pointerId !== pending.id) return\n\n const distance = Math.hypot(event.clientX - pending.x, event.clientY - pending.y)\n\n if (pending.touch) {\n // Moving before the press settles is a scroll.\n if (distance > 8) finish(true)\n return\n }\n\n if (distance > 5) {\n begin()\n position(event.clientX, event.clientY)\n }\n }\n\n const onUp = (event: PointerEvent) => {\n if ((drag && event.pointerId === drag.id) || (pending && event.pointerId === pending.id)) {\n finish(false)\n }\n }\n\n const onCancel = () => finish(true)\n\n const onKey = (event: KeyboardEvent) => {\n if (drag && event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n finish(true)\n }\n }\n\n const onDown = (event: PointerEvent) => {\n if (event.button !== 0 || drag || pending) return\n\n const target = event.target as Element | null\n const card = target?.closest<HTMLElement>(CARD)\n\n if (!card || !root.contains(card) || card.dataset.movable === 'false') return\n\n const blocker = target?.closest(NO_DRAG)\n\n if (blocker && card.contains(blocker)) return\n\n const key = card.dataset.key\n\n if (!key) return\n\n const touch = event.pointerType === 'touch'\n\n pending = {\n id: event.pointerId,\n key,\n element: card,\n x: event.clientX,\n y: event.clientY,\n touch,\n timer: null,\n }\n\n if (touch) pending.timer = setTimeout(begin, touchDelay)\n\n doc.addEventListener('pointermove', onMove)\n doc.addEventListener('pointerup', onUp)\n doc.addEventListener('pointercancel', onCancel)\n doc.addEventListener('keydown', onKey, true)\n doc.addEventListener('touchmove', preventTouch, { passive: false })\n }\n\n // Native drag and drop would fight the pointer handling (images, selected text).\n const onNativeDrag = (event: Event) => {\n if ((event.target as Element | null)?.closest?.(CARD)) event.preventDefault()\n }\n\n root.addEventListener('pointerdown', onDown)\n root.addEventListener('dragstart', onNativeDrag)\n\n return () => {\n finish(true)\n root.removeEventListener('pointerdown', onDown)\n root.removeEventListener('dragstart', onNativeDrag)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"kanban.js","names":[],"sources":["../src/kanban.ts"],"sourcesContent":["import type { ActionContext, ActionRunner } from './actions'\nimport { FormStore } from './form'\nimport { HttpError } from './http'\nimport type { Translator } from './i18n'\nimport { PollScheduler } from './poll'\nimport type { ActionSchema } from './schema/actions'\nimport type {\n KanbanCardSchema,\n KanbanColumnSchema,\n KanbanFetchResult,\n KanbanLaneSchema,\n KanbanMoveResult,\n KanbanSchema,\n} from './schema/kanban'\nimport type { NotificationSchema } from './schema/notifications'\nimport { createStore, type Store } from './store'\n\n/**\n * The framework-agnostic half of the Kanban board (alfasix/alfakit-kanban): pure\n * operations on the columns, a `KanbanController` holding the working copy, the picked-up\n * card, the search and the requests, and `attachKanbanPointer()`, which drives mouse, pen\n * and touch dragging over the DOM every renderer emits. Renderers only draw the state.\n *\n * Moves are optimistic: the card moves at once and returns when the server refuses. The\n * server checks every move again (record, policy, column, transition, WIP limit, lane,\n * tenant); the checks here only keep the UI from offering moves it would refuse.\n */\n\n/** The registry key of the board component. */\nexport const KANBAN_REGISTRY_KEY = 'kanban'\n\n/** The lane key of cards without a swimlane value (`Swimlane::NONE`). */\nexport const KANBAN_NO_LANE = '__none'\n\n/** Where a card is, or should go: a column, a lane (null on boards without lanes) and a position in that lane. */\nexport interface KanbanTarget {\n column: string\n lane: string | null\n /** Position among the lane's cards in the column, from 0. */\n index: number\n}\n\n/** Why a move is not offered. */\nexport type KanbanRefusal = 'locked' | 'transition' | 'wip' | 'lane'\n\nexport interface KanbanLocation {\n column: KanbanColumnSchema\n columnIndex: number\n card: KanbanCardSchema\n /** Position in the column's card list. */\n index: number\n lane: string | null\n /** Position among the lane's cards in the column. */\n laneIndex: number\n laneCount: number\n}\n\n/** The cards of one lane of a column (every card when `lane` is null). */\nexport function kanbanCardsIn(column: KanbanColumnSchema, lane: string | null): KanbanCardSchema[] {\n return lane === null\n ? column.cards\n : column.cards.filter((card) => (card.lane ?? KANBAN_NO_LANE) === lane)\n}\n\nexport function locateKanbanCard(\n columns: KanbanColumnSchema[],\n key: string,\n): KanbanLocation | null {\n for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {\n const column = columns[columnIndex]!\n const index = column.cards.findIndex((card) => card.key === key)\n\n if (index >= 0) {\n const card = column.cards[index]!\n const lane = card.lane ?? null\n const laneCards = kanbanCardsIn(column, lane)\n\n return {\n column,\n columnIndex,\n card,\n index,\n lane,\n laneIndex: laneCards.indexOf(card),\n laneCount: laneCards.length,\n }\n }\n }\n\n return null\n}\n\n/** Where a card is now. */\nexport function kanbanCardPosition(\n columns: KanbanColumnSchema[],\n key: string,\n): KanbanTarget | null {\n const found = locateKanbanCard(columns, key)\n\n return found ? { column: found.column.key, lane: found.lane, index: found.laneIndex } : null\n}\n\nfunction sameTarget(a: KanbanTarget, b: KanbanTarget): boolean {\n return a.column === b.column && a.lane === b.lane && a.index === b.index\n}\n\n/**\n * The columns after moving a card to `target` (counts follow the card), or null when the\n * card or column doesn't exist.\n */\nexport function moveKanbanCard(\n columns: KanbanColumnSchema[],\n key: string,\n target: KanbanTarget,\n): KanbanColumnSchema[] | null {\n const from = locateKanbanCard(columns, key)\n const toIndex = columns.findIndex((column) => column.key === target.column)\n\n if (!from || toIndex < 0) {\n return null\n }\n\n const next = columns.map((column) => ({ ...column, cards: [...column.cards] }))\n const card: KanbanCardSchema =\n target.lane === null || target.lane === from.lane\n ? from.card\n : { ...from.card, lane: target.lane }\n\n next[from.columnIndex]!.cards.splice(from.index, 1)\n\n const destination = next[toIndex]!\n const laneCards = kanbanCardsIn(destination, target.lane)\n let at: number\n\n if (laneCards.length === 0) {\n at = target.lane === null ? 0 : destination.cards.length\n } else if (target.index >= laneCards.length) {\n at = destination.cards.indexOf(laneCards[laneCards.length - 1]!) + 1\n } else {\n at = destination.cards.indexOf(laneCards[Math.max(0, target.index)]!)\n }\n\n destination.cards.splice(at, 0, card)\n\n if (from.columnIndex !== toIndex) {\n const source = next[from.columnIndex]!\n source.count = Math.max(0, source.count - 1)\n source.total = Math.max(0, source.total - 1)\n destination.count += 1\n destination.total += 1\n }\n\n return next\n}\n\n/**\n * Why the board would refuse moving a card from `origin` (where it was picked up) to\n * `target`, or null when the move is offered. `columns` is the working copy, which may\n * already show the card elsewhere.\n */\nexport function kanbanRefusal(\n board: Pick<KanbanSchema, 'movable' | 'transitions' | 'laneMovable'>,\n columns: KanbanColumnSchema[],\n key: string,\n origin: KanbanTarget,\n target: KanbanTarget,\n): KanbanRefusal | null {\n const found = locateKanbanCard(columns, key)\n\n if (!board.movable || !found || found.card.movable === false) {\n return 'locked'\n }\n\n if (target.column !== origin.column) {\n const allowed = board.transitions?.[origin.column]\n\n if (allowed && !allowed.includes(target.column)) {\n return 'transition'\n }\n\n const column = columns.find((candidate) => candidate.key === target.column)\n\n if (column?.wipStrict && column.wipLimit !== undefined) {\n const held = column.count - (found.column.key === column.key ? 1 : 0)\n\n if (held >= column.wipLimit) {\n return 'wip'\n }\n }\n }\n\n if (target.lane !== origin.lane && !board.laneMovable) {\n return 'lane'\n }\n\n return null\n}\n\n/**\n * The move as the server's move action reads it: the target column and lane, and the\n * neighbour the card follows (or precedes) in its column.\n */\nexport function kanbanMovePayload(\n board: Pick<KanbanSchema, 'orderable' | 'lanes'>,\n columns: KanbanColumnSchema[],\n key: string,\n): Record<string, unknown> {\n const found = locateKanbanCard(columns, key)\n\n if (!found) {\n return {}\n }\n\n const payload: Record<string, unknown> = { column: found.column.key }\n\n if (board.lanes && found.lane !== null) {\n payload.lane = found.lane\n }\n\n if (!board.orderable) {\n return payload\n }\n\n const after = found.column.cards[found.index - 1]\n const before = found.column.cards[found.index + 1]\n\n if (after) payload.after = after.key\n else if (before) payload.before = before.key\n else payload.position = 'end'\n\n return payload\n}\n\n/** The initials of a name, for avatars without an image. */\nexport function kanbanInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n const letters = parts.length > 1 ? [parts[0]!, parts[parts.length - 1]!] : parts.slice(0, 1)\n\n return letters\n .map((part) => Array.from(part)[0] ?? '')\n .join('')\n .toUpperCase()\n}\n\n/** `count` against `wipLimit`: `over`, `full` or null. */\nexport function kanbanWipState(column: KanbanColumnSchema): 'over' | 'full' | null {\n if (column.wipLimit === undefined) return null\n if (column.count > column.wipLimit) return 'over'\n\n return column.count === column.wipLimit ? 'full' : null\n}\n\n/** How many filters of a filters form value hold something. */\nexport function kanbanActiveFilters(values: Record<string, unknown> | null | undefined): number {\n const filled = (value: unknown): boolean =>\n Array.isArray(value)\n ? value.length > 0\n : value !== null && value !== undefined && value !== '' && value !== false\n\n return Object.values(values ?? {}).filter(\n (state) => state !== null && typeof state === 'object' && Object.values(state).some(filled),\n ).length\n}\n\n/**\n * The form store of the board's filters (null without filters). Searchable selects load\n * their options from the schema the server names (pages only).\n */\nexport function createKanbanFiltersStore(\n board: Pick<KanbanSchema, 'filters' | 'actionComponent'>,\n token: string,\n optionsUrl?: string,\n): FormStore | null {\n if (!board.filters) return null\n\n return new FormStore(\n {\n components: board.filters.components,\n state: board.filters.state,\n columns: board.filters.columns,\n },\n {\n token,\n component: board.filters.component ?? `${board.actionComponent}.kanbanFilters`,\n refreshUrl: undefined,\n optionsUrl: board.filters.component ? optionsUrl : undefined,\n },\n )\n}\n\nexport interface KanbanPicked {\n key: string\n /** Where the card was picked up (it returns there on cancel or a refused move). */\n origin: KanbanTarget\n /** Where it is shown now. */\n target: KanbanTarget\n via: 'keyboard' | 'pointer'\n}\n\nexport interface KanbanState {\n /** The working copy of the columns. */\n columns: KanbanColumnSchema[]\n collapsed: ReadonlySet<string>\n collapsedLanes: ReadonlySet<string>\n picked: KanbanPicked | null\n /** The column the picked card may not enter, and why (for styling a refused target). */\n refused: { column: string; reason: KanbanRefusal } | null\n /** Cards whose move is being saved. */\n saving: ReadonlySet<string>\n search: string\n /** The filters form values the board was last asked for; null for the defaults. */\n filters: Record<string, unknown> | null\n loading: boolean\n /** For a polite live region. */\n announcement: string\n}\n\nexport interface KanbanEnvironment {\n runner: Pick<ActionRunner, 'executeJson' | 'trigger'>\n token: string\n t: Translator['t']\n notify(notifications: NotificationSchema[]): void\n error(message: string): void\n /** Follow a card's URL (an Inertia visit). */\n visit?(url: string): void\n}\n\nexport interface KanbanKeyResult {\n handled: boolean\n /** The card to focus next. */\n focus?: string\n /** Open the card (Enter). */\n open?: boolean\n}\n\ntype Direction = 'up' | 'down' | 'previous' | 'next'\n\n/**\n * The state of one rendered board. `setBoard()` takes a fresh schema from the server\n * (after an action ran or a poll); collapsed columns and the search are kept.\n */\nexport class KanbanController {\n public readonly store: Store<KanbanState>\n\n private searchTimer: ReturnType<typeof setTimeout> | null = null\n private pendingBoard: KanbanSchema | null = null\n private refreshAfterMove = false\n private fetchId = 0\n\n constructor(\n private board: KanbanSchema,\n private env: KanbanEnvironment,\n ) {\n this.store = createStore<KanbanState>({\n columns: board.columns,\n collapsed: new Set(board.columns.filter((column) => column.collapsed).map((c) => c.key)),\n collapsedLanes: new Set(\n (board.lanes ?? []).filter((lane) => lane.collapsed).map((l) => l.key),\n ),\n picked: null,\n refused: null,\n saving: new Set(),\n search: board.search ?? '',\n filters: null,\n loading: false,\n announcement: '',\n })\n }\n\n get state(): KanbanState {\n return this.store.get()\n }\n\n get schema(): KanbanSchema {\n return this.board\n }\n\n /** The renderer's latest runner, token and translator. */\n setEnvironment(env: KanbanEnvironment): void {\n this.env = env\n }\n\n /** The rows to draw: the board's lanes, or one row (null) without swimlanes. */\n lanes(): Array<KanbanLaneSchema | null> {\n return this.board.lanes && this.board.lanes.length > 0 ? this.board.lanes : [null]\n }\n\n /** The schema changed on the server (an action ran): show it, keeping the user's view. */\n setBoard(board: KanbanSchema): void {\n if (this.state.picked || this.state.saving.size > 0) {\n this.pendingBoard = board\n return\n }\n\n // The server draws the default view; ask again for the user's search and filters.\n if (this.isQueried()) {\n this.board = { ...board, columns: this.board.columns, lanes: this.board.lanes }\n void this.refresh()\n return\n }\n\n this.applyBoard(board)\n }\n\n private isQueried(): boolean {\n return this.state.search !== (this.board.search ?? '') || this.state.filters !== null\n }\n\n private applyBoard(board: KanbanSchema): void {\n const known = new Set(this.board.columns.map((column) => column.key))\n const collapsed = new Set(\n [...this.state.collapsed].filter((key) => board.columns.some((c) => c.key === key)),\n )\n\n for (const column of board.columns) {\n if (!known.has(column.key) && column.collapsed) collapsed.add(column.key)\n }\n\n this.board = board\n this.store.update({ columns: board.columns, collapsed })\n }\n\n // ------------------------------------------------------------- Columns and lanes\n\n isCollapsed(column: string): boolean {\n return this.state.collapsed.has(column)\n }\n\n toggleColumn(column: string, collapsed = !this.isCollapsed(column)): void {\n const next = new Set(this.state.collapsed)\n\n if (collapsed) next.add(column)\n else next.delete(column)\n\n this.store.update({ collapsed: next })\n }\n\n toggleLane(lane: string, collapsed = !this.state.collapsedLanes.has(lane)): void {\n const next = new Set(this.state.collapsedLanes)\n\n if (collapsed) next.add(lane)\n else next.delete(lane)\n\n this.store.update({ collapsedLanes: next })\n }\n\n // ------------------------------------------------------------- Moving\n\n canPick(key: string): boolean {\n const found = locateKanbanCard(this.state.columns, key)\n\n return Boolean(this.board.movable && found && found.card.movable !== false)\n }\n\n /** Pick a card up (Space, or the start of a drag). */\n pick(key: string, via: KanbanPicked['via'] = 'keyboard'): boolean {\n const origin = kanbanCardPosition(this.state.columns, key)\n\n if (!origin || !this.canPick(key) || this.state.saving.has(key)) {\n return false\n }\n\n this.store.update({\n picked: { key, origin, target: origin, via },\n refused: null,\n announcement: this.describe(\n 'kanban.picked_up',\n key,\n ':title picked up in :column, position :position of :count.',\n ),\n })\n\n return true\n }\n\n /** Why the picked card may not go to `target`, or null. */\n refusal(target: KanbanTarget): KanbanRefusal | null {\n const picked = this.state.picked\n\n if (!picked) return 'locked'\n\n return kanbanRefusal(this.board, this.state.columns, picked.key, picked.origin, target)\n }\n\n /** Show the picked card at `target` (a preview until it is dropped). */\n hover(target: KanbanTarget): boolean {\n const picked = this.state.picked\n\n if (!picked) return false\n if (sameTarget(picked.target, target)) return true\n\n const reason = this.refusal(target)\n\n if (reason) {\n this.store.update({\n refused: { column: target.column, reason },\n announcement:\n picked.via === 'keyboard'\n ? this.refusedMessage(picked.key, target.column)\n : this.state.announcement,\n })\n\n return false\n }\n\n const columns = moveKanbanCard(this.state.columns, picked.key, target)\n\n if (!columns) return false\n\n const position = kanbanCardPosition(columns, picked.key) ?? target\n\n this.store.update({\n columns,\n picked: { ...picked, target: position },\n refused: null,\n })\n\n if (picked.via === 'keyboard') {\n this.store.update({\n announcement: this.describe(\n 'kanban.moved',\n picked.key,\n ':title is in :column, position :position of :count.',\n ),\n })\n }\n\n return true\n }\n\n /** Put the picked card back where it was picked up. */\n cancel(): void {\n const picked = this.state.picked\n\n if (!picked) return\n\n const columns =\n moveKanbanCard(this.state.columns, picked.key, picked.origin) ?? this.state.columns\n const column = columns.find((candidate) => candidate.key === picked.origin.column)\n\n this.store.update({\n columns,\n picked: null,\n refused: null,\n announcement: this.env.t(\n 'kanban.cancelled',\n { title: this.title(picked.key), column: column?.label ?? '' },\n 'Move cancelled. :title is back in :column.',\n ),\n })\n this.flushPending()\n }\n\n /** Drop the picked card where it is shown and save the move. */\n async drop(): Promise<boolean> {\n const picked = this.state.picked\n\n if (!picked) return false\n\n if (sameTarget(picked.origin, picked.target)) {\n this.store.update({\n picked: null,\n refused: null,\n announcement: this.describe(\n 'kanban.dropped',\n picked.key,\n ':title dropped in :column, position :position of :count.',\n ),\n })\n this.flushPending()\n\n return false\n }\n\n const saving = new Set(this.state.saving).add(picked.key)\n\n this.store.update({\n picked: null,\n refused: null,\n saving,\n announcement: this.describe(\n 'kanban.dropped',\n picked.key,\n ':title dropped in :column, position :position of :count.',\n ),\n })\n\n return this.save(picked.key, picked.origin)\n }\n\n /** Move a card in one step (the keyboard, or a drop the renderer worked out itself). */\n async moveTo(key: string, target: KanbanTarget): Promise<boolean> {\n if (!this.pick(key, 'pointer')) return false\n if (!this.hover(target)) {\n this.cancel()\n return false\n }\n\n return this.drop()\n }\n\n private async save(key: string, origin: KanbanTarget): Promise<boolean> {\n const action: ActionSchema = { type: 'Action', name: this.board.moveAction, hasAction: true }\n const context: ActionContext = {\n token: this.env.token,\n component: this.board.actionComponent,\n record: key,\n arguments: {\n [this.board.argumentsKey]: kanbanMovePayload(this.board, this.state.columns, key),\n },\n }\n\n try {\n const result = await this.env.runner.executeJson<KanbanMoveResult>(action, context)\n\n if (result && result.ok === false) {\n throw new Error('refused')\n }\n\n this.applySaved(key, result)\n this.env.notify(result?.notifications ?? [])\n\n return true\n } catch (error) {\n const columns = moveKanbanCard(this.state.columns, key, origin) ?? this.state.columns\n const message =\n error instanceof HttpError && error.status === 422\n ? // A `ValidationException` from `beforeCardMoved()` under any key (`move`).\n (error.errors[this.board.argumentsKey]?.[0] ??\n Object.values(error.errors).find((messages) => messages?.[0])?.[0])\n : error instanceof HttpError && error.status === 403\n ? this.env.t('kanban.forbidden', {}, 'You are not allowed to move this card.')\n : undefined\n const text = message ?? this.env.t('kanban.move_failed', {}, 'The card could not be moved.')\n\n this.store.update({ columns, announcement: text })\n this.env.error(text)\n\n // The board changed under us: show the server's version.\n if (error instanceof HttpError && error.status === 422) this.refreshAfterMove = true\n\n return false\n } finally {\n const saving = new Set(this.state.saving)\n saving.delete(key)\n this.store.update({ saving })\n this.flushPending()\n }\n }\n\n private applySaved(key: string, result: KanbanMoveResult | null | undefined): void {\n const fresh = result?.card\n const counts = result?.counts ?? {}\n const columns = this.state.columns.map((column) => {\n const count = counts[column.key]\n const index = fresh ? column.cards.findIndex((card) => card.key === key) : -1\n\n if (count === undefined && index < 0) return column\n\n const cards =\n index >= 0 && fresh\n ? column.cards.map((card, at) =>\n at === index ? { ...fresh, lane: fresh.lane ?? card.lane } : card,\n )\n : column.cards\n\n return { ...column, cards, ...(count !== undefined ? { count } : {}) }\n })\n\n this.store.update({ columns })\n }\n\n private flushPending(): void {\n if (this.state.picked || this.state.saving.size > 0) return\n\n const board = this.pendingBoard\n this.pendingBoard = null\n\n if (board) {\n this.setBoard(board)\n } else if (this.refreshAfterMove) {\n this.refreshAfterMove = false\n void this.refresh()\n }\n }\n\n /** Move the picked card one step: within its lane, to the next lane or column. */\n step(direction: Direction): boolean {\n const picked = this.state.picked\n\n if (!picked) return false\n\n const { column, lane, index } = picked.target\n const columns = this.state.columns\n const current = columns.find((candidate) => candidate.key === column)\n const count = current ? kanbanCardsIn(current, lane).length : 0\n const lanes = this.lanes().map((row) => row?.key ?? null)\n const laneIndex = lanes.indexOf(lane)\n\n if (direction === 'up' || direction === 'down') {\n const within = direction === 'up' ? index - 1 : index + 1\n\n if (this.board.orderable && within >= 0 && within < count) {\n return this.hover({ column, lane, index: within })\n }\n\n const otherLane = lanes[laneIndex + (direction === 'up' ? -1 : 1)]\n\n if (otherLane === undefined || laneIndex < 0) return false\n\n const target = columns.find((candidate) => candidate.key === column)!\n const size = kanbanCardsIn(target, otherLane).length\n\n return this.hover({ column, lane: otherLane, index: direction === 'up' ? size : 0 })\n }\n\n const start = columns.findIndex((candidate) => candidate.key === column)\n const delta = direction === 'previous' ? -1 : 1\n let first: string | null = null\n\n for (let at = start + delta; at >= 0 && at < columns.length; at += delta) {\n const candidate = columns[at]!\n\n if (this.isCollapsed(candidate.key)) continue\n\n first ??= candidate.key\n\n const size = kanbanCardsIn(candidate, lane).length\n const target = { column: candidate.key, lane, index: Math.min(index, size) }\n\n if (!this.refusal(target)) {\n return this.hover(target)\n }\n }\n\n if (first) {\n this.store.update({ announcement: this.refusedMessage(picked.key, first) })\n }\n\n return false\n }\n\n /**\n * Keyboard handling for a focused card. Space picks a card up and drops it, arrow keys\n * move the focus or the picked card (mirrored right to left), Escape cancels, Enter\n * opens the card.\n */\n handleKey(\n key: string,\n event: { key: string },\n direction: 'ltr' | 'rtl' = 'ltr',\n ): KanbanKeyResult {\n const next = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight'\n const previous = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft'\n const picked = this.state.picked\n\n if (picked && picked.key === key) {\n switch (event.key) {\n case ' ':\n case 'Spacebar':\n case 'Enter':\n void this.drop()\n return { handled: true, focus: key }\n case 'Escape':\n this.cancel()\n return { handled: true, focus: key }\n case 'ArrowUp':\n this.step('up')\n return { handled: true, focus: key }\n case 'ArrowDown':\n this.step('down')\n return { handled: true, focus: key }\n case next:\n this.step('next')\n return { handled: true, focus: key }\n case previous:\n this.step('previous')\n return { handled: true, focus: key }\n case 'Tab':\n this.cancel()\n return { handled: false }\n default:\n return { handled: true, focus: key }\n }\n }\n\n switch (event.key) {\n case ' ':\n case 'Spacebar':\n if (!this.pick(key)) {\n this.store.update({\n announcement: this.env.t(\n 'kanban.not_allowed',\n {\n title: this.title(key),\n column: locateKanbanCard(this.state.columns, key)?.column.label ?? '',\n },\n ':title cannot be moved to :column.',\n ),\n })\n }\n return { handled: true, focus: key }\n case 'Enter':\n return { handled: true, open: true }\n case 'ArrowUp':\n case 'ArrowDown':\n case 'Home':\n case 'End':\n return { handled: true, focus: this.focusWithin(key, event.key) }\n case next:\n case previous:\n return { handled: true, focus: this.focusAcross(key, event.key === next ? 1 : -1) }\n default:\n return { handled: false }\n }\n }\n\n private focusWithin(key: string, keyName: string): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return key\n\n const cards = kanbanCardsIn(found.column, found.lane)\n\n switch (keyName) {\n case 'Home':\n return cards[0]?.key ?? key\n case 'End':\n return cards[cards.length - 1]?.key ?? key\n case 'ArrowUp':\n return cards[found.laneIndex - 1]?.key ?? key\n default:\n return cards[found.laneIndex + 1]?.key ?? key\n }\n }\n\n private focusAcross(key: string, delta: number): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return key\n\n const columns = this.state.columns\n\n for (let at = found.columnIndex + delta; at >= 0 && at < columns.length; at += delta) {\n const column = columns[at]!\n\n if (this.isCollapsed(column.key)) continue\n\n const cards = kanbanCardsIn(column, found.lane)\n\n if (cards.length > 0) {\n return cards[Math.min(found.laneIndex, cards.length - 1)]!.key\n }\n }\n\n return key\n }\n\n // ------------------------------------------------------------- Actions\n\n /** Open a card: its URL, else the card action with its record. */\n open(key: string): void {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found || found.card.clickable === false) return\n\n if (found.card.url) {\n this.env.visit?.(found.card.url)\n return\n }\n\n if (this.board.cardAction) {\n void this.env.runner.trigger(this.board.cardAction, {\n token: this.env.token,\n component: this.board.actionComponent,\n record: key,\n })\n }\n }\n\n /** Open the quick-create modal for a column (and lane). */\n create(column: string, lane: string | null = null): void {\n if (!this.board.createAction) return\n\n void this.env.runner.trigger(this.board.createAction, {\n token: this.env.token,\n component: this.board.actionComponent,\n arguments: lane === null ? { column } : { column, lane },\n })\n }\n\n // ------------------------------------------------------------- Search, filters, polling\n\n setSearch(search: string, delay = 300): void {\n this.store.update({ search })\n this.schedule(delay)\n }\n\n setFilters(filters: Record<string, unknown> | null, delay = 300): void {\n this.store.update({ filters })\n this.schedule(delay)\n }\n\n private schedule(delay: number): void {\n if (this.searchTimer !== null) clearTimeout(this.searchTimer)\n\n this.searchTimer = setTimeout(() => {\n this.searchTimer = null\n void this.refresh()\n }, delay)\n }\n\n /** Fetch the board again with the search and filters (skipped while a card moves). */\n async refresh(): Promise<void> {\n if (this.state.picked || this.state.saving.size > 0) {\n this.refreshAfterMove = true\n return\n }\n\n const id = ++this.fetchId\n const action: ActionSchema = { type: 'Action', name: this.board.fetchAction, hasAction: true }\n const state = this.state\n\n this.store.update({ loading: true })\n\n try {\n const result = await this.env.runner.executeJson<KanbanFetchResult>(action, {\n token: this.env.token,\n component: this.board.actionComponent,\n arguments: {\n [this.board.argumentsKey]: {\n search: state.search,\n ...(state.filters ? { filters: state.filters } : {}),\n },\n },\n })\n\n // A newer request or a move started meanwhile: its answer wins.\n if (id !== this.fetchId || !result?.board) return\n\n if (this.state.picked || this.state.saving.size > 0) {\n this.pendingBoard = null\n this.refreshAfterMove = true\n return\n }\n\n this.applyBoard({ ...result.board, search: this.state.search })\n } catch {\n if (id === this.fetchId) {\n this.env.error(this.env.t('kanban.load_failed', {}, 'The board could not be loaded.'))\n }\n } finally {\n if (id === this.fetchId) this.store.update({ loading: false })\n }\n }\n\n /** Poll the board every `pollingInterval`; returns a function that stops. */\n startPolling(): () => void {\n if (!this.board.pollingInterval) return () => {}\n\n const scheduler = new PollScheduler(() => this.refresh())\n\n return scheduler.subscribe(this.board.actionComponent, this.board.pollingInterval)\n }\n\n destroy(): void {\n if (this.searchTimer !== null) clearTimeout(this.searchTimer)\n this.searchTimer = null\n }\n\n // ------------------------------------------------------------- Announcements\n\n private title(key: string): string {\n return locateKanbanCard(this.state.columns, key)?.card.title ?? ''\n }\n\n private describe(translationKey: string, key: string, fallback: string): string {\n const found = locateKanbanCard(this.state.columns, key)\n\n if (!found) return ''\n\n return this.env.t(\n translationKey,\n {\n title: found.card.title,\n column: found.column.label,\n position: found.laneIndex + 1,\n count: found.laneCount,\n },\n fallback,\n )\n }\n\n private refusedMessage(key: string, column: string): string {\n const target = this.state.columns.find((candidate) => candidate.key === column)\n\n return this.env.t(\n 'kanban.not_allowed',\n { title: this.title(key), column: target?.label ?? column },\n ':title cannot be moved to :column.',\n )\n }\n}\n\n// ----------------------------------------------------------------- Pointer dragging\n\nexport interface KanbanPointerOptions {\n /** `document.elementFromPoint` (injectable for tests). */\n elementFromPoint?: (x: number, y: number) => Element | null\n /** Milliseconds a touch must rest on a card before it drags (shorter moves scroll). */\n touchDelay?: number\n}\n\nconst CARD = '[data-slot=\"kanban-card\"]'\nconst DROPZONE = '[data-slot=\"kanban-dropzone\"]'\nconst SCROLLER = '[data-slot=\"kanban-scroller\"]'\nconst NO_DRAG =\n 'button, a, input, textarea, select, label, [contenteditable], [data-kanban-no-drag]'\n\n/**\n * Drag cards with a mouse, pen or finger. The board root delegates `pointerdown`; cards\n * are `[data-slot=\"kanban-card\"][data-key]` (not `[data-movable=\"false\"]`) inside\n * `[data-slot=\"kanban-dropzone\"][data-column][data-lane]` lists. A touch drags after a\n * short press (a quick swipe scrolls); a mouse after a few pixels. While dragging, a copy\n * of the card follows the pointer, the card itself moves to where it would land, the board\n * scrolls near its edges and Escape cancels. Returns a function that detaches everything.\n */\nexport function attachKanbanPointer(\n root: HTMLElement,\n controller: KanbanController,\n options: KanbanPointerOptions = {},\n): () => void {\n const doc = root.ownerDocument\n const view = doc.defaultView ?? window\n const fromPoint =\n options.elementFromPoint ?? ((x: number, y: number) => doc.elementFromPoint(x, y))\n const touchDelay = options.touchDelay ?? 220\n\n let pending: {\n id: number\n key: string\n element: HTMLElement\n x: number\n y: number\n touch: boolean\n timer: ReturnType<typeof setTimeout> | null\n } | null = null\n let drag: {\n id: number\n key: string\n ghost: HTMLElement\n offsetX: number\n offsetY: number\n x: number\n y: number\n frame: number | null\n } | null = null\n\n const preventTouch = (event: Event) => {\n if (drag) event.preventDefault()\n }\n\n const suppressClick = (event: Event) => {\n event.preventDefault()\n event.stopPropagation()\n }\n\n const begin = () => {\n if (!pending) return\n\n const { element, key, x, y, id, touch } = pending\n pending.timer = null\n\n if (!controller.pick(key, 'pointer')) {\n pending = null\n return\n }\n\n const box = element.getBoundingClientRect()\n const ghost = element.cloneNode(true) as HTMLElement\n\n ghost.removeAttribute('id')\n ghost.setAttribute('data-slot', 'kanban-ghost')\n ghost.removeAttribute('data-picked')\n ghost.setAttribute('aria-hidden', 'true')\n ghost.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'))\n Object.assign(ghost.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: `${box.width}px`,\n margin: '0',\n pointerEvents: 'none',\n zIndex: '60',\n transform: `translate(${box.left}px, ${box.top}px)`,\n })\n doc.body.appendChild(ghost)\n doc.body.setAttribute('data-kanban-dragging', '')\n\n drag = { id, key, ghost, offsetX: x - box.left, offsetY: y - box.top, x, y, frame: null }\n pending = null\n\n if (touch) (view.navigator as Navigator | undefined)?.vibrate?.(10)\n\n autoScroll()\n }\n\n const targetAt = (x: number, y: number) => {\n const zone = fromPoint(x, y)?.closest<HTMLElement>(DROPZONE)\n\n if (!zone || !root.contains(zone)) return null\n\n const column = zone.dataset.column\n\n if (!column) return null\n\n const lane = zone.dataset.lane ?? null\n const cards = Array.from(zone.querySelectorAll<HTMLElement>(CARD)).filter(\n (card) => card.dataset.key !== drag?.key,\n )\n let index = 0\n\n for (const card of cards) {\n const box = card.getBoundingClientRect()\n\n if (y > box.top + box.height / 2) index++\n }\n\n return { column, lane: lane === '' ? null : lane, index }\n }\n\n const position = (x: number, y: number) => {\n if (!drag) return\n\n drag.x = x\n drag.y = y\n drag.ghost.style.transform = `translate(${x - drag.offsetX}px, ${y - drag.offsetY}px) rotate(2deg)`\n\n const target = targetAt(x, y)\n\n if (target) controller.hover(target)\n }\n\n // Scroll the board (and the window) while the pointer rests near an edge.\n const autoScroll = () => {\n if (!drag) return\n\n const edge = 48\n const speed = 14\n const scroller = root.querySelector<HTMLElement>(SCROLLER)\n\n if (scroller) {\n const box = scroller.getBoundingClientRect()\n\n if (drag.x < box.left + edge) scroller.scrollLeft -= speed\n else if (drag.x > box.right - edge) scroller.scrollLeft += speed\n }\n\n const zone = fromPoint(drag.x, drag.y)?.closest<HTMLElement>(DROPZONE)\n\n if (zone && zone.scrollHeight > zone.clientHeight) {\n const box = zone.getBoundingClientRect()\n\n if (drag.y < box.top + edge) zone.scrollTop -= speed\n else if (drag.y > box.bottom - edge) zone.scrollTop += speed\n }\n\n if (drag.y < edge) view.scrollBy?.(0, -speed)\n else if (drag.y > view.innerHeight - edge) view.scrollBy?.(0, speed)\n\n drag.frame = view.requestAnimationFrame?.(autoScroll) ?? null\n }\n\n const finish = (cancelled: boolean) => {\n if (pending?.timer) clearTimeout(pending.timer)\n pending = null\n\n if (drag) {\n if (drag.frame !== null) view.cancelAnimationFrame?.(drag.frame)\n drag.ghost.remove()\n doc.body.removeAttribute('data-kanban-dragging')\n drag = null\n\n if (cancelled) {\n controller.cancel()\n } else {\n void controller.drop()\n // The release would also click the card: swallow that one click.\n view.addEventListener('click', suppressClick, { capture: true, once: true })\n setTimeout(() => view.removeEventListener('click', suppressClick, { capture: true }), 0)\n }\n }\n\n doc.removeEventListener('pointermove', onMove)\n doc.removeEventListener('pointerup', onUp)\n doc.removeEventListener('pointercancel', onCancel)\n doc.removeEventListener('keydown', onKey, true)\n doc.removeEventListener('touchmove', preventTouch)\n }\n\n const onMove = (event: PointerEvent) => {\n if (drag && event.pointerId === drag.id) {\n event.preventDefault()\n position(event.clientX, event.clientY)\n return\n }\n\n if (!pending || event.pointerId !== pending.id) return\n\n const distance = Math.hypot(event.clientX - pending.x, event.clientY - pending.y)\n\n if (pending.touch) {\n // Moving before the press settles is a scroll.\n if (distance > 8) finish(true)\n return\n }\n\n if (distance > 5) {\n begin()\n position(event.clientX, event.clientY)\n }\n }\n\n const onUp = (event: PointerEvent) => {\n if ((drag && event.pointerId === drag.id) || (pending && event.pointerId === pending.id)) {\n finish(false)\n }\n }\n\n const onCancel = () => finish(true)\n\n const onKey = (event: KeyboardEvent) => {\n if (drag && event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n finish(true)\n }\n }\n\n const onDown = (event: PointerEvent) => {\n if (event.button !== 0 || drag || pending) return\n\n const target = event.target as Element | null\n const card = target?.closest<HTMLElement>(CARD)\n\n if (!card || !root.contains(card) || card.dataset.movable === 'false') return\n\n const blocker = target?.closest(NO_DRAG)\n\n if (blocker && card.contains(blocker)) return\n\n const key = card.dataset.key\n\n if (!key) return\n\n const touch = event.pointerType === 'touch'\n\n pending = {\n id: event.pointerId,\n key,\n element: card,\n x: event.clientX,\n y: event.clientY,\n touch,\n timer: null,\n }\n\n if (touch) pending.timer = setTimeout(begin, touchDelay)\n\n doc.addEventListener('pointermove', onMove)\n doc.addEventListener('pointerup', onUp)\n doc.addEventListener('pointercancel', onCancel)\n doc.addEventListener('keydown', onKey, true)\n doc.addEventListener('touchmove', preventTouch, { passive: false })\n }\n\n // Native drag and drop would fight the pointer handling (images, selected text).\n const onNativeDrag = (event: Event) => {\n if ((event.target as Element | null)?.closest?.(CARD)) event.preventDefault()\n }\n\n root.addEventListener('pointerdown', onDown)\n root.addEventListener('dragstart', onNativeDrag)\n\n return () => {\n finish(true)\n root.removeEventListener('pointerdown', onDown)\n root.removeEventListener('dragstart', onNativeDrag)\n }\n}\n"],"mappings":"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"}
@@ -49,10 +49,18 @@ export declare function submitFormById(action: ActionSchema, context: ActionCont
49
49
  /** The paths `refreshFormData()` sent for this form, or null. */
50
50
  export declare function formRefreshFor(flash: Pick<AlfakitFlash, 'alfakitFormRefresh'>, component: string): Record<string, unknown> | null;
51
51
  /**
52
- * Copy refreshed values into a form store. The paths stop counting as edited (and lose
53
- * their errors); every other field keeps what the user typed.
52
+ * Copy refreshed values into a form store. The paths, and the fields below them, stop
53
+ * counting as edited (and lose their errors); every other field keeps what the user typed.
54
54
  */
55
55
  export declare function applyFormRefresh(store: FormStore, state: Record<string, unknown>): void;
56
+ /**
57
+ * Whether a page form reloads from the server's new schema after a visit it did not submit
58
+ * itself: another action (one embedded in a field, a table row in a relation manager)
59
+ * succeeded while the form held no edits. Its components may show server state that the
60
+ * action changed (a status, which actions are offered); with unsaved edits the form keeps
61
+ * what the user typed.
62
+ */
63
+ export declare function shouldResyncForm(flash: Pick<AlfakitFlash, 'alfakitAction'>, store: Pick<FormStore, 'isDirty'>): boolean;
56
64
  /** Whether a page form shows its buttons: always, except before a wizard form's last step. */
57
65
  export declare function formActionsVisible(block: Pick<FormBlock, 'wizard'>, progress: WizardProgress | null | undefined): boolean;
58
66
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"page-actions.d.ts","names":[],"sources":["../src/page-actions.ts"],"mappings":";;;;;;;;;;;;;;qBAkBa;;wBAcG,qBACd,MAAM,KAAK,sBACX,eAAe,sBACd;;;;;;wBAgBa,iBACd,MAAM,KAAK,mDACX;;wBAiBc,0BAA0B;iBAiBzB;;EAEf;;;;;;;wBAQc,kBACd,MAAM,wBACN,QAAQ,qBACR,UAAU,QAAQ,cAAc,SAAS,yBAAyB;KA6B/D,iBAAiB,QAAQ,cAAc,SAAS,yBAAyB;;;;;;wBAS9D,sBAAsB,mBAAmB,QAAQ;;;;;;wBAejD,eAAe,QAAQ,cAAc,SAAS;;wBAiB9C,eACd,OAAO,KAAK,qCACZ,oBACC;;;;;wBAUa,iBAAiB,OAAO,WAAW,OAAO;;wBAuB1C,mBACd,OAAO,KAAK,sBACZ,UAAU;;;;;;wBAcI,gBACd,cACA,kBACA,kBACA,OAAO"}
1
+ {"version":3,"file":"page-actions.d.ts","names":[],"sources":["../src/page-actions.ts"],"mappings":";;;;;;;;;;;;;;qBAkBa;;wBAcG,qBACd,MAAM,KAAK,sBACX,eAAe,sBACd;;;;;;wBAgBa,iBACd,MAAM,KAAK,mDACX;;wBAiBc,0BAA0B;iBAiBzB;;EAEf;;;;;;;wBAQc,kBACd,MAAM,wBACN,QAAQ,qBACR,UAAU,QAAQ,cAAc,SAAS,yBAAyB;KA6B/D,iBAAiB,QAAQ,cAAc,SAAS,yBAAyB;;;;;;wBAS9D,sBAAsB,mBAAmB,QAAQ;;;;;;wBAejD,eAAe,QAAQ,cAAc,SAAS;;wBAiB9C,eACd,OAAO,KAAK,qCACZ,oBACC;;;;;wBAUa,iBAAiB,OAAO,WAAW,OAAO;;;;;;;;wBAkC1C,iBACd,OAAO,KAAK,gCACZ,OAAO,KAAK;;wBAQE,mBACd,OAAO,KAAK,sBACZ,UAAU;;;;;;wBAcI,gBACd,cACA,kBACA,kBACA,OAAO"}
@@ -104,8 +104,8 @@ function formRefreshFor(flash, component) {
104
104
  return refresh && refresh.component === component && refresh.state ? refresh.state : null;
105
105
  }
106
106
  /**
107
- * Copy refreshed values into a form store. The paths stop counting as edited (and lose
108
- * their errors); every other field keeps what the user typed.
107
+ * Copy refreshed values into a form store. The paths, and the fields below them, stop
108
+ * counting as edited (and lose their errors); every other field keeps what the user typed.
109
109
  */
110
110
  function applyFormRefresh(store, state) {
111
111
  const paths = Object.keys(state);
@@ -116,8 +116,8 @@ function applyFormRefresh(store, state) {
116
116
  const errors = { ...current.errors };
117
117
  for (const path of paths) {
118
118
  values = setPath(values, path, state[path]);
119
- dirty.delete(path);
120
- delete errors[path];
119
+ for (const key of [...dirty]) if (key === path || key.startsWith(`${path}.`)) dirty.delete(key);
120
+ for (const key of Object.keys(errors)) if (key === path || key.startsWith(`${path}.`)) delete errors[key];
121
121
  }
122
122
  return {
123
123
  ...current,
@@ -127,6 +127,16 @@ function applyFormRefresh(store, state) {
127
127
  };
128
128
  });
129
129
  }
130
+ /**
131
+ * Whether a page form reloads from the server's new schema after a visit it did not submit
132
+ * itself: another action (one embedded in a field, a table row in a relation manager)
133
+ * succeeded while the form held no edits. Its components may show server state that the
134
+ * action changed (a status, which actions are offered); with unsaved edits the form keeps
135
+ * what the user typed.
136
+ */
137
+ function shouldResyncForm(flash, store) {
138
+ return flash.alfakitAction?.status === "success" && !store.isDirty;
139
+ }
130
140
  /** Whether a page form shows its buttons: always, except before a wizard form's last step. */
131
141
  function formActionsVisible(block, progress) {
132
142
  if (!block.wizard || !progress || progress.count === 0) return true;
@@ -153,6 +163,6 @@ function chartFiltersUrl(href, queryKey, widgetId, state) {
153
163
  return /^https?:\/\//.test(href) ? url.toString() : relative;
154
164
  }
155
165
  //#endregion
156
- export { DEFAULT_ACTION_QUERY_KEYS, applyFormRefresh, chartFiltersUrl, defaultActionContext, defaultActionKey, formActionsVisible, formRefreshFor, openDefaultAction, registerFormSubmitter, submitFormById, withoutDefaultActionQuery };
166
+ export { DEFAULT_ACTION_QUERY_KEYS, applyFormRefresh, chartFiltersUrl, defaultActionContext, defaultActionKey, formActionsVisible, formRefreshFor, openDefaultAction, registerFormSubmitter, shouldResyncForm, submitFormById, withoutDefaultActionQuery };
157
167
 
158
168
  //# sourceMappingURL=page-actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-actions.js","names":[],"sources":["../src/page-actions.ts"],"sourcesContent":["import type { ActionContext } from './actions'\nimport type { AlfakitFlash } from './flash'\nimport type { FormStore } from './form'\nimport type { WizardProgress } from './modal-actions'\nimport type { ActionSchema } from './schema/actions'\nimport type { FormBlock } from './schema/form'\nimport type { DefaultActionSchema, PageSchema } from './schema/panel'\nimport { setPath } from './utils/path'\n\n/**\n * Page-level runtime shared by the renderers: the default action opened after a page\n * hydrated, page forms submitted from buttons elsewhere (`Action::formId()`), form data\n * refreshed by the server (`refreshFormData()`), and the filters of chart widgets.\n */\n\n// ---------------------------------------------------------------- Default actions\n\n/** Query-string keys that ask a page to open an action (`?tableAction=edit&tableActionRecord=1`). */\nexport const DEFAULT_ACTION_QUERY_KEYS = [\n 'defaultAction',\n 'defaultActionArguments',\n 'defaultActionContext',\n 'tableAction',\n 'tableActionRecord',\n 'tableActionArguments',\n // Filament's deep-link keys (`?action=sendEmail&actionArguments[source]=email`).\n 'action',\n 'actionArguments',\n 'actionComponent',\n] as const\n\n/** The action context the default action opens in. */\nexport function defaultActionContext(\n page: Pick<PageSchema, 'token'>,\n defaultAction: DefaultActionSchema,\n): ActionContext {\n return {\n token: page.token,\n component: defaultAction.component,\n ...(defaultAction.record !== undefined && defaultAction.record !== null\n ? { record: defaultAction.record }\n : {}),\n ...(defaultAction.arguments ? { arguments: defaultAction.arguments } : {}),\n }\n}\n\n/**\n * Identifies \"this default action on this page\". A page re-rendered after the action ran\n * (the server redirects back) still names it, so renderers remember the key they opened\n * and open it again only for another page or another action.\n */\nexport function defaultActionKey(\n page: Pick<PageSchema, 'class' | 'record' | 'defaultAction'>,\n pathname: string = typeof window !== 'undefined' ? window.location.pathname : '',\n): string | null {\n const action = page.defaultAction\n\n if (!action) return null\n\n return JSON.stringify([\n page.class,\n page.record ?? null,\n pathname,\n action.component,\n action.action.name,\n action.record ?? null,\n ])\n}\n\n/** The URL without the query-string keys that open an action (bracketed forms included). */\nexport function withoutDefaultActionQuery(href: string): string {\n const url = new URL(href, 'http://localhost')\n const keys = [...url.searchParams.keys()]\n\n for (const key of keys) {\n const base = key.replace(/\\[.*$/, '')\n\n if ((DEFAULT_ACTION_QUERY_KEYS as readonly string[]).includes(base)) {\n url.searchParams.delete(key)\n }\n }\n\n const relative = `${url.pathname}${url.search}${url.hash}`\n\n return /^https?:\\/\\//.test(href) ? url.toString() : relative\n}\n\nexport interface DefaultActionOpener {\n /** Key of the default action opened last (see `defaultActionKey()`). */\n opened: string | null\n}\n\n/**\n * Open the page's default action once per page: call after every page render. The query\n * string that asked for it is removed (without a visit), so later redirects back to the\n * page don't open it again. Returns whether it was opened now.\n */\nexport function openDefaultAction(\n page: PageSchema | undefined,\n opener: DefaultActionOpener,\n trigger: (action: ActionSchema, context: ActionContext) => void | Promise<void>,\n): boolean {\n if (!page?.defaultAction) {\n opener.opened = null\n\n return false\n }\n\n const key = defaultActionKey(page)\n\n if (key === null || key === opener.opened) return false\n\n opener.opened = key\n\n if (typeof window !== 'undefined') {\n const clean = withoutDefaultActionQuery(window.location.href)\n\n if (clean !== window.location.href) {\n window.history.replaceState(window.history.state, '', clean)\n }\n }\n\n void trigger(page.defaultAction.action, defaultActionContext(page, page.defaultAction))\n\n return true\n}\n\n// ---------------------------------------------------------------- Page form submitters\n\ntype FormSubmitter = (action: ActionSchema, context: ActionContext) => void | Promise<void>\n\nconst formSubmitters = new Map<string, FormSubmitter>()\n\n/**\n * A page form publishes how it submits itself (with its state, processing flag and\n * error handling), so buttons with `formId()` elsewhere on the page can use it.\n * `form` is the page form; named forms register as `form:{name}`.\n */\nexport function registerFormSubmitter(component: string, submit: FormSubmitter): () => void {\n formSubmitters.set(component, submit)\n\n return () => {\n if (formSubmitters.get(component) === submit) {\n formSubmitters.delete(component)\n }\n }\n}\n\n/**\n * Submit the form an action's `formId` names. Returns false when the action has no\n * `formId`, is a plain link, or no such form is on the page (the caller then runs the\n * action normally).\n */\nexport function submitFormById(action: ActionSchema, context: ActionContext): boolean {\n if (!action.formId || (action.url && !action.hasAction)) return false\n\n const submit =\n formSubmitters.get(action.formId) ??\n (action.formId.startsWith('form:') ? undefined : formSubmitters.get(`form:${action.formId}`))\n\n if (!submit) return false\n\n void submit(action, context)\n\n return true\n}\n\n// ---------------------------------------------------------------- Form refresh\n\n/** The paths `refreshFormData()` sent for this form, or null. */\nexport function formRefreshFor(\n flash: Pick<AlfakitFlash, 'alfakitFormRefresh'>,\n component: string,\n): Record<string, unknown> | null {\n const refresh = flash.alfakitFormRefresh\n\n return refresh && refresh.component === component && refresh.state ? refresh.state : null\n}\n\n/**\n * Copy refreshed values into a form store. The paths stop counting as edited (and lose\n * their errors); every other field keeps what the user typed.\n */\nexport function applyFormRefresh(store: FormStore, state: Record<string, unknown>): void {\n const paths = Object.keys(state)\n\n if (paths.length === 0) return\n\n store.store.set((current) => {\n let values = current.values\n const dirty = new Set(current.dirty)\n const errors = { ...current.errors }\n\n for (const path of paths) {\n values = setPath(values, path, state[path])\n dirty.delete(path)\n delete errors[path]\n }\n\n return { ...current, values, dirty, errors }\n })\n}\n\n// ---------------------------------------------------------------- Wizard pages\n\n/** Whether a page form shows its buttons: always, except before a wizard form's last step. */\nexport function formActionsVisible(\n block: Pick<FormBlock, 'wizard'>,\n progress: WizardProgress | null | undefined,\n): boolean {\n if (!block.wizard || !progress || progress.count === 0) return true\n\n return progress.step >= progress.count - 1\n}\n\n// ---------------------------------------------------------------- Chart filters\n\n/**\n * The URL with a chart widget's filter values in the query string\n * (`chartFilters[{id}][field]=value`, lists as `[field][]`). The widget's previous values\n * are replaced; empty values are left out; other query values stay.\n */\nexport function chartFiltersUrl(\n href: string,\n queryKey: string,\n widgetId: string,\n state: Record<string, unknown> | null,\n): string {\n const url = new URL(href, 'http://localhost')\n const prefix = `${queryKey}[${widgetId}]`\n\n for (const key of [...url.searchParams.keys()]) {\n if (key.startsWith(prefix)) url.searchParams.delete(key)\n }\n\n for (const [field, value] of Object.entries(state ?? {})) {\n if (value === null || value === undefined || value === '') continue\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item !== null && item !== undefined)\n url.searchParams.append(`${prefix}[${field}][]`, String(item))\n }\n\n continue\n }\n\n url.searchParams.set(\n `${prefix}[${field}]`,\n typeof value === 'boolean' ? (value ? '1' : '0') : String(value),\n )\n }\n\n const relative = `${url.pathname}${url.search}${url.hash}`\n\n return /^https?:\\/\\//.test(href) ? url.toString() : relative\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"page-actions.js","names":[],"sources":["../src/page-actions.ts"],"sourcesContent":["import type { ActionContext } from './actions'\nimport type { AlfakitFlash } from './flash'\nimport type { FormStore } from './form'\nimport type { WizardProgress } from './modal-actions'\nimport type { ActionSchema } from './schema/actions'\nimport type { FormBlock } from './schema/form'\nimport type { DefaultActionSchema, PageSchema } from './schema/panel'\nimport { setPath } from './utils/path'\n\n/**\n * Page-level runtime shared by the renderers: the default action opened after a page\n * hydrated, page forms submitted from buttons elsewhere (`Action::formId()`), form data\n * refreshed by the server (`refreshFormData()`), and the filters of chart widgets.\n */\n\n// ---------------------------------------------------------------- Default actions\n\n/** Query-string keys that ask a page to open an action (`?tableAction=edit&tableActionRecord=1`). */\nexport const DEFAULT_ACTION_QUERY_KEYS = [\n 'defaultAction',\n 'defaultActionArguments',\n 'defaultActionContext',\n 'tableAction',\n 'tableActionRecord',\n 'tableActionArguments',\n // Filament's deep-link keys (`?action=sendEmail&actionArguments[source]=email`).\n 'action',\n 'actionArguments',\n 'actionComponent',\n] as const\n\n/** The action context the default action opens in. */\nexport function defaultActionContext(\n page: Pick<PageSchema, 'token'>,\n defaultAction: DefaultActionSchema,\n): ActionContext {\n return {\n token: page.token,\n component: defaultAction.component,\n ...(defaultAction.record !== undefined && defaultAction.record !== null\n ? { record: defaultAction.record }\n : {}),\n ...(defaultAction.arguments ? { arguments: defaultAction.arguments } : {}),\n }\n}\n\n/**\n * Identifies \"this default action on this page\". A page re-rendered after the action ran\n * (the server redirects back) still names it, so renderers remember the key they opened\n * and open it again only for another page or another action.\n */\nexport function defaultActionKey(\n page: Pick<PageSchema, 'class' | 'record' | 'defaultAction'>,\n pathname: string = typeof window !== 'undefined' ? window.location.pathname : '',\n): string | null {\n const action = page.defaultAction\n\n if (!action) return null\n\n return JSON.stringify([\n page.class,\n page.record ?? null,\n pathname,\n action.component,\n action.action.name,\n action.record ?? null,\n ])\n}\n\n/** The URL without the query-string keys that open an action (bracketed forms included). */\nexport function withoutDefaultActionQuery(href: string): string {\n const url = new URL(href, 'http://localhost')\n const keys = [...url.searchParams.keys()]\n\n for (const key of keys) {\n const base = key.replace(/\\[.*$/, '')\n\n if ((DEFAULT_ACTION_QUERY_KEYS as readonly string[]).includes(base)) {\n url.searchParams.delete(key)\n }\n }\n\n const relative = `${url.pathname}${url.search}${url.hash}`\n\n return /^https?:\\/\\//.test(href) ? url.toString() : relative\n}\n\nexport interface DefaultActionOpener {\n /** Key of the default action opened last (see `defaultActionKey()`). */\n opened: string | null\n}\n\n/**\n * Open the page's default action once per page: call after every page render. The query\n * string that asked for it is removed (without a visit), so later redirects back to the\n * page don't open it again. Returns whether it was opened now.\n */\nexport function openDefaultAction(\n page: PageSchema | undefined,\n opener: DefaultActionOpener,\n trigger: (action: ActionSchema, context: ActionContext) => void | Promise<void>,\n): boolean {\n if (!page?.defaultAction) {\n opener.opened = null\n\n return false\n }\n\n const key = defaultActionKey(page)\n\n if (key === null || key === opener.opened) return false\n\n opener.opened = key\n\n if (typeof window !== 'undefined') {\n const clean = withoutDefaultActionQuery(window.location.href)\n\n if (clean !== window.location.href) {\n window.history.replaceState(window.history.state, '', clean)\n }\n }\n\n void trigger(page.defaultAction.action, defaultActionContext(page, page.defaultAction))\n\n return true\n}\n\n// ---------------------------------------------------------------- Page form submitters\n\ntype FormSubmitter = (action: ActionSchema, context: ActionContext) => void | Promise<void>\n\nconst formSubmitters = new Map<string, FormSubmitter>()\n\n/**\n * A page form publishes how it submits itself (with its state, processing flag and\n * error handling), so buttons with `formId()` elsewhere on the page can use it.\n * `form` is the page form; named forms register as `form:{name}`.\n */\nexport function registerFormSubmitter(component: string, submit: FormSubmitter): () => void {\n formSubmitters.set(component, submit)\n\n return () => {\n if (formSubmitters.get(component) === submit) {\n formSubmitters.delete(component)\n }\n }\n}\n\n/**\n * Submit the form an action's `formId` names. Returns false when the action has no\n * `formId`, is a plain link, or no such form is on the page (the caller then runs the\n * action normally).\n */\nexport function submitFormById(action: ActionSchema, context: ActionContext): boolean {\n if (!action.formId || (action.url && !action.hasAction)) return false\n\n const submit =\n formSubmitters.get(action.formId) ??\n (action.formId.startsWith('form:') ? undefined : formSubmitters.get(`form:${action.formId}`))\n\n if (!submit) return false\n\n void submit(action, context)\n\n return true\n}\n\n// ---------------------------------------------------------------- Form refresh\n\n/** The paths `refreshFormData()` sent for this form, or null. */\nexport function formRefreshFor(\n flash: Pick<AlfakitFlash, 'alfakitFormRefresh'>,\n component: string,\n): Record<string, unknown> | null {\n const refresh = flash.alfakitFormRefresh\n\n return refresh && refresh.component === component && refresh.state ? refresh.state : null\n}\n\n/**\n * Copy refreshed values into a form store. The paths, and the fields below them, stop\n * counting as edited (and lose their errors); every other field keeps what the user typed.\n */\nexport function applyFormRefresh(store: FormStore, state: Record<string, unknown>): void {\n const paths = Object.keys(state)\n\n if (paths.length === 0) return\n\n store.store.set((current) => {\n let values = current.values\n const dirty = new Set(current.dirty)\n const errors = { ...current.errors }\n\n for (const path of paths) {\n values = setPath(values, path, state[path])\n\n // A refreshed path replaces everything below it (`content` with its blocks' fields).\n for (const key of [...dirty]) {\n if (key === path || key.startsWith(`${path}.`)) dirty.delete(key)\n }\n\n for (const key of Object.keys(errors)) {\n if (key === path || key.startsWith(`${path}.`)) delete errors[key]\n }\n }\n\n return { ...current, values, dirty, errors }\n })\n}\n\n/**\n * Whether a page form reloads from the server's new schema after a visit it did not submit\n * itself: another action (one embedded in a field, a table row in a relation manager)\n * succeeded while the form held no edits. Its components may show server state that the\n * action changed (a status, which actions are offered); with unsaved edits the form keeps\n * what the user typed.\n */\nexport function shouldResyncForm(\n flash: Pick<AlfakitFlash, 'alfakitAction'>,\n store: Pick<FormStore, 'isDirty'>,\n): boolean {\n return flash.alfakitAction?.status === 'success' && !store.isDirty\n}\n\n// ---------------------------------------------------------------- Wizard pages\n\n/** Whether a page form shows its buttons: always, except before a wizard form's last step. */\nexport function formActionsVisible(\n block: Pick<FormBlock, 'wizard'>,\n progress: WizardProgress | null | undefined,\n): boolean {\n if (!block.wizard || !progress || progress.count === 0) return true\n\n return progress.step >= progress.count - 1\n}\n\n// ---------------------------------------------------------------- Chart filters\n\n/**\n * The URL with a chart widget's filter values in the query string\n * (`chartFilters[{id}][field]=value`, lists as `[field][]`). The widget's previous values\n * are replaced; empty values are left out; other query values stay.\n */\nexport function chartFiltersUrl(\n href: string,\n queryKey: string,\n widgetId: string,\n state: Record<string, unknown> | null,\n): string {\n const url = new URL(href, 'http://localhost')\n const prefix = `${queryKey}[${widgetId}]`\n\n for (const key of [...url.searchParams.keys()]) {\n if (key.startsWith(prefix)) url.searchParams.delete(key)\n }\n\n for (const [field, value] of Object.entries(state ?? {})) {\n if (value === null || value === undefined || value === '') continue\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item !== null && item !== undefined)\n url.searchParams.append(`${prefix}[${field}][]`, String(item))\n }\n\n continue\n }\n\n url.searchParams.set(\n `${prefix}[${field}]`,\n typeof value === 'boolean' ? (value ? '1' : '0') : String(value),\n )\n }\n\n const relative = `${url.pathname}${url.search}${url.hash}`\n\n return /^https?:\\/\\//.test(href) ? url.toString() : relative\n}\n"],"mappings":";;;;;;;;AAkBA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;AACF;;AAGA,SAAgB,qBACd,MACA,eACe;CACf,OAAO;EACL,OAAO,KAAK;EACZ,WAAW,cAAc;EACzB,GAAI,cAAc,WAAW,KAAA,KAAa,cAAc,WAAW,OAC/D,EAAE,QAAQ,cAAc,OAAO,IAC/B,CAAC;EACL,GAAI,cAAc,YAAY,EAAE,WAAW,cAAc,UAAU,IAAI,CAAC;CAC1E;AACF;;;;;;AAOA,SAAgB,iBACd,MACA,WAAmB,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW,IAC/D;CACf,MAAM,SAAS,KAAK;CAEpB,IAAI,CAAC,QAAQ,OAAO;CAEpB,OAAO,KAAK,UAAU;EACpB,KAAK;EACL,KAAK,UAAU;EACf;EACA,OAAO;EACP,OAAO,OAAO;EACd,OAAO,UAAU;CACnB,CAAC;AACH;;AAGA,SAAgB,0BAA0B,MAAsB;CAC9D,MAAM,MAAM,IAAI,IAAI,MAAM,kBAAkB;CAC5C,MAAM,OAAO,CAAC,GAAG,IAAI,aAAa,KAAK,CAAC;CAExC,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,OAAO,IAAI,QAAQ,SAAS,EAAE;EAEpC,IAAK,0BAAgD,SAAS,IAAI,GAChE,IAAI,aAAa,OAAO,GAAG;CAE/B;CAEA,MAAM,WAAW,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;CAEpD,OAAO,eAAe,KAAK,IAAI,IAAI,IAAI,SAAS,IAAI;AACtD;;;;;;AAYA,SAAgB,kBACd,MACA,QACA,SACS;CACT,IAAI,CAAC,MAAM,eAAe;EACxB,OAAO,SAAS;EAEhB,OAAO;CACT;CAEA,MAAM,MAAM,iBAAiB,IAAI;CAEjC,IAAI,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,OAAO;CAElD,OAAO,SAAS;CAEhB,IAAI,OAAO,WAAW,aAAa;EACjC,MAAM,QAAQ,0BAA0B,OAAO,SAAS,IAAI;EAE5D,IAAI,UAAU,OAAO,SAAS,MAC5B,OAAO,QAAQ,aAAa,OAAO,QAAQ,OAAO,IAAI,KAAK;CAE/D;CAEA,QAAa,KAAK,cAAc,QAAQ,qBAAqB,MAAM,KAAK,aAAa,CAAC;CAEtF,OAAO;AACT;AAMA,MAAM,iCAAiB,IAAI,IAA2B;;;;;;AAOtD,SAAgB,sBAAsB,WAAmB,QAAmC;CAC1F,eAAe,IAAI,WAAW,MAAM;CAEpC,aAAa;EACX,IAAI,eAAe,IAAI,SAAS,MAAM,QACpC,eAAe,OAAO,SAAS;CAEnC;AACF;;;;;;AAOA,SAAgB,eAAe,QAAsB,SAAiC;CACpF,IAAI,CAAC,OAAO,UAAW,OAAO,OAAO,CAAC,OAAO,WAAY,OAAO;CAEhE,MAAM,SACJ,eAAe,IAAI,OAAO,MAAM,MAC/B,OAAO,OAAO,WAAW,OAAO,IAAI,KAAA,IAAY,eAAe,IAAI,QAAQ,OAAO,QAAQ;CAE7F,IAAI,CAAC,QAAQ,OAAO;CAEpB,OAAY,QAAQ,OAAO;CAE3B,OAAO;AACT;;AAKA,SAAgB,eACd,OACA,WACgC;CAChC,MAAM,UAAU,MAAM;CAEtB,OAAO,WAAW,QAAQ,cAAc,aAAa,QAAQ,QAAQ,QAAQ,QAAQ;AACvF;;;;;AAMA,SAAgB,iBAAiB,OAAkB,OAAsC;CACvF,MAAM,QAAQ,OAAO,KAAK,KAAK;CAE/B,IAAI,MAAM,WAAW,GAAG;CAExB,MAAM,MAAM,KAAK,YAAY;EAC3B,IAAI,SAAS,QAAQ;EACrB,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK;EACnC,MAAM,SAAS,EAAE,GAAG,QAAQ,OAAO;EAEnC,KAAK,MAAM,QAAQ,OAAO;GACxB,SAAS,QAAQ,QAAQ,MAAM,MAAM,KAAK;GAG1C,KAAK,MAAM,OAAO,CAAC,GAAG,KAAK,GACzB,IAAI,QAAQ,QAAQ,IAAI,WAAW,GAAG,KAAK,EAAE,GAAG,MAAM,OAAO,GAAG;GAGlE,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,IAAI,QAAQ,QAAQ,IAAI,WAAW,GAAG,KAAK,EAAE,GAAG,OAAO,OAAO;EAElE;EAEA,OAAO;GAAE,GAAG;GAAS;GAAQ;GAAO;EAAO;CAC7C,CAAC;AACH;;;;;;;;AASA,SAAgB,iBACd,OACA,OACS;CACT,OAAO,MAAM,eAAe,WAAW,aAAa,CAAC,MAAM;AAC7D;;AAKA,SAAgB,mBACd,OACA,UACS;CACT,IAAI,CAAC,MAAM,UAAU,CAAC,YAAY,SAAS,UAAU,GAAG,OAAO;CAE/D,OAAO,SAAS,QAAQ,SAAS,QAAQ;AAC3C;;;;;;AASA,SAAgB,gBACd,MACA,UACA,UACA,OACQ;CACR,MAAM,MAAM,IAAI,IAAI,MAAM,kBAAkB;CAC5C,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS;CAEvC,KAAK,MAAM,OAAO,CAAC,GAAG,IAAI,aAAa,KAAK,CAAC,GAC3C,IAAI,IAAI,WAAW,MAAM,GAAG,IAAI,aAAa,OAAO,GAAG;CAGzD,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,SAAS,CAAC,CAAC,GAAG;EACxD,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IAAI;EAE3D,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,KAAK,MAAM,QAAQ,OACjB,IAAI,SAAS,QAAQ,SAAS,KAAA,GAC5B,IAAI,aAAa,OAAO,GAAG,OAAO,GAAG,MAAM,MAAM,OAAO,IAAI,CAAC;GAGjE;EACF;EAEA,IAAI,aAAa,IACf,GAAG,OAAO,GAAG,MAAM,IACnB,OAAO,UAAU,YAAa,QAAQ,MAAM,MAAO,OAAO,KAAK,CACjE;CACF;CAEA,MAAM,WAAW,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;CAEpD,OAAO,eAAe,KAAK,IAAI,IAAI,IAAI,SAAS,IAAI;AACtD"}
@@ -23,6 +23,11 @@ export interface BuilderBlockSchema {
23
23
  state: BuilderItemState;
24
24
  /** `Block::previewComponent()`: registry key `builder-preview:{name}`. */
25
25
  previewComponent?: string;
26
+ /**
27
+ * `Block::summary()`: items show a summary made on the server, so collapsing one
28
+ * refreshes the field (its summary follows the edits).
29
+ */
30
+ summary?: boolean;
26
31
  }
27
32
  export interface BuilderItemSchema extends ItemActionsItemProps {
28
33
  /** `Block::preview()` HTML (with `blockPreviews()`), rendered by the server. */
@@ -1 +1 @@
1
- {"version":3,"file":"advanced-fields.d.ts","names":[],"sources":["../../src/schema/advanced-fields.ts"],"mappings":";;;;;iBAYiB;EACf;EACA,MAAM;;iBAGS;EACf;EACA;EACA,OAAO;;EAEP;;EAEA;EACA;EACA,UAAU;;EAEV,QAAQ;;EAER,OAAO;;EAEP;;iBAGe,0BAA0B;;EAEzC;EACA;EACA;EACA;EACA;;EAEA;EACA,OAAO;EACP,UAAU;EACV,QAAQ;;iBAGO,sBAAsB,WAAW;EAChD;EACA,OAAO;EACP,QAAQ;EACR,qBAAqB;EACrB,mBAAmB;;EAEnB;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA,aAAa;;iBAKE,4BAA4B;EAC3C;EACA,SAAS;EACT;EACA;EACA;EACA;;EAEA;;EAEA;EACA;;EAEA,WAAW;;EAEX;;EAEA;;iBAKe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;;EAEA;;EAEA;EACA;EACA;;EAEA,UAAU;;EAEV;EACA;;EAEA;EACA;EACA;;EAEA;;EAEA;;EAEA,WAAW;;EAEX,kBAAkB;;YAGR;iBAEK;EACf;;EAEA;;EAEA;;iBAGe;;EAEf;EACA;;EAEA;;iBAKe,yBAAyB;EACxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAKe,+BAA+B;EAC9C;EACA;;EAEA,iBAAiB;EACjB;EACA;EACA;EACA;;EAEA,mBAAmB;;EAEnB;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,OAAO;EACP,QAAQ"}
1
+ {"version":3,"file":"advanced-fields.d.ts","names":[],"sources":["../../src/schema/advanced-fields.ts"],"mappings":";;;;;iBAYiB;EACf;EACA,MAAM;;iBAGS;EACf;EACA;EACA,OAAO;;EAEP;;EAEA;EACA;EACA,UAAU;;EAEV,QAAQ;;EAER,OAAO;;EAEP;;;;;EAKA;;iBAGe,0BAA0B;;EAEzC;EACA;EACA;EACA;EACA;;EAEA;EACA,OAAO;EACP,UAAU;EACV,QAAQ;;iBAGO,sBAAsB,WAAW;EAChD;EACA,OAAO;EACP,QAAQ;EACR,qBAAqB;EACrB,mBAAmB;;EAEnB;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA,aAAa;;iBAKE,4BAA4B;EAC3C;EACA,SAAS;EACT;EACA;EACA;EACA;;EAEA;;EAEA;EACA;;EAEA,WAAW;;EAEX;;EAEA;;iBAKe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;;EAEA;;EAEA;EACA;EACA;;EAEA,UAAU;;EAEV;EACA;;EAEA;EACA;EACA;;EAEA;;EAEA;;EAEA,WAAW;;EAEX,kBAAkB;;YAGR;iBAEK;EACf;;EAEA;;EAEA;;iBAGe;;EAEf;EACA;;EAEA;;iBAKe,yBAAyB;EACxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAKe,+BAA+B;EAC9C;EACA;;EAEA,iBAAiB;EACjB;EACA;EACA;EACA;;EAEA,mBAAmB;;EAEnB;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,OAAO;EACP,QAAQ"}
@@ -238,6 +238,8 @@ export interface CmsLivePreviewSchema {
238
238
  changedStatus?: CmsEditorStatusSchema | null;
239
239
  /** The editor's main actions ("Save draft", "Publish"…), run as page actions. */
240
240
  actions?: ActionSchema[];
241
+ /** The actions once the draft differs from the live page ("Publish changes"). */
242
+ changedActions?: ActionSchema[] | null;
241
243
  }
242
244
  /** The page editor's status line: a badge and a plain-language explanation. */
243
245
  export interface CmsEditorStatusSchema {
@@ -251,6 +253,8 @@ export interface CmsAutosaveResult {
251
253
  savedAt: string | null;
252
254
  hasChanges: boolean;
253
255
  previewUrl: string | null;
256
+ /** The record's version after the draft was stored, when the editor's was still current. */
257
+ recordVersion?: string | null;
254
258
  }
255
259
  export interface CmsPageProps {
256
260
  cms: CmsPayload;
@@ -1 +1 @@
1
- {"version":3,"file":"cms.d.ts","names":[],"sources":["../../src/schema/cms.ts"],"mappings":";;;;;;;;iBASiB;EACf;EACA;EACA;;;iBAIe;EACf;EACA;EACA;;;iBAIe,eAAe,QAAQ;EACtC;EACA;EACA,MAAM;;iBAGS;EACf;EACA;EACA;EACA,eAAe;EACf,iBAAiB;EACjB,OAAO;EACP;;iBAGe;;EAEf;EACA;;iBAGe;EACf,OAAO;EACP;EACA;;iBAGe;EACf;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA,eAAe;EACf,iBAAiB;EACjB;;iBAGe;;EAEf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;IAAS;IAAkB;;;iBAGZ;EACf;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA,OAAO;;YAGG;iBAEK;EACf;IACE;;IAEA;IACA;;EAEF;EACA,aAAa;;iBAGE;EACf;EACA;IAAW,QAAQ;;;iBAGJ;EACf;EACA;;EAEA;;EAEA,SAAS;;iBAGM;;EAEf;;EAEA,QAAQ;;;iBAIO;EACf,MAAM;EACN,aAAa;EACb,OAAO;EACP,SAAS;EACT,kBAAkB;EAClB,UAAU;EACV,KAAK;EACL,cAAc;EACd,OAAO;EACP,SAAS;EACT,MAAM;EACN,SAAS;;iBAGM;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;IAAc;IAAkB;;;EAEhC;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,aAAa;EACb,YAAY;EACZ,KAAK;;iBAGU;EACf;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA,UAAU;;iBAGK;EACf;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;EACA;EACA;EACA;EACA;IAAW;IAAc;IAAe;IAAa;;;;iBAItC;EACf,MAAM;EACN,OAAO,eAAe;EACtB,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA,cAAc;;iBAGC;EACf;;EAEA;EACA;EACA;;;;;;iBAOe;EACf;EACA;EACA;EACA;;EAEA;EACA;EACA;;EAEA,SAAS;;EAET,gBAAgB;;EAEhB,UAAU;;;iBAIK;EACf;;EAEA;EACA;;;iBAIe;EACf;EACA;EACA;;iBAGe;EACf,KAAK"}
1
+ {"version":3,"file":"cms.d.ts","names":[],"sources":["../../src/schema/cms.ts"],"mappings":";;;;;;;;iBASiB;EACf;EACA;EACA;;;iBAIe;EACf;EACA;EACA;;;iBAIe,eAAe,QAAQ;EACtC;EACA;EACA,MAAM;;iBAGS;EACf;EACA;EACA;EACA,eAAe;EACf,iBAAiB;EACjB,OAAO;EACP;;iBAGe;;EAEf;EACA;;iBAGe;EACf,OAAO;EACP;EACA;;iBAGe;EACf;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA,eAAe;EACf,iBAAiB;EACjB;;iBAGe;;EAEf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;IAAS;IAAkB;;;iBAGZ;EACf;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA,OAAO;;YAGG;iBAEK;EACf;IACE;;IAEA;IACA;;EAEF;EACA,aAAa;;iBAGE;EACf;EACA;IAAW,QAAQ;;;iBAGJ;EACf;EACA;;EAEA;;EAEA,SAAS;;iBAGM;;EAEf;;EAEA,QAAQ;;;iBAIO;EACf,MAAM;EACN,aAAa;EACb,OAAO;EACP,SAAS;EACT,kBAAkB;EAClB,UAAU;EACV,KAAK;EACL,cAAc;EACd,OAAO;EACP,SAAS;EACT,MAAM;EACN,SAAS;;iBAGM;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;IAAc;IAAkB;;;EAEhC;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,aAAa;EACb,YAAY;EACZ,KAAK;;iBAGU;EACf;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA,UAAU;;iBAGK;EACf;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;EACA;EACA;EACA;EACA;IAAW;IAAc;IAAe;IAAa;;;;iBAItC;EACf,MAAM;EACN,OAAO,eAAe;EACtB,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA,cAAc;;iBAGC;EACf;;EAEA;EACA;EACA;;;;;;iBAOe;EACf;EACA;EACA;EACA;;EAEA;EACA;EACA;;EAEA,SAAS;;EAET,gBAAgB;;EAEhB,UAAU;;EAEV,iBAAiB;;;iBAIF;EACf;;EAEA;EACA;;;iBAIe;EACf;EACA;EACA;;EAEA;;iBAGe;EACf,KAAK"}
@@ -626,6 +626,11 @@ export interface FieldsetSchema extends BaseComponent {
626
626
  }
627
627
  export interface TabSchema extends BaseComponent {
628
628
  type: 'Tab';
629
+ /**
630
+ * The tab's readable value in the query string when its tabs `persistTabInQueryString()`:
631
+ * its `id()`, explicit `key()` or label slug (`?tab=billing-address`).
632
+ */
633
+ queryValue?: string;
629
634
  icon?: IconName;
630
635
  iconPosition?: IconPosition;
631
636
  badge?: string | number;
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","names":[],"sources":["../../src/schema/components.ts"],"mappings":"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"}
1
+ {"version":3,"file":"components.d.ts","names":[],"sources":["../../src/schema/components.ts"],"mappings":"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"}