datocms-plugin-sdk 2.0.15 → 2.0.17

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.
Files changed (42) hide show
  1. package/dist/cjs/connect.js +4 -0
  2. package/dist/cjs/connect.js.map +1 -1
  3. package/dist/cjs/hooks/mainNavigationTabs.js +4 -1
  4. package/dist/cjs/hooks/mainNavigationTabs.js.map +1 -1
  5. package/dist/cjs/hooks/renderInspector.js +11 -0
  6. package/dist/cjs/hooks/renderInspector.js.map +1 -0
  7. package/dist/cjs/hooks/renderInspectorPanel.js +11 -0
  8. package/dist/cjs/hooks/renderInspectorPanel.js.map +1 -0
  9. package/dist/cjs/index.js +2 -0
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/manifest.js +134 -0
  12. package/dist/cjs/manifest.js.map +1 -1
  13. package/dist/esm/connect.d.ts +3 -1
  14. package/dist/esm/connect.js +4 -0
  15. package/dist/esm/connect.js.map +1 -1
  16. package/dist/esm/hooks/mainNavigationTabs.d.ts +4 -0
  17. package/dist/esm/hooks/mainNavigationTabs.js +4 -1
  18. package/dist/esm/hooks/mainNavigationTabs.js.map +1 -1
  19. package/dist/esm/hooks/renderInspector.d.ts +147 -0
  20. package/dist/esm/hooks/renderInspector.js +8 -0
  21. package/dist/esm/hooks/renderInspector.js.map +1 -0
  22. package/dist/esm/hooks/renderInspectorPanel.d.ts +20 -0
  23. package/dist/esm/hooks/renderInspectorPanel.js +8 -0
  24. package/dist/esm/hooks/renderInspectorPanel.js.map +1 -0
  25. package/dist/esm/index.d.ts +2 -0
  26. package/dist/esm/index.js +2 -0
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/manifest.js +134 -0
  29. package/dist/esm/manifest.js.map +1 -1
  30. package/dist/types/connect.d.ts +3 -1
  31. package/dist/types/hooks/mainNavigationTabs.d.ts +4 -0
  32. package/dist/types/hooks/renderInspector.d.ts +147 -0
  33. package/dist/types/hooks/renderInspectorPanel.d.ts +20 -0
  34. package/dist/types/index.d.ts +2 -0
  35. package/manifest.json +134 -0
  36. package/package.json +2 -2
  37. package/src/connect.ts +12 -0
  38. package/src/hooks/mainNavigationTabs.ts +13 -4
  39. package/src/hooks/renderInspector.ts +176 -0
  40. package/src/hooks/renderInspectorPanel.ts +38 -0
  41. package/src/index.ts +2 -0
  42. package/src/manifest.ts +145 -0
@@ -27,6 +27,8 @@ import type { OverrideFieldExtensionsHook } from './hooks/overrideFieldExtension
27
27
  import { RenderAssetSourceHook } from './hooks/renderAssetSource';
28
28
  import { RenderConfigScreenHook } from './hooks/renderConfigScreen';
29
29
  import { RenderFieldExtensionHook } from './hooks/renderFieldExtension';
30
+ import { RenderInspectorHook } from './hooks/renderInspector';
31
+ import { RenderInspectorPanelHook } from './hooks/renderInspectorPanel';
30
32
  import { RenderItemCollectionOutletHook } from './hooks/renderItemCollectionOutlet';
31
33
  import { RenderItemFormOutletHook } from './hooks/renderItemFormOutlet';
32
34
  import { RenderItemFormSidebarHook } from './hooks/renderItemFormSidebar';
@@ -43,5 +45,5 @@ import { UploadSidebarsHook } from './hooks/uploadSidebars';
43
45
  import type { UploadsDropdownActionsHook } from './hooks/uploadsDropdownActions';
44
46
  import type { ValidateManualFieldExtensionParametersHook } from './hooks/validateManualFieldExtensionParameters';
45
47
  /** The full options you can pass to the `connect` function */
