1.0.23 • Published 2 months ago

gumdecks v1.0.23

Weekly downloads
-
License
ISC
Repository
-
Last release
2 months ago

Decks

Decks is a technology that leverages the Google Slides API and a custom action-based language to automate and output custom Google Slides presentations. With Decks, users can create a template that can be customized to point to data sent to Decks. This allows for the creation of dynamic and up-to-date presentations that can be easily shared and collaborated on with team members.

The custom action-based language enables users to define the logic and behavior of each slide in the presentation, including the formatting, images, and text to be displayed. With Decks, users can easily customize the look and feel of their presentations and streamline the presentation creation process, making it an excellent tool for individuals and businesses who frequently create and share presentations.

Prerequisites

Node 18 and npm >= 7 are required to run Decks. You can install them here.

You'll need a Google Cloud service account file to use Decks. You can create one by following the instructions here. You'll need read/write permissions for Google Drive and Google Slides as the generated presentation will be saved to your Google Drive.

Once you have the service account json file, save it in your project root (be sure to omit it from your git repo). You'll need to set the environment variable GOOGLE_SERVICE_ACCOUNT_FILE to the filename of the service account file. Simply create a .env file in the root directory:

GOOGLE_SERVICE_ACCOUNT_FILE=service-account.json

Puppeteer

You may need to update your .env file to include a path to a chromium executable if you're using textTransform: true on any actions, Decks uses puppeteer to calculate the font-size to fit to a element as the Google Slide api does not support auto fit (yet):

PUPPETEER_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome

Installation

npm i gumdecks -D

Usage

  1. Create a template, copy the template id from the url
    • Example, if the url to your presentation is - https://docs.google.com/presentation/u/0/d/1GOrgjv0rSJvGuVU36pFNqoj93c9vEcnmFIrPOofn8JI/edit - "1GOrgjv0rSJvGuVU36pFNqoj93c9vEcnmFIrPOofn8JI" is the templateId
  2. For your first test, add the config to the speaker notes as described in the above doc, and add a text element to the slide, right click and add the action replaceText::{"value": "name"} and a title of "test-name"
  3. Create a folder in Google Drive, then copy the folderId from the url
    • Example - https://drive.google.com/drive/u/0/folders/cv8XsDNG1Q5ZvHXcq5ctcwMiWt6jTOqRo "cv8XsDNG1Q5ZvHXcq5ctcwMiWt6jTOqRo" is the folderId
  4. Create an index.ts file with the following content and replace the folderId and templateId
import { DeckGenerator } from 'gumdecks';

(async () => {
  const engine = DeckGenerator({
    "filename": "Output File Name",
    "folderId": "<<FOLDER_ID_IN_GOOGLE_DRIVE>>", // example 1GOrgjv0rSJvGuVU36pFNqoj93c9vEcnmFIrPOofn8JI
    "templateId": "<<TEMPLATE_ID>>", // example 1ZeswfIn3XO9cOmeMVAKC7tYc8K8941qQ7I3mpVnAviI
    "dataset": {
        "name": "Jane Doe"
    },
  });
  try {
    const result = await engine.process();
    console.log('Result', result);
  } catch (e) {
    console.log('Error', e);
  }
})()
  1. Create a tsconfig.json file in the root directory:
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "noEmit": true,
  },
  "include": ["index.ts"],
}
  1. Execute the file by running npx ts-node --esm ./index.ts in your terminal

If all was successful, you should see a console log with "Result" and a url of the generated presentation!

The available params you can send to the DeckGenerator are all available through typescript and documented.

Documentation

There's detailed documentation including examples on how to write your actions in your template.

  1. Build your own template
  2. Tips & tricks
  3. Conditional logic
  4. Technical Overview
  5. Misc

Actions

Here we outline all the available actions and how to use them.

How to build a Template to work with Decks

Let's assume we're starting with a blank slate, there's only one rule that must be followed.

  1. Every slide must have a config set in the speaker notes, and this config must have a unique name between slides, this helps us provide useful feedback to the user when something goes wrong, eg (Slide 1 (intro) on page element (logo) is missing the "value" input)
