Digital ACT-191 ONEAPP-6830 story: added new Carousel Scrollbar page and change log file

This commit is contained in:
vasavk 2024-03-19 17:49:01 +05:30
parent 4abcc313cf
commit eb3d6b7a34
3 changed files with 425 additions and 0 deletions

View File

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; };
1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */; };
186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; };
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; };
18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; };
@ -178,6 +180,8 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = "<group>"; };
1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = "<group>"; };
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = "<group>"; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = "<group>"; };
18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = "<group>"; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = "<group>"; };
18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = "<group>"; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = "<group>"; };
@ -362,6 +366,15 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = {
isa = PBXGroup;
children = (
1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */,
1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */,
);
path = CarouselScrollbar;
sourceTree = "<group>";
};
445BA07629C07ABA0036A7C5 /* Notification */ = { 445BA07629C07ABA0036A7C5 /* Notification */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -496,6 +509,7 @@
EA4DB2FE28DCBC1900103EE3 /* Badge */, EA4DB2FE28DCBC1900103EE3 /* Badge */,
EAD062AE2A3B87210015965D /* BadgeIndicator */, EAD062AE2A3B87210015965D /* BadgeIndicator */,
EA0FC2BE2912D18200DF80B4 /* Buttons */, EA0FC2BE2912D18200DF80B4 /* Buttons */,
1808BEBA2BA41B1D00129230 /* CarouselScrollbar */,
EAF7F092289985E200B287F5 /* Checkbox */, EAF7F092289985E200B287F5 /* Checkbox */,
EA985BF3296C609E00F2FF2E /* Icon */, EA985BF3296C609E00F2FF2E /* Icon */,
EA3362412892EF700071C351 /* Label */, EA3362412892EF700071C351 /* Label */,
@ -968,6 +982,7 @@
EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */, EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */,
EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */, EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */,
EAEEEC9A2B1F8E4400531FC2 /* TextLinkChangeLog.txt in Resources */, EAEEEC9A2B1F8E4400531FC2 /* TextLinkChangeLog.txt in Resources */,
1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */,
EAEEECAF2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt in Resources */, EAEEECAF2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt in Resources */,
EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */, EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */,
EAEEEC9E2B1F8F7700531FC2 /* ButtonGroupChangeLog.txt in Resources */, EAEEEC9E2B1F8F7700531FC2 /* ButtonGroupChangeLog.txt in Resources */,
@ -1082,6 +1097,7 @@
EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */,
EA513A952A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift in Sources */, EA513A952A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift in Sources */,
44604AD729CE196600E62B51 /* Line.swift in Sources */, 44604AD729CE196600E62B51 /* Line.swift in Sources */,
1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */,
EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */, EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */,
EA5E3058295105A40082B959 /* Tilelet.swift in Sources */, EA5E3058295105A40082B959 /* Tilelet.swift in Sources */,
EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */, EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */,

View File

@ -0,0 +1,381 @@
//
// CarouselScrollbar.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 12/03/24.
//
import Foundation
import UIKit
import VDSColorTokens
import VDSFormControlsTokens
import Combine
/// A carousel scrollbar is a control that allows to navigate between items in a carousel.
/// It's also a status indicator that conveys the relative amount of content in a carousel and a location within it.
@objc(VDSCarouselScrollbar)
open class CarouselScrollbar: View {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: .zero)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
/// TO Do: take three view containers with fixed sizes - track view, active track view, thumb view.
/// add pan gesture to background container
/// set variable width to thumb container based on inputs. add pan gesture to track.
/// Not applicable to touch or keyboard devices - set width to 8 for three views if is in active state
/// scrollbar track only changes color on the side of the scrollbar that was interacted with. Track color changes will apply to whatever state the scrollbar is in before becoming active. if hovered via mouse, the scrollbar height will be increased when active. For touch or keyboard devices, the scrollbar will be the same size as the default state when the active track color change is applied.
/// <CarouselScrollbar
// surface="light"
// numberOfSlides={12}
// layout="3UP"
// position={0}
// />
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
// Sizes are from InVision design specs.
internal var containerSize: CGSize { CGSize(width: 45, height: 44) }
// TO DO: to be remove
// open var numberOfSlides: Int? { didSet { setNeedsUpdate() } }
/// Used to set total number of slides within carousel
open var numberOfSlides: Int? {
get { return _numberOfSlides }
set {
if let newValue {
_numberOfSlides = newValue
} else {
_numberOfSlides = 1
}
setThumbWidth()
setNeedsUpdate()
}
}
/// The amount of slides visible in the carousel container at one time.
open var selectedLayout: Layout? {
get { return _selectedLayout }
set {
if let newValue {
_selectedLayout = newValue
} else {
_selectedLayout = .oneUP
}
setThumbWidth()
setNeedsUpdate()
}
}
/// Enum used to describe the amount of slides visible in the carousel container at one time.
public enum Layout: String, CaseIterable {
case oneUP = "1UP"
case twoUP = "2UP"
case threeUP = "3UP"
case fourUP = "4UP"
case fiveUP = "5UP"
case sixUP = "6UP"
case eightUP = "8UP"
var value: Int {
switch self {
case .oneUP:
1
case .twoUP:
2
case .threeUP:
3
case .fourUP:
4
case .fiveUP:
5
case .sixUP:
6
case .eightUP:
8
}
}
}
/// Used to set the position of the thumb(scrubber). This is used when the carousel container changes position, it will align the position of thumb(scrubber).
open var position: Int? { didSet { setNeedsUpdate() } }
/// Allows a unique id to be passed into the thumb and track of the thumb(scrubber).
open var scrubberId: Int? { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private let trackViewWidth = 96
private let trackViewHeight: CGFloat = 4
private let minThumbWidth: Float = 16.0
private var thumbWidth: Float = 16.0
private var actualThumbWidth: Float = 0.0
private var _selectedLayout: Layout = .oneUP
private var _numberOfSlides: Int? = 1
internal var cornerRadius: CGFloat = 4.0
internal var leftOverlayWidth: NSLayoutConstraint?
internal var rightOverlayXPos: NSLayoutConstraint?
internal var rightOverlayTrailingConstraint: NSLayoutConstraint?
internal var heightConstraint: NSLayoutConstraint?
internal var containerView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
/// Track view with fixed width
internal var trackView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
// $0.tag = 1
}
}()
/// Left Active Track overlay with variable width
internal var leftActiveOverlay: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.tag = 2
}
}()
/// Right Active Track overlay with variable width
internal var rightActiveOverlay: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.tag = 3
}
}()
/// Thumb view with variable width
internal var thumbView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
// $0.tag = 4
}
}()
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
}
open override func setup() {
super.setup()
accessibilityLabel = "Carousel Scrollbar"
//create the wrapping view
heightConstraint = self.heightAnchor.constraint(equalToConstant: containerSize.height)
heightConstraint?.priority = .defaultHigh
heightConstraint?.isActive = true
//Trackview
trackView.frame = CGRectMake(20, 20, CGFloat(trackViewWidth), trackViewHeight)
// trackView.backgroundColor = .blue
// trackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
// trackView.isUserInteractionEnabled = true
trackView.layer.cornerRadius = cornerRadius
addSubview(trackView)
///Left active overlay width variable depends on thumb position
leftActiveOverlay.frame = CGRectMake(trackView.frame.origin.x, 20, CGFloat(trackViewWidth), trackViewHeight)
// leftActiveOverlay.backgroundColor = .yellow
leftActiveOverlay.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
leftActiveOverlay.isUserInteractionEnabled = true
leftActiveOverlay.layer.cornerRadius = cornerRadius
addSubview(leftActiveOverlay)
///Right active overlay width variable depends on thumbView position & trailing positon of trackView
rightActiveOverlay.frame = CGRectMake(thumbView.frame.origin.x + thumbView.frame.size.width, 20, CGFloat(trackViewWidth), trackViewHeight)
// rightActiveOverlay.backgroundColor = .orange
rightActiveOverlay.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
rightActiveOverlay.isUserInteractionEnabled = true
rightActiveOverlay.layer.cornerRadius = cornerRadius
addSubview(rightActiveOverlay)
//Thumbview
thumbView.frame = CGRectMake(20, 20, CGFloat(thumbWidth), trackViewHeight)
// thumbView.backgroundColor = .green
thumbView.layer.cornerRadius = cornerRadius
// thumbView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
// thumbView.isUserInteractionEnabled = true
thumbView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action:(#selector(self.handleGesture(_:)))))
addSubview(thumbView)
updateFrames()
}
open override func updateView() {
super.updateView()
// updateFrames()
//left active overlay origin - x, width
// leftActiveOverlay.leadingAnchor.constraint(equalTo: trackView.leadingAnchor).activate()
// leftActiveOverlay.trailingAnchor.constraint(equalTo: thumbView.leadingAnchor).activate()
//right active overlay origin - x, width, trailing position exact to trackView
// rightActiveOverlay.leadingAnchor.constraint(equalTo: thumbView.trailingAnchor).activate()
// rightActiveOverlay.trailingAnchor.constraint(equalTo: trackView.trailingAnchor).activate()
}
open override func updateAccessibility() {
super.updateAccessibility()
}
open override func reset() {
for subview in subviews {
for recognizer in subview.gestureRecognizers ?? [] {
subview.removeGestureRecognizer(recognizer)
}
}
super.reset()
}
// open override func layoutSubviews() {
// super.layoutSubviews()
// setNeedsUpdate()
// }
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
@objc func handleTap(_ gestureRecognizer: UITapGestureRecognizer) {
// handling code
let tag = gestureRecognizer.view?.tag
switch tag! {
// case 1 :
// print("select first view")
case 2 :
thumbView.frame.origin.x = thumbView.frame.origin.x - CGFloat(actualThumbWidth)
updateFrames()
// print("select second view")
case 3 :
thumbView.frame.origin.x = thumbView.frame.origin.x + CGFloat(actualThumbWidth)
updateFrames()
// print("select third view")
// case 4 :
// print("select fourth view")
default:
print("default")
}
}
@objc func handleGesture(_ sender: UIPanGestureRecognizer) {
switch sender.state {
case .began:
print("began")
break
case .changed:
print("changed")
break
case .cancelled:
print("cancelled")
break
case .ended:
print("ended")
break
default:
break
}
}
private func setThumbWidth() {
let width = Float (trackViewWidth / (numberOfSlides ?? 1) * _selectedLayout.value)
actualThumbWidth = width
thumbWidth = width < minThumbWidth ? minThumbWidth : width
thumbView.frame.size.width = CGFloat(thumbWidth)
thumbView.frame.origin.x = trackView.frame.origin.x
// print("trackViewWidth: \(trackViewWidth), numberOfSlides: \(String(describing: numberOfSlides)), tiles per set: \(_selectedLayout.value), actualThumbWidth: \(actualThumbWidth), thumbWidth: \(thumbWidth)")
//carousel position also to be update accordingly
updateFrames()
}
//Known bug: when actual width is less than minimum width
// private func updateThumbFrame() {
//
// //right active overlay origin - x, width, trailing position exact to trackView
// // adjusting thumb position if it goes beyond trackView.
// let position1 = thumbView.frame.origin.x + thumbView.frame.size.width
// let position2 = trackView.frame.origin.x + trackView.frame.size.width
// if position1 > position2 {
// thumbView.frame.origin.x = position2 - thumbView.frame.size.width
// } else if thumbView.frame.origin.x < 0 {
// thumbView.frame.origin.x = trackView.frame.origin.x
// } else {
// rightActiveOverlay.frame.origin.x = position1
// rightActiveOverlay.frame.size.width = position2 - position1
// }
// DispatchQueue.main.asyncAfter(deadline: .now() + 2 , execute: {
// //left active overlay origin - x, width
// self.updateOverlay()
// })
// }
//
// private func updateOverlay() {
// //left active overlay origin - x, width
// leftActiveOverlay.frame.size.width = thumbView.frame.origin.x - trackView.frame.origin.x
// }
//
private func updateFrames() {
//left active overlay origin - x, width
leftActiveOverlay.frame.size.width = thumbView.frame.origin.x - trackView.frame.origin.x
//right active overlay origin - x, width, trailing position exact to trackView
let position1 = thumbView.frame.origin.x + thumbView.frame.size.width
let position2 = trackView.frame.origin.x + trackView.frame.size.width
rightActiveOverlay.frame.origin.x = position1
rightActiveOverlay.frame.size.width = position2 - position1
}
}
//private func updateFrames() {
// //right active overlay origin - x, width, trailing position exact to trackView
// let position1 = thumbView.frame.origin.x + thumbView.frame.size.width
// let position2 = trackView.frame.origin.x + trackView.frame.size.width
// rightActiveOverlay.frame.origin.x = position1
// rightActiveOverlay.frame.size.width = position2 - position1
////right active overlay origin - x, width, trailing position exact to trackView
//// adjusting thumb position if it goes beyond trackView.
//let position1 = thumbView.frame.origin.x + thumbView.frame.size.width
//let position2 = trackView.frame.origin.x + trackView.frame.size.width
//if position1 > position2 {
// thumbView.frame.origin.x = position2 - thumbView.frame.size.width
//} else if thumbView.frame.origin.x < 0 {
// thumbView.frame.origin.x = trackView.frame.origin.x
//} else {
// rightActiveOverlay.frame.origin.x = position1
// rightActiveOverlay.frame.size.width = position2 - position1
//}

View File

@ -0,0 +1,28 @@
MM/DD/YYYY
----------------
07/29/22
----------------
- Initial Brand 3.0 handoff
08/10/2022
----------------
- Updated default and inverted prop to light and dark surface.
11/30/2022
----------------
- Added "(web only)" to any instance of "keyboard focus"
12/13/2022
----------------
- Replaced focus border pixel and style & spacing values with tokens.
01/09/2023
----------------
- Updated Specs to use new SPEC Templates and SPEC DOC Components.
05/19/2023
----------------
- Changed Carousel Scrubber to Carousel Scrollbar and replaced all instances of Scrubber to Scrollbar.
- Removed KF states and behaviors from States and Behaviors > Interaction Types.