@netsapiens/horizon-sdk 0.1.2 → 0.1.3
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/README.md +263 -84
- package/dist/index.cjs +43 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +169 -25
- package/dist/index.d.ts +169 -25
- package/dist/index.js +43 -16
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
package/dist/index.d.cts
CHANGED
|
@@ -141,12 +141,86 @@ interface ThemeTokens {
|
|
|
141
141
|
* components instead of bringing their own MUI/styling stack — that keeps the
|
|
142
142
|
* remote bundle small and ensures visual consistency with Horizon.
|
|
143
143
|
*/
|
|
144
|
+
/** One step of a {@link HorizonUITemplates.FormPanel} multi-step wizard. */
|
|
145
|
+
interface FormPanelStep {
|
|
146
|
+
/** Label shown in the stepper header. */
|
|
147
|
+
label: string;
|
|
148
|
+
/** Content rendered when this step is active. */
|
|
149
|
+
content: React.ReactNode;
|
|
150
|
+
/**
|
|
151
|
+
* Optional gate run before advancing past this step (Next / forward header
|
|
152
|
+
* click). Return false to block. Async supported (e.g. react-hook-form `trigger`).
|
|
153
|
+
*/
|
|
154
|
+
validate?: () => boolean | Promise<boolean>;
|
|
155
|
+
}
|
|
144
156
|
interface HorizonUITemplates {
|
|
145
157
|
PageTemplate: React.ComponentType<unknown>;
|
|
146
158
|
PageTemplateWithExtensions: React.ComponentType<unknown>;
|
|
147
159
|
FormTemplate: React.ComponentType<unknown>;
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Shared right-side drawer shell: sticky header (icon/title/subtitle/close),
|
|
162
|
+
* scrollable body, optional sticky footer. Use for detail/view/settings panels.
|
|
163
|
+
*/
|
|
164
|
+
SidePanel: React.ComponentType<{
|
|
165
|
+
open: boolean;
|
|
166
|
+
onClose: () => void;
|
|
167
|
+
title: React.ReactNode;
|
|
168
|
+
subtitle?: React.ReactNode;
|
|
169
|
+
icon?: React.ReactNode;
|
|
170
|
+
width?: "sm" | "md" | "lg" | "xl";
|
|
171
|
+
footer?: React.ReactNode;
|
|
172
|
+
disableBackdropClick?: boolean;
|
|
173
|
+
children: React.ReactNode;
|
|
174
|
+
}>;
|
|
175
|
+
/**
|
|
176
|
+
* SidePanel + form semantics: a <form>, a standard Submit/Cancel footer, and
|
|
177
|
+
* the `form-section-before` / `form-section-after` extension zones built in.
|
|
178
|
+
* Pass `steps` to render a multi-step wizard (stepper header + Back/Next/Submit
|
|
179
|
+
* footer) instead. Use this for add/edit forms.
|
|
180
|
+
*/
|
|
181
|
+
FormPanel: React.ComponentType<{
|
|
182
|
+
open: boolean;
|
|
183
|
+
onClose: () => void;
|
|
184
|
+
title: React.ReactNode;
|
|
185
|
+
subtitle?: React.ReactNode;
|
|
186
|
+
icon?: React.ReactNode;
|
|
187
|
+
width?: "sm" | "md" | "lg" | "xl";
|
|
188
|
+
formType: string;
|
|
189
|
+
mode: "add" | "edit";
|
|
190
|
+
entityId?: string | number;
|
|
191
|
+
formData?: Record<string, unknown>;
|
|
192
|
+
onSubmit?: (event?: React.BaseSyntheticEvent) => void | Promise<void>;
|
|
193
|
+
submitLabel?: React.ReactNode;
|
|
194
|
+
cancelLabel?: React.ReactNode;
|
|
195
|
+
isSubmitting?: boolean;
|
|
196
|
+
submitDisabled?: boolean;
|
|
197
|
+
error?: Error | string | null;
|
|
198
|
+
/** Multi-step wizard: pass one step per page of fields. */
|
|
199
|
+
steps?: FormPanelStep[];
|
|
200
|
+
onReset?: () => void;
|
|
201
|
+
isComplete?: boolean;
|
|
202
|
+
loading?: boolean;
|
|
203
|
+
children?: React.ReactNode;
|
|
204
|
+
}>;
|
|
205
|
+
DatagridTemplate: React.ComponentType<{
|
|
206
|
+
data: unknown[];
|
|
207
|
+
columns: unknown[];
|
|
208
|
+
actions?: unknown[];
|
|
209
|
+
toolbar?: {
|
|
210
|
+
enableSearch?: boolean;
|
|
211
|
+
searchPlaceholder?: string;
|
|
212
|
+
enableExport?: boolean;
|
|
213
|
+
enableFilter?: boolean;
|
|
214
|
+
enableColumns?: boolean;
|
|
215
|
+
};
|
|
216
|
+
defaultPageSize?: number;
|
|
217
|
+
/**
|
|
218
|
+
* Node rendered inline within the toolbar row, alongside search and date
|
|
219
|
+
* range controls. Use for status filter chips (ToggleButtonGroup) and
|
|
220
|
+
* `table-filter-bar` extension zone renderers.
|
|
221
|
+
*/
|
|
222
|
+
filterBar?: React.ReactNode;
|
|
223
|
+
}>;
|
|
150
224
|
Icon: React.ComponentType<{
|
|
151
225
|
name: string;
|
|
152
226
|
size?: number | string;
|
|
@@ -207,11 +281,11 @@ interface HorizonUI {
|
|
|
207
281
|
IconButton?: React.ComponentType<{
|
|
208
282
|
icon: string;
|
|
209
283
|
iconSize?: number | string;
|
|
210
|
-
|
|
211
|
-
color?:
|
|
284
|
+
"aria-label"?: string;
|
|
285
|
+
color?: "default" | "inherit" | "primary" | "secondary" | "error" | "info" | "success" | "warning";
|
|
212
286
|
disabled?: boolean;
|
|
213
|
-
edge?:
|
|
214
|
-
size?:
|
|
287
|
+
edge?: "start" | "end" | false;
|
|
288
|
+
size?: "small" | "medium" | "large";
|
|
215
289
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
216
290
|
sx?: Record<string, unknown>;
|
|
217
291
|
}>;
|
|
@@ -250,7 +324,7 @@ interface HorizonContext {
|
|
|
250
324
|
user: HorizonUser;
|
|
251
325
|
auth: HorizonAuth;
|
|
252
326
|
api: HorizonApiClient;
|
|
253
|
-
theme:
|
|
327
|
+
theme: "light" | "dark";
|
|
254
328
|
locale: string;
|
|
255
329
|
/**
|
|
256
330
|
* Host's i18next translation function. All host strings (common, telecom,
|
|
@@ -311,8 +385,54 @@ interface RemoteModuleConfig {
|
|
|
311
385
|
* Generic extension zones available throughout Horizon. Pages may also define
|
|
312
386
|
* custom string zone IDs — `ExtensionZone` accepts both.
|
|
313
387
|
*/
|
|
314
|
-
type ExtensionZoneId =
|
|
388
|
+
type ExtensionZoneId = "page-header-actions" | "page-header-secondary" | "page-content-after" | "sidetray" | "table-row-actions" | "table-toolbar" | "table-filter-bar" | "inbound-call-content" | "topbar-actions" | "form-section-before" | "form-section-after";
|
|
315
389
|
type ExtensionZone = ExtensionZoneId | (string & {});
|
|
390
|
+
/**
|
|
391
|
+
* Context passed via `pageContext` to extension components registered in the
|
|
392
|
+
* `'table-filter-bar'` zone. Extensions provide a filter function that the host
|
|
393
|
+
* applies to table rows. This keeps filtering logic in the extension where the
|
|
394
|
+
* business rules live, and keeps the host generic.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* export function RecordingFilter({ context }: ExtensionComponentProps) {
|
|
398
|
+
* const filterCtx = context.pageContext as TableFilterBarContext | undefined;
|
|
399
|
+
* const { ToggleButtonGroup } = context.ui ?? {};
|
|
400
|
+
* const [active, setActive] = useState(false);
|
|
401
|
+
*
|
|
402
|
+
* function handleChange(_e: React.SyntheticEvent, value: string | null) {
|
|
403
|
+
* const next = value === 'recorded';
|
|
404
|
+
* setActive(next);
|
|
405
|
+
* if (next) {
|
|
406
|
+
* filterCtx?.onFilterChange((row) => {
|
|
407
|
+
* const r = row as Record<string, unknown>;
|
|
408
|
+
* return r['call-record-keep'] === 'yes' || r['call-record-keep'] === true;
|
|
409
|
+
* });
|
|
410
|
+
* } else {
|
|
411
|
+
* filterCtx?.onFilterChange(null);
|
|
412
|
+
* }
|
|
413
|
+
* }
|
|
414
|
+
*
|
|
415
|
+
* if (!ToggleButtonGroup) return null;
|
|
416
|
+
* return (
|
|
417
|
+
* <ToggleButtonGroup
|
|
418
|
+
* value={active ? 'recorded' : null}
|
|
419
|
+
* exclusive
|
|
420
|
+
* onChange={handleChange}
|
|
421
|
+
* options={[{ value: 'recorded', label: '● Recording' }]}
|
|
422
|
+
* />
|
|
423
|
+
* );
|
|
424
|
+
* }
|
|
425
|
+
*
|
|
426
|
+
* // NOTE: Track active state locally with useState — TableFilterBarContext does
|
|
427
|
+
* // not reflect the current filter value back to the extension.
|
|
428
|
+
*/
|
|
429
|
+
interface TableFilterBarContext<TRow = any> {
|
|
430
|
+
/**
|
|
431
|
+
* Provide a filter function to apply to table rows. Pass `null` to reset.
|
|
432
|
+
* The function receives a row and returns true to include it, false to hide it.
|
|
433
|
+
*/
|
|
434
|
+
onFilterChange: (filterFn: ((row: TRow) => boolean) | null) => void;
|
|
435
|
+
}
|
|
316
436
|
/**
|
|
317
437
|
* Route pattern with wildcard (`*`) and named-parameter (`:param`) support.
|
|
318
438
|
* Example patterns: `/manage/call-logs`, `/manage/*\/call-logs`, `/manage/:domain/users`.
|
|
@@ -337,7 +457,7 @@ interface ExtensionContext {
|
|
|
337
457
|
ui?: HorizonUI;
|
|
338
458
|
eventBus?: HorizonEventBus;
|
|
339
459
|
/** Current color scheme — reactive to host theme changes. */
|
|
340
|
-
theme:
|
|
460
|
+
theme: "light" | "dark";
|
|
341
461
|
/** Host's i18next translation function — all host strings available immediately. */
|
|
342
462
|
t?: (key: string, options?: Record<string, unknown>) => string;
|
|
343
463
|
}
|
|
@@ -386,9 +506,9 @@ interface DynamicColumnDefinition {
|
|
|
386
506
|
filterable?: boolean;
|
|
387
507
|
hideable?: boolean;
|
|
388
508
|
resizable?: boolean;
|
|
389
|
-
type?:
|
|
390
|
-
align?:
|
|
391
|
-
headerAlign?:
|
|
509
|
+
type?: "string" | "number" | "date" | "dateTime" | "boolean";
|
|
510
|
+
align?: "left" | "center" | "right";
|
|
511
|
+
headerAlign?: "left" | "center" | "right";
|
|
392
512
|
renderCell?: (params: {
|
|
393
513
|
row: Record<string, unknown>;
|
|
394
514
|
value?: unknown;
|
|
@@ -425,17 +545,17 @@ declare class RemoteAppSDK {
|
|
|
425
545
|
private dynamicExtensions;
|
|
426
546
|
private dynamicColumns;
|
|
427
547
|
constructor(eventBus: HorizonEventBus, appId: string);
|
|
428
|
-
registerRoute(config: Omit<RouteConfig,
|
|
548
|
+
registerRoute(config: Omit<RouteConfig, "appId">): Promise<void>;
|
|
429
549
|
unregisterRoute(routeId: string): void;
|
|
430
550
|
/**
|
|
431
551
|
* Convenience: load a component out of a federated module's webpack
|
|
432
552
|
* container and register it as a route in one step. Useful when the route
|
|
433
553
|
* component lives in a sibling exposed module rather than the entry App.
|
|
434
554
|
*/
|
|
435
|
-
registerRouteFromModule(routeConfig: Omit<RouteConfig,
|
|
436
|
-
registerDynamicExtension(config: Omit<DynamicExtensionConfig,
|
|
555
|
+
registerRouteFromModule(routeConfig: Omit<RouteConfig, "appId" | "component">, moduleConfig: RemoteModuleConfig): Promise<void>;
|
|
556
|
+
registerDynamicExtension(config: Omit<DynamicExtensionConfig, "appId">): void;
|
|
437
557
|
unregisterDynamicExtension(extensionId: string): void;
|
|
438
|
-
registerDynamicColumn(config: Omit<DynamicColumnConfig,
|
|
558
|
+
registerDynamicColumn(config: Omit<DynamicColumnConfig, "appId">): void;
|
|
439
559
|
unregisterDynamicColumn(columnId: string): void;
|
|
440
560
|
/**
|
|
441
561
|
* Unregister everything this SDK instance has registered. Call from your
|
|
@@ -522,8 +642,8 @@ declare function useHorizonContext(): HorizonContext;
|
|
|
522
642
|
* Inside a page component wrapped by `HorizonContextProvider`, both params
|
|
523
643
|
* can be omitted — the provider context is used automatically.
|
|
524
644
|
*/
|
|
525
|
-
declare function useTheme(eventBus?: HorizonContext[
|
|
526
|
-
theme:
|
|
645
|
+
declare function useTheme(eventBus?: HorizonContext["eventBus"], initialTheme?: "light" | "dark"): {
|
|
646
|
+
theme: "light" | "dark";
|
|
527
647
|
};
|
|
528
648
|
/**
|
|
529
649
|
* Returns the host's translation function and current locale.
|
|
@@ -579,7 +699,7 @@ declare function useTheme(eventBus?: HorizonContext['eventBus'], initialTheme?:
|
|
|
579
699
|
* }
|
|
580
700
|
*/
|
|
581
701
|
declare function useLocale(): {
|
|
582
|
-
t: HorizonContext[
|
|
702
|
+
t: HorizonContext["t"];
|
|
583
703
|
locale: string;
|
|
584
704
|
};
|
|
585
705
|
/**
|
|
@@ -618,12 +738,12 @@ declare function useRemoteApp(horizonContext: HorizonContext, appId: string): {
|
|
|
618
738
|
/**
|
|
619
739
|
* Register a route for the lifetime of the calling component.
|
|
620
740
|
*/
|
|
621
|
-
declare function useRoute(eventBus: HorizonContext[
|
|
741
|
+
declare function useRoute(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<RouteConfig, "appId">): RemoteAppSDK;
|
|
622
742
|
/**
|
|
623
743
|
* Register a route by pulling its component out of a federated module's
|
|
624
744
|
* webpack container.
|
|
625
745
|
*/
|
|
626
|
-
declare function useRouteFromModule(eventBus: HorizonContext[
|
|
746
|
+
declare function useRouteFromModule(eventBus: HorizonContext["eventBus"], appId: string, routeConfig: Omit<RouteConfig, "appId" | "component">, moduleConfig: RemoteModuleConfig): {
|
|
627
747
|
loading: boolean;
|
|
628
748
|
error: Error | null;
|
|
629
749
|
sdk: RemoteAppSDK;
|
|
@@ -632,17 +752,41 @@ declare function useRouteFromModule(eventBus: HorizonContext['eventBus'], appId:
|
|
|
632
752
|
* Register a dynamic extension (pattern-based UI injection) for the lifetime
|
|
633
753
|
* of the calling component.
|
|
634
754
|
*/
|
|
635
|
-
declare function useDynamicExtension(eventBus: HorizonContext[
|
|
755
|
+
declare function useDynamicExtension(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<DynamicExtensionConfig, "appId">): RemoteAppSDK;
|
|
756
|
+
/**
|
|
757
|
+
* Returns the current page context as a typed value.
|
|
758
|
+
*
|
|
759
|
+
* The host passes page-specific data (e.g. active filters, date range, selected
|
|
760
|
+
* row) through `ExtensionContext.pageContext`. This hook casts it to the caller's
|
|
761
|
+
* expected shape and keeps it reactive — when the host updates `pageContext` the
|
|
762
|
+
* extension re-renders automatically because it flows in as a React prop.
|
|
763
|
+
*
|
|
764
|
+
* Define a typed interface for each page's context in your remote app and pass
|
|
765
|
+
* it as the type parameter. Returns `undefined` when the host has not populated
|
|
766
|
+
* any context for the current zone.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* interface CallLogsPageContext {
|
|
770
|
+
* dateRange?: [Date | null, Date | null];
|
|
771
|
+
* }
|
|
772
|
+
*
|
|
773
|
+
* export function AnalyticsWidget({ context }: ExtensionComponentProps) {
|
|
774
|
+
* const pageCtx = usePageContext<CallLogsPageContext>(context);
|
|
775
|
+
* const [start, end] = pageCtx?.dateRange ?? [null, null];
|
|
776
|
+
* // ...
|
|
777
|
+
* }
|
|
778
|
+
*/
|
|
779
|
+
declare function usePageContext<T = unknown>(context: ExtensionContext): T | undefined;
|
|
636
780
|
/**
|
|
637
781
|
* Register a dynamic table column for the lifetime of the calling component.
|
|
638
782
|
*/
|
|
639
|
-
declare function useDynamicColumn(eventBus: HorizonContext[
|
|
783
|
+
declare function useDynamicColumn(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<DynamicColumnConfig, "appId">): RemoteAppSDK;
|
|
640
784
|
|
|
641
785
|
/**
|
|
642
786
|
* Federation Error
|
|
643
787
|
* Structured error class with error codes for better error handling
|
|
644
788
|
*/
|
|
645
|
-
type HorizonSDKErrorCode =
|
|
789
|
+
type HorizonSDKErrorCode = "PERMISSION_DENIED" | "RATE_LIMIT_EXCEEDED" | "INVALID_MESSAGE" | "SIGNATURE_VERIFICATION_FAILED" | "API_ERROR" | "NETWORK_ERROR" | "INVALID_EXTENSION_POINT" | "INVALID_CONFIGURATION" | "APP_NOT_FOUND" | "MODULE_LOAD_FAILED" | "INITIALIZATION_FAILED" | "UNKNOWN_ERROR";
|
|
646
790
|
interface HorizonSDKErrorOptions {
|
|
647
791
|
code: HorizonSDKErrorCode;
|
|
648
792
|
message: string;
|
|
@@ -858,4 +1002,4 @@ type AnchorId = (typeof MANAGE_ANCHORS)[keyof typeof MANAGE_ANCHORS] | (typeof P
|
|
|
858
1002
|
|
|
859
1003
|
declare const VERSION: string;
|
|
860
1004
|
|
|
861
|
-
export { ANCHORS, APPS_ANCHORS, type AnchorId, type BreadcrumbItem, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, useRemoteApp, useRoute, useRouteFromModule, useTheme };
|
|
1005
|
+
export { ANCHORS, APPS_ANCHORS, type AnchorId, type BreadcrumbItem, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type FormPanelStep, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type TableFilterBarContext, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useTheme };
|
package/dist/index.d.ts
CHANGED
|
@@ -141,12 +141,86 @@ interface ThemeTokens {
|
|
|
141
141
|
* components instead of bringing their own MUI/styling stack — that keeps the
|
|
142
142
|
* remote bundle small and ensures visual consistency with Horizon.
|
|
143
143
|
*/
|
|
144
|
+
/** One step of a {@link HorizonUITemplates.FormPanel} multi-step wizard. */
|
|
145
|
+
interface FormPanelStep {
|
|
146
|
+
/** Label shown in the stepper header. */
|
|
147
|
+
label: string;
|
|
148
|
+
/** Content rendered when this step is active. */
|
|
149
|
+
content: React.ReactNode;
|
|
150
|
+
/**
|
|
151
|
+
* Optional gate run before advancing past this step (Next / forward header
|
|
152
|
+
* click). Return false to block. Async supported (e.g. react-hook-form `trigger`).
|
|
153
|
+
*/
|
|
154
|
+
validate?: () => boolean | Promise<boolean>;
|
|
155
|
+
}
|
|
144
156
|
interface HorizonUITemplates {
|
|
145
157
|
PageTemplate: React.ComponentType<unknown>;
|
|
146
158
|
PageTemplateWithExtensions: React.ComponentType<unknown>;
|
|
147
159
|
FormTemplate: React.ComponentType<unknown>;
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Shared right-side drawer shell: sticky header (icon/title/subtitle/close),
|
|
162
|
+
* scrollable body, optional sticky footer. Use for detail/view/settings panels.
|
|
163
|
+
*/
|
|
164
|
+
SidePanel: React.ComponentType<{
|
|
165
|
+
open: boolean;
|
|
166
|
+
onClose: () => void;
|
|
167
|
+
title: React.ReactNode;
|
|
168
|
+
subtitle?: React.ReactNode;
|
|
169
|
+
icon?: React.ReactNode;
|
|
170
|
+
width?: "sm" | "md" | "lg" | "xl";
|
|
171
|
+
footer?: React.ReactNode;
|
|
172
|
+
disableBackdropClick?: boolean;
|
|
173
|
+
children: React.ReactNode;
|
|
174
|
+
}>;
|
|
175
|
+
/**
|
|
176
|
+
* SidePanel + form semantics: a <form>, a standard Submit/Cancel footer, and
|
|
177
|
+
* the `form-section-before` / `form-section-after` extension zones built in.
|
|
178
|
+
* Pass `steps` to render a multi-step wizard (stepper header + Back/Next/Submit
|
|
179
|
+
* footer) instead. Use this for add/edit forms.
|
|
180
|
+
*/
|
|
181
|
+
FormPanel: React.ComponentType<{
|
|
182
|
+
open: boolean;
|
|
183
|
+
onClose: () => void;
|
|
184
|
+
title: React.ReactNode;
|
|
185
|
+
subtitle?: React.ReactNode;
|
|
186
|
+
icon?: React.ReactNode;
|
|
187
|
+
width?: "sm" | "md" | "lg" | "xl";
|
|
188
|
+
formType: string;
|
|
189
|
+
mode: "add" | "edit";
|
|
190
|
+
entityId?: string | number;
|
|
191
|
+
formData?: Record<string, unknown>;
|
|
192
|
+
onSubmit?: (event?: React.BaseSyntheticEvent) => void | Promise<void>;
|
|
193
|
+
submitLabel?: React.ReactNode;
|
|
194
|
+
cancelLabel?: React.ReactNode;
|
|
195
|
+
isSubmitting?: boolean;
|
|
196
|
+
submitDisabled?: boolean;
|
|
197
|
+
error?: Error | string | null;
|
|
198
|
+
/** Multi-step wizard: pass one step per page of fields. */
|
|
199
|
+
steps?: FormPanelStep[];
|
|
200
|
+
onReset?: () => void;
|
|
201
|
+
isComplete?: boolean;
|
|
202
|
+
loading?: boolean;
|
|
203
|
+
children?: React.ReactNode;
|
|
204
|
+
}>;
|
|
205
|
+
DatagridTemplate: React.ComponentType<{
|
|
206
|
+
data: unknown[];
|
|
207
|
+
columns: unknown[];
|
|
208
|
+
actions?: unknown[];
|
|
209
|
+
toolbar?: {
|
|
210
|
+
enableSearch?: boolean;
|
|
211
|
+
searchPlaceholder?: string;
|
|
212
|
+
enableExport?: boolean;
|
|
213
|
+
enableFilter?: boolean;
|
|
214
|
+
enableColumns?: boolean;
|
|
215
|
+
};
|
|
216
|
+
defaultPageSize?: number;
|
|
217
|
+
/**
|
|
218
|
+
* Node rendered inline within the toolbar row, alongside search and date
|
|
219
|
+
* range controls. Use for status filter chips (ToggleButtonGroup) and
|
|
220
|
+
* `table-filter-bar` extension zone renderers.
|
|
221
|
+
*/
|
|
222
|
+
filterBar?: React.ReactNode;
|
|
223
|
+
}>;
|
|
150
224
|
Icon: React.ComponentType<{
|
|
151
225
|
name: string;
|
|
152
226
|
size?: number | string;
|
|
@@ -207,11 +281,11 @@ interface HorizonUI {
|
|
|
207
281
|
IconButton?: React.ComponentType<{
|
|
208
282
|
icon: string;
|
|
209
283
|
iconSize?: number | string;
|
|
210
|
-
|
|
211
|
-
color?:
|
|
284
|
+
"aria-label"?: string;
|
|
285
|
+
color?: "default" | "inherit" | "primary" | "secondary" | "error" | "info" | "success" | "warning";
|
|
212
286
|
disabled?: boolean;
|
|
213
|
-
edge?:
|
|
214
|
-
size?:
|
|
287
|
+
edge?: "start" | "end" | false;
|
|
288
|
+
size?: "small" | "medium" | "large";
|
|
215
289
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
216
290
|
sx?: Record<string, unknown>;
|
|
217
291
|
}>;
|
|
@@ -250,7 +324,7 @@ interface HorizonContext {
|
|
|
250
324
|
user: HorizonUser;
|
|
251
325
|
auth: HorizonAuth;
|
|
252
326
|
api: HorizonApiClient;
|
|
253
|
-
theme:
|
|
327
|
+
theme: "light" | "dark";
|
|
254
328
|
locale: string;
|
|
255
329
|
/**
|
|
256
330
|
* Host's i18next translation function. All host strings (common, telecom,
|
|
@@ -311,8 +385,54 @@ interface RemoteModuleConfig {
|
|
|
311
385
|
* Generic extension zones available throughout Horizon. Pages may also define
|
|
312
386
|
* custom string zone IDs — `ExtensionZone` accepts both.
|
|
313
387
|
*/
|
|
314
|
-
type ExtensionZoneId =
|
|
388
|
+
type ExtensionZoneId = "page-header-actions" | "page-header-secondary" | "page-content-after" | "sidetray" | "table-row-actions" | "table-toolbar" | "table-filter-bar" | "inbound-call-content" | "topbar-actions" | "form-section-before" | "form-section-after";
|
|
315
389
|
type ExtensionZone = ExtensionZoneId | (string & {});
|
|
390
|
+
/**
|
|
391
|
+
* Context passed via `pageContext` to extension components registered in the
|
|
392
|
+
* `'table-filter-bar'` zone. Extensions provide a filter function that the host
|
|
393
|
+
* applies to table rows. This keeps filtering logic in the extension where the
|
|
394
|
+
* business rules live, and keeps the host generic.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* export function RecordingFilter({ context }: ExtensionComponentProps) {
|
|
398
|
+
* const filterCtx = context.pageContext as TableFilterBarContext | undefined;
|
|
399
|
+
* const { ToggleButtonGroup } = context.ui ?? {};
|
|
400
|
+
* const [active, setActive] = useState(false);
|
|
401
|
+
*
|
|
402
|
+
* function handleChange(_e: React.SyntheticEvent, value: string | null) {
|
|
403
|
+
* const next = value === 'recorded';
|
|
404
|
+
* setActive(next);
|
|
405
|
+
* if (next) {
|
|
406
|
+
* filterCtx?.onFilterChange((row) => {
|
|
407
|
+
* const r = row as Record<string, unknown>;
|
|
408
|
+
* return r['call-record-keep'] === 'yes' || r['call-record-keep'] === true;
|
|
409
|
+
* });
|
|
410
|
+
* } else {
|
|
411
|
+
* filterCtx?.onFilterChange(null);
|
|
412
|
+
* }
|
|
413
|
+
* }
|
|
414
|
+
*
|
|
415
|
+
* if (!ToggleButtonGroup) return null;
|
|
416
|
+
* return (
|
|
417
|
+
* <ToggleButtonGroup
|
|
418
|
+
* value={active ? 'recorded' : null}
|
|
419
|
+
* exclusive
|
|
420
|
+
* onChange={handleChange}
|
|
421
|
+
* options={[{ value: 'recorded', label: '● Recording' }]}
|
|
422
|
+
* />
|
|
423
|
+
* );
|
|
424
|
+
* }
|
|
425
|
+
*
|
|
426
|
+
* // NOTE: Track active state locally with useState — TableFilterBarContext does
|
|
427
|
+
* // not reflect the current filter value back to the extension.
|
|
428
|
+
*/
|
|
429
|
+
interface TableFilterBarContext<TRow = any> {
|
|
430
|
+
/**
|
|
431
|
+
* Provide a filter function to apply to table rows. Pass `null` to reset.
|
|
432
|
+
* The function receives a row and returns true to include it, false to hide it.
|
|
433
|
+
*/
|
|
434
|
+
onFilterChange: (filterFn: ((row: TRow) => boolean) | null) => void;
|
|
435
|
+
}
|
|
316
436
|
/**
|
|
317
437
|
* Route pattern with wildcard (`*`) and named-parameter (`:param`) support.
|
|
318
438
|
* Example patterns: `/manage/call-logs`, `/manage/*\/call-logs`, `/manage/:domain/users`.
|
|
@@ -337,7 +457,7 @@ interface ExtensionContext {
|
|
|
337
457
|
ui?: HorizonUI;
|
|
338
458
|
eventBus?: HorizonEventBus;
|
|
339
459
|
/** Current color scheme — reactive to host theme changes. */
|
|
340
|
-
theme:
|
|
460
|
+
theme: "light" | "dark";
|
|
341
461
|
/** Host's i18next translation function — all host strings available immediately. */
|
|
342
462
|
t?: (key: string, options?: Record<string, unknown>) => string;
|
|
343
463
|
}
|
|
@@ -386,9 +506,9 @@ interface DynamicColumnDefinition {
|
|
|
386
506
|
filterable?: boolean;
|
|
387
507
|
hideable?: boolean;
|
|
388
508
|
resizable?: boolean;
|
|
389
|
-
type?:
|
|
390
|
-
align?:
|
|
391
|
-
headerAlign?:
|
|
509
|
+
type?: "string" | "number" | "date" | "dateTime" | "boolean";
|
|
510
|
+
align?: "left" | "center" | "right";
|
|
511
|
+
headerAlign?: "left" | "center" | "right";
|
|
392
512
|
renderCell?: (params: {
|
|
393
513
|
row: Record<string, unknown>;
|
|
394
514
|
value?: unknown;
|
|
@@ -425,17 +545,17 @@ declare class RemoteAppSDK {
|
|
|
425
545
|
private dynamicExtensions;
|
|
426
546
|
private dynamicColumns;
|
|
427
547
|
constructor(eventBus: HorizonEventBus, appId: string);
|
|
428
|
-
registerRoute(config: Omit<RouteConfig,
|
|
548
|
+
registerRoute(config: Omit<RouteConfig, "appId">): Promise<void>;
|
|
429
549
|
unregisterRoute(routeId: string): void;
|
|
430
550
|
/**
|
|
431
551
|
* Convenience: load a component out of a federated module's webpack
|
|
432
552
|
* container and register it as a route in one step. Useful when the route
|
|
433
553
|
* component lives in a sibling exposed module rather than the entry App.
|
|
434
554
|
*/
|
|
435
|
-
registerRouteFromModule(routeConfig: Omit<RouteConfig,
|
|
436
|
-
registerDynamicExtension(config: Omit<DynamicExtensionConfig,
|
|
555
|
+
registerRouteFromModule(routeConfig: Omit<RouteConfig, "appId" | "component">, moduleConfig: RemoteModuleConfig): Promise<void>;
|
|
556
|
+
registerDynamicExtension(config: Omit<DynamicExtensionConfig, "appId">): void;
|
|
437
557
|
unregisterDynamicExtension(extensionId: string): void;
|
|
438
|
-
registerDynamicColumn(config: Omit<DynamicColumnConfig,
|
|
558
|
+
registerDynamicColumn(config: Omit<DynamicColumnConfig, "appId">): void;
|
|
439
559
|
unregisterDynamicColumn(columnId: string): void;
|
|
440
560
|
/**
|
|
441
561
|
* Unregister everything this SDK instance has registered. Call from your
|
|
@@ -522,8 +642,8 @@ declare function useHorizonContext(): HorizonContext;
|
|
|
522
642
|
* Inside a page component wrapped by `HorizonContextProvider`, both params
|
|
523
643
|
* can be omitted — the provider context is used automatically.
|
|
524
644
|
*/
|
|
525
|
-
declare function useTheme(eventBus?: HorizonContext[
|
|
526
|
-
theme:
|
|
645
|
+
declare function useTheme(eventBus?: HorizonContext["eventBus"], initialTheme?: "light" | "dark"): {
|
|
646
|
+
theme: "light" | "dark";
|
|
527
647
|
};
|
|
528
648
|
/**
|
|
529
649
|
* Returns the host's translation function and current locale.
|
|
@@ -579,7 +699,7 @@ declare function useTheme(eventBus?: HorizonContext['eventBus'], initialTheme?:
|
|
|
579
699
|
* }
|
|
580
700
|
*/
|
|
581
701
|
declare function useLocale(): {
|
|
582
|
-
t: HorizonContext[
|
|
702
|
+
t: HorizonContext["t"];
|
|
583
703
|
locale: string;
|
|
584
704
|
};
|
|
585
705
|
/**
|
|
@@ -618,12 +738,12 @@ declare function useRemoteApp(horizonContext: HorizonContext, appId: string): {
|
|
|
618
738
|
/**
|
|
619
739
|
* Register a route for the lifetime of the calling component.
|
|
620
740
|
*/
|
|
621
|
-
declare function useRoute(eventBus: HorizonContext[
|
|
741
|
+
declare function useRoute(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<RouteConfig, "appId">): RemoteAppSDK;
|
|
622
742
|
/**
|
|
623
743
|
* Register a route by pulling its component out of a federated module's
|
|
624
744
|
* webpack container.
|
|
625
745
|
*/
|
|
626
|
-
declare function useRouteFromModule(eventBus: HorizonContext[
|
|
746
|
+
declare function useRouteFromModule(eventBus: HorizonContext["eventBus"], appId: string, routeConfig: Omit<RouteConfig, "appId" | "component">, moduleConfig: RemoteModuleConfig): {
|
|
627
747
|
loading: boolean;
|
|
628
748
|
error: Error | null;
|
|
629
749
|
sdk: RemoteAppSDK;
|
|
@@ -632,17 +752,41 @@ declare function useRouteFromModule(eventBus: HorizonContext['eventBus'], appId:
|
|
|
632
752
|
* Register a dynamic extension (pattern-based UI injection) for the lifetime
|
|
633
753
|
* of the calling component.
|
|
634
754
|
*/
|
|
635
|
-
declare function useDynamicExtension(eventBus: HorizonContext[
|
|
755
|
+
declare function useDynamicExtension(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<DynamicExtensionConfig, "appId">): RemoteAppSDK;
|
|
756
|
+
/**
|
|
757
|
+
* Returns the current page context as a typed value.
|
|
758
|
+
*
|
|
759
|
+
* The host passes page-specific data (e.g. active filters, date range, selected
|
|
760
|
+
* row) through `ExtensionContext.pageContext`. This hook casts it to the caller's
|
|
761
|
+
* expected shape and keeps it reactive — when the host updates `pageContext` the
|
|
762
|
+
* extension re-renders automatically because it flows in as a React prop.
|
|
763
|
+
*
|
|
764
|
+
* Define a typed interface for each page's context in your remote app and pass
|
|
765
|
+
* it as the type parameter. Returns `undefined` when the host has not populated
|
|
766
|
+
* any context for the current zone.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* interface CallLogsPageContext {
|
|
770
|
+
* dateRange?: [Date | null, Date | null];
|
|
771
|
+
* }
|
|
772
|
+
*
|
|
773
|
+
* export function AnalyticsWidget({ context }: ExtensionComponentProps) {
|
|
774
|
+
* const pageCtx = usePageContext<CallLogsPageContext>(context);
|
|
775
|
+
* const [start, end] = pageCtx?.dateRange ?? [null, null];
|
|
776
|
+
* // ...
|
|
777
|
+
* }
|
|
778
|
+
*/
|
|
779
|
+
declare function usePageContext<T = unknown>(context: ExtensionContext): T | undefined;
|
|
636
780
|
/**
|
|
637
781
|
* Register a dynamic table column for the lifetime of the calling component.
|
|
638
782
|
*/
|
|
639
|
-
declare function useDynamicColumn(eventBus: HorizonContext[
|
|
783
|
+
declare function useDynamicColumn(eventBus: HorizonContext["eventBus"], appId: string, config: Omit<DynamicColumnConfig, "appId">): RemoteAppSDK;
|
|
640
784
|
|
|
641
785
|
/**
|
|
642
786
|
* Federation Error
|
|
643
787
|
* Structured error class with error codes for better error handling
|
|
644
788
|
*/
|
|
645
|
-
type HorizonSDKErrorCode =
|
|
789
|
+
type HorizonSDKErrorCode = "PERMISSION_DENIED" | "RATE_LIMIT_EXCEEDED" | "INVALID_MESSAGE" | "SIGNATURE_VERIFICATION_FAILED" | "API_ERROR" | "NETWORK_ERROR" | "INVALID_EXTENSION_POINT" | "INVALID_CONFIGURATION" | "APP_NOT_FOUND" | "MODULE_LOAD_FAILED" | "INITIALIZATION_FAILED" | "UNKNOWN_ERROR";
|
|
646
790
|
interface HorizonSDKErrorOptions {
|
|
647
791
|
code: HorizonSDKErrorCode;
|
|
648
792
|
message: string;
|
|
@@ -858,4 +1002,4 @@ type AnchorId = (typeof MANAGE_ANCHORS)[keyof typeof MANAGE_ANCHORS] | (typeof P
|
|
|
858
1002
|
|
|
859
1003
|
declare const VERSION: string;
|
|
860
1004
|
|
|
861
|
-
export { ANCHORS, APPS_ANCHORS, type AnchorId, type BreadcrumbItem, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, useRemoteApp, useRoute, useRouteFromModule, useTheme };
|
|
1005
|
+
export { ANCHORS, APPS_ANCHORS, type AnchorId, type BreadcrumbItem, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type FormPanelStep, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type TableFilterBarContext, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useTheme };
|