config::{"value": "intro"}

Note: There are no other options (currently) for the config input other than name.

The speaker notes is a panel that sits below each slide:

npm.io

When you need to add an element action, you can do this by selecting the element on the slide, right click and pick "Alt text", you'll be presented with a title/description field.

The title is simply a name to give your element, eg "advertiser name". The Description is a multi line field where you'll put your action(s), separate your actions on a new line.

npm.io

NOTE:: The UI for this has changed and the "Alt text" option is now a part of the sidebar options, to add the "title" it's now under "advanced options" in the sidebar, if you do not add a title, the action will not run.

Tips & tricks

Text Formatting

When designing the Google Slides presentation, it's recommended to disable "text fit on overflow" on text/shape elements that have actions as we have our own calculations that perform during runtime, enabling this feature can interfere with the calculations.

Text Formatting - Decks uses the styles returned from the presentation, if a specific text element has multiple different font styles applied there may be issues when running the actions.

Skip Slides

If you're only focusing on one slide, and you want the generation process to skip others while you're testing, in Google Slides, simply select the slide(s) you want to skip, right click and select "Skip slide(s)", the slides will not output in the generated presentation.

Conditional Logic

If you want to apply an action to an element or slide but conditionally based on the data provided, expressions can be provided any any input argument that is translated, eg (skip, value, fontSize, columnPercentageWidths etc)

Eg, if a slide has a "logo" image element and want to conditionally remove or skip an action based on a value from the dataset.

{
  dataset: {
    logo: null,
    age: 24,
    name: 'Jane Doe'
  }
}

On the element, provide your replaceImage action with the following:

replaceImage::{"value": "logo", "skip": "logo === null"}

Now the replaceImage action will only run when the logo value is not null.


Another way of achieving this would be to delete the element in this condition where if the logo is null, no other actions will process on that element and it will be removed from the final presentation.

deleteElement::{"skip": "logo === null"}
replaceImage::{"value": "logo"}
repositionElement:{"x": 0, "y": 1}

The evaluations here can be quite complicated, you can use pretty well any javascript operator, as well as string endsWith, startsWith and includes.

replaceText::{"value": "logo.includes('Jane') ? value + ' is a girl' : value + 'is a boy'"}
replaceText::{"search": "{age}", "value": "age >= 18 ? 'adult' : 'child'"}

Technical Overview

Decks is a powerful tool that runs and processes data in a very specific fashion. This document will go over the technical details of how Decks works and how it processes data.

npm.io

  1. Decks expects to receive a dataset, templateId and filename as the payload for the request.
  2. Decks then authenticates with Google using the service account credentials.
  3. Decks then retrieves the template from the templateId provided.
  4. Decks then validates the template to ensure all actions are valid
    • Will validate action names are correct
    • Will validate input parameters are correct on every action
    • Will validate the "value" of the action is pointing to a value on the dataset provided.
  5. Decks then creates a base configuration which is all slides and all actions applied to their respective elements.
  6. Decks then creates a slide configuration which is the new configuration with actions like repeatSlide and repeatElement applied and automatically translates the indexes of action values.
  7. Decks then processes all the actions and further validation applies before each action is performed.
  8. Decks then creates a new presentation with the new configuration and the dataset provided.

Misc

Some documentation on specific topics that don't fit into the other categories.


Getter String

NOTE: All getter strings support string evaluation

A getter string is a dot notated string that will be used to get a value from the dataset sent to the decks api. This is automatically translated and validated at runtime. You can also provide a fixed index from an array, or an empty index which is automatically translated if you're inside a repeatElement or repeatSlide or even combined with a repeatElement or repeatSlide.

Here's an example dataset that could be sent to the decks api:

   {
    people: [{
      name: 'John',
      traits: [{
        name: 'happy'
      }, {
        name: 'quick'
      }, {
        name: 'dumb'
      }]
    }, {
      name: 'Jane',
      traits: [{
        name: 'smart'
      }, {
        name: 'lucky'
      }, {
        name: 'funny'
      }]
    }, {
      name: 'Joe',
      traits: [{
        name: 'beautiful'
      }, {
        name: 'bearded'
      }, {
        name: 'tall'
      }]
    }],
   }