46
- export type FullConnectParameters = AssetSourcesHook & BuildItemPresentationInfoHook & ContentAreaSidebarItemsHook & CustomBlockStylesForStructuredTextFieldHook & CustomMarksForStructuredTextFieldHook & ExecuteFieldDropdownActionHook & ExecuteItemFormDropdownActionHook & ExecuteItemsDropdownActionHook & ExecuteSchemaItemTypeDropdownActionHook & ExecuteUploadsDropdownActionHook & FieldDropdownActionsHook & InitialLocationQueryForItemSelectorHook & ItemCollectionOutletsHook & ItemFormDropdownActionsHook & ItemFormOutletsHook & ItemFormSidebarPanelsHook & ItemFormSidebarsHook & ItemsDropdownActionsHook & MainNavigationTabsHook & ManualFieldExtensionsHook & OnBeforeItemsDestroyHook & OnBeforeItemsPublishHook & OnBeforeItemsUnpublishHook & OnBeforeItemUpsertHook & OnBootHook & OverrideFieldExtensionsHook & RenderAssetSourceHook & RenderConfigScreenHook & RenderFieldExtensionHook & RenderItemCollectionOutletHook & RenderItemFormOutletHook & RenderItemFormSidebarHook & RenderItemFormSidebarPanelHook & RenderManualFieldExtensionConfigScreenHook & RenderModalHook & RenderPageHook & RenderUploadSidebarHook & RenderUploadSidebarPanelHook & SchemaItemTypeDropdownActionsHook & SettingsAreaSidebarItemGroupsHook & UploadsDropdownActionsHook & UploadSidebarPanelsHook & UploadSidebarsHook & ValidateManualFieldExtensionParametersHook;
48
+ export type FullConnectParameters = AssetSourcesHook & BuildItemPresentationInfoHook & ContentAreaSidebarItemsHook & CustomBlockStylesForStructuredTextFieldHook & CustomMarksForStructuredTextFieldHook & ExecuteFieldDropdownActionHook & ExecuteItemFormDropdownActionHook & ExecuteItemsDropdownActionHook & ExecuteSchemaItemTypeDropdownActionHook & ExecuteUploadsDropdownActionHook & FieldDropdownActionsHook & InitialLocationQueryForItemSelectorHook & ItemCollectionOutletsHook & ItemFormDropdownActionsHook & ItemFormOutletsHook & ItemFormSidebarPanelsHook & ItemFormSidebarsHook & ItemsDropdownActionsHook & MainNavigationTabsHook & ManualFieldExtensionsHook & OnBeforeItemsDestroyHook & OnBeforeItemsPublishHook & OnBeforeItemsUnpublishHook & OnBeforeItemUpsertHook & OnBootHook & OverrideFieldExtensionsHook & RenderAssetSourceHook & RenderConfigScreenHook & RenderFieldExtensionHook & RenderItemCollectionOutletHook & RenderItemFormOutletHook & RenderItemFormSidebarHook & RenderItemFormSidebarPanelHook & RenderManualFieldExtensionConfigScreenHook & RenderModalHook & RenderPageHook & RenderInspectorHook & RenderInspectorPanelHook & RenderUploadSidebarHook & RenderUploadSidebarPanelHook & SchemaItemTypeDropdownActionsHook & SettingsAreaSidebarItemGroupsHook & UploadsDropdownActionsHook & UploadSidebarPanelsHook & UploadSidebarsHook & ValidateManualFieldExtensionParametersHook;
47
49
  export declare function connect(rawConfiguration?: Partial<FullConnectParameters>): Promise<void>;
