@contrail/extensions-sdk 1.0.27 → 1.0.29

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.
@@ -8,7 +8,8 @@ export declare enum BoardCommand {
8
8
  RECOLOR_IMAGES = "board:recolor_images",
9
9
  ADD_CONTENT_TO_ENTITY_FROM_ELEMENTS = "board:add_content_to_entity_from_elements",
10
10
  ADD_FRAMES_FROM_TEMPLATE = "board:add_frames_from_template",
11
- ADD_ITEMS_TO_CLIPBOARD = "board:add_items_to_clipboard"
11
+ ADD_ITEMS_TO_CLIPBOARD = "board:add_items_to_clipboard",
12
+ COMPOSE_CONTENT = "board:compose_content"
12
13
  }
13
14
  /** Payload for bulk-add items to clipboard (Board and Showcase). */
14
15
  export interface ClipboardItemsPayload {
@@ -17,6 +18,59 @@ export interface ClipboardItemsPayload {
17
18
  projectItemId?: string | null;
18
19
  }>;
19
20
  }
21
+ /** One image in a composition, placed in fractions of the output box. */
22
+ export interface ComposeContentLayer {
23
+ /** The content to draw. The host resolves it to an image, so no URL is needed - and none can
24
+ * expire between planning the layout and composing it. */
25
+ contentId: string;
26
+ /**
27
+ * Top-left corner as a fraction of the composition box, so `{x: 0.5, y: 0}` is the top middle.
28
+ *
29
+ * Fractions rather than pixels: the layout is then independent of whatever size the extension
30
+ * previewed it at, and the same plan can be reused at any output resolution.
31
+ */
32
+ position: {
33
+ x: number;
34
+ y: number;
35
+ };
36
+ /** Size as a fraction of the composition box. `{width: 1, height: 1}` fills it. */
37
+ size: {
38
+ width: number;
39
+ height: number;
40
+ };
41
+ /** Degrees clockwise about the layer's centre. */
42
+ rotation?: number;
43
+ /** 0-1. Defaults to fully opaque. */
44
+ opacity?: number;
45
+ }
46
+ export interface ComposeContentRequest {
47
+ /** Drawn back to front: the first layer is behind, the last on top. */
48
+ layers: Array<ComposeContentLayer>;
49
+ /**
50
+ * What the new content is filed against, e.g. `item:<id>`.
51
+ *
52
+ * Required: content cannot be created free-floating - the platform derives its access policies
53
+ * from the holder.
54
+ */
55
+ contentHolderReference: string;
56
+ /** Output size in pixels. The host derives one from the source images when omitted. */
57
+ outputSize?: {
58
+ width: number;
59
+ height: number;
60
+ };
61
+ /** Defaults to transparent, which is usually what a composed pack shot wants. */
62
+ backgroundColor?: string;
63
+ fileName?: string;
64
+ }
65
+ export interface ComposeContentResult {
66
+ contentId: string;
67
+ primaryFileId?: string;
68
+ primaryFileUrl?: string;
69
+ size: {
70
+ width: number;
71
+ height: number;
72
+ };
73
+ }
20
74
  export interface BoardDocumentElement extends DocumentElement {
21
75
  [key: string]: any;
22
76
  }