Based on the above dataset, here's some examples of what actions will resolve

replaceText::{"value": "people[0].name"} -> John replaceText::{"value": "people[1].name"} -> Jane replaceText::{"value": "people[1].traits[1]"} -> smart

If there's a slide action of repeatSlide::{"value": "people"} the slide will repeat for however many people are in the dataset. This means we can automatically translate the index as well, in the slide that has the repeatSlide action, let's say there's a title element that should have the name of the person.

  replaceText::{"value": "people[].name"}

The above will resolve to John on the first slide, Jane on the second slide, and Joe on the third slide.

You can also have a repeatElement action within the repeatSlide action

Let's say there's a shape element where we want to repeat to list out the traits, with the following action inside the slide with the repeatSlide:

  repeatElement::{"value": "people[].traits"}
  replaceText::{"value": "people[].traits[].name"}

The above will repeat the element and replace the text on each element to happy, quick and dumb on the first slide, smart, lucky and funny on the second slide, and beautiful, bearded and tall on the third slide.

Text Actions (replaceText, replaceAllText)

All the below features are supported by both replaceText and replaceAllText

Numbers

We can turn an ordinary number into something more readable:

Assuming the dataset provided is:

{
  campaign: {
    budget: 1230032,
  }
}

replaceText::{"value": "campaign.budget", "type": "number"} -> 1,230,032

Dates

We can convert a date from the dataset to a specific format, this is highly customizable and the reference for the formats is here

All dates should be sent in UTC!

Note: The default date format is: MM/dd/yyyy Note: If no timezone is passed, dates will be displayed in UTC

Examples:

Assuming the dataset provided is

{
  campaign: {
    startAt: '2022-12-18T12:04:55Z',
    advertiser: {
      timezone: 'Asia/Tokyo'
    }
  }
}

To convert the raw value to a formatted date in utc: replaceText::{"value": "campaign.startAt", "type": "date"} -> 12/18/2022

To convert the raw value to a formatted date with timezone: replaceText::{"value": "campaign.startAt", "type": "date", "timezone": "campaign.advertiser.timezone"} -> 12/18/2022

To show the long month name replaceText::{"value": "campaign.startAt", "type": "date", "timezone": "campaign.advertiser.timezone", "format": "MMMM"} -> December

To show the hour suffixed with time values: replaceText::{"value": "campaign.startAt", "type": "date", "timezone": "campaign.advertiser.timezone", "format": "h 'o''clock'"} -> 1 o'clock

To show a detailed date string: replaceText::{"value": "campaign.startAt", "type": "date", "timezone": "campaign.advertiser.timezone", "format": "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"} -> 2022-12-19T01:04:55.000+09:00

Currencies / Custom

Note: The default currency format is: s0,0.00 Note: passing no currency object to the request to the service will not format within the locale for USD/US-EN locale

Examples:

Assuming the dataset provided is

{
  campaign: {
    totalCost: 123456.54,
  }
}

To convert the raw value to a readable currency: replaceText::{"value": "campaign.totalCost", "type": "currency"} -> $123,456.54 To convert the raw value to a readable currency with a custom currency code: replaceText::{"value": "campaign.totalCost", "type": "currency", "code": "EUR"} -> €123,456.54

We can perform custom formatting on currencies & more

We can convert a currency from the dataset to a specific format, this also handles a lot more than just currencies, so the reference here

To convert the raw value to compact thousandths display replaceText::{"value": "campaign.totalCost", "type": "custom", "format": "a"} -> 123k (currency code is ignored here)

If you want to include the currency symbol by the "code" value that's passed to the main request: replaceText::{"value": "campaign.totalCost", "type": "custom", "format": "a", "prefix": "%currencySymbol%"} -> €123k Note: This will only work when the "code" property is passed to the payload which is the currencyCode of the expected currency, you can also pass the "code" property to the action itself if need be.

