@asteby/metacore-runtime-react 4.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 +31 -0
- package/LICENSE +201 -0
- package/README.md +59 -0
- package/dist/action-modal-dispatcher.d.ts +4 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -0
- package/dist/action-modal-dispatcher.js +123 -0
- package/dist/addon-loader.d.ts +27 -0
- package/dist/addon-loader.d.ts.map +1 -0
- package/dist/addon-loader.js +73 -0
- package/dist/api-context.d.ts +40 -0
- package/dist/api-context.d.ts.map +1 -0
- package/dist/api-context.js +25 -0
- package/dist/capability-gate.d.ts +29 -0
- package/dist/capability-gate.d.ts.map +1 -0
- package/dist/capability-gate.js +43 -0
- package/dist/dialogs/_primitives.d.ts +29 -0
- package/dist/dialogs/_primitives.d.ts.map +1 -0
- package/dist/dialogs/_primitives.js +35 -0
- package/dist/dialogs/dynamic-record.d.ts +11 -0
- package/dist/dialogs/dynamic-record.d.ts.map +1 -0
- package/dist/dialogs/dynamic-record.js +377 -0
- package/dist/dialogs/export.d.ts +12 -0
- package/dist/dialogs/export.d.ts.map +1 -0
- package/dist/dialogs/export.js +146 -0
- package/dist/dialogs/import.d.ts +11 -0
- package/dist/dialogs/import.d.ts.map +1 -0
- package/dist/dialogs/import.js +128 -0
- package/dist/dynamic-columns-shim.d.ts +25 -0
- package/dist/dynamic-columns-shim.d.ts.map +1 -0
- package/dist/dynamic-columns-shim.js +1 -0
- package/dist/dynamic-form.d.ts +12 -0
- package/dist/dynamic-form.d.ts.map +1 -0
- package/dist/dynamic-form.js +51 -0
- package/dist/dynamic-icon.d.ts +6 -0
- package/dist/dynamic-icon.d.ts.map +1 -0
- package/dist/dynamic-icon.js +11 -0
- package/dist/dynamic-table.d.ts +22 -0
- package/dist/dynamic-table.d.ts.map +1 -0
- package/dist/dynamic-table.js +516 -0
- package/dist/i18n-provider.d.ts +16 -0
- package/dist/i18n-provider.d.ts.map +1 -0
- package/dist/i18n-provider.js +16 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/metadata-cache.d.ts +42 -0
- package/dist/metadata-cache.d.ts.map +1 -0
- package/dist/metadata-cache.js +71 -0
- package/dist/navigation-builder.d.ts +34 -0
- package/dist/navigation-builder.d.ts.map +1 -0
- package/dist/navigation-builder.js +45 -0
- package/dist/options-context.d.ts +8 -0
- package/dist/options-context.d.ts.map +1 -0
- package/dist/options-context.js +5 -0
- package/dist/slot.d.ts +32 -0
- package/dist/slot.d.ts.map +1 -0
- package/dist/slot.js +45 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +67 -0
- package/src/action-modal-dispatcher.tsx +275 -0
- package/src/addon-loader.tsx +111 -0
- package/src/api-context.tsx +55 -0
- package/src/capability-gate.tsx +69 -0
- package/src/dialogs/_primitives.tsx +114 -0
- package/src/dialogs/dynamic-record.tsx +770 -0
- package/src/dialogs/export.tsx +339 -0
- package/src/dialogs/import.tsx +404 -0
- package/src/dynamic-columns-shim.ts +36 -0
- package/src/dynamic-form.tsx +108 -0
- package/src/dynamic-icon.tsx +15 -0
- package/src/dynamic-table.tsx +766 -0
- package/src/i18n-provider.tsx +33 -0
- package/src/index.ts +30 -0
- package/src/metadata-cache.ts +103 -0
- package/src/navigation-builder.tsx +77 -0
- package/src/options-context.tsx +11 -0
- package/src/slot.tsx +77 -0
- package/src/types.ts +112 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// I18nProvider — merges `manifest.i18n` resources from every loaded addon
|
|
2
|
+
// into the host's i18next instance. The host passes the i18n instance; this
|
|
3
|
+
// component just hydrates new namespaces and keeps them live.
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import type { i18n as I18nInstance } from 'i18next'
|
|
6
|
+
|
|
7
|
+
export interface AddonI18nResources {
|
|
8
|
+
/** Addon key — used as the i18next namespace. */
|
|
9
|
+
source: string
|
|
10
|
+
/** Map of locale → key/value tree. */
|
|
11
|
+
resources: Record<string, Record<string, any>>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface I18nProviderProps {
|
|
15
|
+
/** The host's i18next instance. */
|
|
16
|
+
i18n: I18nInstance
|
|
17
|
+
/** All addon translations contributed via `manifest.i18n`. */
|
|
18
|
+
contributions: AddonI18nResources[]
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function I18nProvider({ i18n, contributions, children }: I18nProviderProps) {
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
for (const c of contributions) {
|
|
25
|
+
for (const [locale, tree] of Object.entries(c.resources)) {
|
|
26
|
+
// addBundle(locale, namespace, resources, deep?, overwrite?)
|
|
27
|
+
i18n.addResourceBundle(locale, c.source, tree, true, false)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, [i18n, contributions])
|
|
31
|
+
|
|
32
|
+
return <>{children}</>
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Public surface — keep names stable. `ActionMetadata` is intentionally
|
|
2
|
+
// re-exported once from `./types` (the mirror used by runtime-react internally)
|
|
3
|
+
// and NOT re-exported from `./action-modal-dispatcher` here to avoid a
|
|
4
|
+
// duplicate-symbol conflict; consumers who want the canonical SDK type
|
|
5
|
+
// should import from `@asteby/metacore-sdk` directly.
|
|
6
|
+
export * from './types'
|
|
7
|
+
export * from './options-context'
|
|
8
|
+
export * from './dynamic-table'
|
|
9
|
+
export * from './dynamic-form'
|
|
10
|
+
export {
|
|
11
|
+
ActionModalDispatcher,
|
|
12
|
+
type ActionModalProps,
|
|
13
|
+
} from './action-modal-dispatcher'
|
|
14
|
+
export * from './addon-loader'
|
|
15
|
+
export * from './slot'
|
|
16
|
+
export * from './capability-gate'
|
|
17
|
+
export * from './navigation-builder'
|
|
18
|
+
export * from './i18n-provider'
|
|
19
|
+
export * from './api-context'
|
|
20
|
+
export * from './metadata-cache'
|
|
21
|
+
export * from './dynamic-icon'
|
|
22
|
+
export type {
|
|
23
|
+
ColumnFilterConfig,
|
|
24
|
+
FilterOption as DynamicColumnFilterOption,
|
|
25
|
+
GetDynamicColumns,
|
|
26
|
+
DynamicIconComponent,
|
|
27
|
+
} from './dynamic-columns-shim'
|
|
28
|
+
export { DynamicRecordDialog } from './dialogs/dynamic-record'
|
|
29
|
+
export { ExportDialog } from './dialogs/export'
|
|
30
|
+
export { ImportDialog } from './dialogs/import'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Metadata cache — a zustand store that memoizes table/modal metadata
|
|
2
|
+
// responses across dynamic-table mounts. Ported from the ops starter's
|
|
3
|
+
// `@/stores/metadata-cache` so the runtime-react package no longer depends
|
|
4
|
+
// on a host-specific alias.
|
|
5
|
+
//
|
|
6
|
+
// The prefetchAll() method needs an `api` client (axios-like); we keep that
|
|
7
|
+
// as an injectable parameter so the store stays host-agnostic. If a caller
|
|
8
|
+
// never invokes prefetchAll, the `api` dep is not required.
|
|
9
|
+
import { create } from 'zustand'
|
|
10
|
+
import { persist } from 'zustand/middleware'
|
|
11
|
+
import type { TableMetadata } from './types'
|
|
12
|
+
|
|
13
|
+
export interface MetadataApiClient {
|
|
14
|
+
get: (url: string, config?: any) => Promise<{ data: any }>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface MetadataCacheState {
|
|
18
|
+
cache: Record<string, TableMetadata>
|
|
19
|
+
modalCache: Record<string, TableMetadata>
|
|
20
|
+
metadataVersion: string
|
|
21
|
+
prefetched: boolean
|
|
22
|
+
getMetadata: (key: string) => TableMetadata | undefined
|
|
23
|
+
getModalMetadata: (key: string) => TableMetadata | undefined
|
|
24
|
+
setMetadata: (key: string, metadata: TableMetadata) => void
|
|
25
|
+
setModalMetadata: (key: string, metadata: TableMetadata) => void
|
|
26
|
+
hasMetadata: (key: string) => boolean
|
|
27
|
+
hasModalMetadata: (key: string) => boolean
|
|
28
|
+
prefetchAll: (api: MetadataApiClient) => Promise<void>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const useMetadataCache = create<MetadataCacheState>()(
|
|
32
|
+
persist(
|
|
33
|
+
(set, get) => ({
|
|
34
|
+
cache: {},
|
|
35
|
+
modalCache: {},
|
|
36
|
+
metadataVersion: '',
|
|
37
|
+
prefetched: false,
|
|
38
|
+
|
|
39
|
+
getMetadata: (key: string) => get().cache[key],
|
|
40
|
+
getModalMetadata: (key: string) => get().modalCache[key],
|
|
41
|
+
|
|
42
|
+
setMetadata: (key: string, metadata: TableMetadata) => {
|
|
43
|
+
set((state) => ({
|
|
44
|
+
cache: { ...state.cache, [key]: metadata },
|
|
45
|
+
}))
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
setModalMetadata: (key: string, metadata: TableMetadata) => {
|
|
49
|
+
set((state) => ({
|
|
50
|
+
modalCache: { ...state.modalCache, [key]: metadata },
|
|
51
|
+
}))
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
hasMetadata: (key: string) => key in get().cache,
|
|
55
|
+
hasModalMetadata: (key: string) => key in get().modalCache,
|
|
56
|
+
|
|
57
|
+
prefetchAll: async (api: MetadataApiClient) => {
|
|
58
|
+
if (get().prefetched) return
|
|
59
|
+
try {
|
|
60
|
+
const res = await api.get('/metadata/all')
|
|
61
|
+
const { tables, modals, version } = res.data.data
|
|
62
|
+
|
|
63
|
+
const serverVersion = version || ''
|
|
64
|
+
const localVersion = get().metadataVersion
|
|
65
|
+
const versionChanged = serverVersion !== localVersion && localVersion !== ''
|
|
66
|
+
|
|
67
|
+
const newCache: Record<string, TableMetadata> = versionChanged ? {} : { ...get().cache }
|
|
68
|
+
const newModalCache: Record<string, TableMetadata> = versionChanged ? {} : { ...get().modalCache }
|
|
69
|
+
|
|
70
|
+
if (tables) {
|
|
71
|
+
for (const [key, meta] of Object.entries(tables)) {
|
|
72
|
+
newCache[key] = meta as TableMetadata
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (modals) {
|
|
76
|
+
for (const [key, meta] of Object.entries(modals)) {
|
|
77
|
+
newModalCache[key] = meta as TableMetadata
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
set({
|
|
82
|
+
cache: newCache,
|
|
83
|
+
modalCache: newModalCache,
|
|
84
|
+
metadataVersion: serverVersion,
|
|
85
|
+
prefetched: true,
|
|
86
|
+
})
|
|
87
|
+
} catch {
|
|
88
|
+
// Offline or error — keep using cached data.
|
|
89
|
+
set({ prefetched: true })
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}),
|
|
93
|
+
{
|
|
94
|
+
name: 'metacore-metadata-cache',
|
|
95
|
+
version: 3,
|
|
96
|
+
partialize: (state) => ({
|
|
97
|
+
cache: state.cache,
|
|
98
|
+
modalCache: state.modalCache,
|
|
99
|
+
metadataVersion: state.metadataVersion,
|
|
100
|
+
}),
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// NavigationBuilder — merges a host's base sidebar with `manifest.navigation`
|
|
2
|
+
// contributions from every loaded addon. Pure function + a React hook.
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
|
|
5
|
+
export interface NavItem {
|
|
6
|
+
key: string
|
|
7
|
+
label: string
|
|
8
|
+
icon?: string
|
|
9
|
+
to?: string
|
|
10
|
+
/** Sort weight; higher = earlier. Default 0. */
|
|
11
|
+
priority?: number
|
|
12
|
+
/** Capability required to see this item. */
|
|
13
|
+
requires?: string
|
|
14
|
+
/** Nested children (rendered as a collapsible section). */
|
|
15
|
+
children?: NavItem[]
|
|
16
|
+
/** Group this item belongs to — items with the same group are clustered. */
|
|
17
|
+
group?: string
|
|
18
|
+
/** Source addon key (for debugging / telemetry). */
|
|
19
|
+
source?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface AddonNavigationContribution {
|
|
23
|
+
source: string
|
|
24
|
+
items: NavItem[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Deep-merge nav trees by `key`. Children are merged recursively; on
|
|
28
|
+
* conflict the higher-priority wins, then the later contribution.
|
|
29
|
+
*/
|
|
30
|
+
export function mergeNavigation(base: NavItem[], contributions: AddonNavigationContribution[]): NavItem[] {
|
|
31
|
+
const byKey = new Map<string, NavItem>()
|
|
32
|
+
const order: string[] = []
|
|
33
|
+
|
|
34
|
+
const absorb = (items: NavItem[], source?: string) => {
|
|
35
|
+
for (const raw of items) {
|
|
36
|
+
const item: NavItem = { ...raw, source: raw.source ?? source }
|
|
37
|
+
const existing = byKey.get(item.key)
|
|
38
|
+
if (!existing) {
|
|
39
|
+
byKey.set(item.key, item)
|
|
40
|
+
order.push(item.key)
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
const a = existing.priority ?? 0
|
|
44
|
+
const b = item.priority ?? 0
|
|
45
|
+
const winner = b >= a ? item : existing
|
|
46
|
+
const loser = b >= a ? existing : item
|
|
47
|
+
const mergedChildren = (winner.children || loser.children)
|
|
48
|
+
? mergeNavigation(winner.children ?? [], [{ source: winner.source ?? '', items: loser.children ?? [] }])
|
|
49
|
+
: undefined
|
|
50
|
+
byKey.set(item.key, { ...winner, children: mergedChildren })
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
absorb(base, 'host')
|
|
55
|
+
for (const c of contributions) absorb(c.items, c.source)
|
|
56
|
+
|
|
57
|
+
return order
|
|
58
|
+
.map(k => byKey.get(k)!)
|
|
59
|
+
.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function useNavigation(base: NavItem[], contributions: AddonNavigationContribution[]): NavItem[] {
|
|
63
|
+
return useMemo(() => mergeNavigation(base, contributions), [base, contributions])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface NavigationBuilderProps {
|
|
67
|
+
base: NavItem[]
|
|
68
|
+
contributions: AddonNavigationContribution[]
|
|
69
|
+
render: (items: NavItem[]) => React.ReactNode
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Render-prop component for hosts that want the merge logic but render
|
|
73
|
+
* the sidebar with their own primitives. */
|
|
74
|
+
export function NavigationBuilder({ base, contributions, render }: NavigationBuilderProps) {
|
|
75
|
+
const items = useNavigation(base, contributions)
|
|
76
|
+
return <>{render(items)}</>
|
|
77
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
interface OptionsContextValue {
|
|
4
|
+
optionsMap: Map<string, any[]>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const OptionsContext = React.createContext<OptionsContextValue>({
|
|
8
|
+
optionsMap: new Map(),
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
export const useOptions = () => React.useContext(OptionsContext)
|
package/src/slot.tsx
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Slot / SlotRegistry — named extension points the host renders and addons
|
|
2
|
+
// contribute to at register() time. Keyed by a slot id (e.g. "dashboard.widgets",
|
|
3
|
+
// "invoice.footer"). Each contribution is an arbitrary React element factory.
|
|
4
|
+
import React, { useSyncExternalStore } from 'react'
|
|
5
|
+
|
|
6
|
+
export type SlotComponent<P = any> = React.ComponentType<P>
|
|
7
|
+
|
|
8
|
+
interface SlotEntry {
|
|
9
|
+
id: string
|
|
10
|
+
component: SlotComponent
|
|
11
|
+
priority: number
|
|
12
|
+
source?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Listener = () => void
|
|
16
|
+
|
|
17
|
+
class SlotRegistryImpl {
|
|
18
|
+
private slots = new Map<string, SlotEntry[]>()
|
|
19
|
+
private listeners = new Set<Listener>()
|
|
20
|
+
|
|
21
|
+
register(slotId: string, component: SlotComponent, opts?: { priority?: number; source?: string }): () => void {
|
|
22
|
+
const entry: SlotEntry = { id: slotId, component, priority: opts?.priority ?? 0, source: opts?.source }
|
|
23
|
+
const list = this.slots.get(slotId) ?? []
|
|
24
|
+
list.push(entry)
|
|
25
|
+
list.sort((a, b) => b.priority - a.priority)
|
|
26
|
+
this.slots.set(slotId, list)
|
|
27
|
+
this.emit()
|
|
28
|
+
return () => {
|
|
29
|
+
const arr = this.slots.get(slotId)
|
|
30
|
+
if (!arr) return
|
|
31
|
+
const idx = arr.indexOf(entry)
|
|
32
|
+
if (idx >= 0) {
|
|
33
|
+
arr.splice(idx, 1)
|
|
34
|
+
this.emit()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get(slotId: string): SlotEntry[] {
|
|
40
|
+
return this.slots.get(slotId) ?? []
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
subscribe(listener: Listener): () => void {
|
|
44
|
+
this.listeners.add(listener)
|
|
45
|
+
return () => { this.listeners.delete(listener) }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private emit() { this.listeners.forEach(l => l()) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const slotRegistry = new SlotRegistryImpl()
|
|
52
|
+
|
|
53
|
+
export interface SlotProps {
|
|
54
|
+
/** Slot id. */
|
|
55
|
+
name: string
|
|
56
|
+
/** Props forwarded to each contribution component. */
|
|
57
|
+
props?: Record<string, any>
|
|
58
|
+
/** Fallback element shown when no contribution is registered. */
|
|
59
|
+
fallback?: React.ReactNode
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function Slot({ name, props, fallback = null }: SlotProps) {
|
|
63
|
+
const entries = useSyncExternalStore(
|
|
64
|
+
(cb) => slotRegistry.subscribe(cb),
|
|
65
|
+
() => slotRegistry.get(name),
|
|
66
|
+
() => slotRegistry.get(name),
|
|
67
|
+
)
|
|
68
|
+
if (entries.length === 0) return <>{fallback}</>
|
|
69
|
+
return (
|
|
70
|
+
<>
|
|
71
|
+
{entries.map((entry, i) => {
|
|
72
|
+
const C = entry.component
|
|
73
|
+
return <C key={`${entry.source ?? 'anon'}-${i}`} {...(props ?? {})} />
|
|
74
|
+
})}
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Union of the two host copies (link + ops). Ops adds the `link` action type + `linkUrl`.
|
|
2
|
+
export interface TableMetadata {
|
|
3
|
+
title: string
|
|
4
|
+
endpoint: string
|
|
5
|
+
columns: ColumnDefinition[]
|
|
6
|
+
actions: ActionDefinition[]
|
|
7
|
+
filters?: FilterDefinition[]
|
|
8
|
+
perPageOptions: number[]
|
|
9
|
+
defaultPerPage: number
|
|
10
|
+
searchPlaceholder: string
|
|
11
|
+
enableCRUDActions: boolean
|
|
12
|
+
hasActions: boolean
|
|
13
|
+
canExport?: boolean
|
|
14
|
+
canImport?: boolean
|
|
15
|
+
canCreate?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface FilterDefinition {
|
|
19
|
+
key: string
|
|
20
|
+
label: string
|
|
21
|
+
type: 'select' | 'boolean' | 'date_range' | 'number_range' | 'text'
|
|
22
|
+
column: string
|
|
23
|
+
options?: { value: string | boolean; label: string; icon?: string; color?: string }[]
|
|
24
|
+
searchEndpoint?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ColumnDefinition {
|
|
28
|
+
key: string
|
|
29
|
+
label: string
|
|
30
|
+
type: 'text' | 'number' | 'date' | 'select' | 'search' | 'relation-badge-list' | 'avatar' | 'boolean' | 'phone' | 'media-gallery' | 'image'
|
|
31
|
+
sortable: boolean
|
|
32
|
+
filterable: boolean
|
|
33
|
+
hidden?: boolean
|
|
34
|
+
styleConfig?: Record<string, any>
|
|
35
|
+
tooltip?: string
|
|
36
|
+
description?: string
|
|
37
|
+
cellStyle?: string
|
|
38
|
+
searchEndpoint?: string
|
|
39
|
+
filterField?: string
|
|
40
|
+
basePath?: string
|
|
41
|
+
displayField?: string
|
|
42
|
+
iconField?: string
|
|
43
|
+
relationPath?: string
|
|
44
|
+
useOptions?: boolean
|
|
45
|
+
options?: { value: string; label: string; icon?: string; color?: string }[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ActionCondition {
|
|
49
|
+
field: string
|
|
50
|
+
operator: 'eq' | 'neq' | 'in' | 'not_in'
|
|
51
|
+
value: string | string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ActionFieldDef {
|
|
55
|
+
key: string
|
|
56
|
+
label: string
|
|
57
|
+
type: string
|
|
58
|
+
required?: boolean
|
|
59
|
+
options?: { value: string; label: string }[]
|
|
60
|
+
defaultValue?: any
|
|
61
|
+
placeholder?: string
|
|
62
|
+
searchEndpoint?: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ActionDefinition {
|
|
66
|
+
key: string
|
|
67
|
+
name: string
|
|
68
|
+
label: string
|
|
69
|
+
icon: string
|
|
70
|
+
class: string
|
|
71
|
+
color?: string
|
|
72
|
+
type: 'view' | 'edit' | 'delete' | 'custom' | 'link'
|
|
73
|
+
linkUrl?: string
|
|
74
|
+
condition?: ActionCondition
|
|
75
|
+
confirm?: boolean
|
|
76
|
+
confirmMessage?: string
|
|
77
|
+
fields?: ActionFieldDef[]
|
|
78
|
+
requiresState?: string[]
|
|
79
|
+
executable?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ApiResponse<T> {
|
|
83
|
+
success: boolean
|
|
84
|
+
data: T
|
|
85
|
+
meta?: PaginationMeta
|
|
86
|
+
filters?: Record<string, any>
|
|
87
|
+
message?: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface PaginationMeta {
|
|
91
|
+
current_page: number
|
|
92
|
+
from: number
|
|
93
|
+
last_page: number
|
|
94
|
+
per_page: number
|
|
95
|
+
to: number
|
|
96
|
+
total: number
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ActionMetadata re-exported from the sdk's action-registry. We mirror the
|
|
100
|
+
// subset needed for the dispatcher so consumers of runtime-react don't have to
|
|
101
|
+
// import the sdk directly for prop typings.
|
|
102
|
+
export interface ActionMetadata {
|
|
103
|
+
key: string
|
|
104
|
+
label: string
|
|
105
|
+
icon: string
|
|
106
|
+
color?: string
|
|
107
|
+
confirm?: boolean
|
|
108
|
+
confirmMessage?: string
|
|
109
|
+
fields?: ActionFieldDef[]
|
|
110
|
+
requiresState?: string[]
|
|
111
|
+
executable?: boolean
|
|
112
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"declarationMap": true,
|
|
12
|
+
"outDir": "./dist",
|
|
13
|
+
"rootDir": "./src"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"]
|
|
16
|
+
}
|