@pipedream/google_slides 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/actions/create-image/create-image.mjs +1 -1
  2. package/actions/create-page-element/create-page-element.mjs +1 -1
  3. package/actions/create-presentation/create-presentation.mjs +1 -1
  4. package/actions/create-slide/create-slide.mjs +1 -1
  5. package/actions/create-table/create-table.mjs +1 -1
  6. package/actions/delete-page-element/delete-page-element.mjs +1 -1
  7. package/actions/delete-slide/delete-slide.mjs +1 -1
  8. package/actions/delete-table-column/delete-table-column.mjs +1 -1
  9. package/actions/delete-table-row/delete-table-row.mjs +1 -1
  10. package/actions/find-presentation/find-presentation.mjs +1 -1
  11. package/actions/format-paragraph/format-paragraph.mjs +236 -0
  12. package/actions/format-shape/format-shape.mjs +193 -0
  13. package/actions/format-slide-background/format-slide-background.mjs +74 -0
  14. package/actions/format-table-cell/format-table-cell.mjs +162 -0
  15. package/actions/format-text/format-text.mjs +248 -0
  16. package/actions/get-presentation/get-presentation.mjs +1 -1
  17. package/actions/insert-table-columns/insert-table-columns.mjs +1 -1
  18. package/actions/insert-table-rows/insert-table-rows.mjs +1 -1
  19. package/actions/insert-text/insert-text.mjs +1 -1
  20. package/actions/insert-text-into-table/insert-text-into-table.mjs +1 -1
  21. package/actions/merge-data/merge-data.mjs +1 -1
  22. package/actions/position-element/position-element.mjs +219 -0
  23. package/actions/refresh-chart/refresh-chart.mjs +1 -1
  24. package/actions/replace-all-text/replace-all-text.mjs +1 -1
  25. package/common/constants.mjs +110 -0
  26. package/common/utils.mjs +115 -0
  27. package/google_slides.app.mjs +101 -0
  28. package/package.json +1 -1
  29. package/sources/new-presentation/new-presentation.mjs +1 -1