If you want to extract the index of the current element that's being repeated, you can add in %index% to either the translated value, or prefix/suffix input arguments

To remove decimal places and round the dollar value:

repeatElement::{"value": "people"}
replaceText::{"value": "people[].name", "prefix": "%index% - "}

-> 1 - John, 2 - Jane, 3 - Bob

To remove decimal places and round the dollar value: replaceText::{"value": "campaign.totalCost", "type": "custom", "format": "s0,0"} -> €123,457

To suffix the value with something else: (prefix also works here) replaceText::{"value": "campaign.totalCost", "type": "custom", "format": "s0,0", "suffix": " dollar billz"} -> €123,457 dollar billz

Bulleted Lists

If you have a bulleted list that you want to replace with another set of bulleted values, simply pass the value as a string separated by a new line char.

Examples:

Assuming the dataset provided is

{
  campaign: {
    list: "bullet1\nbullet2\nbullet3"",
  }
}

Then add the action, the original styles will be applied and the text will be replaced with the new value: replaceText::{"value": "campaign.list"}

Anchor to slide (anchorToSlide)

This action will allow you to link an element to another slide when that element is clicked in presentation mode.

Supported Shape Types: "ELEMENTGROUP", "IMAGE", "SHAPE", "TEXT_BOX"

Let's assume you've got a slide with this config: config::{"name": "end-slide"}

On any other slide on the supported element you can add the action to point to this slide:

anchorToSlide::{"value": "end-slide"}

Note: The "value" provided to the action is a partial search and will link to the first found slide with the match

If you have multiple slides, one that's deleted based on conditional logic like "deleteSlide",

First Slide: config::{"name": "contents"}, inside slide 1, you have an action anchorToSlide::{"value": "campaign-summary"}

Then there's two other slides, if the campaign-summary-1 slide is deleted, the anchorToSlide action will link to the campaign-summary-2 slide.

[ config::{"name": "campaign-summary-1"}, deleteSlide::{"value": "rules.deleteFirstSummary"} ]
[ config::{"name": "campaign-summary-2"}, deleteSlide::{"value": "rules.deleteSecondSummary"} ]

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a string matching a name of a slide
skipstring, booleantruefalsea getter string or boolean

Delete Element (deleteElement)

This action can conditionally remove an element from a slide. You can have multiple deleteElement actions on a single element if there's multiple rules that an element needs to pass.

Supported Shape Types: "ALL"

Let's say the dataset sent to the decks api is:

  {
    dataset: {
      rules: {
        shouldDeleteElement: true
      }
    },
    templateId: 'XXX',
    filename: 'XXX'
  }

When you have an element on a slide that you want to conditionally delete, you can add the action to the element:

deleteElement::{"value": "rules.shouldDeleteElement"}

Note: When there's a deleteElement action on an element and the value is true, the element will be deleted causing all other actions on the element to be ignored.


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
skipstring, booleantruefalsea getter string or boolean

Delete Slide (deleteSlide)

This action will conditionally remove a slide from a deck. This action is only processed in the speaker notes, usually this is based on rules, this will remove the slide if the value is truthy. These actions are processed at the very beginning before repeatSlide.

Supported Shape Types: "SPEAKER_NOTES"

Let's say the dataset sent to the decks api is:

  {
    dataset: {
      rules: {
        shouldDeleteSomeSlide: true
      }
    },
    templateId: 'XXX',
    filename: 'XXX'
  }

Add this action to the speaker notes of the slide you want to conditionally delete:

deleteSlide::{"value": "rules.shouldDeleteSomeSlide"}

Note: When the value resolves true, no other actions on the slide will be executed.


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
skipstring, booleantruefalsea getter string or boolean

Populate Table (populateTable)

This action will allow you to populate the data of an existing table, all data in the table will be replaced with the data provided from the action.

Supported Shape Types: "TABLE"

Rows/columns are automatically created / removed based on the data provided.

Styles should retain their formatting unless there's more rows being inserted than the table in the template has.

