Files
divkit/client/ios/DivKit/Extensions/DivIndicatorExtensions.swift
2025-04-17 16:17:51 +03:00

168 lines
5.7 KiB
Swift

import CoreGraphics
import Foundation
import LayoutKit
import VGSL
extension DivIndicator: DivBlockModeling {
public func makeBlock(context: DivBlockModelingContext) throws -> Block {
let context = modifiedContextParentPath(context)
return try applyBaseProperties(
to: { try makeBaseBlock(context: context) },
context: context,
actionsHolder: nil
)
}
private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block {
guard let rectangle = shape.rectangle else {
context.addError(message: "Unsupported shape type")
return EmptyBlock()
}
let expressionResolver = context.expressionResolver
let activeRect = makeRoundedRectangle(with: activeShape, resolver: expressionResolver)
let inactiveRect = makeRoundedRectangle(with: inactiveShape, resolver: expressionResolver)
let inactiveMinimumRect = makeRoundedRectangle(
with: inactiveMinimumShape,
resolver: expressionResolver
)
let pageSize = CGSize(
width: inactiveRect?.size
.width ?? CGFloat(rectangle.itemWidth.resolveValue(expressionResolver) ?? 0),
height: inactiveRect?.size
.height ?? CGFloat(rectangle.itemHeight.resolveValue(expressionResolver) ?? 0)
)
let hightightedScaleX: CGFloat
let hightightedScaleY: CGFloat
if let activeRect, let inactiveRect {
hightightedScaleX = activeRect.size.width / inactiveRect.size.width
hightightedScaleY = activeRect.size.height / inactiveRect.size.height
} else {
let scale = CGFloat(resolveActiveItemSize(expressionResolver))
hightightedScaleX = scale
hightightedScaleY = scale
}
let disappearingScaleX: CGFloat
let disappearingScaleY: CGFloat
if let inactiveRect, let inactiveMinimumRect {
disappearingScaleX = inactiveMinimumRect.size.width / inactiveRect.size.width
disappearingScaleY = inactiveMinimumRect.size.height / inactiveRect.size.height
} else {
let scale = CGFloat(resolveMinimumItemSize(expressionResolver))
disappearingScaleX = scale
disappearingScaleY = scale
}
let pagerPath = pagerId.map {
PagerPath(
cardId: context.cardId.rawValue,
pagerId: $0
)
}
let state: PagerViewState = pagerPath.flatMap {
context.blockStateStorage.getState($0.pagerId, cardId: context.cardId)
} ?? .default
let spaceBetweenCenters = CGFloat(spaceBetweenCenters.resolveValue(expressionResolver) ?? 0)
let configuration = PageIndicatorConfiguration(
highlightedColor: activeRect?.backgroundColor ?? resolveActiveItemColor(expressionResolver),
normalColor: inactiveRect?.backgroundColor ?? resolveInactiveItemColor(expressionResolver),
highlightedBorder: BlockBorder(
color: activeRect?.borderColor ?? .clear,
width: activeRect?.borderWidth ?? 0
),
normalBorder: BlockBorder(
color: inactiveRect?.borderColor ?? .clear,
width: inactiveRect?.borderWidth ?? 0
),
highlightedHeightScale: hightightedScaleY,
highlightedWidthScale: hightightedScaleX,
disappearingHeightScale: disappearingScaleY,
disappearingWidthScale: disappearingScaleX,
pageSize: pageSize,
highlightedPageCornerRadius: activeRect?.cornerRadius,
pageCornerRadius: inactiveRect?.cornerRadius
?? CGFloat(rectangle.cornerRadius.resolveValue(expressionResolver) ?? 0),
animation: resolveAnimation(expressionResolver).asBlockAnimation,
itemPlacement: itemsPlacement?.resolve(expressionResolver)
?? .fixed(spaceBetweenCenters: spaceBetweenCenters)
)
return PageControlBlock(
layoutDirection: context.layoutDirection,
pageControlPath: context.parentPath,
pagerPath: pagerPath,
widthTrait: resolveContentWidthTrait(context),
heightTrait: resolveContentHeightTrait(context),
configuration: configuration,
state: state
)
}
private func makeRoundedRectangle(
with shape: DivRoundedRectangleShape?,
resolver: ExpressionResolver
) -> PageIndicatorConfiguration.RoundedRectangleIndicator? {
guard let shape else {
return nil
}
return PageIndicatorConfiguration.RoundedRectangleIndicator(
size: CGSize(
width: shape.itemWidth.resolveValue(resolver) ?? 0,
height: shape.itemHeight.resolveValue(resolver) ?? 0
),
cornerRadius: CGFloat(shape.cornerRadius.resolveValue(resolver) ?? 0),
backgroundColor: shape.resolveBackgroundColor(resolver) ?? .clear,
borderWidth: CGFloat(shape.stroke?.resolveWidth(resolver) ?? 0),
borderColor: shape.stroke?.resolveColor(resolver) ?? .clear
)
}
}
extension DivShape {
fileprivate var rectangle: DivRoundedRectangleShape? {
switch self {
case let .divRoundedRectangleShape(rectangle):
rectangle
case .divCircleShape:
nil
}
}
}
extension DivIndicator.Animation {
fileprivate var asBlockAnimation: PageIndicatorConfiguration.Animation {
switch self {
case .scale:
.scale
case .worm:
.worm
case .slider:
.slider
}
}
}
extension DivIndicatorItemPlacement {
fileprivate func resolve(
_ expressionResolver: ExpressionResolver
) -> PageIndicatorConfiguration.ItemPlacement {
switch self {
case let .divDefaultIndicatorItemPlacement(placement):
.fixed(
spaceBetweenCenters: CGFloat(
placement.spaceBetweenCenters.resolveValue(expressionResolver) ?? 0
)
)
case let .divStretchIndicatorItemPlacement(placement):
.stretch(
spacing: CGFloat(placement.itemSpacing.resolveValue(expressionResolver) ?? 0),
maxVisibleItems: placement.resolveMaxVisibleItems(expressionResolver)
)
}
}
}