@mosaicoo/kanban 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,689 @@
1
+ /**
2
+ * Versão do pacote em runtime.
3
+ *
4
+ * Mantida em sincronia com projects/kanban/package.json pelo processo de
5
+ * release (standard-version updater — mesmo mecanismo do @mosaicoo/svg-engine).
6
+ */
7
+ declare const MKB_VERSION = "0.1.0";
8
+
9
+ /**
10
+ * Modelo DECLARATIVO de cartão — serializável (docs/01 §4). O cartão é uma
11
+ * LISTA ORDENADA de blocos tipados; qualquer tipo pode repetir à vontade, em
12
+ * qualquer posição (três `date`, dois `text`… tudo válido). Nenhum campo do
13
+ * item é especial — até o título é só um bloco `text` apontando um campo.
14
+ *
15
+ * Estilo NÃO mora aqui (docs: decisão de F5.5): posicionamento/altura/tema são
16
+ * CSS externo. Cada bloco carrega apenas `class` (combinação de classes do
17
+ * host) e recebe classes-base estáveis (`mkb-block`, `mkb-block-<tipo>`…) que
18
+ * são o CONTRATO de estilo. `tone` é a exceção — carrega SIGNIFICADO
19
+ * (a11y/semântica), não só aparência, e vira `mkb-tone-<tom>`.
20
+ */
21
+ type KanbanTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
22
+ /** Estilo aplicado a um VALOR (mapa valor→estilo em tag/badge). */
23
+ interface KanbanValueStyle {
24
+ /** Rótulo exibido no lugar do valor cru. */
25
+ label?: string;
26
+ tone?: KanbanTone;
27
+ /** Nome de ícone resolvido no registry (`icon:<nome>`). */
28
+ icon?: string;
29
+ }
30
+ /** Um "fato" inline (ex.: 2 🛏, 3 🛁) — campo + ícone opcional. */
31
+ interface KanbanFact {
32
+ field: string;
33
+ icon?: string;
34
+ prefix?: string;
35
+ suffix?: string;
36
+ label?: string;
37
+ }
38
+ /** Ação declarada no cartão; dispara pelo mesmo (itemAction) do menu. */
39
+ interface KanbanCardAction {
40
+ id: string;
41
+ label: string;
42
+ icon?: string;
43
+ tone?: KanbanTone;
44
+ /** Mostra a ação só quando o item está na coluna/lane indicada. */
45
+ when?: {
46
+ column?: string | string[];
47
+ lane?: string | string[];
48
+ };
49
+ }
50
+ interface BlockBase {
51
+ /** Classes CSS extras do host (string ou lista) — combinadas às base. */
52
+ class?: string | string[];
53
+ /** Rótulo curto antes do valor (ex.: "criado em"). */
54
+ label?: string;
55
+ }
56
+ interface FieldBlockBase extends BlockBase {
57
+ /** Campo do item lido por este bloco. */
58
+ field: string;
59
+ /** Oculta o bloco quando o campo está vazio/ausente (default true). */
60
+ hideWhenEmpty?: boolean;
61
+ }
62
+ type DateStyle = 'short' | 'medium' | 'long' | 'full';
63
+ type TimeStyle = 'short' | 'medium';
64
+ /** Texto de um campo. `format` (opcional) aponta um formatador do registry
65
+ * (`format:<nome>`) para casos que fogem dos blocos estruturados. */
66
+ interface TextBlock extends FieldBlockBase {
67
+ type: 'text';
68
+ variant?: 'title' | 'subtitle' | 'body' | 'caption' | 'highlight';
69
+ prefix?: string;
70
+ suffix?: string;
71
+ format?: string;
72
+ /** Nº máximo de linhas antes de truncar com reticências. */
73
+ clamp?: number;
74
+ }
75
+ /** Número formatado via Intl (decimal/moeda/percentual). */
76
+ interface NumberBlock extends FieldBlockBase {
77
+ type: 'number';
78
+ variant?: 'body' | 'highlight';
79
+ style?: 'decimal' | 'currency' | 'percent';
80
+ /** Código ISO da moeda quando style='currency' (ex.: 'BRL'). */
81
+ currency?: string;
82
+ decimals?: number;
83
+ prefix?: string;
84
+ suffix?: string;
85
+ }
86
+ /** Data formatada via Intl; `relative` → "há 4 horas". */
87
+ interface DateBlock extends FieldBlockBase {
88
+ type: 'date';
89
+ dateStyle?: DateStyle;
90
+ relative?: boolean;
91
+ }
92
+ /** Hora formatada via Intl. */
93
+ interface TimeBlock extends FieldBlockBase {
94
+ type: 'time';
95
+ timeStyle?: TimeStyle;
96
+ }
97
+ /** Data + hora; `relative` tem precedência quando true. */
98
+ interface DateTimeBlock extends FieldBlockBase {
99
+ type: 'datetime';
100
+ dateStyle?: DateStyle;
101
+ timeStyle?: TimeStyle;
102
+ relative?: boolean;
103
+ }
104
+ /** Imagem cujo src vem do campo. */
105
+ interface ImageBlock extends FieldBlockBase {
106
+ type: 'image';
107
+ alt?: string;
108
+ rounded?: boolean;
109
+ }
110
+ /** Chip/etiqueta; `map` estiliza por valor (label/tom/ícone). */
111
+ interface TagBlock extends FieldBlockBase {
112
+ type: 'tag';
113
+ tone?: KanbanTone;
114
+ map?: Record<string, KanbanValueStyle>;
115
+ }
116
+ /** Selo compacto (ícone + tom), tipicamente mapeado por valor. */
117
+ interface BadgeBlock extends FieldBlockBase {
118
+ type: 'badge';
119
+ icon?: string;
120
+ tone?: KanbanTone;
121
+ map?: Record<string, KanbanValueStyle>;
122
+ /** Mostra o texto ao lado do ícone (default false). */
123
+ showText?: boolean;
124
+ }
125
+ /** Ícone estático (não lê campo) do registry. */
126
+ interface IconBlock extends BlockBase {
127
+ type: 'icon';
128
+ icon: string;
129
+ tone?: KanbanTone;
130
+ }
131
+ /** Avatar (foto ou iniciais) + nome opcional. */
132
+ interface AvatarBlock extends BlockBase {
133
+ type: 'avatar';
134
+ nameField?: string;
135
+ photoField?: string;
136
+ hideWhenEmpty?: boolean;
137
+ }
138
+ /** Lista inline de fatos (campo + ícone). */
139
+ interface FactsBlock extends BlockBase {
140
+ type: 'facts';
141
+ items: KanbanFact[];
142
+ }
143
+ /**
144
+ * ENTRIES do card (docs/02 §7): renderiza `card[field]: KanbanEntry[]` como
145
+ * itens internos com capacidades configuráveis — nada de regra de domínio.
146
+ */
147
+ interface ItemsBlock extends FieldBlockBase {
148
+ type: 'items';
149
+ /** Campo da entry exibido como rótulo (default 'label'). */
150
+ labelField?: string;
151
+ /** Campo booleano de conclusão — habilita 'check' e o progresso. */
152
+ checkField?: string;
153
+ /** Capacidades opt-in: 'check' (marcar/desmarcar via updateEntry),
154
+ * 'reorder' (arrastar dentro do card via moveEntry), 'move-across'
155
+ * (arrastar entre CARDS — listas do mesmo field se conectam; implica
156
+ * 'reorder'), 'action' (botões por entry → (entryAction)).
157
+ * Default: só exibição. */
158
+ capabilities?: Array<'check' | 'reorder' | 'move-across' | 'action'>;
159
+ /** Ações por entry (exige capability 'action'). */
160
+ actions?: KanbanCardAction[];
161
+ /** Mostra "N/M" + barra de progresso (exige checkField). */
162
+ showProgress?: boolean;
163
+ /** Exibe só as N primeiras entries (indicador "+k" para o resto). */
164
+ maxVisible?: number;
165
+ }
166
+ /** Faixa de alerta (a faixa amarela "Sem resposta há…" da referência). */
167
+ interface AlertBlock extends FieldBlockBase {
168
+ type: 'alert';
169
+ tone?: KanbanTone;
170
+ icon?: string;
171
+ }
172
+ /** Barra de progresso (0..max). */
173
+ interface ProgressBlock extends FieldBlockBase {
174
+ type: 'progress';
175
+ max?: number;
176
+ tone?: KanbanTone;
177
+ }
178
+ /** Divisor horizontal (não lê campo). */
179
+ interface DividerBlock extends BlockBase {
180
+ type: 'divider';
181
+ }
182
+ /** Botões de ação no rodapé do cartão. */
183
+ interface ActionsBlock extends BlockBase {
184
+ type: 'actions';
185
+ items: KanbanCardAction[];
186
+ }
187
+ /** Container horizontal para compor blocos lado a lado. */
188
+ interface RowBlock extends BlockBase {
189
+ type: 'row';
190
+ blocks: KanbanBlock[];
191
+ align?: 'start' | 'center' | 'end' | 'between';
192
+ wrap?: boolean;
193
+ }
194
+ /** Escape hatch leve: renderer registrado (`renderer:<nome>`) que devolve
195
+ * conteúdo do host. Para UI pesada, prefira o <ng-template mosKanbanCard>. */
196
+ interface CustomBlock extends BlockBase {
197
+ type: 'custom';
198
+ renderer: string;
199
+ }
200
+ /** União discriminada de todos os blocos. */
201
+ type KanbanBlock = TextBlock | NumberBlock | DateBlock | TimeBlock | DateTimeBlock | ImageBlock | TagBlock | BadgeBlock | IconBlock | AvatarBlock | FactsBlock | ItemsBlock | AlertBlock | ProgressBlock | DividerBlock | ActionsBlock | RowBlock | CustomBlock;
202
+ type KanbanBlockType = KanbanBlock['type'];
203
+ /** Configuração do cartão: a árvore de blocos. */
204
+ interface KanbanCardConfig {
205
+ blocks: KanbanBlock[];
206
+ /** true = o card só arrasta pela ALÇA ⠿ (recomendado quando o cartão tem
207
+ * muito conteúdo interativo, ex.: bloco items). Default: corpo inteiro
208
+ * com guarda de interativos. */
209
+ dragHandle?: boolean;
210
+ }
211
+ /** Todos os tipos de bloco conhecidos (usado pela validação). */
212
+ declare const KANBAN_BLOCK_TYPES: readonly KanbanBlockType[];
213
+ declare function isFieldBlock(type: KanbanBlockType): boolean;
214
+
215
+ /**
216
+ * Modelo do board — TUDO aqui é serializável (JSON): é o que permite editar a
217
+ * configuração numa tela do host e persistir em banco (docs/01 §4). Código
218
+ * custom (regras, filtros programáticos) entra por NOME, via KanbanRegistry.
219
+ */
220
+
221
+ /** Item do board: registro serializável com `id` obrigatório. Os demais
222
+ * campos são livres — são eles que posicionam o item nos eixos. */
223
+ interface KanbanItem {
224
+ readonly id: string;
225
+ readonly [field: string]: unknown;
226
+ }
227
+ /**
228
+ * Item INTERNO de um card (checklist ágil, ações de negociação…): registro
229
+ * serializável que vive num ARRAY dentro de um campo do card
230
+ * (`card[field]: KanbanEntry[]`). Como no KanbanItem, os campos são livres —
231
+ * tipo/estado/responsável/prazo/prioridade são vocabulário do DOMÍNIO.
232
+ */
233
+ interface KanbanEntry {
234
+ readonly id: string;
235
+ readonly [field: string]: unknown;
236
+ }
237
+ /** Endereço de uma entry: card + campo + posição. */
238
+ interface EntryRef {
239
+ cardId: string;
240
+ field: string;
241
+ index: number;
242
+ }
243
+ /** Definição de um grupo (coluna ou lane) de eixo estático. */
244
+ interface KanbanGroupDef {
245
+ id: string;
246
+ title?: string;
247
+ /** WIP limit: máximo de itens no grupo; mover/adicionar acima disso é rejeitado. */
248
+ limit?: number;
249
+ /** Grupo travado: itens não entram nem saem dele. */
250
+ locked?: boolean;
251
+ /** Metadados livres do host (ícone, cor…) — a lib não interpreta. */
252
+ meta?: Record<string, unknown>;
253
+ }
254
+ /**
255
+ * Um eixo (colunas ou lanes):
256
+ * - ESTÁTICO: `groups` declarados; o campo `field` do item aponta o grupo.
257
+ * - DERIVADO (agrupamento dinâmico): sem `groups`; os grupos são os valores
258
+ * distintos de `field` nos itens.
259
+ */
260
+ interface KanbanAxisConfig {
261
+ /** Campo do item que posiciona neste eixo (default: 'column' / 'lane'). */
262
+ field?: string;
263
+ groups?: Array<string | KanbanGroupDef>;
264
+ /** Ordenação dos grupos por id; ausente = ordem declarada/de aparição. */
265
+ sortGroups?: 'asc' | 'desc';
266
+ /** Opt-in: permite reordenar os grupos (comando moveGroup / drag do header).
267
+ * Só vale para eixo ESTÁTICO sem sortGroups. Default false — nenhum host
268
+ * ganha colunas/lanes arrastáveis sem pedir. */
269
+ reorderable?: boolean;
270
+ }
271
+ /** Açúcar: um array de grupos equivale a um eixo estático com field default. */
272
+ type KanbanAxisInput = Array<string | KanbanGroupDef> | KanbanAxisConfig;
273
+ /** Ordenação dos itens dentro da célula; ausente = ordem MANUAL (drag). */
274
+ interface KanbanSortConfig {
275
+ field: string;
276
+ direction?: 'asc' | 'desc';
277
+ }
278
+ /** Filtros serializáveis; múltiplos filtros = AND. `rule` aponta um predicado
279
+ * de item registrado no registry: (item) => boolean. */
280
+ type KanbanFilter = {
281
+ field: string;
282
+ equals: unknown;
283
+ } | {
284
+ field: string;
285
+ oneOf: unknown[];
286
+ } | {
287
+ text: string;
288
+ fields?: string[];
289
+ } | {
290
+ rule: string;
291
+ };
292
+ /** Whitelist de transição entre colunas; lado omitido = qualquer. */
293
+ interface KanbanTransition {
294
+ from?: string | string[];
295
+ to?: string | string[];
296
+ }
297
+ interface KanbanRulesConfig {
298
+ /** Presente = só as transições listadas são permitidas entre colunas. */
299
+ transitions?: KanbanTransition[];
300
+ /** Regras nomeadas no registry: (ctx: MoveRuleContext) => true | 'motivo'. */
301
+ rules?: string[];
302
+ /** Regras nomeadas para movimento de ENTRIES:
303
+ * (ctx: EntryMoveContext) => true | 'motivo'. */
304
+ entryRules?: string[];
305
+ }
306
+ /** A configuração COMPLETA do board — serializável de ponta a ponta. */
307
+ interface BoardConfig {
308
+ columns: KanbanAxisInput;
309
+ lanes?: KanbanAxisInput;
310
+ items?: KanbanItem[];
311
+ sort?: KanbanSortConfig;
312
+ filters?: KanbanFilter[];
313
+ rules?: KanbanRulesConfig;
314
+ readOnly?: boolean;
315
+ /** Modelo declarativo do cartão; ausente = cartão default (título/id). */
316
+ card?: KanbanCardConfig;
317
+ }
318
+ /** Snapshot imutável do estado observável do board. */
319
+ interface BoardState {
320
+ readonly items: readonly KanbanItem[];
321
+ readonly filters: readonly KanbanFilter[];
322
+ readonly selection: readonly string[];
323
+ readonly readOnly: boolean;
324
+ /** Ordem atual dos grupos ESTÁTICOS de cada eixo (ids). Vazio em eixo
325
+ * derivado (a ordem vem dos dados). Mutável via comando moveGroup. */
326
+ readonly columnOrder: readonly string[];
327
+ readonly laneOrder: readonly string[];
328
+ }
329
+ /** Qual eixo um comando/consulta de grupo endereça. */
330
+ type KanbanAxis = 'columns' | 'lanes';
331
+ /** Célula = interseção coluna × lane (lane ausente quando o eixo não existe). */
332
+ interface CellRef {
333
+ column: string | undefined;
334
+ lane?: string | undefined;
335
+ }
336
+ /** Destino de movimento; lados omitidos mantêm a posição atual do item. */
337
+ interface MoveTarget {
338
+ column?: string;
339
+ lane?: string;
340
+ /** Posição dentro da célula (ordem manual); ausente = fim da célula. */
341
+ index?: number;
342
+ }
343
+ type KanbanCommand = {
344
+ type: 'moveItem';
345
+ itemId: string;
346
+ to: MoveTarget;
347
+ } | {
348
+ type: 'addItem';
349
+ item: KanbanItem;
350
+ to?: MoveTarget;
351
+ } | {
352
+ type: 'updateItem';
353
+ itemId: string;
354
+ changes: Record<string, unknown>;
355
+ } | {
356
+ type: 'removeItem';
357
+ itemId: string;
358
+ } | {
359
+ type: 'moveGroup';
360
+ axis: KanbanAxis;
361
+ groupId: string;
362
+ index: number;
363
+ } | {
364
+ type: 'moveEntry';
365
+ cardId: string;
366
+ field: string;
367
+ entryId: string;
368
+ /** Posição na lista de destino (SEM contar a entry movida). */
369
+ index: number;
370
+ /** Card/campo de destino; omitidos = mesmo card/campo. */
371
+ toCardId?: string;
372
+ toField?: string;
373
+ } | {
374
+ type: 'addEntry';
375
+ cardId: string;
376
+ field: string;
377
+ entry: KanbanEntry;
378
+ index?: number;
379
+ } | {
380
+ type: 'updateEntry';
381
+ cardId: string;
382
+ field: string;
383
+ entryId: string;
384
+ changes: Record<string, unknown>;
385
+ } | {
386
+ type: 'removeEntry';
387
+ cardId: string;
388
+ field: string;
389
+ entryId: string;
390
+ } | {
391
+ type: 'setFilters';
392
+ filters: KanbanFilter[];
393
+ } | {
394
+ type: 'setSelection';
395
+ itemIds: string[];
396
+ } | {
397
+ type: 'setReadOnly';
398
+ readOnly: boolean;
399
+ };
400
+ type CommandResult = {
401
+ ok: true;
402
+ } | {
403
+ ok: false;
404
+ reason: string;
405
+ };
406
+ /** Grupo resolvido pelo query (estático ou derivado), com contagem. */
407
+ interface KanbanGroup extends KanbanGroupDef {
408
+ count: number;
409
+ derived: boolean;
410
+ }
411
+ /** Contexto entregue às regras nomeadas de movimentação. As regras devem ser
412
+ * PURAS: elas rodam tanto no preview (canMove) quanto no commit — é isso que
413
+ * garante preview == commit. */
414
+ interface MoveRuleContext {
415
+ item: KanbanItem;
416
+ from: CellRef;
417
+ to: CellRef;
418
+ /** Itens já presentes na célula de destino (sem contar o item movido). */
419
+ targetCount: number;
420
+ state: BoardState;
421
+ }
422
+ type KanbanRuleFn = (ctx: MoveRuleContext) => true | string;
423
+ type KanbanItemPredicate = (item: KanbanItem) => boolean;
424
+ /** Contexto entregue às regras nomeadas de movimento de ENTRY. Puras, rodam
425
+ * no preview (canMoveEntry) e no commit — preview == commit. */
426
+ interface EntryMoveContext {
427
+ entry: KanbanEntry;
428
+ fromCard: KanbanItem;
429
+ toCard: KanbanItem;
430
+ from: EntryRef;
431
+ to: EntryRef;
432
+ /** Entries já presentes na lista de destino (sem contar a movida). */
433
+ targetCount: number;
434
+ state: BoardState;
435
+ }
436
+ type KanbanEntryRuleFn = (ctx: EntryMoveContext) => true | string;
437
+ type KanbanAction = 'move' | 'add' | 'update' | 'remove' | 'entry-move' | 'entry-update';
438
+ /**
439
+ * O que o HOST fornece à instância (docs/01 §8) — permissões e locale.
440
+ * Sem noção de tenant: o host entrega o adapter/contexto já contextualizados.
441
+ * `can` roda no preview E no commit (preview == commit) e também protege o
442
+ * undo/redo; NÃO se aplica a eventos remotos (applyRemote), que já
443
+ * aconteceram na origem.
444
+ */
445
+ interface KanbanHostContext {
446
+ can?(action: KanbanAction, item?: KanbanItem): boolean;
447
+ locale?: string;
448
+ }
449
+ /** Evento cancelável (before:*). `cancel()` aborta o comando; o motivo vira o
450
+ * `reason` do CommandResult. Prefira validar em REGRAS (que rodam também no
451
+ * preview); before:* é veto de última instância / gancho de efeito. */
452
+ interface CancelableEvent<T> {
453
+ readonly data: T;
454
+ readonly canceled: boolean;
455
+ readonly reason: string | undefined;
456
+ cancel(reason?: string): void;
457
+ }
458
+ interface MoveEventData {
459
+ item: KanbanItem;
460
+ from: CellRef;
461
+ to: CellRef;
462
+ index?: number;
463
+ }
464
+ interface AddEventData {
465
+ item: KanbanItem;
466
+ }
467
+ interface UpdateEventData {
468
+ item: KanbanItem;
469
+ previous: KanbanItem;
470
+ changes: Record<string, unknown>;
471
+ }
472
+ interface RemoveEventData {
473
+ item: KanbanItem;
474
+ }
475
+ interface GroupMoveEventData {
476
+ axis: KanbanAxis;
477
+ groupId: string;
478
+ from: number;
479
+ to: number;
480
+ /** Nova ordem completa dos ids do eixo (já aplicada nos after:*). */
481
+ order: readonly string[];
482
+ }
483
+ interface EntryMoveEventData {
484
+ entry: KanbanEntry;
485
+ from: EntryRef;
486
+ to: EntryRef;
487
+ }
488
+ /** add/update/remove de entry num só par de eventos, discriminado por `op`. */
489
+ interface EntryUpdateEventData {
490
+ op: 'add' | 'update' | 'remove';
491
+ cardId: string;
492
+ field: string;
493
+ entry: KanbanEntry;
494
+ /** Presente em op='update': a entry ANTES das mudanças. */
495
+ previous?: KanbanEntry;
496
+ changes?: Record<string, unknown>;
497
+ }
498
+ type KanbanEvents = {
499
+ change: BoardState;
500
+ 'before:move': CancelableEvent<MoveEventData>;
501
+ 'after:move': MoveEventData;
502
+ 'before:add': CancelableEvent<AddEventData>;
503
+ 'after:add': AddEventData;
504
+ 'before:update': CancelableEvent<UpdateEventData>;
505
+ 'after:update': UpdateEventData;
506
+ 'before:remove': CancelableEvent<RemoveEventData>;
507
+ 'after:remove': RemoveEventData;
508
+ 'before:group-move': CancelableEvent<GroupMoveEventData>;
509
+ 'after:group-move': GroupMoveEventData;
510
+ 'before:entry-move': CancelableEvent<EntryMoveEventData>;
511
+ 'after:entry-move': EntryMoveEventData;
512
+ 'before:entry-update': CancelableEvent<EntryUpdateEventData>;
513
+ 'after:entry-update': EntryUpdateEventData;
514
+ };
515
+ interface ValidationIssue {
516
+ path: string;
517
+ message: string;
518
+ }
519
+ interface ValidationResult {
520
+ valid: boolean;
521
+ errors: ValidationIssue[];
522
+ }
523
+
524
+ /**
525
+ * Valida um BoardConfig vindo de fora (tela de configuração do host, banco,
526
+ * arquivo). Estrutural e barata — regras nomeadas NÃO são resolvidas aqui
527
+ * (o registry é runtime; a config só carrega nomes).
528
+ */
529
+ declare function validateConfig(config: unknown): ValidationResult;
530
+
531
+ /**
532
+ * Registro nomeado de código custom (docs/01 §4): a config serializável
533
+ * referencia funções por NOME ('rule:berco-livre', 'filter:atrasados'…) e o
534
+ * host registra as implementações aqui, uma vez.
535
+ *
536
+ * É uma INSTÂNCIA (injetada em createBoard), nunca um singleton de módulo —
537
+ * higiene de instância (docs/01 §8).
538
+ */
539
+ declare class KanbanRegistry {
540
+ private readonly entries;
541
+ register(name: string, value: unknown): this;
542
+ get<T>(name: string): T | undefined;
543
+ has(name: string): boolean;
544
+ names(): string[];
545
+ }
546
+
547
+ /** Motivos padronizados de recusa (regras nomeadas retornam texto livre). */
548
+ declare const MOVE_REASONS: {
549
+ readonly readOnly: "read-only";
550
+ readonly locked: "locked";
551
+ readonly transitionNotAllowed: "transition-not-allowed";
552
+ readonly wipLimit: "wip-limit";
553
+ readonly notFound: "not-found";
554
+ readonly unknownGroup: "unknown-group";
555
+ readonly forbidden: "forbidden";
556
+ readonly notReorderable: "not-reorderable";
557
+ readonly canceled: "canceled";
558
+ readonly destroyed: "destroyed";
559
+ };
560
+
561
+ /**
562
+ * Helpers PUROS de entries (itens internos de card) — docs/02 §7.
563
+ * Uma entry vive num array serializável dentro de um campo do card:
564
+ * `card[field]: KanbanEntry[]`. Nada aqui toca estado.
565
+ */
566
+ /** Lê as entries de um campo do card; valor ausente/malformado → []. */
567
+ declare function entriesOf(card: KanbanItem, field: string): KanbanEntry[];
568
+
569
+ interface CreateBoardOptions {
570
+ /** Registro de código nomeado (regras, predicados de filtro). Sem ele, o
571
+ * board cria um registro próprio vazio. */
572
+ registry?: KanbanRegistry;
573
+ /** Contexto do host: permissões (can) e locale — docs/01 §8. */
574
+ host?: KanbanHostContext;
575
+ }
576
+ /**
577
+ * O board headless: estado imutável + comandos + regras + eventos + undo/redo.
578
+ * Instâncias são totalmente isoladas entre si (docs/01 §8) e nada aqui toca
579
+ * DOM, Angular ou persistência — a UI e os adapters consomem esta API.
580
+ */
581
+ declare class KanbanBoard {
582
+ private readonly columnsAxis;
583
+ private readonly lanesAxis;
584
+ private readonly config;
585
+ private readonly store;
586
+ private readonly emitter;
587
+ private readonly host;
588
+ readonly registry: KanbanRegistry;
589
+ private undoStack;
590
+ private redoStack;
591
+ private destroyed;
592
+ constructor(config: BoardConfig, options?: CreateBoardOptions);
593
+ get state(): BoardState;
594
+ /** Modelo declarativo do cartão (docs/01 §4); undefined = cartão default. */
595
+ get cardConfig(): KanbanCardConfig | undefined;
596
+ /** Locale fornecido pelo host — usado por formatadores de data/número. */
597
+ get locale(): string | undefined;
598
+ subscribe(listener: (state: BoardState) => void): () => void;
599
+ on<K extends keyof KanbanEvents>(event: K, handler: (payload: KanbanEvents[K]) => void): () => void;
600
+ execute(command: KanbanCommand): CommandResult;
601
+ /**
602
+ * Aplica um comando de DADOS vindo de FORA (tempo real / rollback do
603
+ * adapter): não entra no histórico de undo, não dispara before:* e NÃO
604
+ * passa por readOnly/regras/permissões — o evento já aconteceu na origem
605
+ * (last-write-wins). A estrutura ainda é validada (item/grupo existem).
606
+ */
607
+ applyRemote(command: KanbanCommand): CommandResult;
608
+ get canUndo(): boolean;
609
+ get canRedo(): boolean;
610
+ /** Desfaz o último comando de DADOS. Regras são revalidadas (o mundo pode
611
+ * ter mudado); before:* não dispara — undo não é vetável, mas é auditável
612
+ * pelos after:*. */
613
+ undo(): CommandResult;
614
+ redo(): CommandResult;
615
+ private applyHistoryCmd;
616
+ /** Grupos resolvidos do eixo de colunas (estático ou derivado), com contagem. */
617
+ columns(): KanbanGroup[];
618
+ /** Grupos do eixo de lanes; null quando o board não tem lanes. */
619
+ lanes(): KanbanGroup[] | null;
620
+ /** Itens da célula (ordem manual ou `sort` da config). NÃO aplica filtros. */
621
+ itemsIn(columnId: string, laneId?: string): KanbanItem[];
622
+ /** Como itemsIn, mas só os itens visíveis pelos filtros ativos. */
623
+ visibleItemsIn(columnId: string, laneId?: string): KanbanItem[];
624
+ isVisible(item: KanbanItem): boolean;
625
+ getItem(id: string): KanbanItem | undefined;
626
+ find(predicate: KanbanItemPredicate): KanbanItem[];
627
+ /**
628
+ * Dry-run do moveItem: roda EXATAMENTE a mesma cadeia de regras do commit
629
+ * (preview == commit). Não dispara eventos nem altera estado — é o que a UI
630
+ * usa para indicar drop válido/inválido durante o arrasto.
631
+ */
632
+ canMove(itemId: string, to: MoveTarget): CommandResult;
633
+ destroy(): void;
634
+ private validateMove;
635
+ /**
636
+ * Nota: mover grava o ID DO GRUPO (string) no campo do eixo — um campo
637
+ * numérico agrupado dinamicamente passa a string após um movimento.
638
+ */
639
+ private applyMove;
640
+ private applyAdd;
641
+ private applyUpdate;
642
+ private applyRemove;
643
+ /** O eixo é reordenável? (estático, opt-in `reorderable`, sem sortGroups). */
644
+ isReorderable(axis: KanbanAxis): boolean;
645
+ /** Dry-run do moveGroup — mesma cadeia do commit (preview == commit). */
646
+ canMoveGroup(axis: KanbanAxis, groupId: string, index: number): CommandResult;
647
+ private validateMoveGroup;
648
+ private applyMoveGroup;
649
+ /** Entries de um campo do card ([] quando ausente/malformado). */
650
+ entries(cardId: string, field: string): KanbanEntry[];
651
+ /** Dry-run do moveEntry — mesma cadeia do commit (preview == commit). */
652
+ canMoveEntry(cmd: Omit<Extract<KanbanCommand, {
653
+ type: 'moveEntry';
654
+ }>, 'type'>): CommandResult;
655
+ private validateEntryMove;
656
+ private applyEntryMove;
657
+ private applyAddEntry;
658
+ private applyUpdateEntry;
659
+ private applyRemoveEntry;
660
+ /** Guardas comuns de escrita de entry (card existe, readOnly, permissão). */
661
+ private entryWriteGuard;
662
+ /** Aplica patches de campos a um ou mais cards num único snapshot. */
663
+ private patchCards;
664
+ private pushHistory;
665
+ private cellOf;
666
+ private inCell;
667
+ private countInCell;
668
+ private countInColumn;
669
+ private countInLane;
670
+ /** Posição do item entre os itens da própria célula (ordem manual). */
671
+ private indexInCell;
672
+ /** Insere `item` (já com os campos novos) na posição `index` da sua célula,
673
+ * mapeada para a posição global correspondente. `items` NÃO contém o item. */
674
+ private placeInCell;
675
+ private groupExists;
676
+ private groupDef;
677
+ private resolveGroups;
678
+ private countByField;
679
+ private applySort;
680
+ }
681
+ /**
682
+ * Cria um board a partir de uma config serializável. Falha RÁPIDO (throw) em
683
+ * config estruturalmente inválida — para validar sem lançar (ex.: config
684
+ * vinda do banco), use validateConfig antes.
685
+ */
686
+ declare function createBoard(config: BoardConfig, options?: CreateBoardOptions): KanbanBoard;
687
+
688
+ export { KANBAN_BLOCK_TYPES, KanbanBoard, KanbanRegistry, MKB_VERSION, MOVE_REASONS, createBoard, entriesOf, isFieldBlock, validateConfig };
689
+ export type { ActionsBlock, AddEventData, AlertBlock, AvatarBlock, BadgeBlock, BoardConfig, BoardState, CancelableEvent, CellRef, CommandResult, CreateBoardOptions, CustomBlock, DateBlock, DateTimeBlock, DividerBlock, EntryMoveContext, EntryMoveEventData, EntryRef, EntryUpdateEventData, FactsBlock, GroupMoveEventData, IconBlock, ImageBlock, ItemsBlock, KanbanAction, KanbanAxis, KanbanAxisConfig, KanbanAxisInput, KanbanBlock, KanbanBlockType, KanbanCardAction, KanbanCardConfig, KanbanCommand, KanbanEntry, KanbanEntryRuleFn, KanbanEvents, KanbanFact, KanbanFilter, KanbanGroup, KanbanGroupDef, KanbanHostContext, KanbanItem, KanbanItemPredicate, KanbanRuleFn, KanbanRulesConfig, KanbanSortConfig, KanbanTone, KanbanTransition, KanbanValueStyle, MoveEventData, MoveRuleContext, MoveTarget, NumberBlock, ProgressBlock, RemoveEventData, RowBlock, TagBlock, TextBlock, TimeBlock, UpdateEventData, ValidationIssue, ValidationResult };