Note: The action will not pass validation if all the rows do not have the same number of columns.

Custom Table Cell Properties

Each cell has different params that are available if you provide an object as the cell value instead of (string|null|number|undefined), a more detailed example is present below in the dataset example.

interface TableCell {
  content: string|null|number|undefined;
  fontSize?: number;
  width?: number;
  backgroundColor: [number, number, number];
  textColor: [number, number, number];
  bold?: boolean;
}

The structure of the data source is expected to be (string|null|number|undefined|TableCell)

If you specify a width for a table cell, which ever "column" this associates with, the same width value will apply to all cells in that column, if another cell in the same column specifies a width thats different Decks will throw an error.

Additionally, if you specify width values where the values exceed 100% of the remaining width it will throw an error as well.


Let's say the dataset sent to the decks api is:

  {
    dataset: {
      tableData: [
            [
                "Unit",
                "Impressions"
            ],
            [
                "In-Image",
                "4,755,153"
            ],
            [
                "In-Screen Expandable",
                "13,742,145"
            ],
            [
                {
                  content: "Totals",
                  fontSize: 20,
                  bold: true,
                  backgroundColor: [0, 0, 0],
                  textColor: [255, 255, 255]
                },
                "71,501,718"
            ]
        ],
    },
    templateId: 'XXX',
    filename: 'XXX'
  }

Usage:

populateTable::{"value": "tableData"}

The above data source will create a table with 4 rows and 2 columns.


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
fontSizestring, numbertrue-a getter string or number, The table font size, this will update every cell's font size
skipstring, booleantruefalsea getter string or boolean
rowLimitnumbertrueInfinitya number representing the maximum amount of rows the action will generate

Replace Image (replaceImage)

This will replace the current image with the image url or url that returns a supported image.

Supported Shape Types: "IMAGE"

The image is fetched once at insertion time and a copy is stored for display inside the presentation. Images must be less than 50MB, cannot exceed 25 megapixels, and must be in PNG, JPEG, or GIF format.

The provided URL can't surpass 2 KB in length.

The below will simply replace the image with the image from brandImageUrl from the dataset

replaceImage::{"value": "campaign.brandImageUrl"}

We can perform other tricks, like using a service to retrieve an image based on a domain name:

replaceImage::{"value": "campaign.advertiser.domain", "prefix": "https://logo.clearbit.com/"}

which will be translated at runtime to something like: https://logo.clearbit.com/mastercard.com.au


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
prefixstringtrue-The prefix to apply to the value
suffixstringtrue-The suffix to apply to the value
fallbackImagestringtrue-A http(s) image url to use if the provided value isn't valid or supported
skipstringtruefalsea getter string

Replace All Text (replaceAllText)

This action is only processed in the speaker notes, this will retrieve the value from the dataset, and search for the "search" term and replace all elements matching the search term with the value from the dataset.

Supported Shape Types: "SPEAKER_NOTES"

replaceAllText::{"search": "{{SOMETHING}}","value": "person.name"}

Detailed Examples

Examples of different types of formatted values using replaceAllText can be found here


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
searchstringfalse-The search string to find within the slide.
wrapbooleantruefalseWhether or not the text should wrap.
formatstringtrueThe format to apply to the value. This is only used when the type is set to "custom" or "date".
codestringtrueThe currency code to use when the type is set to "currency".
tzstringtrueThe timezone to use when the type is set to "date" (this is automatic if not provided)
prefixstringtrue-The prefix to apply to the value
suffixstringtrue-The suffix to apply to the value
type"currency", "number", "custom" or "date"true-The type of value to format.
maxFontSizenumbertrue96The maximum font size to use when auto resizing the text.
minFontSizenumbertrue3The minimum font size to use when auto resizing the text.
truncatenumbertruefalseThe number of characters to truncate the text to, if "true" it will attempt to auto truncate to fit the text box.
textTransformbooleantruefalseWhether or not to perform a text resizing algorithm on the element.
skipstringtruefalsea getter string

Replace Text (replaceText)

This action is only processed on elements, this will retrieve the value from the dataset and replace the contents of the element with the value from the dataset.

