@01.software/sdk 0.4.0 → 0.4.2
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/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/{const-BpirbGBD.d.cts → const-CWZ70tFe.d.cts} +1 -1
- package/dist/{const-qZSQiSSC.d.ts → const-nftKBzYp.d.ts} +1 -1
- package/dist/index.cjs +73 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +73 -9
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-CZiaL4Wr.d.cts → payload-types-BbEEk9Ji.d.cts} +68 -3
- package/dist/{payload-types-CZiaL4Wr.d.ts → payload-types-BbEEk9Ji.d.ts} +68 -3
- package/dist/realtime.cjs +5 -3
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +5 -3
- package/dist/realtime.js.map +1 -1
- package/dist/ui/flow.cjs +264 -36
- package/dist/ui/flow.cjs.map +1 -1
- package/dist/ui/flow.d.cts +105 -4
- package/dist/ui/flow.d.ts +105 -4
- package/dist/ui/flow.js +252 -21
- package/dist/ui/flow.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/rich-text.cjs +8 -1
- package/dist/ui/rich-text.cjs.map +1 -1
- package/dist/ui/rich-text.d.cts +20 -1
- package/dist/ui/rich-text.d.ts +20 -1
- package/dist/ui/rich-text.js +8 -1
- package/dist/ui/rich-text.js.map +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/{webhook-DuTqrH9x.d.ts → webhook-C4nmpBmQ.d.ts} +2 -2
- package/dist/{webhook-3iL9OEyq.d.cts → webhook-DRGm9rPn.d.cts} +2 -2
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +2 -1
package/dist/realtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n if (typeof process !== 'undefined' && process.env) {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":"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","names":[]}
|
package/dist/ui/flow.cjs
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
|
+
var __defProps = Object.defineProperties;
|
|
5
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
8
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
9
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
10
|
var __getProtoOf = Object.getPrototypeOf;
|
|
@@ -20,6 +22,7 @@ var __spreadValues = (a, b) => {
|
|
|
20
22
|
}
|
|
21
23
|
return a;
|
|
22
24
|
};
|
|
25
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
26
|
var __export = (target, all) => {
|
|
24
27
|
for (var name in all)
|
|
25
28
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -68,13 +71,16 @@ __export(Flow_exports, {
|
|
|
68
71
|
BUILT_IN_EDGE_TYPES: () => BUILT_IN_EDGE_TYPES,
|
|
69
72
|
BUILT_IN_NODE_TYPES: () => BUILT_IN_NODE_TYPES,
|
|
70
73
|
FlowRenderer: () => FlowRenderer,
|
|
74
|
+
getFrames: () => getFrames,
|
|
75
|
+
getNodeBounds: () => getNodeBounds,
|
|
71
76
|
isDynamicNode: () => isDynamicNode,
|
|
72
77
|
isFrameNode: () => isFrameNode,
|
|
73
|
-
useFlow: () => useFlow
|
|
78
|
+
useFlow: () => useFlow,
|
|
79
|
+
useFlowData: () => useFlowData
|
|
74
80
|
});
|
|
75
81
|
module.exports = __toCommonJS(Flow_exports);
|
|
76
|
-
var
|
|
77
|
-
var
|
|
82
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
83
|
+
var import_react4 = require("@xyflow/react");
|
|
78
84
|
|
|
79
85
|
// src/ui/Flow/types.ts
|
|
80
86
|
function isDynamicNode(node) {
|
|
@@ -240,6 +246,104 @@ function useFlow(options) {
|
|
|
240
246
|
};
|
|
241
247
|
}
|
|
242
248
|
|
|
249
|
+
// src/ui/Flow/useFlowData.ts
|
|
250
|
+
var import_react2 = require("react");
|
|
251
|
+
function useFlowData(options) {
|
|
252
|
+
const { data, nodeTypeDefs: inputNodeDefs, edgeTypeDefs: inputEdgeDefs } = options;
|
|
253
|
+
const nodeTypeDefsMap = (0, import_react2.useMemo)(() => {
|
|
254
|
+
const allDefs = inputNodeDefs != null ? inputNodeDefs : BUILT_IN_NODE_TYPES;
|
|
255
|
+
return new Map(allDefs.map((d) => [d.slug, d]));
|
|
256
|
+
}, [inputNodeDefs]);
|
|
257
|
+
const edgeTypeDefsMap = (0, import_react2.useMemo)(() => {
|
|
258
|
+
const allDefs = inputEdgeDefs != null ? inputEdgeDefs : BUILT_IN_EDGE_TYPES;
|
|
259
|
+
return new Map(allDefs.map((d) => [d.slug, d]));
|
|
260
|
+
}, [inputEdgeDefs]);
|
|
261
|
+
const nodes = (0, import_react2.useMemo)(() => {
|
|
262
|
+
var _a;
|
|
263
|
+
return (_a = data == null ? void 0 : data.nodes) != null ? _a : [];
|
|
264
|
+
}, [data == null ? void 0 : data.nodes]);
|
|
265
|
+
const edges = (0, import_react2.useMemo)(() => {
|
|
266
|
+
var _a;
|
|
267
|
+
return (_a = data == null ? void 0 : data.edges) != null ? _a : [];
|
|
268
|
+
}, [data == null ? void 0 : data.edges]);
|
|
269
|
+
return {
|
|
270
|
+
nodes,
|
|
271
|
+
edges,
|
|
272
|
+
nodeTypeDefsMap,
|
|
273
|
+
edgeTypeDefsMap
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/ui/Flow/utils.ts
|
|
278
|
+
function getNodeBounds(nodes, nodeIds) {
|
|
279
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
280
|
+
const idSet = new Set(nodeIds);
|
|
281
|
+
const targetNodes = nodes.filter((n) => idSet.has(n.id));
|
|
282
|
+
if (targetNodes.length === 0) return void 0;
|
|
283
|
+
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
284
|
+
function getAbsolutePosition(node) {
|
|
285
|
+
let x = node.position.x;
|
|
286
|
+
let y = node.position.y;
|
|
287
|
+
let current = node;
|
|
288
|
+
const visited = /* @__PURE__ */ new Set([node.id]);
|
|
289
|
+
while (current.parentId) {
|
|
290
|
+
const parentId = current.parentId;
|
|
291
|
+
if (visited.has(parentId)) break;
|
|
292
|
+
const parent = nodeMap.get(parentId);
|
|
293
|
+
if (!parent) break;
|
|
294
|
+
visited.add(parent.id);
|
|
295
|
+
x += parent.position.x;
|
|
296
|
+
y += parent.position.y;
|
|
297
|
+
current = parent;
|
|
298
|
+
}
|
|
299
|
+
return { x, y };
|
|
300
|
+
}
|
|
301
|
+
let minX = Infinity;
|
|
302
|
+
let minY = Infinity;
|
|
303
|
+
let maxX = -Infinity;
|
|
304
|
+
let maxY = -Infinity;
|
|
305
|
+
for (const node of targetNodes) {
|
|
306
|
+
const abs = getAbsolutePosition(node);
|
|
307
|
+
const w = (_e = (_d = (_c = (_a = node.style) == null ? void 0 : _a.width) != null ? _c : (_b = node.measured) == null ? void 0 : _b.width) != null ? _d : node.width) != null ? _e : 200;
|
|
308
|
+
const h = (_j = (_i = (_h = (_f = node.style) == null ? void 0 : _f.height) != null ? _h : (_g = node.measured) == null ? void 0 : _g.height) != null ? _i : node.height) != null ? _j : 200;
|
|
309
|
+
minX = Math.min(minX, abs.x);
|
|
310
|
+
minY = Math.min(minY, abs.y);
|
|
311
|
+
maxX = Math.max(maxX, abs.x + w);
|
|
312
|
+
maxY = Math.max(maxY, abs.y + h);
|
|
313
|
+
}
|
|
314
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
315
|
+
}
|
|
316
|
+
function getFrames(nodes) {
|
|
317
|
+
const frames = nodes.filter((n) => n.type === "frame");
|
|
318
|
+
if (frames.length === 0) return [];
|
|
319
|
+
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
320
|
+
function getAbsolutePosition(node) {
|
|
321
|
+
let x = node.position.x;
|
|
322
|
+
let y = node.position.y;
|
|
323
|
+
let current = node;
|
|
324
|
+
const visited = /* @__PURE__ */ new Set([node.id]);
|
|
325
|
+
while (current.parentId) {
|
|
326
|
+
const parentId = current.parentId;
|
|
327
|
+
if (visited.has(parentId)) break;
|
|
328
|
+
const parent = nodeMap.get(parentId);
|
|
329
|
+
if (!parent) break;
|
|
330
|
+
visited.add(parent.id);
|
|
331
|
+
x += parent.position.x;
|
|
332
|
+
y += parent.position.y;
|
|
333
|
+
current = parent;
|
|
334
|
+
}
|
|
335
|
+
return { x, y };
|
|
336
|
+
}
|
|
337
|
+
return frames.map((f) => {
|
|
338
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
339
|
+
const data = f.data;
|
|
340
|
+
const abs = getAbsolutePosition(f);
|
|
341
|
+
const w = (_e = (_d = (_c = (_a = f.style) == null ? void 0 : _a.width) != null ? _c : (_b = f.measured) == null ? void 0 : _b.width) != null ? _d : f.width) != null ? _e : 200;
|
|
342
|
+
const h = (_j = (_i = (_h = (_f = f.style) == null ? void 0 : _f.height) != null ? _h : (_g = f.measured) == null ? void 0 : _g.height) != null ? _i : f.height) != null ? _j : 200;
|
|
343
|
+
return { id: f.id, label: (_k = data.label) != null ? _k : "", bounds: { x: abs.x, y: abs.y, width: w, height: h } };
|
|
344
|
+
}).sort((a, b) => a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x);
|
|
345
|
+
}
|
|
346
|
+
|
|
243
347
|
// src/ui/Flow/index.tsx
|
|
244
348
|
function sanitizeUrl(url) {
|
|
245
349
|
if (!url) return url;
|
|
@@ -252,8 +356,8 @@ function sanitizeUrl(url) {
|
|
|
252
356
|
}
|
|
253
357
|
}
|
|
254
358
|
function toMarkerType(value) {
|
|
255
|
-
if (value === "arrow") return
|
|
256
|
-
if (value === "arrowclosed") return
|
|
359
|
+
if (value === "arrow") return import_react4.MarkerType.Arrow;
|
|
360
|
+
if (value === "arrowclosed") return import_react4.MarkerType.ArrowClosed;
|
|
257
361
|
return void 0;
|
|
258
362
|
}
|
|
259
363
|
function renderFieldValue(key, val, fieldDef) {
|
|
@@ -263,7 +367,7 @@ function renderFieldValue(key, val, fieldDef) {
|
|
|
263
367
|
const imgUrl = typeof val === "string" ? val : val == null ? void 0 : val.url;
|
|
264
368
|
const safeUrl = sanitizeUrl(imgUrl);
|
|
265
369
|
if (!safeUrl) return null;
|
|
266
|
-
return /* @__PURE__ */
|
|
370
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
267
371
|
"img",
|
|
268
372
|
{
|
|
269
373
|
key,
|
|
@@ -274,7 +378,7 @@ function renderFieldValue(key, val, fieldDef) {
|
|
|
274
378
|
}
|
|
275
379
|
);
|
|
276
380
|
}
|
|
277
|
-
return /* @__PURE__ */
|
|
381
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
278
382
|
"div",
|
|
279
383
|
{
|
|
280
384
|
key,
|
|
@@ -291,7 +395,7 @@ function renderFieldValue(key, val, fieldDef) {
|
|
|
291
395
|
}
|
|
292
396
|
function DefaultDynamicNode({ data }) {
|
|
293
397
|
const d = data;
|
|
294
|
-
return /* @__PURE__ */
|
|
398
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
295
399
|
"div",
|
|
296
400
|
{
|
|
297
401
|
style: {
|
|
@@ -308,7 +412,7 @@ function EnhancedDynamicNode({
|
|
|
308
412
|
data,
|
|
309
413
|
typeDef
|
|
310
414
|
}) {
|
|
311
|
-
return /* @__PURE__ */
|
|
415
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
312
416
|
"div",
|
|
313
417
|
{
|
|
314
418
|
style: {
|
|
@@ -337,7 +441,7 @@ function DefaultFrameNode({ data }) {
|
|
|
337
441
|
if (m) return `rgba(${m[1]},${m[2]},${m[3]},${opacity})`;
|
|
338
442
|
return baseColor;
|
|
339
443
|
})();
|
|
340
|
-
return /* @__PURE__ */
|
|
444
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
341
445
|
"div",
|
|
342
446
|
{
|
|
343
447
|
style: {
|
|
@@ -348,7 +452,7 @@ function DefaultFrameNode({ data }) {
|
|
|
348
452
|
border: borderStyle === "none" ? "none" : `2px ${borderStyle} rgba(128,128,128,0.3)`
|
|
349
453
|
}
|
|
350
454
|
},
|
|
351
|
-
/* @__PURE__ */
|
|
455
|
+
/* @__PURE__ */ import_react3.default.createElement(
|
|
352
456
|
"div",
|
|
353
457
|
{
|
|
354
458
|
style: {
|
|
@@ -362,14 +466,15 @@ function DefaultFrameNode({ data }) {
|
|
|
362
466
|
)
|
|
363
467
|
);
|
|
364
468
|
}
|
|
365
|
-
function createNodeTypes(nodeRenderers, nodeTypeDefsMap) {
|
|
469
|
+
function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
|
|
366
470
|
const types = {};
|
|
367
471
|
types.dynamic = ((props) => {
|
|
368
472
|
const d = props.data;
|
|
369
473
|
const typeDef = nodeTypeDefsMap == null ? void 0 : nodeTypeDefsMap.get(d.nodeTypeSlug);
|
|
370
474
|
const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
|
|
475
|
+
let content;
|
|
371
476
|
if (CustomRenderer) {
|
|
372
|
-
|
|
477
|
+
content = /* @__PURE__ */ import_react3.default.createElement(
|
|
373
478
|
CustomRenderer,
|
|
374
479
|
{
|
|
375
480
|
id: props.id,
|
|
@@ -379,15 +484,94 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap) {
|
|
|
379
484
|
nodeTypeDef: typeDef
|
|
380
485
|
}
|
|
381
486
|
);
|
|
487
|
+
} else if (typeDef) {
|
|
488
|
+
content = /* @__PURE__ */ import_react3.default.createElement(EnhancedDynamicNode, { data: d, typeDef });
|
|
489
|
+
} else {
|
|
490
|
+
content = /* @__PURE__ */ import_react3.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
|
|
382
491
|
}
|
|
383
|
-
if (
|
|
384
|
-
|
|
492
|
+
if (renderNode) {
|
|
493
|
+
const slotProps = {
|
|
494
|
+
id: props.id,
|
|
495
|
+
nodeTypeSlug: d.nodeTypeSlug,
|
|
496
|
+
label: d.label,
|
|
497
|
+
fields: d.fields,
|
|
498
|
+
nodeTypeDef: typeDef
|
|
499
|
+
};
|
|
500
|
+
const result = renderNode(slotProps, content);
|
|
501
|
+
if (result !== null) content = result;
|
|
385
502
|
}
|
|
386
|
-
|
|
503
|
+
if (nodeWrapper) {
|
|
504
|
+
const Wrapper = nodeWrapper;
|
|
505
|
+
content = /* @__PURE__ */ import_react3.default.createElement(
|
|
506
|
+
Wrapper,
|
|
507
|
+
{
|
|
508
|
+
id: props.id,
|
|
509
|
+
nodeTypeSlug: d.nodeTypeSlug,
|
|
510
|
+
label: d.label,
|
|
511
|
+
selected: props.selected,
|
|
512
|
+
nodeTypeDef: typeDef
|
|
513
|
+
},
|
|
514
|
+
content
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return content;
|
|
387
518
|
});
|
|
388
|
-
types.frame =
|
|
519
|
+
types.frame = frameRenderer ? ((props) => {
|
|
520
|
+
const d = props.data;
|
|
521
|
+
const Renderer = frameRenderer;
|
|
522
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
523
|
+
Renderer,
|
|
524
|
+
{
|
|
525
|
+
id: props.id,
|
|
526
|
+
label: d.label,
|
|
527
|
+
color: d.color,
|
|
528
|
+
padding: d.padding,
|
|
529
|
+
borderStyle: d.borderStyle,
|
|
530
|
+
opacity: d.opacity
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
}) : DefaultFrameNode;
|
|
534
|
+
return types;
|
|
535
|
+
}
|
|
536
|
+
function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
|
|
537
|
+
if (!edgeRenderers || Object.keys(edgeRenderers).length === 0) return void 0;
|
|
538
|
+
const types = {};
|
|
539
|
+
for (const [slug, Renderer] of Object.entries(edgeRenderers)) {
|
|
540
|
+
types[slug] = ((props) => {
|
|
541
|
+
var _a;
|
|
542
|
+
const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
|
|
543
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
544
|
+
Renderer,
|
|
545
|
+
{
|
|
546
|
+
id: props.id,
|
|
547
|
+
edgeTypeSlug: slug,
|
|
548
|
+
source: props.source,
|
|
549
|
+
target: props.target,
|
|
550
|
+
label: props.label,
|
|
551
|
+
fields: (_a = props.data) == null ? void 0 : _a.fields,
|
|
552
|
+
edgeTypeDef: def,
|
|
553
|
+
style: props.style
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
389
558
|
return types;
|
|
390
559
|
}
|
|
560
|
+
function FocusHandler({
|
|
561
|
+
bounds,
|
|
562
|
+
padding,
|
|
563
|
+
animation
|
|
564
|
+
}) {
|
|
565
|
+
const { fitBounds } = (0, import_react4.useReactFlow)();
|
|
566
|
+
const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
|
|
567
|
+
const boundsRef = import_react3.default.useRef(bounds);
|
|
568
|
+
boundsRef.current = bounds;
|
|
569
|
+
import_react3.default.useEffect(() => {
|
|
570
|
+
const duration = animation === true ? 300 : typeof animation === "number" ? animation : 0;
|
|
571
|
+
fitBounds(boundsRef.current, { padding, duration });
|
|
572
|
+
}, [boundsKey, padding, animation, fitBounds]);
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
391
575
|
var EDGE_TYPE_MAP = {
|
|
392
576
|
step: "step",
|
|
393
577
|
smoothstep: "smoothstep",
|
|
@@ -434,50 +618,78 @@ function FlowRenderer({
|
|
|
434
618
|
nodeRenderers,
|
|
435
619
|
nodeTypeDefs,
|
|
436
620
|
edgeTypeDefs,
|
|
437
|
-
background =
|
|
621
|
+
background = false,
|
|
438
622
|
interactive = false,
|
|
439
623
|
fitView = true,
|
|
440
624
|
onNodeClick,
|
|
441
|
-
onEdgeClick
|
|
625
|
+
onEdgeClick,
|
|
626
|
+
frameRenderer,
|
|
627
|
+
edgeRenderers,
|
|
628
|
+
nodeWrapper,
|
|
629
|
+
controls,
|
|
630
|
+
minimap,
|
|
631
|
+
minimapNodeColor,
|
|
632
|
+
children,
|
|
633
|
+
renderNode,
|
|
634
|
+
onViewportChange,
|
|
635
|
+
defaultViewport: defaultViewportProp,
|
|
636
|
+
bounds,
|
|
637
|
+
focusPadding,
|
|
638
|
+
focusAnimation
|
|
442
639
|
}) {
|
|
443
640
|
var _a;
|
|
444
|
-
const nodeTypeDefsMap =
|
|
641
|
+
const nodeTypeDefsMap = import_react3.default.useMemo(() => {
|
|
445
642
|
if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
|
|
446
643
|
return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
|
|
447
644
|
}, [nodeTypeDefs]);
|
|
448
|
-
const edgeTypeDefsMap =
|
|
645
|
+
const edgeTypeDefsMap = import_react3.default.useMemo(() => {
|
|
449
646
|
if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
|
|
450
647
|
return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
|
|
451
648
|
}, [edgeTypeDefs]);
|
|
452
|
-
const nodeTypes =
|
|
453
|
-
() => createNodeTypes(nodeRenderers, nodeTypeDefsMap),
|
|
454
|
-
[nodeRenderers, nodeTypeDefsMap]
|
|
649
|
+
const nodeTypes = import_react3.default.useMemo(
|
|
650
|
+
() => createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode),
|
|
651
|
+
[nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
|
|
455
652
|
);
|
|
456
|
-
const
|
|
457
|
-
() =>
|
|
458
|
-
|
|
459
|
-
return applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
|
|
460
|
-
},
|
|
461
|
-
[data == null ? void 0 : data.edges, edgeTypeDefsMap]
|
|
653
|
+
const customEdgeTypes = import_react3.default.useMemo(
|
|
654
|
+
() => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
|
|
655
|
+
[edgeRenderers, edgeTypeDefsMap]
|
|
462
656
|
);
|
|
657
|
+
const styledEdges = import_react3.default.useMemo(() => {
|
|
658
|
+
var _a2;
|
|
659
|
+
let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
|
|
660
|
+
if (edgeRenderers) {
|
|
661
|
+
edges = edges.map((edge) => {
|
|
662
|
+
const slug = edge.edgeTypeSlug;
|
|
663
|
+
if (slug && edgeRenderers[slug]) {
|
|
664
|
+
return __spreadProps(__spreadValues({}, edge), { type: slug });
|
|
665
|
+
}
|
|
666
|
+
return edge;
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
return edges;
|
|
670
|
+
}, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
|
|
463
671
|
if (!data) return null;
|
|
464
|
-
const
|
|
465
|
-
return /* @__PURE__ */
|
|
672
|
+
const resolvedDefaultViewport = defaultViewportProp != null ? defaultViewportProp : !fitView && data.viewport ? data.viewport : void 0;
|
|
673
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react4.ReactFlowProvider, null, /* @__PURE__ */ import_react3.default.createElement(
|
|
466
674
|
"div",
|
|
467
675
|
{
|
|
468
676
|
className,
|
|
469
677
|
style: __spreadValues({ width: "100%", height: "100%" }, style)
|
|
470
678
|
},
|
|
471
|
-
/* @__PURE__ */
|
|
472
|
-
|
|
679
|
+
/* @__PURE__ */ import_react3.default.createElement(
|
|
680
|
+
import_react4.ReactFlow,
|
|
473
681
|
{
|
|
474
682
|
nodes: (_a = data.nodes) != null ? _a : [],
|
|
475
683
|
edges: styledEdges,
|
|
476
684
|
nodeTypes,
|
|
477
|
-
|
|
685
|
+
edgeTypes: customEdgeTypes,
|
|
686
|
+
defaultViewport: resolvedDefaultViewport,
|
|
478
687
|
fitView,
|
|
479
688
|
onNodeClick,
|
|
480
689
|
onEdgeClick,
|
|
690
|
+
onMoveEnd: onViewportChange ? ((_, vp) => {
|
|
691
|
+
onViewportChange(vp);
|
|
692
|
+
}) : void 0,
|
|
481
693
|
nodesDraggable: interactive ? void 0 : false,
|
|
482
694
|
nodesConnectable: false,
|
|
483
695
|
elementsSelectable: interactive || !!onNodeClick || !!onEdgeClick,
|
|
@@ -486,7 +698,23 @@ function FlowRenderer({
|
|
|
486
698
|
zoomOnPinch: interactive,
|
|
487
699
|
zoomOnDoubleClick: false
|
|
488
700
|
},
|
|
489
|
-
background && /* @__PURE__ */
|
|
701
|
+
background && /* @__PURE__ */ import_react3.default.createElement(import_react4.Background, null),
|
|
702
|
+
controls && /* @__PURE__ */ import_react3.default.createElement(import_react4.Controls, null),
|
|
703
|
+
minimap && /* @__PURE__ */ import_react3.default.createElement(
|
|
704
|
+
import_react4.MiniMap,
|
|
705
|
+
{
|
|
706
|
+
nodeColor: minimapNodeColor
|
|
707
|
+
}
|
|
708
|
+
),
|
|
709
|
+
bounds && /* @__PURE__ */ import_react3.default.createElement(
|
|
710
|
+
FocusHandler,
|
|
711
|
+
{
|
|
712
|
+
bounds,
|
|
713
|
+
padding: focusPadding != null ? focusPadding : 0.1,
|
|
714
|
+
animation: focusAnimation != null ? focusAnimation : true
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
children
|
|
490
718
|
)
|
|
491
719
|
));
|
|
492
720
|
}
|