Skip to content
This repository has been archived by the owner on Dec 30, 2021. It is now read-only.

Commit

Permalink
Render ImageSet elements
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalezreal committed Oct 9, 2020
1 parent e9eb14f commit 9df363d
Show file tree
Hide file tree
Showing 19 changed files with 405 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
82C107E8252F7CA60046764A /* Photos.json in Resources */ = {isa = PBXBuildFile; fileRef = 82C107E7252F7CA60046764A /* Photos.json */; };
82C107E9252F7CA60046764A /* Photos.json in Resources */ = {isa = PBXBuildFile; fileRef = 82C107E7252F7CA60046764A /* Photos.json */; };
9913B76B2514D5EC002C695C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9913B7562514D5EC002C695C /* Assets.xcassets */; };
9913B76C2514D5EC002C695C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9913B7562514D5EC002C695C /* Assets.xcassets */; };
9913B7822514D695002C695C /* ActivityUpdate.json in Resources */ = {isa = PBXBuildFile; fileRef = 9913B7812514D695002C695C /* ActivityUpdate.json */; };
Expand Down Expand Up @@ -62,6 +64,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
82C107E7252F7CA60046764A /* Photos.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Photos.json; sourceTree = "<group>"; };
9913B7562514D5EC002C695C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
9913B75B2514D5EC002C695C /* AdaptiveCardVisualizer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AdaptiveCardVisualizer.app; sourceTree = BUILT_PRODUCTS_DIR; };
9913B75E2514D5EC002C695C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -186,6 +189,7 @@
9913B7D92514F18F002C695C /* WeatherCompact.json */,
9913B7DE2514F458002C695C /* WeatherLarge.json */,
9913B7ED2514F6A8002C695C /* FlightDetails.json */,
82C107E7252F7CA60046764A /* Photos.json */,
);
path = Cards;
sourceTree = "<group>";
Expand Down Expand Up @@ -311,6 +315,7 @@
9913B78A2514D6B5002C695C /* prism.js in Resources */,
9913B7D52514E065002C695C /* StockUpdate.json in Resources */,
9913B7822514D695002C695C /* ActivityUpdate.json in Resources */,
82C107E8252F7CA60046764A /* Photos.json in Resources */,
9913B7DF2514F458002C695C /* WeatherLarge.json in Resources */,
9913B78E2514D6B5002C695C /* okaidia.css in Resources */,
9913B7CB2514DB63002C695C /* FlightUpdate.json in Resources */,
Expand All @@ -331,6 +336,7 @@
9913B78B2514D6B5002C695C /* prism.js in Resources */,
9913B7D62514E065002C695C /* StockUpdate.json in Resources */,
9913B7832514D695002C695C /* ActivityUpdate.json in Resources */,
82C107E9252F7CA60046764A /* Photos.json in Resources */,
9913B7E02514F458002C695C /* WeatherLarge.json in Resources */,
9913B78F2514D6B5002C695C /* okaidia.css in Resources */,
9913B7CC2514DB63002C695C /* FlightUpdate.json in Resources */,
Expand Down
69 changes: 69 additions & 0 deletions Examples/AdaptiveCardVisualizer/Cards/Photos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Here are some cool photos",
"size": "large"
},
{
"type": "TextBlock",
"text": "from picsum.photos",
"size": "medium",
"weight": "lighter",
"spacing": "none"
},
{
"type": "ImageSet",
"images": [
{
"type": "Image",
"url": "https://picsum.photos/200/200?image=100",
"altText": "White beach panorama"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=200",
"altText": "Cow on a grassy field"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=301",
"altText": "Orange leaves on the sidewalk of a park"
},
{
"type": "Image",
"url": "https://picsum.photos/200/200?image=400",
"altText": "Green leaves"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=500",
"altText": "Top of a sky scrapper"
},
{
"type": "Image",
"url": "https://picsum.photos/200/200?image=600",
"altText": "Foggy forest"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=700",
"altText": "Picure of the blue ocean"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=800",
"altText": "Crowded train station"
},
{
"type": "Image",
"url": "https://picsum.photos/300/200?image=900",
"altText": "Sunset under a dock"
}
]
}
]
}
7 changes: 7 additions & 0 deletions Examples/AdaptiveCardVisualizer/Shared/SampleCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ extension SampleCard {
resourceName: "FlightDetails.json"
)