Supported Shape Types: "TEXT_BOX", "SHAPE"

replaceText::{"value": "person.name"}

Detailed Examples

Examples of different types of formatted values using replaceAllText can be found here


Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
wrapbooleantruefalseWhether or not the text should wrap.
formatstringtrueThe format to apply to the value. This is only used when the type is set to "custom" or "date".
codestringtrueThe currency code to use when the type is set to "currency".
tzstringtrueThe timezone to use when the type is set to "date" (this is automatic if not provided)
searchstringtrue-The string to search for in the text to replace.
prefixstringtrue-The prefix to apply to the value
suffixstringtrue-The suffix to apply to the value
type"currency", "number", "custom" or "date"true-The type of value to format.
maxFontSizenumbertrue96The maximum font size to use when auto resizing the text.
minFontSizenumbertrue3The minimum font size to use when auto resizing the text.
truncatenumbertruefalseThe number of characters to truncate the text to, if "true" it will attempt to auto truncate to fit the text box.
textTransformbooleantruefalseWhether or not to perform a text resizing algorithm on the element.
skipstringtruefalsea getter string

Set Hyperlink (setHyperlink)

Will allow you to provide a clickable element by setting the "hyperlink" value on the action which will convert the element to a LINK element.

Supported Shape Types: "IMAGE", "SHAPE"'

Assuming the dataset is:

{
  website: {
    domain: "https://www.google.com"
  }
}

When the below action is provided to the element, in presentation mode the element will take the user to Google when clicked.

setHyperlink::{"value": "website.domain"}

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string that should point to a valid url
skipstringtruefalsea getter string

Repeat Element (repeatElement)

This action will allow you to repeat an element X amount of times by pointing it to an array based data source in the payloads dataset.

Supported Shape Types: "ALL"

Furthermore, you can also "overflow" onto a new copy of the same slide if need be by specifying an overflowAfter value.

By default, this element will be repeated horizontally with a small gap between them. (default of 100 cols)


Let's say the dataset sent to the decks api is:

  {
    dataset: {
      chickens: ['Pecky', 'KFC', 'Bock Bock', 'Fluffy', 'Henny Penny', 'Popcorn'],
    },
    templateId: 'XXX',
    filename: 'XXX'
  }

Usage:

repeatElement::{"value": "chickens"}

The above will repeat the element 6 times.


Let's create a more detailed example:

If you have a text box, and a background element or icon you want to be repeated, add the repeatElement action to any of the two elements, on the text box, add a replaceText action replaceText::{"value": "chickens[]"} then select them both and "group" them. This will repeat the group and run the replaceText action. Take note that you do not need to provide the index value of the array, the getter string is automatically translated.

repeatElement::{"value": "chickens", "max": 4, "cols" 2}

The above will repeat the element 4 times and the text box will have the name of the chicken as well as repeating the elements in a 2x2 grid.


You can also tell the repeatElement to overflow and copy the slide again, eg if there's a repeatElement but you only want 6 on one slide, you can tell the input to overflowAfter: 6, and the current slide will duplicate and put the remaining items on the next slide.

Note: you can only have ONE repeatElement action with overflowAfter on a slide.

repeatElement::{"value": "campaign.ads", "cols": 3, "overflowAfter": 6}

Custom reposition logic

The below will repeat the element X times, and for every item the repositionElement action will apply it's position logic to the current element with the "pos" value of the current item in the array.

This does mean you'd have to pre-compute the position values on the client side.

repeatElement::{"value": "campaign.ads", "overflowAfter": 6, "reposition": false}
repositionElement::{"value": "campaign.ads[].pos", "applyToGroup": true}

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
overflowAfternumbertrueundefineda number representing how many times the element can be repeated on a single slide
colsnumbertrue100how many columns should be created when repeating the element
maxnumbertrueundefinedthe maximum amount of times the element should be repeated
gapnumbertrue0.2the gap between elements represented in inches
repositionbooleantruetrueif the action should reposition in the elements in a grid, if false it will duplicate them in place
skipstring, booleantruefalsea getter string or boolean

