Digital ACT-191 ONEAPP-7013 story: removing width prop, and passing selectedGroupIndex through onChange
This commit is contained in:
parent
928db0f1fc
commit
4ae07b6402
@ -81,31 +81,6 @@ open class Carousel: View {
|
|||||||
/// views used to render view in the carousel slots.
|
/// views used to render view in the carousel slots.
|
||||||
open var views: [UIView] = [] { didSet { setNeedsUpdate() } }
|
open var views: [UIView] = [] { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered.
|
|
||||||
open var width: Width? {
|
|
||||||
get { _width }
|
|
||||||
set {
|
|
||||||
if let newValue {
|
|
||||||
switch newValue {
|
|
||||||
case .percentage(let percentage):
|
|
||||||
if percentage >= 10 && percentage <= 100.0 {
|
|
||||||
let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100)
|
|
||||||
if expectedWidth > carouselScrollbarMinWidth {
|
|
||||||
_width = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .value(let value):
|
|
||||||
if value > carouselScrollbarMinWidth {
|
|
||||||
_width = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_width = nil
|
|
||||||
}
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile.
|
/// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile.
|
||||||
open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } }
|
open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
@ -117,7 +92,7 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A callback when moving the carousel. Returns initial visible slide's index in the carousel.
|
/// A callback when moving the carousel. Returns selectedGroupIndex.
|
||||||
open var onChange: ((Int) -> Void)? {
|
open var onChange: ((Int) -> Void)? {
|
||||||
get { nil }
|
get { nil }
|
||||||
set {
|
set {
|
||||||
@ -150,12 +125,6 @@ open class Carousel: View {
|
|||||||
/// If provided, will set the alignment for slot content when the slots has different heights.
|
/// If provided, will set the alignment for slot content when the slots has different heights.
|
||||||
open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } }
|
open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Render item style. If provided, the slot gets the background, width, height, border-radius.
|
|
||||||
open var renderItemStyle: CarouselRenderItemStyle? = nil { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
// /// Render item. It passes a data array object and expects the styled component to apply in return.
|
|
||||||
// open var renderItem: CarouselSlotItemModel? = nil { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -207,15 +176,10 @@ open class Carousel: View {
|
|||||||
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
|
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A publisher for when the scrubber position changes. Passes parameters (position).
|
/// A publisher for when moving the carousel. Passes parameters selectedGroupIndex (position).
|
||||||
open var onChangePublisher = PassthroughSubject<Int, Never>()
|
open var onChangePublisher = PassthroughSubject<Int, Never>()
|
||||||
private var onChangeCancellable: AnyCancellable?
|
private var onChangeCancellable: AnyCancellable?
|
||||||
|
|
||||||
/// A publisher for when the carousel moves. Passes parameters (data).
|
|
||||||
open var onScrollPublisher = PassthroughSubject<Array<Any>, Never>()
|
|
||||||
private var onScrollCancellable: AnyCancellable?
|
|
||||||
private var _width: Width? = nil
|
|
||||||
private var selectedGroupIndex: Int? = nil
|
|
||||||
private var containerStackHeightConstraint: NSLayoutConstraint?
|
private var containerStackHeightConstraint: NSLayoutConstraint?
|
||||||
private var containerViewHeightConstraint: NSLayoutConstraint?
|
private var containerViewHeightConstraint: NSLayoutConstraint?
|
||||||
private var prevButtonLeadingConstraint: NSLayoutConstraint?
|
private var prevButtonLeadingConstraint: NSLayoutConstraint?
|
||||||
@ -225,7 +189,7 @@ open class Carousel: View {
|
|||||||
// The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile.
|
// The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile.
|
||||||
let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X
|
let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X
|
||||||
|
|
||||||
var slotHeight = 50.0
|
var slotDefaultHeight = 50.0
|
||||||
var peekMinimum = 24.0
|
var peekMinimum = 24.0
|
||||||
var minimumSlotWidth = 0.0
|
var minimumSlotWidth = 0.0
|
||||||
var carouselScrollbarMinWidth = 96.0
|
var carouselScrollbarMinWidth = 96.0
|
||||||
@ -248,13 +212,10 @@ open class Carousel: View {
|
|||||||
containerView
|
containerView
|
||||||
.pinTop()
|
.pinTop()
|
||||||
.pinBottom()
|
.pinBottom()
|
||||||
.pinLeadingGreaterThanOrEqualTo()
|
.pinLeading()
|
||||||
.pinTrailing()
|
.pinTrailing()
|
||||||
.heightGreaterThanEqualTo(containerSize.height)
|
.heightGreaterThanEqualTo(containerSize.height)
|
||||||
|
|
||||||
containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate()
|
containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate()
|
||||||
containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 0)
|
|
||||||
containerLeadingConstraint?.activate()
|
|
||||||
|
|
||||||
// Add content stackview
|
// Add content stackview
|
||||||
containerView.addSubview(contentStackView)
|
containerView.addSubview(contentStackView)
|
||||||
@ -293,23 +254,6 @@ open class Carousel: View {
|
|||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
|
|
||||||
if containerView.frame.size.width > 0 {
|
|
||||||
if let width {
|
|
||||||
containerLeadingConstraint?.deactivate()
|
|
||||||
switch width {
|
|
||||||
case .value(let value):
|
|
||||||
var expectedWidth = value
|
|
||||||
let fullWidth = safeAreaLayoutGuide.layoutFrame.size.width
|
|
||||||
expectedWidth = expectedWidth > fullWidth ? fullWidth : expectedWidth
|
|
||||||
containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth
|
|
||||||
case .percentage(let percentage):
|
|
||||||
let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100)
|
|
||||||
containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth
|
|
||||||
}
|
|
||||||
containerLeadingConstraint?.activate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
carouselScrollBar.numberOfSlides = views.count
|
carouselScrollBar.numberOfSlides = views.count
|
||||||
carouselScrollBar.layout = layout
|
carouselScrollBar.layout = layout
|
||||||
if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) {
|
if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) {
|
||||||
@ -347,7 +291,6 @@ open class Carousel: View {
|
|||||||
paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X
|
paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X
|
||||||
gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX
|
gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX
|
||||||
peek = .standard
|
peek = .standard
|
||||||
width = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -412,17 +355,16 @@ open class Carousel: View {
|
|||||||
return estItemSize.height
|
return estItemSize.height
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchCarouselHeight() {
|
private func fetchCarouselHeight() -> CGFloat {
|
||||||
|
var height = slotDefaultHeight
|
||||||
if views.count > 0 {
|
if views.count > 0 {
|
||||||
var height = slotHeight
|
|
||||||
for x in 0...views.count - 1 {
|
for x in 0...views.count - 1 {
|
||||||
// Add received component
|
let item : CarouselSlotItemModel = .init(component: views[x])
|
||||||
let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x])
|
let estHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
||||||
slotHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
height = estHeight > height ? estHeight : height
|
||||||
height = slotHeight > height ? slotHeight : height
|
|
||||||
}
|
}
|
||||||
slotHeight = height
|
|
||||||
}
|
}
|
||||||
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add carousel slots and load data if any
|
// Add carousel slots and load data if any
|
||||||
@ -431,6 +373,7 @@ open class Carousel: View {
|
|||||||
if containerView.frame.size.width > 0 {
|
if containerView.frame.size.width > 0 {
|
||||||
containerViewHeightConstraint?.isActive = false
|
containerViewHeightConstraint?.isActive = false
|
||||||
containerStackHeightConstraint?.isActive = false
|
containerStackHeightConstraint?.isActive = false
|
||||||
|
let slotHeight = fetchCarouselHeight()
|
||||||
|
|
||||||
// Perform a loop to iterate each subView
|
// Perform a loop to iterate each subView
|
||||||
scrollView.subviews.forEach { subView in
|
scrollView.subviews.forEach { subView in
|
||||||
@ -438,8 +381,6 @@ open class Carousel: View {
|
|||||||
subView.removeFromSuperview()
|
subView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCarouselHeight()
|
|
||||||
|
|
||||||
// Add carousel items
|
// Add carousel items
|
||||||
if views.count > 0 {
|
if views.count > 0 {
|
||||||
var xPos = 0.0
|
var xPos = 0.0
|
||||||
@ -463,7 +404,7 @@ open class Carousel: View {
|
|||||||
xPos = xPos + minimumSlotWidth + gutter.value
|
xPos = xPos + minimumSlotWidth + gutter.value
|
||||||
|
|
||||||
// Add received component
|
// Add received component
|
||||||
let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x])
|
let item : CarouselSlotItemModel = .init(component: views[x])
|
||||||
let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
||||||
|
|
||||||
// Add subview for content to Carousel Slot
|
// Add subview for content to Carousel Slot
|
||||||
@ -479,12 +420,15 @@ open class Carousel: View {
|
|||||||
contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate()
|
contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate()
|
||||||
setSlotAlignment(contentView: contentView, parentView: carouselSlot)
|
setSlotAlignment(contentView: contentView, parentView: carouselSlot)
|
||||||
} else {
|
} else {
|
||||||
// // If no slotAlignment, should use full slot
|
// If no slotAlignment, should use full slot
|
||||||
contentView.pinToSuperView()
|
contentView.pinToSuperView()
|
||||||
}
|
}
|
||||||
|
carouselSlot.backgroundColor = .clear
|
||||||
|
carouselSlot.layer.cornerRadius = 0
|
||||||
contentView.addSubview(component)
|
contentView.addSubview(component)
|
||||||
component.pinToSuperView()
|
component.pinToSuperView()
|
||||||
if var surfacedView = component as? Surfaceable {
|
if var surfacedView = component as? Surfaceable {
|
||||||
|
contentView.surface = surface
|
||||||
surfacedView.surface = surface
|
surfacedView.surface = surface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -628,9 +572,7 @@ open class Carousel: View {
|
|||||||
let yPos = scrollView.contentOffset.y
|
let yPos = scrollView.contentOffset.y
|
||||||
scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
|
scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
|
||||||
showPaginationControls()
|
showPaginationControls()
|
||||||
selectedIndex = ((position-1) * layout.value) + 1
|
onChangePublisher.send(position-1)
|
||||||
onChangePublisher.send(selectedIndex ?? 1)
|
|
||||||
selectedGroupIndex = position
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,22 +12,12 @@ import VDSCoreTokens
|
|||||||
/// A custom data type that holds the style and component for a slot of the 'Carousel' component.
|
/// A custom data type that holds the style and component for a slot of the 'Carousel' component.
|
||||||
public struct CarouselSlotItemModel {
|
public struct CarouselSlotItemModel {
|
||||||
|
|
||||||
/// Style props if provided any
|
|
||||||
public var style: CarouselRenderItemStyle?
|
|
||||||
|
|
||||||
public let defaultHeight: CGFloat = 50.0
|
public let defaultHeight: CGFloat = 50.0
|
||||||
|
|
||||||
/// Component to be show on Carousel slot
|
/// Component to be show on Carousel slot
|
||||||
public var component: UIView?
|
public var component: UIView?
|
||||||
|
|
||||||
public init(style: CarouselRenderItemStyle? = nil, component: UIView? = nil) {
|
public init(component: UIView? = nil) {
|
||||||
self.style = style
|
|
||||||
self.component = component
|
self.component = component
|
||||||
if let color = style?.backgroundColor {
|
|
||||||
self.component?.backgroundColor = .init(hexString: color)
|
|
||||||
}
|
|
||||||
if let borderRadius = style?.borderRadius {
|
|
||||||
self.component?.layer.cornerRadius = borderRadius
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user