@@ -28,6 +28,10 @@ export type MainNavigationTab = {
28
28
  /** ID of the page linked to the tab */
29
29
  pointsTo: {
30
30
  pageId: string;
31
+ } | {
32
+ inspectorId: string;
33
+ /** The preferred width for the sidebar */
34
+ preferredWidth?: number;
31
35
  };
32
36
  /**
33
37
  * Expresses where you want to place the tab in the top-bar. If not specified,
@@ -0,0 +1,147 @@
1
+ import { ImposedSizePluginFrameCtx } from '../ctx/pluginFrame';
2
+ /**
3
+ * Defines the different modes in which an inspector can be displayed
4
+ */
5
+ export type InspectorMode = {
6
+ /** Display a list of records in the inspector */
7
+ type: 'itemList';
8
+ } | {
9
+ /** Display a single record editor in the inspector */
10
+ type: 'itemEditor';
11
+ /** The ID of the record to edit */
12
+ itemId: string;
13
+ /** Optional field path to highlight/focus within the record editor */
14
+ fieldPath?: string;
15
+ } | {
16
+ /** Display a custom panel in the inspector */
17
+ type: 'customPanel';
18
+ /** ID of the inspector panel to render */
19
+ panelId: string;
20
+ /**
21
+ * An arbitrary configuration object that will be passed as the `parameters`
22
+ * property of the second argument of the `renderInspectorPanel` function
23
+ */
24
+ parameters?: Record<string, unknown>;
25
+ };
26
+ /**
27
+ * Options for configuring inspector mode changes
28
+ */
29
+ export type SetInspectorModeOptions = {
30
+ /**
31
+ * When true, the mode change will be ignored if there are unsaved changes
32
+ * in the current inspector. Useful for "low intent" mode changes that
33
+ * shouldn't interrupt active editing sessions.
34
+ * @default false
35
+ */
36
+ ignoreIfUnsavedChanges?: boolean;
37
+ };
38
+ export type RenderInspectorHook = {
39
+ /**
40
+ * This function will be called when the plugin needs to render a specific
41
+ * inspector. Inspectors provide a side panel interface for displaying and
42
+ * interacting with content alongside a custom interface.
43
+ *
44
+ * @tag inspector
45
+ *
46
+ * @example
47
+ *
48
+ * ```js
49
+ * connect({
50
+ * renderInspector(inspectorId, ctx) {
51
+ * render(
52
+ * <div>
53
+ * <h1>Inspector: {inspectorId}</h1>
54
+ * <button onClick={() => ctx.setInspectorMode({
55
+ * type: 'itemEditor',
56
+ * itemId: 'some-item-id'
57
+ * })}>
58
+ * Show Item Editor
59
+ * </button>
60
+ * </div>
61
+ * );
62
+ * }
63
+ * });
64
+ * ```
65
+ */
66
+ renderInspector: (inspectorId: string, ctx: RenderInspectorCtx) => void;
67
+ };
68
+ export type RenderInspectorCtx = ImposedSizePluginFrameCtx<'renderInspector', {
69
+ /** The ID of the inspector that needs to be rendered */
70
+ inspectorId: string;
71
+ /** The ID of the record the currently is highlighted by the user */
72
+ highlightedItemId: string | undefined;
73
+ /** Current page location */
74
+ location: {
75
+ pathname: string;
76
+ search: string;
77
+ hash: string;
78
+ };
79
+ }, {
80
+ /**
81
+ * Changes the current display mode of the inspector. This allows the plugin
82
+ * to dynamically switch between showing a record list, record editor, or custom
83
+ * panel within the inspector interface.
84
+ *
85
+ * @param mode - The inspector mode to switch to
86
+ * @param options - Optional configuration for the mode change
87
+ * @param options.ignoreIfUnsavedChanges - When true, the mode change request will be
88
+ * ignored if the current inspector is in itemEditor mode and has unsaved changes.
89
+ * This allows for "low intent" mode changes that shouldn't interrupt active editing.
90
+ * Default is false, meaning mode changes will proceed regardless of unsaved changes.
91
+ *
92
+ * @example
93
+ *
94
+ * ```js
95
+ * // Switch to record editor mode
96
+ * await ctx.setInspectorMode({
97
+ * type: 'itemEditor',
98
+ * itemId: 'item-123',
99
+ * fieldPath: 'title'
100
+ * });
101
+ *
102
+ * // Switch to record list mode
103
+ * await ctx.setInspectorMode({ type: 'itemList' });
104
+ * await ctx.setInspectorItemListData({
105
+ * title: 'Related Records',
106
+ * itemIds: ['item-1', 'item-2', 'item-3']
107
+ * });
108
+ *
109
+ * // Switch to custom panel mode
110
+ * await ctx.setInspectorMode({
111
+ * type: 'customPanel',
112
+ * panelId: 'my-custom-panel',
113
+ * parameters: { filter: 'active' }
114
+ * });
115
+ *
116
+ * // Low intent mode change - won't interrupt editing with unsaved changes
117
+ * await ctx.setInspectorMode(
118
+ * { type: 'itemList' },
119
+ * { ignoreIfUnsavedChanges: true }
120
+ * );
121
+ * ```
122
+ */
123
+ setInspectorMode: (mode: InspectorMode, options?: SetInspectorModeOptions) => Promise<void>;
124
+ /**
125
+ * Sets the data for the item list inspector mode.
126
+ *
127
+ * @example
128
+ *
129
+ * ```js
130
+ * // Set the item list data
131
+ * await ctx.setInspectorItemListData({
132
+ * title: 'Related Records',
133
+ * itemIds: ['item-1', 'item-2', 'item-3']
134
+ * });
135
+ *
136
+ * // Switch to item list mode
137
+ * await ctx.setInspectorMode({ type: 'itemList' });
138
+ * ```
139
+ */
140
+ setInspectorItemListData: (data: {
141
+ /** The title to show in the inspector header */
142
+ title: string;
143
+ /** Array of record IDs to display in the list */
144
+ itemIds: string[];
145
+ }) => Promise<void>;
146
+ }>;
147
+ export declare const renderInspectorBootstrapper: import("../utils").Bootstrapper<"renderInspector">;
@@ -0,0 +1,20 @@
1
+ import { ImposedSizePluginFrameCtx } from '../ctx/pluginFrame';
2
+ export type RenderInspectorPanelHook = {
3
+ /**
4
+ * This function will be called when an inspector needs to render a specific
5
+ * panel (see the `renderInspector` and `setInspectorMode` functions)
6
+ *
7
+ * @tag inspector
8
+ */
9
+ renderInspectorPanel: (panelId: string, ctx: RenderInspectorPanelCtx) => void;
10
+ };
11
+ export type RenderInspectorPanelCtx = ImposedSizePluginFrameCtx<'renderInspectorPanel', {
12
+ /** The ID of the inspector panel that needs to be rendered */
13
+ panelId: string;
14
+ /**
15
+ * The arbitrary `parameters` of the modal declared in the `setInspectorMode`
16
+ * function
17
+ */
18
+ parameters: Record<string, unknown>;
19
+ }>;
20
+ export declare const renderInspectorPanelBootstrapper: import("../utils").Bootstrapper<"renderInspectorPanel">;
@@ -51,6 +51,8 @@ export * from './hooks/renderItemFormSidebarPanel';
51
51
  export * from './hooks/renderManualFieldExtensionConfigScreen';
52
52
  export * from './hooks/renderModal';
53
53
  export * from './hooks/renderPage';
54
+ export * from './hooks/renderInspector';
55
+ export * from './hooks/renderInspectorPanel';
54
56
  export * from './hooks/renderUploadSidebar';
55
57
  export * from './hooks/renderUploadSidebarPanel';
56
58
  export * from './hooks/schemaItemTypeDropdownActions';
package/manifest.json CHANGED
@@ -1139,6 +1139,140 @@
1139
1139
  "lineNumber": 14
1140
1140
  }
