Skip to content

Commit 0152d5c

Browse files
authored
[NodeJS] Add grid ImageSet style (#8845)
1 parent ad8eee4 commit 0152d5c

File tree

10 files changed

+115
-45
lines changed

10 files changed

+115
-45
lines changed

schemas/src/elements/ImageSet.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
"type": "ImageSize",
1313
"default":"medium",
1414
"description": "Controls the approximate size of each image. The physical dimensions will vary per host. Auto and stretch are not supported for ImageSet. The size will default to medium if those values are set."
15-
}
15+
},
16+
"style": {
17+
"type": "ImageSetStyle",
18+
"version": "1.6",
19+
"description": "Controls how the images in the `ImageSet` are displayed."
20+
}
1621
}
1722
}

schemas/src/enums/ImageSetStyle.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/microsoft/AdaptiveCards/6f39aedce45864ae1067ed44a5551dc973790bb5/source/nodejs/typed-schema/schema/lib/Type.json",
3+
"classType": "Enum",
4+
"description": "Controls how an `ImageSet` is displayed.",
5+
"version": "1.6",
6+
"values": [
7+
{
8+
"value": "default",
9+
"description": "This is the default style for `ImageSet`. Images are displayed in their original aspect ratio."
10+
},
11+
{
12+
"value": "stacked",
13+
"description": "Display images in this `ImageSet` stack on top of each other (similar to a stack of photos)."
14+
},
15+
{
16+
"value": "grid",
17+
"description": "Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space."
18+
}
19+
]
20+
}

schemas/src/enums/TextBlockStyle.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
"values": [
77
{
88
"value": "default",
9-
"description": "This is the default style which provide no special styling or behavior."
9+
"description": "This is the default style which provides no special styling or behavior."
1010
},
1111
{
1212
"value": "heading",
13-
"description": "The TextBlock is a heading. This will apply the heading styling defaults and mark the text block as a heading for accessiblity."
13+
"description": "The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity."
1414
}
1515
]
1616
}

source/nodejs/ac-typed-schema/src/markdown/languages/de.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,11 @@
257257
"The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.": "The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.",
258258
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.",
259259
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.",
260-
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields."
260+
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.",
261+
"This is the default style which provides no special styling or behavior.": "This is the default style which provides no special styling or behavior.",
262+
"The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.": "The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.",
263+
"This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.": "This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.",
264+
"Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).": "Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).",
265+
"Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.": "Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.",
266+
"Controls how the images in the `ImageSet` are displayed.": "Controls how the images in the `ImageSet` are displayed."
261267
}

source/nodejs/ac-typed-schema/src/markdown/languages/en.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,11 @@
257257
"The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.": "The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.",
258258
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.",
259259
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.",
260-
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields."
260+
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.",
261+
"This is the default style which provides no special styling or behavior.": "This is the default style which provides no special styling or behavior.",
262+
"The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.": "The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.",
263+
"This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.": "This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.",
264+
"Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).": "Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).",
265+
"Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.": "Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.",
266+
"Controls how the images in the `ImageSet` are displayed.": "Controls how the images in the `ImageSet` are displayed."
261267
}

source/nodejs/ac-typed-schema/src/markdown/languages/sp.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,11 @@
257257
"The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.": "The number of choices to be skipped in the list of choices returned by the query. It can be ignored if the card-author does not want pagination.",
258258
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the position of the label. It can take 'inline' and 'above' values. By default, the label is placed 'above' when label position is not specified.",
259259
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Determines the width of the label in percent like 40 or a specific pixel width like '40px' when label is placed inline with the input. labelWidth would be ignored when the label is displayed above the input.",
260-
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields."
260+
"[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.": "[SUPPORTED ONLY IN JAVASCRIPT SDK] Style hint for input fields. Allows input fields to appear as read-only but when user clicks/focuses on the field, it allows them to update those fields.",
261+
"This is the default style which provides no special styling or behavior.": "This is the default style which provides no special styling or behavior.",
262+
"The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.": "The `TextBlock` is a heading. This will apply the heading styling defaults and mark the `TextBlock` as a heading for accessiblity.",
263+
"This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.": "This is the default style for `ImageSet`. Images are displayed in their original aspect ratio.",
264+
"Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).": "Display images in this `ImageSet` stack on top of each other (similar to a stack of photos).",
265+
"Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.": "Display images in a grid. They are all shown at the same size. Images are clipped as needed to fit in the available space.",
266+
"Controls how the images in the `ImageSet` are displayed.": "Controls how the images in the `ImageSet` are displayed."
261267
}