static let photos = SampleCard(
id: "Photos",
title: "Photos",
resourceName: "Photos.json"
)

static let all: [SampleCard] = [
.gitHubRepository,
.activityUpdate,
Expand All @@ -71,5 +77,6 @@ extension SampleCard {
.weatherCompact,
.weatherLarge,
.flightDetails,
.photos,
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extension CardElement: FeatureAdaptable {
}

switch self {
case .textBlock, .image, .richTextBlock, .factSet, .custom:
case .textBlock, .image, .richTextBlock, .factSet, .imageSet, .custom:
return self
case var .actionSet(actionSet):
var elementShouldFallback = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ private extension CardElement {
return [container.id] + container.items.flatMap(\.identifiers)
case let .columnSet(columnSet):
return [columnSet.id] + columnSet.columns.flatMap(\.identifiers)
case let .imageSet(imageSet):
return [imageSet.id] + imageSet.images.map(\.id)
case .unknown:
return []
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ private extension CardElement {
[container.backgroundImage?.url].compactMap { $0 }
case let .columnSet(columnSet):
return columnSet.columns.flatMap(\.imageURLs)
case let .imageSet(imageSet):
return imageSet.images.map(\.url)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ extension CardElement: Toggleable {
} else {
return false
}
case var .imageSet(imageSet):
if imageSet.images.toggleVisibility(of: target) {
self = .imageSet(imageSet)
return true
} else {
return false
}
case .textBlock, .image, .richTextBlock, .actionSet, .factSet, .custom, .unknown:
return false
}
Expand Down Expand Up @@ -52,6 +59,9 @@ private extension CardElement {
case var .factSet(element):
element.toggleVisibility(isVisible)
self = .factSet(element)
case var .imageSet(element):
element.toggleVisibility(isVisible)
self = .imageSet(element)
case var .custom(element):
element.toggleVisibility(isVisible)
self = .custom(element)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

extension Image: Toggleable {
mutating func toggleVisibility(of target: TargetElement) -> Bool {
guard id == target.elementId else { return false }

if let isVisible = target.isVisible {
self.isVisible = isVisible
} else {
isVisible.toggle()
}

return true
}
}
57 changes: 57 additions & 0 deletions Sources/AdaptiveCardUI/Model/Containers/ImageSet.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import DefaultCodable
import Foundation

public enum MediumImageSize: DefaultValueProvider {
public static var `default` = ImageSize.medium
}

/// The `ImageSet` element displays a collection of images.
public struct ImageSet: CardElementProtocol, Codable, Equatable {
/// A unique identifier associated with the item.
@ItemIdentifier public var id: String

/// If `false`, this item will be removed from the visual tree.
@Default<True> public var isVisible: Bool

/// When `true`, draw a separating line at the top of the element.
@Default<False> public var separator: Bool

/// Controls the amount of spacing between this element and the preceding element.
@Default<FirstCase> public var spacing: Spacing

/// Describes what to do when an unknown element is encountered or the requires of this or any children can’t be met.
@Default<Fallback.None> public var fallback: Fallback<CardElement>

/// A series of key/value pairs indicating features that the item requires with corresponding minimum version.
/// When a feature is missing or of insufficient version, fallback is triggered.
@Default<EmptyDictionary> public var requires: [String: SemanticVersion]

/// The array of `Image` elements to show.
public var images: [Image]

/// Controls the approximate size of each image.
///
/// `auto` and `stretch` are not supported for ImageSet. The size will
/// default to `medium` if those values are set.
@Default<MediumImageSize> public var imageSize: ImageSize

public init(
id: String = "",
isVisible: Bool = true,
separator: Bool = false,
spacing: Spacing = .default,
fallback: Fallback<CardElement> = .none,
requires: [String: SemanticVersion] = [:],
images: [Image],
imageSize: ImageSize = .medium
) {
self.id = id
self.isVisible = isVisible
self.separator = separator
self.spacing = spacing
self.fallback = fallback
self.requires = requires
self.images = images
self.imageSize = imageSize
}
}
12 changes: 12 additions & 0 deletions Sources/AdaptiveCardUI/Model/Elements/CardElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public indirect enum CardElement {
/// A series of facts (i.e. name / value pairs) in a tabular form.
case factSet(FactSet)

/// A collection of images.
case imageSet(ImageSet)

/// A custom card element.
case custom(CustomCardElement)

Expand Down Expand Up @@ -56,6 +59,8 @@ extension CardElement: Codable {
self = .columnSet(try ColumnSet(from: decoder))
case String(describing: FactSet.self):
self = .factSet(try FactSet(from: decoder))
case String(describing: ImageSet.self):
self = .imageSet(try ImageSet(from: decoder))
default:
if let decodeCustomCardElement = Self.customCardElementDecoders[type] {
self = .custom(try decodeCustomCardElement(decoder))
Expand Down Expand Up @@ -90,6 +95,9 @@ extension CardElement: Codable {
case let .factSet(element):
try container.encode(String(describing: FactSet.self), forKey: .type)
try element.encode(to: encoder)
case let .imageSet(element):
try container.encode(String(describing: ImageSet.self), forKey: .type)
try element.encode(to: encoder)
case let .custom(element):
let typeName = type(of: element).typeName
guard let encodeCustomCardElement = Self.customCardElementEncoders[typeName] else {
Expand Down Expand Up @@ -126,6 +134,8 @@ public extension CardElement {
return element[keyPath: keyPath]
case let .factSet(element):
return element[keyPath: keyPath]
case let .imageSet(element):
return element[keyPath: keyPath]
case let .custom(element):
return element[keyPath: keyPath]
case let .unknown(element):
Expand Down Expand Up @@ -174,6 +184,8 @@ extension CardElement: Equatable {
return l == r
case let (.factSet(l), .factSet(r)):
return l == r
case let (.imageSet(l), .imageSet(r)):
return l == r
case let (.custom(l), .custom(r)):
let typeName = type(of: l).typeName
return Self.customCardElementIsEqual[typeName]?(l, r) ?? false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
private extension CardElement {
var bleed: Bool {
switch self {
case .textBlock, .image, .richTextBlock, .actionSet, .factSet, .custom:
case .textBlock, .image, .richTextBlock, .actionSet, .factSet, .imageSet, .custom:
return false
case let .container(element):
return element.bleed
Expand Down
2 changes: 2 additions & 0 deletions Sources/AdaptiveCardUI/UI/CardElement/CardElementView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
ColumnSetView(columnSet)
case let .factSet(factSet):
FactSetView(factSet)
case let .imageSet(imageSet):
ImageSetView(imageSet)
case let .custom(customCardElement):
CustomCardElementView(customCardElement)
default:
Expand Down
41 changes: 41 additions & 0 deletions Sources/AdaptiveCardUI/UI/Helpers/FlowLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#if canImport(SwiftUI)

import SwiftUI

// Adapted from https://gist.github.com/chriseidhof/3c6ea3fb2102052d1898d8ea27fbee07
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
struct FlowLayout {
private let proposedSize: CGSize
private let horizontalSpacing: CGFloat
private let verticalSpacing: CGFloat

private var position = CGPoint.zero
private var lineHeight: CGFloat = 0

var size: CGSize {
CGSize(width: proposedSize.width, height: position.y + lineHeight)
}

init(proposedSize: CGSize, horizontalSpacing: CGFloat, verticalSpacing: CGFloat) {
self.proposedSize = proposedSize
self.horizontalSpacing = horizontalSpacing
self.verticalSpacing = verticalSpacing
}

mutating func addElementWithSize(_ size: CGSize) -> CGRect {
if position.x + size.width > proposedSize.width {
position.x = 0
position.y += lineHeight + verticalSpacing
lineHeight = 0
}

let result = CGRect(origin: position, size: size)

lineHeight = max(lineHeight, size.height)
position.x += size.width + horizontalSpacing

return result
}
}

#endif
Loading

0 comments on commit 9df363d

Please sign in to comment.