Search code examples
google-apps-scriptgoogle-appsgoogle-slidesgoogle-slides-api

Mix text and list in one shape on a Google Slide using Google Apps Script


I'm using Google Apps Script (SlidesApp) to dynamically generate a Google Slides presentation.

On one of the slides, I need to mix text and a list. I prefer to keep these both within the same textarea as it's provided as a Body placeholder element by the theme I created for this presentation.

The main issue I'm encountering now is that I can't seem to end the list.

The TextRange.appendText(text) function maintains the styling of the end of the existing text. So as soon as I add a list to my textbox, whatever I add next is also added to the list.

I had hoped I could do something like this to remove the list from the newly added text, but that results in an error.

const textRange = SlidesApp.getActivePresentation()
                    .getSlides()[0]
                    .getPlaceholders()
                    .find((ph) => ph.asShape().getPlaceholderType() === SlidesApp.PlaceholderType.BODY)
                    .asShape()
                    .getText();

const head1 = textRange.appendText("HEADER A\n");
head1.getTextStyle()
     .setBold(true)
     .setForegroundColor(SlidesApp.ThemeColorType.ACCENT1)
     .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);

const list1 = textRange.appendText(["item 1", "item 2", "item 3"].join('\n'));
list1.getTextStyle()
     .setBold(false)
     .setForegroundColor(SlidesApp.ThemeColorType.DARK1)
     .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
list1.getListStyle()
     .applyListPreset(SlidesApp.ListPreset.DISC_CIRCLE_SQUARE);

textRange.appendText("\n\n");

const head2 = textRange.appendText("HEADER B\n");
head2.getTextStyle()
     .setBold(true)
     .setForegroundColor(SlidesApp.ThemeColorType.ACCENT1)
     .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
head2.getListStyle()
     .applyListPreset(null); // <== THIS RESULTS IN AN ERROR

The end result I'd like to achieve is

HEADER A

* item 1
* item 2
* item 3

HEADER B

* item x
* item y
* item z

Another reason I'd like to avoid working with 2 or 4 placeholders is that the list size is dynamic and I don't necessarily want to have big gaps between the first list and the 2nd header.

Having to work with 2 placeholders of the same type will also add extra complexity I prefer not to get involved with.
For example, you cannot assume the 1st Body placeholder you encounter is the top one.


Solution

  • After some trial-and-error I figured out I should do the list styling last.

    So now I have something like this, and it works perfect!

    const textRange = SlidesApp.getActivePresentation()
                        .getSlides()[0]
                        .getPlaceholders()
                        .find((ph) => ph.asShape().getPlaceholderType() === SlidesApp.PlaceholderType.BODY)
                        .asShape()
                        .getText();
    
    const head1 = textRange.appendText("HEADER A\n");
    head1.getTextStyle()
         .setBold(true)
         .setForegroundColor(SlidesApp.ThemeColorType.ACCENT1)
         .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
    
    const list1 = textRange.appendText(["item 1", "item 2", "item 3"].join('\n'));
    list1.getTextStyle()
         .setBold(false)
         .setForegroundColor(SlidesApp.ThemeColorType.DARK1)
         .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
    
    textRange.appendText("\n\n");
    
    const head2 = textRange.appendText("HEADER B\n");
    head2.getTextStyle()
         .setBold(true)
         .setForegroundColor(SlidesApp.ThemeColorType.ACCENT1)
         .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
    
    const list2 = textRange.appendText(["item x", "item y", "item z"].join('\n'));
    list2.getTextStyle()
         .setBold(false)
         .setForegroundColor(SlidesApp.ThemeColorType.DARK1)
         .setBackgroundColor(SlidesApp.ThemeColorType.LIGHT1);
    
    
    // do styling as list LAST!
    list1.getListStyle()
         .applyListPreset(SlidesApp.ListPreset.DISC_CIRCLE_SQUARE);
    list2.getListStyle()
         .applyListPreset(SlidesApp.ListPreset.DISC_CIRCLE_SQUARE);