source/nodejs/adaptivecards/src/card-elements.ts

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,18 +2000,7 @@ export class Image extends CardElement {
20002000
// auto and stretch are ignored (default to medium). THis is necessary for
20012001
// ImageSet which uses a maximum image height as opposed to the cards width
20022002
// as a constraining dimension
2003-
switch (this.size) {
2004-
case Enums.Size.Small:
2005-
element.style.height = this.hostConfig.imageSizes.small + "px";
2006-
break;
2007-
case Enums.Size.Large:
2008-
element.style.height = this.hostConfig.imageSizes.large + "px";
2009-
break;
2010-
default:
2011-
element.style.height = this.hostConfig.imageSizes.medium + "px";
2012-
break;
2013-
}
2014-
2003+
element.style.height = this.hostConfig.getEffectiveImageSize(this.size) + "px";
20152004
element.style.maxHeight = this.maxHeight + "px";
20162005
} else {
20172006
switch (this.size) {
@@ -2021,14 +2010,8 @@ export class Image extends CardElement {
20212010
case Enums.Size.Auto:
20222011
element.style.maxWidth = "100%";
20232012
break;
2024-
case Enums.Size.Small:
2025-
element.style.width = this.hostConfig.imageSizes.small + "px";
2026-
break;
2027-
case Enums.Size.Large:
2028-
element.style.width = this.hostConfig.imageSizes.large + "px";
2029-
break;
2030-
case Enums.Size.Medium:
2031-
element.style.width = this.hostConfig.imageSizes.medium + "px";
2013+
default:
2014+
element.style.width = this.hostConfig.getEffectiveImageSize(this.size) + "px";
20322015
break;
20332016
}
20342017

@@ -2518,38 +2501,64 @@ export class ImageSet extends CardElementContainer {
25182501
let element: HTMLElement | undefined = undefined;
25192502

25202503
if (this._images.length > 0) {
2504+
const imageSetIsGrid = (this.presentationStyle === Enums.ImageSetPresentationStyle.Grid);
2505+
25212506
element = document.createElement("div");
25222507
element.style.display = "flex";
25232508
element.style.flexWrap = "wrap";
25242509
element.classList.add(this.hostConfig.makeCssClassName("ac-imageSet"));
2510+
element.classList.toggle(this.hostConfig.makeCssClassName("ac-imageSetStyle-grid"), imageSetIsGrid);
2511+
element.style.gap = "5px";
2512+
2513+
let renderImageSize : Enums.Size;
2514+
switch (this.imageSize) {
2515+
case Enums.ImageSize.Small:
2516+
renderImageSize = Enums.Size.Small;
2517+
break;
2518+
case Enums.ImageSize.Large:
2519+
renderImageSize = Enums.Size.Large;
2520+
break;
2521+
default:
2522+
renderImageSize = Enums.Size.Medium;
2523+
break;
2524+
}
2525+
2526+
const effectiveImageSize = this.hostConfig.getEffectiveImageSize(renderImageSize);
25252527

25262528
for (const image of this._images) {
2527-
switch (this.imageSize) {
2528-
case Enums.ImageSize.Small:
2529-
image.size = Enums.Size.Small;
2530-
break;
2531-
case Enums.ImageSize.Large:
2532-
image.size = Enums.Size.Large;
2533-
break;
2534-
default:
2535-
image.size = Enums.Size.Medium;
2536-
break;
2529+
image.maxHeight = this.hostConfig.imageSet.maxImageHeight;
2530+
2531+
if (imageSetIsGrid) {
2532+
image.pixelWidth = effectiveImageSize;
2533+
} else {
2534+
image.size = renderImageSize;
25372535
}
25382536

2539-
image.maxHeight = this.hostConfig.imageSet.maxImageHeight;
2537+
const imageContainer = image.render();
25402538

2541-
const renderedImage = image.render();
2539+
if (imageContainer) {
2540+
imageContainer.style.display = "inline-flex";
2541+
imageContainer.style.margin = "0px";
25422542

2543-
if (renderedImage) {
2544-
renderedImage.style.display = "inline-flex";
2545-
renderedImage.style.margin = "0px";
2546-
if (this.presentationStyle == Enums.ImageSetPresentationStyle.Default) {
2547-
renderedImage.style.marginRight = "10px";
2543+
if (imageSetIsGrid) {
2544+
imageContainer.style.flexBasis = effectiveImageSize + "px";
2545+
imageContainer.style.height = effectiveImageSize + "px";
2546+
imageContainer.style.flexGrow = "0";
2547+
imageContainer.style.flexShrink = "0";
2548+
2549+
const renderedImageStyle = image.renderedImageElement?.style;
2550+
if (renderedImageStyle) {
2551+
renderedImageStyle.width = "100%";
2552+
renderedImageStyle.height = "100%";
2553+
renderedImageStyle.objectFit = "cover";
2554+
renderedImageStyle.verticalAlign = "middle";
2555+
}
25482556
}
25492557

2550-
Utils.appendChild(element, renderedImage);
2558+
Utils.appendChild(element, imageContainer);
25512559
}
25522560
}
2561+
25532562
if (this.presentationStyle == Enums.ImageSetPresentationStyle.Stacked) {
25542563
this.applyStackedPresentationStyle();
25552564
}

source/nodejs/adaptivecards/src/enums.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export enum ImageSize {
5656

5757
export enum ImageSetPresentationStyle {
5858
Default,
59-
Stacked
59+
Stacked,
60+
Grid
6061
}
6162

6263
export enum SizeUnit {

source/nodejs/adaptivecards/src/host-config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,20 @@ export class HostConfig {
839839
}
840840
}
841841

842+
getEffectiveImageSize(imageSize: Enums.ImageSize | Enums.Size): number {
843+
switch (imageSize) {
844+
case Enums.Size.Small:
845+
return this.imageSizes.small;
846+
847+
case Enums.Size.Large:
848+
return this.imageSizes.large;
849+
850+
case Enums.Size.Medium:
851+
default:
852+
return this.imageSizes.medium;
853+
}
854+
}
855+
842856
getEffectiveSpacing(spacing: Enums.Spacing): number {
843857
switch (spacing) {
844858
case Enums.Spacing.Small:

source/nodejs/adaptivecards/src/schema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
3+
import { ImageSetPresentationStyle } from "./enums";
4+
35
export type Size = "auto" | "stretch" | "small" | "medium" | "large";
46
export type TextSize = "small" | "default" | "medium" | "large" | "extraLarge";
57
export type HorizontalAlignment = "left" | "center" | "right";
@@ -110,6 +112,7 @@ export interface IImageSet extends ICardElement {
110112
type: "ImageSet";
111113
images: IImage[];
112114
size?: Size;
115+
style?: ImageSetPresentationStyle;
113116
}
114117

115118
export interface IInput extends ICardElement {

0 commit comments

Comments
 (0)