@@ -4,7 +4,7 @@ export default {
4
4
  key: "google_slides-replace-all-text",
5
5
  name: "Replace All Text",
6
6
  description: "Replace all text in a presentation. [See the documentation](https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request#ReplaceAllTextRequest)",
7
- version: "0.0.4",
7
+ version: "0.0.5",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -141,3 +141,113 @@ export const SHAPE_TYPES = [
141
141
  "ELLIPSE_RIBBON_2",
142
142
  "CLOUD_CALLOUT",
143
143
  ];
144
+
145
+ // Enum values used by the styling requests, checked against the current API
146
+ // reference. https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request
147
+
148
+ export const ALIGNMENTS = [
149
+ "START",
150
+ "CENTER",
151
+ "END",
152
+ "JUSTIFIED",
153
+ ];
154
+
155
+ export const CONTENT_ALIGNMENTS = [
156
+ "TOP",
157
+ "MIDDLE",
158
+ "BOTTOM",
159
+ ];
160
+
161
+ export const DASH_STYLES = [
162
+ "SOLID",
163
+ "DOT",
164
+ "DASH",
165
+ "DASH_DOT",
166
+ "LONG_DASH",
167
+ "LONG_DASH_DOT",
168
+ ];
169
+
170
+ export const Z_ORDER_OPERATIONS = [
171
+ "BRING_TO_FRONT",
172
+ "BRING_FORWARD",
173
+ "SEND_BACKWARD",
174
+ "SEND_TO_BACK",
175
+ ];
176
+
177
+ // Theme colors resolve against the deck's own ColorScheme, so a caller can style
178
+ // with the presentation's palette instead of hardcoding hex values.
179
+ export const THEME_COLORS = [
180
+ "DARK1",
181
+ "LIGHT1",
182
+ "DARK2",
183
+ "LIGHT2",
184
+ "ACCENT1",
185
+ "ACCENT2",
186
+ "ACCENT3",
187
+ "ACCENT4",
188
+ "ACCENT5",
189
+ "ACCENT6",
190
+ "HYPERLINK",
191
+ "FOLLOWED_HYPERLINK",
192
+ "TEXT1",
193
+ "BACKGROUND1",
194
+ "TEXT2",
195
+ "BACKGROUND2",
196
+ ];
197
+
198
+ // Dimension.unit - points, rather than the API's other unit (EMU).
199
+ export const POINTS = "PT";
200
+ export const EMU = "EMU";
201
+
202
+ // 1 inch = 72 pt = 914400 EMU. A transform carries a single unit for all six
203
+ // matrix elements, so caller-supplied points must be converted into whatever
204
+ // unit the element already uses rather than mixed in.
205
+ export const EMU_PER_POINT = 12700;
206
+
207
+ // Range.type. The API expresses the three range shapes as distinct types rather
208
+ // than as optional bounds, and rejects FIXED_RANGE that carries no end index.
209
+ export const TEXT_RANGE_ALL = "ALL";
210
+ export const TEXT_RANGE_FROM_START_INDEX = "FROM_START_INDEX";
211
+ export const TEXT_RANGE_FIXED_RANGE = "FIXED_RANGE";
212
+
213
+ // BulletGlyphPreset. NOTE: these are NOT the same spellings the Google Docs API
214
+ // uses - Slides says DIGIT where Docs says DECIMAL - so they cannot be shared
215
+ // with the google_docs connector.
216
+ export const BULLET_PRESETS = [
217
+ "BULLET_DISC_CIRCLE_SQUARE",
218
+ "BULLET_DIAMONDX_ARROW3D_SQUARE",
219
+ "BULLET_CHECKBOX",
220
+ "BULLET_ARROW_DIAMOND_DISC",
221
+ "BULLET_STAR_CIRCLE_SQUARE",
222
+ "BULLET_ARROW3D_CIRCLE_SQUARE",
223
+ "BULLET_LEFTTRIANGLE_DIAMOND_DISC",
224
+ "BULLET_DIAMONDX_HOLLOWDIAMOND_SQUARE",
225
+ "BULLET_DIAMOND_CIRCLE_SQUARE",
226
+ "NUMBERED_DIGIT_ALPHA_ROMAN",
227
+ "NUMBERED_DIGIT_ALPHA_ROMAN_PARENS",
228
+ "NUMBERED_DIGIT_NESTED",
229
+ "NUMBERED_UPPERALPHA_ALPHA_ROMAN",
230
+ "NUMBERED_UPPERROMAN_UPPERALPHA_DIGIT",
231
+ "NUMBERED_ZERODIGIT_ALPHA_ROMAN",
232
+ ];
233
+
234
+ // Sentinel for the Bullets prop meaning "strip existing bullets".
235
+ export const BULLETS_NONE = "NONE";
236
+
237
+ export const BASELINE_OFFSETS = [
238
+ "NONE",
239
+ "SUPERSCRIPT",
240
+ "SUBSCRIPT",
241
+ ];
242
+
243
+ export const TEXT_DIRECTIONS = [
244
+ "LEFT_TO_RIGHT",
245
+ "RIGHT_TO_LEFT",
246
+ ];
247
+
248
+ export const SPACING_MODES = [
249
+ "NEVER_COLLAPSE",
250
+ "COLLAPSE_LISTS",
251
+ ];
252
+
253
+ export const OPACITY_SCALE = 100;
@@ -0,0 +1,115 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import {
3
+ TEXT_RANGE_ALL,
4
+ TEXT_RANGE_FIXED_RANGE,
5
+ OPACITY_SCALE,
6
+ POINTS,
7
+ TEXT_RANGE_FROM_START_INDEX,
8
+ THEME_COLORS,
9
+ } from "./constants.mjs";
10
+
11
+ function toOpaqueColor(value) {
12
+ const trimmed = String(value).trim();
13
+ const themeColor = trimmed.toUpperCase();
14
+ if (THEME_COLORS.includes(themeColor)) {
15
+ return {
16
+ themeColor,
17
+ };
18
+ }
19
+
20
+ const hex = trimmed.replace(/^#/, "");
21
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) {
22
+ return null;
23
+ }
24
+ const channel = (start) => parseInt(hex.slice(start, start + 2), 16) / 255;
25
+ return {
26
+ rgbColor: {
27
+ red: channel(0),
28
+ green: channel(2),
29
+ blue: channel(4),
30
+ },
31
+ };
32
+ }
33
+
34
+ function toSolidFill(value, opacityPercent) {
35
+ const color = value == null || value === ""
36
+ ? null
37
+ : toOpaqueColor(value);
38
+ if (value && !color) {
39
+ return null;
40
+ }
41
+ const fill = {};
42
+ if (color) {
43
+ fill.color = color;
44
+ }
45
+ if (opacityPercent != null) {
46
+ fill.alpha = opacityPercent / OPACITY_SCALE;
47
+ }
48
+ return Object.keys(fill).length
49
+ ? fill
50
+ : null;
51
+ }
52
+
53
+ function buildTextRange(startIndex, endIndex) {
54
+ if (startIndex == null && endIndex == null) {
55
+ return {
56
+ type: TEXT_RANGE_ALL,
57
+ };
58
+ }
59
+ if (startIndex == null) {
60
+ throw new ConfigurationError("End Index needs a Start Index. Provide both to style a fixed span, Start Index on its own to style from there to the end, or neither to style all of the text.");
61
+ }
62
+ if (endIndex == null) {
63
+ return {
64
+ type: TEXT_RANGE_FROM_START_INDEX,
65
+ startIndex,
66
+ };
67
+ }
68
+ if (endIndex <= startIndex) {
69
+ throw new ConfigurationError(`End Index (${endIndex}) must be greater than Start Index (${startIndex}).`);
70
+ }
71
+ return {
72
+ type: TEXT_RANGE_FIXED_RANGE,
73
+ startIndex,
74
+ endIndex,
75
+ };
76
+ }
77
+
78
+ function styleBuilder() {
79
+ const style = {};
80
+ const fields = [];
81
+
82
+ return {
83
+ style,
84
+ fields,
85
+ set(name, value) {
86
+ if (value == null) {
87
+ return;
88
+ }
89
+ style[name] = value;
90
+ fields.push(name);
91
+ },
92
+ setDimension(name, magnitude, unit = POINTS) {
93
+ if (magnitude == null) {
94
+ return;
95
+ }
96
+ this.set(name, {
97
+ magnitude,
98
+ unit,
99
+ });
100
+ },
101
+ get isEmpty() {
102
+ return !fields.length;
103
+ },
104
+ get mask() {
105
+ return fields.join(",");
106
+ },
107
+ };
108
+ }
109
+
110
+ export default {
111
+ styleBuilder,
112
+ toOpaqueColor,
113
+ toSolidFill,
114
+ buildTextRange,
115
+ };
@@ -1,6 +1,7 @@
1
1
  import { ConfigurationError } from "@pipedream/platform";
2
2
  import slides from "@googleapis/slides";
3
3
  import googleDrive from "@pipedream/google_drive";
4
+ import { CONTENT_ALIGNMENTS } from "./common/constants.mjs";
4
5
 
5
6
  export default {
6
7
  ...googleDrive,
@@ -20,6 +21,72 @@ export default {
20
21
  return this.listPresentationsOptions(driveId, nextPageToken);
21
22
  },
22
23
  },
24
+ staticPresentationId: {
25
+ type: "string",
26
+ label: "Presentation ID",
27
+ description: "The ID of the presentation. This is the long string in the URL: `https://docs.google.com/presentation/d/{PRESENTATION_ID}/edit`. A full presentation URL is also accepted. Use **Find Presentation** to resolve a name to its ID.",
28
+ },
29
+ staticSlideId: {
30
+ type: "string",
31
+ label: "Slide ID",
32
+ description: "The object ID of the slide (e.g. `p1` or `g1a2b3c4d5`). Use **Get Presentation** and read `slides[].objectId`.",
33
+ },
34
+ pageElementId: {
35
+ type: "string",
36
+ label: "Page Element ID",
37
+ description: "The object ID of the shape, image, table, or other page element to act on. Use **Get Presentation** and read `slides[].pageElements[].objectId`.",
38
+ },
39
+ // Shared by the styling actions; individual actions override the label or
40
+ // description where their wording differs.
41
+ rowIndex: {
42
+ type: "integer",
43
+ label: "Row Index",
44
+ description: "The 0-based row of the target table cell.",
45
+ min: 0,
46
+ optional: true,
47
+ },
48
+ columnIndex: {
49
+ type: "integer",
50
+ label: "Column Index",
51
+ description: "The 0-based column of the target table cell.",
52
+ min: 0,
53
+ optional: true,
54
+ },
55
+ startIndex: {
56
+ type: "integer",
57
+ label: "Start Index",
58
+ description: "Character index to style from, inclusive. On its own, styles from here to the end of the text. Omit both indices to style all of it.",
59
+ min: 0,
60
+ optional: true,
61
+ },
62
+ endIndex: {
63
+ type: "integer",
64
+ label: "End Index",
65
+ description: "Character index to style up to, exclusive. Requires **Start Index**, and must be greater than it.",
66
+ min: 0,
67
+ optional: true,
68
+ },
69
+ backgroundColor: {
70
+ type: "string",
71
+ label: "Background Color",
72
+ description: "A hex code (e.g. `#EEEEEE`) or one of the deck's theme colors (e.g. `ACCENT1`).",
73
+ optional: true,
74
+ },
75
+ backgroundOpacity: {
76
+ type: "integer",
77
+ label: "Background Opacity",
78
+ description: "Opacity as a whole percentage, from `0` (fully transparent) to `100` (fully opaque). Can be set on its own to change an existing fill's opacity without restating its color.",
79
+ min: 0,
80
+ max: 100,
81
+ optional: true,
82
+ },
83
+ contentAlignment: {
84
+ type: "string",
85
+ label: "Content Alignment",
86
+ description: "Vertical alignment of the text within the element. One of `TOP`, `MIDDLE` or `BOTTOM` - e.g. `MIDDLE` to centre the text vertically.",
87
+ options: CONTENT_ALIGNMENTS,
88
+ optional: true,
89
+ },
23
90
  layoutId: {
24
91
  type: "string",
25
92
  label: "Layout ID",
@@ -280,6 +347,40 @@ export default {
280
347
  };
281
348
  return (await slides.presentations.get(request)).data;
282
349
  },
350
+ _findPageElement(presentation, objectId) {
351
+ const walk = (elements, slideId, groupId) => {
352
+ for (const element of elements || []) {
353
+ if (element.objectId === objectId) {
354
+ return {
355
+ element,
356
+ slideId,
357
+ groupId,
358
+ };
359
+ }
360
+ const found = walk(element.elementGroup?.children, slideId, element.objectId);
361
+ if (found) {
362
+ return found;
363
+ }
364
+ }
365
+ return null;
366
+ };
367
+
368
+ for (const slide of presentation.slides || []) {
369
+ const found = walk(slide.pageElements, slide.objectId);
370
+ if (found) {
371
+ return found;
372
+ }
373
+ }
374
+ return null;
375
+ },
376
+ async getPageElement(presentationId, objectId) {
377
+ const presentation = await this.getPresentation(presentationId);
378
+ const found = this._findPageElement(presentation, objectId);
379
+ if (!found) {
380
+ throw new ConfigurationError(`No page element with ID "${objectId}" was found in presentation ${presentationId}. Use Get Presentation to list slides[].pageElements[].objectId.`);
381
+ }
382
+ return found;
383
+ },
283
384
  async getSlide(presentationId, slideId) {
284
385
  const slides = this.slides();
285
386
  const request = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/google_slides",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Pipedream Google Slides Components",
5
5
  "main": "google_slides.app.mjs",
6
6
  "keywords": [
@@ -7,7 +7,7 @@ export default {
7
7
  type: "source",
8
8
  name: "New Presentation (Instant)",
9
9
  description: "Emit new event each time a new presentation is created in a drive.",
10
- version: "0.0.6",
10
+ version: "0.0.7",
11
11
  hooks: {
12
12
  ...newFilesInstant.hooks,
13
13
  async deploy() {