@asteby/metacore-runtime-react 18.28.2 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +35 -6
- package/dist/dynamic-kanban.d.ts +66 -0
- package/dist/dynamic-kanban.d.ts.map +1 -0
- package/dist/dynamic-kanban.js +341 -0
- package/dist/dynamic-view.d.ts +18 -0
- package/dist/dynamic-view.d.ts.map +1 -0
- package/dist/dynamic-view.js +75 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -5
- package/src/__tests__/dynamic-kanban.test.tsx +268 -0
- package/src/action-modal-dispatcher.tsx +32 -0
- package/src/dynamic-kanban.tsx +767 -0
- package/src/dynamic-view.tsx +99 -0
- package/src/index.ts +15 -0
- package/src/types.ts +48 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// DynamicView — the single entry point a host renders for a model's "list"
|
|
2
|
+
// surface. It picks the renderer from the model's `view_type`:
|
|
3
|
+
// - `'kanban'` → <DynamicKanban>
|
|
4
|
+
// - anything else / absent → <DynamicTable> (the default)
|
|
5
|
+
//
|
|
6
|
+
// The decision is metadata-driven (RFC §1.2): the kernel serves `view_type` +
|
|
7
|
+
// `group_by` on the table metadata, derived from the nav item. A host that
|
|
8
|
+
// already knows the view type can skip this and render the concrete component
|
|
9
|
+
// directly; a generic host route (e.g. ops `/m/$model`) mounts <DynamicView>
|
|
10
|
+
// and lets the metadata decide, so the same model can expose a `table` nav and
|
|
11
|
+
// a `kanban` nav with no host code change.
|
|
12
|
+
//
|
|
13
|
+
// Both child components fetch their own metadata (cache-backed), so the extra
|
|
14
|
+
// read this wrapper does to learn `view_type` is served from the same cache —
|
|
15
|
+
// no duplicate network round-trip in practice.
|
|
16
|
+
import { useEffect, useState } from 'react'
|
|
17
|
+
import { useApi } from './api-context'
|
|
18
|
+
import { useMetadataCache } from './metadata-cache'
|
|
19
|
+
import { DynamicTable, type DynamicTableProps } from './dynamic-table'
|
|
20
|
+
import { DynamicKanban, type DynamicKanbanProps } from './dynamic-kanban'
|
|
21
|
+
import type { TableMetadata, ApiResponse } from './types'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pure routing decision: which renderer a `view_type` maps onto. Exported so a
|
|
25
|
+
* host that resolves metadata itself can branch without mounting this wrapper.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveViewRenderer(
|
|
28
|
+
viewType: string | undefined,
|
|
29
|
+
): 'kanban' | 'table' {
|
|
30
|
+
return viewType === 'kanban' ? 'kanban' : 'table'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DynamicViewProps extends DynamicTableProps {
|
|
34
|
+
/**
|
|
35
|
+
* Props forwarded to <DynamicKanban> when the model resolves to a kanban
|
|
36
|
+
* view. `model`/`endpoint`/`refreshTrigger`/`timeZone`/`currency` are shared
|
|
37
|
+
* with the table props and forwarded automatically; this is for the
|
|
38
|
+
* kanban-only extras (e.g. `onCardClick`, `pageSize`).
|
|
39
|
+
*/
|
|
40
|
+
kanbanProps?: Partial<Omit<DynamicKanbanProps, 'model' | 'endpoint'>>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function DynamicView({ kanbanProps, ...tableProps }: DynamicViewProps) {
|
|
44
|
+
const { model, endpoint, refreshTrigger, timeZone, currency } = tableProps
|
|
45
|
+
const api = useApi()
|
|
46
|
+
const cached = useMetadataCache((s) => s.getMetadata(model))
|
|
47
|
+
const setMeta = useMetadataCache((s) => s.setMetadata)
|
|
48
|
+
const [viewType, setViewType] = useState<string | undefined>(cached?.view_type)
|
|
49
|
+
const [resolved, setResolved] = useState<boolean>(!!cached)
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
let cancelled = false
|
|
53
|
+
const c = useMetadataCache.getState().getMetadata(model)
|
|
54
|
+
if (c) {
|
|
55
|
+
setViewType(c.view_type)
|
|
56
|
+
setResolved(true)
|
|
57
|
+
}
|
|
58
|
+
api
|
|
59
|
+
.get(`/metadata/table/${model}`)
|
|
60
|
+
.then((res) => {
|
|
61
|
+
if (cancelled) return
|
|
62
|
+
const body = res.data as ApiResponse<TableMetadata>
|
|
63
|
+
const meta = body?.success ? body.data : (res.data as TableMetadata)
|
|
64
|
+
if (meta) {
|
|
65
|
+
setViewType(meta.view_type)
|
|
66
|
+
setMeta(model, meta)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.catch(() => {
|
|
70
|
+
/* fall back to the table renderer */
|
|
71
|
+
})
|
|
72
|
+
.finally(() => {
|
|
73
|
+
if (!cancelled) setResolved(true)
|
|
74
|
+
})
|
|
75
|
+
return () => {
|
|
76
|
+
cancelled = true
|
|
77
|
+
}
|
|
78
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
|
+
}, [model])
|
|
80
|
+
|
|
81
|
+
// Until we know the view type, render nothing transient-heavy: default to the
|
|
82
|
+
// table renderer only once resolved to avoid a table→kanban flash.
|
|
83
|
+
if (!resolved && !cached) return null
|
|
84
|
+
|
|
85
|
+
if (resolveViewRenderer(viewType) === 'kanban') {
|
|
86
|
+
return (
|
|
87
|
+
<DynamicKanban
|
|
88
|
+
model={model}
|
|
89
|
+
endpoint={endpoint}
|
|
90
|
+
refreshTrigger={refreshTrigger}
|
|
91
|
+
timeZone={timeZone}
|
|
92
|
+
currency={currency}
|
|
93
|
+
{...kanbanProps}
|
|
94
|
+
/>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return <DynamicTable {...tableProps} />
|
|
99
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,21 @@
|
|
|
6
6
|
export * from './types'
|
|
7
7
|
export * from './options-context'
|
|
8
8
|
export * from './dynamic-table'
|
|
9
|
+
export {
|
|
10
|
+
DynamicKanban,
|
|
11
|
+
type DynamicKanbanProps,
|
|
12
|
+
deriveStages,
|
|
13
|
+
groupByStage,
|
|
14
|
+
isTransitionAllowed,
|
|
15
|
+
applyOptimisticMove,
|
|
16
|
+
selectCardColumns,
|
|
17
|
+
UNASSIGNED_LANE,
|
|
18
|
+
} from './dynamic-kanban'
|
|
19
|
+
export {
|
|
20
|
+
DynamicView,
|
|
21
|
+
resolveViewRenderer,
|
|
22
|
+
type DynamicViewProps,
|
|
23
|
+
} from './dynamic-view'
|
|
9
24
|
export * from './dynamic-form'
|
|
10
25
|
export {
|
|
11
26
|
ActionModalDispatcher,
|
package/src/types.ts
CHANGED
|
@@ -22,6 +22,54 @@ export interface TableMetadata {
|
|
|
22
22
|
* and attachments. Absent on hosts/older kernels — purely additive.
|
|
23
23
|
*/
|
|
24
24
|
relations?: RelationMeta[]
|
|
25
|
+
/**
|
|
26
|
+
* Which renderer the host should use for this view. `'table'` (default, or
|
|
27
|
+
* absent) → `DynamicTable`; `'kanban'` → `DynamicKanban`. Served by the
|
|
28
|
+
* kernel from the nav item's `view_type` (RFC §1.2). Purely additive — older
|
|
29
|
+
* kernels omit it and the SDK falls back to the table renderer.
|
|
30
|
+
*/
|
|
31
|
+
view_type?: 'table' | 'kanban' | (string & {})
|
|
32
|
+
/**
|
|
33
|
+
* Column key the board groups by when `view_type === 'kanban'` (the stage
|
|
34
|
+
* column, e.g. `'stage'`). Each distinct value of this column becomes a board
|
|
35
|
+
* lane. Mirrors the nav item's `group_by` (RFC §1.2).
|
|
36
|
+
*/
|
|
37
|
+
group_by?: string
|
|
38
|
+
/**
|
|
39
|
+
* Board lanes (the stage machine of the `group_by`/`stage_field` column).
|
|
40
|
+
* When present the kanban renders one lane per stage in `order`. When absent
|
|
41
|
+
* the SDK derives lanes from the `group_by` column's `options` (the kernel
|
|
42
|
+
* already projects `stages[]` onto the status display — RFC §1.1). Snake_case
|
|
43
|
+
* keys as the kernel serves them.
|
|
44
|
+
*/
|
|
45
|
+
stages?: StageMeta[]
|
|
46
|
+
/**
|
|
47
|
+
* Allowed stage transitions (RFC §1.1). When present, the kanban only lets a
|
|
48
|
+
* card drop into a lane reachable from its current stage; disallowed lanes
|
|
49
|
+
* are dimmed and reject the drop. `from`/`to` accept `'*'` as a wildcard.
|
|
50
|
+
* Absent → any move is allowed (the kernel still validates server-side).
|
|
51
|
+
*/
|
|
52
|
+
transitions?: StageTransition[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* One board lane / pipeline stage. Mirrors the kernel v3 `Stage` (RFC §1.1).
|
|
57
|
+
* `color` is a semantic palette name (`'slate'`, `'blue'`, `'amber'`, `'green'`)
|
|
58
|
+
* or a hex literal — resolved through the same `generateBadgeStyles` helper as
|
|
59
|
+
* option badges. `is_final` flags a terminal stage (e.g. "Done").
|
|
60
|
+
*/
|
|
61
|
+
export interface StageMeta {
|
|
62
|
+
key: string
|
|
63
|
+
label: string
|
|
64
|
+
color?: string
|
|
65
|
+
order?: number
|
|
66
|
+
is_final?: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Allowed `from → to` stage transition (RFC §1.1). `'*'` is a wildcard. */
|
|
70
|
+
export interface StageTransition {
|
|
71
|
+
from: string
|
|
72
|
+
to: string
|
|
25
73
|
}
|
|
26
74
|
|
|
27
75
|
/**
|