1141
1141
  },
1142
+ "renderInspectorPanel": {
1143
+ "name": "renderInspectorPanel",
1144
+ "comment": {
1145
+ "markdownText": "This function will be called when an inspector needs to render a specific\npanel (see the `renderInspector` and `setInspectorMode` functions).",
1146
+ "tag": "inspector"
1147
+ },
1148
+ "nonCtxArguments": [
1149
+ {
1150
+ "name": "panelId",
1151
+ "typeName": "string"
1152
+ }
1153
+ ],
1154
+ "ctxArgument": {
1155
+ "type": "ImposedSizePluginFrameCtx",
1156
+ "additionalProperties": [
1157
+ {
1158
+ "items": {
1159
+ "panelId": {
1160
+ "comment": {
1161
+ "markdownText": "The ID of the inspector panel that needs to be rendered."
1162
+ },
1163
+ "location": {
1164
+ "filePath": "src/hooks/renderInspectorPanel.ts",
1165
+ "lineNumber": 18
1166
+ },
1167
+ "type": "string"
1168
+ },
1169
+ "parameters": {
1170
+ "comment": {
1171
+ "markdownText": "The arbitrary `parameters` of the modal declared in the `setInspectorMode`\nfunction."
1172
+ },
1173
+ "location": {
1174
+ "filePath": "src/hooks/renderInspectorPanel.ts",
1175
+ "lineNumber": 24
1176
+ },
1177
+ "type": "Record<string, unknown>"
1178
+ }
1179
+ }
1180
+ }
1181
+ ],
1182
+ "additionalMethods": []
1183
+ },
1184
+ "returnType": "void",
1185
+ "location": {
1186
+ "filePath": "src/hooks/renderInspectorPanel.ts",
1187
+ "lineNumber": 11
1188
+ }
1189
+ },
1190
+ "renderInspector": {
1191
+ "name": "renderInspector",
1192
+ "comment": {
1193
+ "markdownText": "This function will be called when the plugin needs to render a specific\ninspector. Inspectors provide a side panel interface for displaying and\ninteracting with content alongside a custom interface.",
1194
+ "tag": "inspector",
1195
+ "example": "connect({\n renderInspector(inspectorId, ctx) {\n render(\n <div>\n <h1>Inspector: {inspectorId}</h1>\n <button onClick={() => ctx.setInspectorMode({\n type: 'itemEditor',\n itemId: 'some-item-id'\n })}>\n Show Item Editor\n </button>\n </div>\n );\n }\n});"
1196
+ },
1197
+ "nonCtxArguments": [
1198
+ {
1199
+ "name": "inspectorId",
1200
+ "typeName": "string"
1201
+ }
1202
+ ],
1203
+ "ctxArgument": {
1204
+ "type": "ImposedSizePluginFrameCtx",
1205
+ "additionalProperties": [
1206
+ {
1207
+ "items": {
1208
+ "inspectorId": {
1209
+ "comment": {
1210
+ "markdownText": "The ID of the inspector that needs to be rendered."
1211
+ },
1212
+ "location": {
1213
+ "filePath": "src/hooks/renderInspector.ts",
1214
+ "lineNumber": 80
1215
+ },
1216
+ "type": "string"
1217
+ },
1218
+ "highlightedItemId": {
1219
+ "comment": {
1220
+ "markdownText": "The ID of the record the currently is highlighted by the user."
1221
+ },
1222
+ "location": {
1223
+ "filePath": "src/hooks/renderInspector.ts",
1224
+ "lineNumber": 83
1225
+ },
1226
+ "type": "string | undefined"
1227
+ },
1228
+ "location": {
1229
+ "comment": {
1230
+ "markdownText": "Current page location."
1231
+ },
1232
+ "location": {
1233
+ "filePath": "src/hooks/renderInspector.ts",
1234
+ "lineNumber": 86
1235
+ },
1236
+ "type": "{\n pathname: string;\n search: string;\n hash: string;\n }"
1237
+ }
1238
+ }
1239
+ }
1240
+ ],
1241
+ "additionalMethods": [
1242
+ {
1243
+ "items": {
1244
+ "setInspectorMode": {
1245
+ "comment": {
1246
+ "markdownText": "Changes the current display mode of the inspector. This allows the plugin\nto dynamically switch between showing a record list, record editor, or custom\npanel within the inspector interface.",
1247
+ "example": "// Switch to record editor mode\nawait ctx.setInspectorMode({\n type: 'itemEditor',\n itemId: 'item-123',\n fieldPath: 'title'\n});\n\n// Switch to record list mode\nawait ctx.setInspectorMode({ type: 'itemList' });\nawait ctx.setInspectorItemListData({\n title: 'Related Records',\n itemIds: ['item-1', 'item-2', 'item-3']\n});\n\n// Switch to custom panel mode\nawait ctx.setInspectorMode({\n type: 'customPanel',\n panelId: 'my-custom-panel',\n parameters: { filter: 'active' }\n});\n\n// Low intent mode change - won't interrupt editing with unsaved changes\nawait ctx.setInspectorMode(\n { type: 'itemList' },\n { ignoreIfUnsavedChanges: true }\n);"
1248
+ },
1249
+ "location": {
1250
+ "filePath": "src/hooks/renderInspector.ts",
1251
+ "lineNumber": 136
1252
+ },
1253
+ "type": "(\n mode: InspectorMode,\n options?: SetInspectorModeOptions,\n ) => Promise<void>"
1254
+ },
1255
+ "setInspectorItemListData": {
1256
+ "comment": {
1257
+ "markdownText": "Sets the data for the item list inspector mode.",
1258
+ "example": "// Set the item list data\nawait ctx.setInspectorItemListData({\n title: 'Related Records',\n itemIds: ['item-1', 'item-2', 'item-3']\n});\n\n// Switch to item list mode\nawait ctx.setInspectorMode({ type: 'itemList' });"
1259
+ },
1260
+ "location": {
1261
+ "filePath": "src/hooks/renderInspector.ts",
1262
+ "lineNumber": 157
1263
+ },
1264
+ "type": "(data: {\n /** The title to show in the inspector header */\n title: string;\n /** Array of record IDs to display in the list */\n itemIds: string[];\n }) => Promise<void>"
1265
+ }
1266
+ }
1267
+ }
1268
+ ]
1269
+ },
1270
+ "returnType": "void",
1271
+ "location": {
1272
+ "filePath": "src/hooks/renderInspector.ts",
1273
+ "lineNumber": 73
1274
+ }
1275
+ },
1142
1276
  "renderFieldExtension": {
1143
1277
  "name": "renderFieldExtension",
1144
1278
  "comment": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datocms-plugin-sdk",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "DatoCMS Plugin SDK",
5
5
  "keywords": [
6
6
  "datocms",
@@ -45,5 +45,5 @@
45
45
  "glob": "^11.0.0",
46
46
  "typescript": "^5.6.2"
47
47
  },
48
- "gitHead": "6698d6f38549b0487ee4721862483d811512bf73"
48
+ "gitHead": "ce4d1125e8f52a499965e5aad358ffd78d9cc8fd"
49
49
  }