@@ -59,6 +113,28 @@ export declare class BoardsApp {
59
113
  static getElements(criteria: object): Promise<Array<DocumentElement>>;
60
114
  /** Creates new content from a set of elements and assigns to an entity (item, etc) as a viewable */
61
115
  static createNewContentAndAssignToEntity(assignmentOptions: any, entity: any, elements: Array<DocumentElement>): Promise<Array<DocumentElement>>;
116
+ /**
117
+ * Composes several contents into one new image, and returns it as a new content.
118
+ *
119
+ * The extension supplies a layout - which content goes where, at what size - and the board
120
+ * rasterises it with the same renderer its own image export uses. Nothing is added to the
121
+ * board: the elements exist only for the duration of the render.
122
+ *
123
+ * This is the way to build a composed image from an extension. Reading the source images into
124
+ * a canvas the extension owns would taint it and make the export throw, because the image
125
+ * files are cross-origin.
126
+ *
127
+ * ```ts
128
+ * const composed = await BoardsApp.composeContent({
129
+ * layers: [
130
+ * { contentId: 'c1', position: { x: 0, y: 0 }, size: { width: 0.6, height: 0.6 } },
131
+ * { contentId: 'c2', position: { x: 0.4, y: 0.4 }, size: { width: 0.6, height: 0.6 } },
132
+ * ],
133
+ * contentHolderReference: `item:${setItem.id}`,
134
+ * });
135
+ * ```
136
+ */
137
+ static composeContent(request: ComposeContentRequest): Promise<ComposeContentResult>;
62
138
  static recolorImages(imageElements: Array<DocumentElement>, hexCode: string, hexMap?: {}): Promise<Array<DocumentElement>>;
63
139
  /** Request the host to bulk-add items to the user's clipboard. */
64
140
  static addItemsToClipboard(clipboardItems: Array<{
@@ -22,6 +22,7 @@ var BoardCommand;
22
22
  BoardCommand["ADD_CONTENT_TO_ENTITY_FROM_ELEMENTS"] = "board:add_content_to_entity_from_elements";
23
23
  BoardCommand["ADD_FRAMES_FROM_TEMPLATE"] = "board:add_frames_from_template";
24
24
  BoardCommand["ADD_ITEMS_TO_CLIPBOARD"] = "board:add_items_to_clipboard";
25
+ BoardCommand["COMPOSE_CONTENT"] = "board:compose_content";
25
26
  })(BoardCommand = exports.BoardCommand || (exports.BoardCommand = {}));
26
27
  class BoardsApp {
27
28
  static getCurrentBoard() {
@@ -92,6 +93,44 @@ class BoardsApp {
92
93
  return this.validateAndReturnResults(results);
93
94
  });
94
95
  }
96
+ /**
97
+ * Composes several contents into one new image, and returns it as a new content.
98
+ *
99
+ * The extension supplies a layout - which content goes where, at what size - and the board
100
+ * rasterises it with the same renderer its own image export uses. Nothing is added to the
101
+ * board: the elements exist only for the duration of the render.
102
+ *
103
+ * This is the way to build a composed image from an extension. Reading the source images into
104
+ * a canvas the extension owns would taint it and make the export throw, because the image
105
+ * files are cross-origin.
106
+ *
107
+ * ```ts
108
+ * const composed = await BoardsApp.composeContent({
109
+ * layers: [
110
+ * { contentId: 'c1', position: { x: 0, y: 0 }, size: { width: 0.6, height: 0.6 } },
111
+ * { contentId: 'c2', position: { x: 0.4, y: 0.4 }, size: { width: 0.6, height: 0.6 } },
112
+ * ],
113
+ * contentHolderReference: `item:${setItem.id}`,
114
+ * });
115
+ * ```
116
+ */
117
+ static composeContent(request) {
118
+ var _a;
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ BoardsApp.validateBoardContext();
121
+ if (!((_a = request === null || request === void 0 ? void 0 : request.layers) === null || _a === void 0 ? void 0 : _a.length)) {
122
+ throw new Error('composeContent requires at least one layer.');
123
+ }
124
+ if (!(request === null || request === void 0 ? void 0 : request.contentHolderReference)) {
125
+ throw new Error('composeContent requires a contentHolderReference, e.g. `item:<id>`.');
126
+ }
127
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
128
+ command: BoardCommand.COMPOSE_CONTENT,
129
+ data: request,
130
+ });
131
+ return this.validateAndReturnResults(results);
132
+ });
133
+ }
95
134
  static recolorImages(imageElements, hexCode, hexMap = {}) {
96
135
  return __awaiter(this, void 0, void 0, function* () {
97
136
  BoardsApp.validateBoardContext();
@@ -7,7 +7,8 @@ export declare enum PlanCommand {
7
7
  ADD_PLACEHOLDERS_WITH_ITEMS = "plan:add_placeholders_with_items",
8
8
  GET_PLACEHOLDERS = "plan:get_placeholders",
9
9
  GET_VIEW_STATE = "plan:get_view_state",
10
- ASSIGN_ITEMS = "plan:assign_items"
10
+ ASSIGN_ITEMS = "plan:assign_items",
11
+ SCROLL_TO_PLACEHOLDER = "plan:scroll_to_placeholder"
11
12
  }
12
13
  /**
13
14
  * A row on a plan. Beyond the item linkage below, properties are the org-configured
@@ -42,6 +43,30 @@ export interface PlaceholderItemAssignment {
42
43
  /** The item to assign — an item family or an item option. */
43
44
  itemId: string;
44
45
  }
46
+ export interface ScrollToPlaceholderOptions {
47
+ /**
48
+ * Also bring this column into view horizontally.
49
+ *
50
+ * Omit to scroll vertically only, which is usually what you want after writing to a row —
51
+ * it leaves the user's horizontal position alone.
52
+ */
53
+ propertySlug?: string;
54
+ }
55
+ /** Why a row could not be brought into view. */
56
+ export declare type ScrollToPlaceholderFailure = 'not-on-plan' | 'not-displayed' | 'no-grid';
57
+ export interface ScrollToPlaceholderResult {
58
+ /** Whether the row was brought into view. Already-visible rows report `true`. */
59
+ revealed: boolean;
60
+ /**
61
+ * Set only when `revealed` is false.
62
+ *
63
+ * - `not-on-plan` - no row on this plan has that id.
64
+ * - `not-displayed` - the row exists but the user's filter or sort excludes it, so there is
65
+ * no position to scroll to. Reading {@link PlanApp.getViewState} will say why.
66
+ * - `no-grid` - the plan is not currently showing the grid, so there is nothing to scroll.
67
+ */
68
+ reason?: ScrollToPlaceholderFailure;
69
+ }
45
70
  export interface GetPlaceholdersOptions {
46
71
  /** Defaults to `all`. */
47
72
  scope?: PlanPlaceholderScope;
@@ -130,6 +155,27 @@ export declare class PlanApp {
130
155
  * Requires the plan to be editable.
131
156
  */
132
157
  static assignItems(assignments: Array<PlaceholderItemAssignment>): Promise<void>;
158
+ /**
159
+ * Scrolls the plan's grid so a row is in view.
160
+ *
161
+ * Use this after writing to the plan - assigning items or adding rows - so the user can see
162
+ * what changed instead of having to hunt for it. A row that is already on screen is left
163
+ * alone rather than re-centred.
164
+ *
165
+ * This is a view-only operation: it does not select the row, change the filter, or edit
166
+ * anything, and it does not require the plan to be editable.
167
+ *
168
+ * Check the result rather than assuming success - a row outside the user's current filter has
169
+ * no position in the grid to scroll to:
170
+ *
171
+ * ```ts
172
+ * const result = await PlanApp.scrollToPlaceholder(rowId);
173
+ * if (!result.revealed && result.reason === 'not-displayed') {
174
+ * // The row was written, but the user's filter hides it.
175
+ * }
176
+ * ```
177
+ */
178
+ static scrollToPlaceholder(placeholderId: string, options?: ScrollToPlaceholderOptions): Promise<ScrollToPlaceholderResult>;
133
179
  /**
134
180
  * Creates new rows on the plan from property data, and returns them.
135
181
  *
package/lib/apps/plan.js CHANGED
@@ -22,6 +22,7 @@ var PlanCommand;
22
22
  PlanCommand["GET_PLACEHOLDERS"] = "plan:get_placeholders";
23
23
  PlanCommand["GET_VIEW_STATE"] = "plan:get_view_state";
24
24
  PlanCommand["ASSIGN_ITEMS"] = "plan:assign_items";
25
+ PlanCommand["SCROLL_TO_PLACEHOLDER"] = "plan:scroll_to_placeholder";
25
26
  })(PlanCommand = exports.PlanCommand || (exports.PlanCommand = {}));
26
27
  class PlanApp {
27
28
  static getCurrentPlan() {
@@ -171,6 +172,39 @@ class PlanApp {
171
172
  PlanApp.validateAndReturnResults(results);
172
173
  });
173
174
  }
175
+ /**
176
+ * Scrolls the plan's grid so a row is in view.
177
+ *
178
+ * Use this after writing to the plan - assigning items or adding rows - so the user can see
179
+ * what changed instead of having to hunt for it. A row that is already on screen is left
180
+ * alone rather than re-centred.
181
+ *
182
+ * This is a view-only operation: it does not select the row, change the filter, or edit
183
+ * anything, and it does not require the plan to be editable.
184
+ *
185
+ * Check the result rather than assuming success - a row outside the user's current filter has
186
+ * no position in the grid to scroll to:
187
+ *
188
+ * ```ts
189
+ * const result = await PlanApp.scrollToPlaceholder(rowId);
190
+ * if (!result.revealed && result.reason === 'not-displayed') {
191
+ * // The row was written, but the user's filter hides it.
192
+ * }
193
+ * ```
194
+ */
195
+ static scrollToPlaceholder(placeholderId, options) {
196
+ return __awaiter(this, void 0, void 0, function* () {
197
+ PlanApp.validatePlanContext();
198
+ if (!placeholderId) {
199
+ return { revealed: false, reason: 'not-on-plan' };
200
+ }
201
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
202
+ command: PlanCommand.SCROLL_TO_PLACEHOLDER,
203
+ data: { placeholderId, options },
204
+ });
205
+ return PlanApp.validateAndReturnResults(results);
206
+ });
207
+ }
174
208
  /**
175
209
  * Creates new rows on the plan from property data, and returns them.
176
210
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/extensions-sdk",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "Client library for interfacing with VibeIQ's services and apps from an extension.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",