Repeat Slide (repeatSlide)

This action is only processed in the speaker notes, this will retrieve the value from the dataset, if it's an array it will repeat the slide for every iteration of the array in the dataset.

Supported Shape Types: "SPEAKER_NOTES"

Note: Any element actions that use this dataset will be translated to the correct index, ie something.data[].name will retrieve the correct index during runtime.

If you have more than one slide with the same repeatSlide action, they will be grouped together if they proceed one another, if this isn't expected behaviour you can pass "group": false to the input of the action.

repeatSlide::{"value": "campaign.placements"}

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringfalse-a getter string
groupbooleantruetrueif multiple slides with the same repeatElement data source should group together instead of the order created in the deck
skipstring, booleantruefalsea getter string or boolean

Reposition Element (repositionElement)

This action will be processed on elements or applied to the parent group using the applyToGroup input argument. This will move the element or group to the specified coordinates relative to the elements current position.

If you only want to move an element in one axis, simply omit the other axis value.

Supported Shape Types: "ALL"

Assuming the dataset is:

{
  person: {
    name: "John Doe",
    isMale: true,
    isFemale: false
  }
}

This will reposition the element with the action by 1 inch on the x axis and 1 inch on the y axis when the person is a female.

repositionElement::{"x": 1, "y": 1, "skip": "person.isFemale"}

If you want to dynamically move something based on the data you can use a getter string to get the value you want to use.

{
  people: [{
    name: "John Doe",
    move: {
      x: 0.25,
      y: 0.25
    }
  }, {
    name: "Jane Doe",
    move: {
      x: 0,
      y: 0.25
    }
  }]
}

Then the action can retrieve the value from the dataset.

repeatElement::{"value": "people"}
repositionElement::{"value": "people[].move"}

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringtrue-a getter string that should point to and object with x/y values in inches
xnumbertrue-The x axis value to move in inches
ynumbertrue-The y axis value to move in inches
applyToGroupbooleantruefalseif true, the reposition action will be applied to the parent group element
skipstringtruefalsea getter string

Resize Element (resizeElement)

This action will be processed on elements or applied to the parent group using the applyToGroup input argument. This will resize the element or group to the specified coordinates relative to the elements/groups top left corner.

If you only want to resize an element in one axis, simply omit the other axis value and the omitted axis will remain as the same size.

Supported Shape Types: "ALL" This will resize the element with the action so it's output size is 1x1 inch's

resizeElement::{"width": 1, "height": 1}

If you want to dynamically move something based on the data you can use a getter string to get the value you want to use.

{
  people: [{
    name: "John Doe",
    notImportant: true,
    resize: {
      width: 1.25,
      height: 1.25
    }
  }, {
    name: "Jane Doe",
    notImportant: false,
    resize: {
      width: 2,
      height: 2
    }
  }]
}

Then the action can retrieve the value from the dataset, the below will make John Doe smaller, and Jane Doe larger.

repeatElement::{"value": "people"}
resizeElement::{"value": "people[].resize", "skip": "people[].notImportant"}

Action Input Params

ParamTypeOptionalDefaultDescription
valuestringtrue-a getter string that should point to and object with x/y values in inches
widthnumbertrue-The width value to resize in inches
heightnumbertrue-The height value to resize in inches
applyToGroupbooleantruefalseif true, the resize action will be applied to the parent group element
skipstringtruefalsea getter string
1.0.18

2 months ago

1.0.23

2 months ago

1.0.17

5 months ago

1.0.16

7 months ago

1.0.15

7 months ago

1.0.14

7 months ago

1.0.13

7 months ago

1.0.12

7 months ago

1.0.11

7 months ago

1.0.10

7 months ago

1.0.9

7 months ago

1.0.8

7 months ago

1.0.7

7 months ago

1.0.6

7 months ago

1.0.5

7 months ago

1.0.4

7 months ago

1.0.3

7 months ago

1.0.2

7 months ago

1.0.1

7 months ago

1.0.0

7 months ago