package/src/connect.ts CHANGED
@@ -37,6 +37,14 @@ import {
37
37
  RenderFieldExtensionHook,
38
38
  renderFieldExtensionBootstrapper,
39
39
  } from './hooks/renderFieldExtension';
40
+ import {
41
+ RenderInspectorHook,
42
+ renderInspectorBootstrapper,
43
+ } from './hooks/renderInspector';
44
+ import {
45
+ RenderInspectorPanelHook,
46
+ renderInspectorPanelBootstrapper,
47
+ } from './hooks/renderInspectorPanel';
40
48
  import {
41
49
  RenderItemCollectionOutletHook,
42
50
  renderItemCollectionOutletBootstrapper,
@@ -117,6 +125,8 @@ export type FullConnectParameters = AssetSourcesHook &
117
125
  RenderManualFieldExtensionConfigScreenHook &
118
126
  RenderModalHook &
119
127
  RenderPageHook &
128
+ RenderInspectorHook &
129
+ RenderInspectorPanelHook &
120
130
  RenderUploadSidebarHook &
121
131
  RenderUploadSidebarPanelHook &
122
132
  SchemaItemTypeDropdownActionsHook &
@@ -264,6 +274,8 @@ export async function connect(
264
274
  renderManualFieldExtensionConfigScreenBootstrapper,
265
275
  renderModal: renderModalBootstrapper,
266
276
  renderPage: renderPageBootstrapper,
277
+ renderInspector: renderInspectorBootstrapper,
278
+ renderInspectorPanel: renderInspectorPanelBootstrapper,
267
279
  renderUploadSidebar: renderUploadSidebarBootstrapper,
268
280
  renderUploadSidebarPanel: renderUploadSidebarPanelBootstrapper,
269
281
  };
@@ -37,9 +37,15 @@ export type MainNavigationTab = {
37
37
  */
38
38
  icon: Icon;
39
39
  /** ID of the page linked to the tab */
40
- pointsTo: {
41
- pageId: string;
42
- };
40
+ pointsTo:
41
+ | {
42
+ pageId: string;
43
+ }
44
+ | {
45
+ inspectorId: string;
46
+ /** The preferred width for the sidebar */
47
+ preferredWidth?: number;
48
+ };
43
49
  /**
44
50
  * Expresses where you want to place the tab in the top-bar. If not specified,
45
51
  * the tab will be placed after the standard tabs provided by DatoCMS itself.
@@ -66,7 +72,10 @@ export function isMainNavigationTab(
66
72
  isString(value.label) &&
67
73
  isIcon(value.icon) &&
68
74
  isRecord(value.pointsTo) &&
69
- isString(value.pointsTo.pageId) &&
75
+ (isString(value.pointsTo.pageId) ||
76
+ (isString(value.pointsTo.inspectorId) &&
77
+ (isNullish(value.pointsTo.preferredWidth) ||
78
+ isNumber(value.pointsTo.preferredWidth)))) &&
70
79
  (isNullish(value.placement) || isPlacement(value.placement)) &&
71
80
  (isNullish(value.rank) || isNumber(value.rank))
72
81
  );
@@ -0,0 +1,176 @@
1
+ import { ImposedSizePluginFrameCtx } from '../ctx/pluginFrame';
2
+ import { fullScreenRenderModeBootstrapper } from '../utils';
3
+
4
+ /**
5
+ * Defines the different modes in which an inspector can be displayed
6
+ */
7
+ export type InspectorMode =
8
+ | {
9
+ /** Display a list of records in the inspector */
10
+ type: 'itemList';
11
+ }
12
+ | {
13
+ /** Display a single record editor in the inspector */
14
+ type: 'itemEditor';
15
+ /** The ID of the record to edit */
16
+ itemId: string;
17
+ /** Optional field path to highlight/focus within the record editor */
18
+ fieldPath?: string;
19
+ }
20
+ | {
21
+ /** Display a custom panel in the inspector */
22
+ type: 'customPanel';
23
+ /** ID of the inspector panel to render */
24
+ panelId: string;
25
+ /**
26
+ * An arbitrary configuration object that will be passed as the `parameters`
27
+ * property of the second argument of the `renderInspectorPanel` function
28
+ */
29
+ parameters?: Record<string, unknown>;
30
+ };
31
+
32
+ /**
33
+ * Options for configuring inspector mode changes
34
+ */
35
+ export type SetInspectorModeOptions = {
36
+ /**
37
+ * When true, the mode change will be ignored if there are unsaved changes
38
+ * in the current inspector. Useful for "low intent" mode changes that
39
+ * shouldn't interrupt active editing sessions.
40
+ * @default false
41
+ */
42
+ ignoreIfUnsavedChanges?: boolean;
43
+ };
44
+
45
+ export type RenderInspectorHook = {
46
+ /**
47
+ * This function will be called when the plugin needs to render a specific
48
+ * inspector. Inspectors provide a side panel interface for displaying and
49
+ * interacting with content alongside a custom interface.
50
+ *
51
+ * @tag inspector
52
+ *
53
+ * @example
54
+ *
55
+ * ```js
56
+ * connect({
57
+ * renderInspector(inspectorId, ctx) {
58
+ * render(
59
+ * <div>
60
+ * <h1>Inspector: {inspectorId}</h1>
61
+ * <button onClick={() => ctx.setInspectorMode({
62
+ * type: 'itemEditor',
63
+ * itemId: 'some-item-id'
64
+ * })}>
65
+ * Show Item Editor
66
+ * </button>
67
+ * </div>
68
+ * );
69
+ * }
70
+ * });
71
+ * ```
72
+ */
73
+ renderInspector: (inspectorId: string, ctx: RenderInspectorCtx) => void;
74
+ };
75
+
76
+ export type RenderInspectorCtx = ImposedSizePluginFrameCtx<
77
+ 'renderInspector',
78
+ {
79
+ /** The ID of the inspector that needs to be rendered */
80
+ inspectorId: string;
81
+
82
+ /** The ID of the record the currently is highlighted by the user */
83
+ highlightedItemId: string | undefined;
84
+
85
+ /** Current page location */
86
+ location: {
87
+ pathname: string;
88
+ search: string;
89
+ hash: string;
90
+ };
91
+ },
92
+ {
93
+ /**
94
+ * Changes the current display mode of the inspector. This allows the plugin
95
+ * to dynamically switch between showing a record list, record editor, or custom
96
+ * panel within the inspector interface.
97
+ *
98
+ * @param mode - The inspector mode to switch to
99
+ * @param options - Optional configuration for the mode change
100
+ * @param options.ignoreIfUnsavedChanges - When true, the mode change request will be
101
+ * ignored if the current inspector is in itemEditor mode and has unsaved changes.
102
+ * This allows for "low intent" mode changes that shouldn't interrupt active editing.
103
+ * Default is false, meaning mode changes will proceed regardless of unsaved changes.
104
+ *
105
+ * @example
106
+ *
107
+ * ```js
108
+ * // Switch to record editor mode
109
+ * await ctx.setInspectorMode({
110
+ * type: 'itemEditor',
111
+ * itemId: 'item-123',
112
+ * fieldPath: 'title'
113
+ * });
114
+ *
115
+ * // Switch to record list mode
116
+ * await ctx.setInspectorMode({ type: 'itemList' });
117
+ * await ctx.setInspectorItemListData({
118
+ * title: 'Related Records',
119
+ * itemIds: ['item-1', 'item-2', 'item-3']
120
+ * });
121
+ *
122
+ * // Switch to custom panel mode
123
+ * await ctx.setInspectorMode({
124
+ * type: 'customPanel',
125
+ * panelId: 'my-custom-panel',
126
+ * parameters: { filter: 'active' }
127
+ * });
128
+ *
129
+ * // Low intent mode change - won't interrupt editing with unsaved changes
130
+ * await ctx.setInspectorMode(
131
+ * { type: 'itemList' },
132
+ * { ignoreIfUnsavedChanges: true }
133
+ * );
134
+ * ```
135
+ */
136
+ setInspectorMode: (
137
+ mode: InspectorMode,
138
+ options?: SetInspectorModeOptions,
139
+ ) => Promise<void>;
140
+
141
+ /**
142
+ * Sets the data for the item list inspector mode.
143
+ *
144
+ * @example
145
+ *
146
+ * ```js
147
+ * // Set the item list data
148
+ * await ctx.setInspectorItemListData({
149
+ * title: 'Related Records',
150
+ * itemIds: ['item-1', 'item-2', 'item-3']
151
+ * });
152
+ *
153
+ * // Switch to item list mode
154
+ * await ctx.setInspectorMode({ type: 'itemList' });
155
+ * ```
156
+ */
157
+ setInspectorItemListData: (data: {
158
+ /** The title to show in the inspector header */
159
+ title: string;
160
+ /** Array of record IDs to display in the list */
161
+ itemIds: string[];
162
+ }) => Promise<void>;
163
+ }
164
+ >;
165
+
166
+ export const renderInspectorBootstrapper =
167
+ fullScreenRenderModeBootstrapper<RenderInspectorCtx>(
168
+ 'renderInspector',
169
+ (configuration, ctx) => {
170
+ if (!configuration.renderInspector) {
171
+ return;
172
+ }
173
+
174
+ configuration.renderInspector(ctx.inspectorId, ctx);
175
+ },
176
+ );
@@ -0,0 +1,38 @@
1
+ import { ImposedSizePluginFrameCtx } from '../ctx/pluginFrame';
2
+ import { fullScreenRenderModeBootstrapper } from '../utils';
3
+
4
+ export type RenderInspectorPanelHook = {
5
+ /**
6
+ * This function will be called when an inspector needs to render a specific
7
+ * panel (see the `renderInspector` and `setInspectorMode` functions)
8
+ *
9
+ * @tag inspector
10
+ */
11
+ renderInspectorPanel: (panelId: string, ctx: RenderInspectorPanelCtx) => void;
12
+ };
13
+
14
+ export type RenderInspectorPanelCtx = ImposedSizePluginFrameCtx<
15
+ 'renderInspectorPanel',
16
+ {
17
+ /** The ID of the inspector panel that needs to be rendered */
18
+ panelId: string;
19
+
20
+ /**
21
+ * The arbitrary `parameters` of the modal declared in the `setInspectorMode`
22
+ * function
23
+ */
24
+ parameters: Record<string, unknown>;
25
+ }
26
+ >;
27
+
28
+ export const renderInspectorPanelBootstrapper =
29
+ fullScreenRenderModeBootstrapper<RenderInspectorPanelCtx>(
30
+ 'renderInspectorPanel',
31
+ (configuration, ctx) => {
32
+ if (!configuration.renderInspectorPanel) {
33
+ return;
34
+ }
35
+
36
+ configuration.renderInspectorPanel(ctx.panelId, ctx);
37
+ },
38
+ );
package/src/index.ts CHANGED
@@ -53,6 +53,8 @@ export * from './hooks/renderItemFormSidebarPanel';
53
53
  export * from './hooks/renderManualFieldExtensionConfigScreen';
54
54
  export * from './hooks/renderModal';
55
55
  export * from './hooks/renderPage';
56
+ export * from './hooks/renderInspector';
57
+ export * from './hooks/renderInspectorPanel';
56
58
  export * from './hooks/renderUploadSidebar';
57
59
  export * from './hooks/renderUploadSidebarPanel';
58
60
  export * from './hooks/schemaItemTypeDropdownActions';