diff --git a/.mapping.json b/.mapping.json index d9127b223..09964d6e0 100644 --- a/.mapping.json +++ b/.mapping.json @@ -8722,6 +8722,7 @@ "client/ios/DivKit/Extensions/DivInputExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivInputExtensions.swift", "client/ios/DivKit/Extensions/DivPagerExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivPagerExtensions.swift", "client/ios/DivKit/Extensions/DivPivotExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivPivotExtensions.swift", + "client/ios/DivKit/Extensions/DivPointExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivPointExtensions.swift", "client/ios/DivKit/Extensions/DivSelectExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivSelectExtensions.swift", "client/ios/DivKit/Extensions/DivSeparatorExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivSeparatorExtensions.swift", "client/ios/DivKit/Extensions/DivSizeExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivSizeExtensions.swift", @@ -8730,6 +8731,7 @@ "client/ios/DivKit/Extensions/DivStateExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivStateExtensions.swift", "client/ios/DivKit/Extensions/DivTabsExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivTabsExtensions.swift", "client/ios/DivKit/Extensions/DivTextExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivTextExtensions.swift", + "client/ios/DivKit/Extensions/DivTooltipExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivTooltipExtensions.swift", "client/ios/DivKit/Extensions/DivTransformExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivTransformExtensions.swift", "client/ios/DivKit/Extensions/DivVideoExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivVideoExtensions.swift", "client/ios/DivKit/Extensions/DivVisibilityActionExtensions.swift":"divkit/public/client/ios/DivKit/Extensions/DivVisibilityActionExtensions.swift", diff --git a/client/ios/DivKit/DivBlockModelingContext.swift b/client/ios/DivKit/DivBlockModelingContext.swift index 7f55233e5..6a11f952a 100644 --- a/client/ios/DivKit/DivBlockModelingContext.swift +++ b/client/ios/DivKit/DivBlockModelingContext.swift @@ -2,6 +2,7 @@ import CoreGraphics import BasePublic import BaseUIPublic +import CommonCorePublic import LayoutKit import NetworkingPublic import Serialization @@ -38,6 +39,7 @@ public struct DivBlockModelingContext { public let errorsStorage: DivErrorsStorage internal let variableTracker: DivVariableTracker? private let persistentValuesStorage: DivPersistentValuesStorage + public let tooltipViewFactory: DivTooltipViewFactory? var overridenWidth: DivOverridenSize? var overridenHeight: DivOverridenSize? @@ -80,7 +82,8 @@ public struct DivBlockModelingContext { errorsStorage: DivErrorsStorage = DivErrorsStorage(errors: []), layoutDirection: UserInterfaceLayoutDirection = .leftToRight, variableTracker: DivVariableTracker? = nil, - persistentValuesStorage: DivPersistentValuesStorage = DivPersistentValuesStorage() + persistentValuesStorage: DivPersistentValuesStorage = DivPersistentValuesStorage(), + tooltipViewFactory: DivTooltipViewFactory? = nil ) { self.cardId = cardId self.cardLogId = cardLogId @@ -105,6 +108,7 @@ public struct DivBlockModelingContext { self.layoutDirection = layoutDirection self.variableTracker = variableTracker self.persistentValuesStorage = persistentValuesStorage + self.tooltipViewFactory = tooltipViewFactory var extensionsHandlersDictionary = [String: DivExtensionHandler]() extensionHandlers.forEach { diff --git a/client/ios/DivKit/DivKitComponents.swift b/client/ios/DivKit/DivKitComponents.swift index c6da35980..9e708b93d 100644 --- a/client/ios/DivKit/DivKitComponents.swift +++ b/client/ios/DivKit/DivKitComponents.swift @@ -278,7 +278,8 @@ public final class DivKitComponents { parentScrollView: parentScrollView, layoutDirection: layoutDirection, variableTracker: variableTracker, - persistentValuesStorage: persistentValuesStorage + persistentValuesStorage: persistentValuesStorage, + tooltipViewFactory: makeTooltipViewFactory(divKitComponents: self, cardId: cardId) ) } diff --git a/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift b/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift index 6c8c92184..2870d8ef8 100644 --- a/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift +++ b/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift @@ -84,7 +84,7 @@ extension DivBase { } ), scheduler: context.scheduler, - tooltips: try makeTooltips(context: context) + tooltips: try tooltips.makeTooltips(context: context) ) .addingTransform( transform: transform.resolveRotation(expressionResolver) @@ -178,15 +178,6 @@ extension DivBase { return nil } - private func makeTooltips(context: DivBlockModelingContext) throws -> [BlockTooltip] { - try tooltips?.iterativeFlatMap { div, index in - let tooltipContext = modified(context) { - $0.parentPath = $0.parentPath + "tooltip" + index - } - return try div.makeTooltip(context: tooltipContext) - } ?? [] - } - private func applyTransitioningAnimations( to block: Block, context: DivBlockModelingContext, @@ -368,46 +359,3 @@ extension DivBorder { ) } } - -extension DivTooltip { - fileprivate func makeTooltip(context: DivBlockModelingContext) throws - -> BlockTooltip? { - let expressionResolver = context.expressionResolver - guard let position = resolvePosition(expressionResolver)?.cast() else { - return nil - } - let block = try div.value.makeBlock(context: context) - return BlockTooltip( - id: id, - block: block, - duration: Duration(milliseconds: resolveDuration(expressionResolver)), - offset: offset?.cast(with: expressionResolver) ?? .zero, - position: position - ) - } -} - -extension DivTooltip.Position { - fileprivate func cast() -> BlockTooltip.Position { - switch self { - case .left: return .left - case .topLeft: return .topLeft - case .top: return .top - case .topRight: return .topRight - case .right: return .right - case .bottomRight: return .bottomRight - case .bottom: return .bottom - case .bottomLeft: return .bottomLeft - case .center: return .center - } - } -} - -extension DivPoint { - fileprivate func cast(with expressionResolver: ExpressionResolver) -> CGPoint { - CGPoint( - x: x.resolveUnit(expressionResolver).makeScaledValue(x.resolveValue(expressionResolver) ?? 0), - y: y.resolveUnit(expressionResolver).makeScaledValue(y.resolveValue(expressionResolver) ?? 0) - ) - } -} diff --git a/client/ios/DivKit/Extensions/DivPointExtensions.swift b/client/ios/DivKit/Extensions/DivPointExtensions.swift new file mode 100644 index 000000000..7621572b5 --- /dev/null +++ b/client/ios/DivKit/Extensions/DivPointExtensions.swift @@ -0,0 +1,10 @@ +import Foundation + +extension DivPoint { + internal func cast(with expressionResolver: ExpressionResolver) -> CGPoint { + CGPoint( + x: x.resolveUnit(expressionResolver).makeScaledValue(x.resolveValue(expressionResolver) ?? 0), + y: y.resolveUnit(expressionResolver).makeScaledValue(y.resolveValue(expressionResolver) ?? 0) + ) + } +} diff --git a/client/ios/DivKit/Extensions/DivTooltipExtensions.swift b/client/ios/DivKit/Extensions/DivTooltipExtensions.swift new file mode 100644 index 000000000..53f7b4c17 --- /dev/null +++ b/client/ios/DivKit/Extensions/DivTooltipExtensions.swift @@ -0,0 +1,85 @@ +import BasePublic +import CommonCorePublic +import Foundation +import LayoutKit + +#if os(iOS) +public typealias DivTooltipViewFactory = (Div) -> VisibleBoundsTrackingView +#else +public typealias DivTooltipViewFactory = (Div) -> ViewType +#endif + +extension DivTooltip { + fileprivate func makeTooltip(context: DivBlockModelingContext) throws + -> BlockTooltip? { + let expressionResolver = context.expressionResolver + guard let position = resolvePosition(expressionResolver)?.cast() else { + return nil + } + + let block = try div.value.makeBlock(context: context) + + let tooltipViewFactory: TooltipViewFactory = + Variable { [tooltipViewFactory = context.tooltipViewFactory, div = self.div] in + guard let tooltipViewFactory = tooltipViewFactory else { return nil } + return tooltipViewFactory(div) + } + + return BlockTooltip( + id: id, + block: block, + duration: Duration(milliseconds: resolveDuration(expressionResolver)), + offset: offset?.cast(with: expressionResolver) ?? .zero, + position: position, + tooltipViewFactory: tooltipViewFactory + ) + } +} + +extension DivTooltip.Position { + fileprivate func cast() -> BlockTooltip.Position { + switch self { + case .left: return .left + case .topLeft: return .topLeft + case .top: return .top + case .topRight: return .topRight + case .right: return .right + case .bottomRight: return .bottomRight + case .bottom: return .bottom + case .bottomLeft: return .bottomLeft + case .center: return .center + } + } +} + +extension Optional where Wrapped == [DivTooltip] { + func makeTooltips( + context: DivBlockModelingContext + ) throws -> [BlockTooltip] { + try self?.iterativeFlatMap { div, index in + let tooltipContext = modified(context) { + $0.parentPath = $0.parentPath + "tooltip" + index + } + return try div.makeTooltip(context: tooltipContext) + } ?? [] + } +} + +public func makeTooltipViewFactory( + divKitComponents: DivKitComponents, + cardId: DivCardID +) -> DivTooltipViewFactory { + { (div: Div) in + let view = DivView(divKitComponents: divKitComponents) + let divData = DivData( + logId: cardId.rawValue, + states: [.init(div: div, stateId: 0)], + timers: nil, + transitionAnimationSelector: nil, + variableTriggers: nil, + variables: nil + ) + view.setSource(.init(kind: .divData(divData), cardId: cardId)) + return view + } +} diff --git a/client/ios/LayoutKit/LayoutKit/Blocks/BlockTooltip.swift b/client/ios/LayoutKit/LayoutKit/Blocks/BlockTooltip.swift index bb42ad66c..60c02c6c2 100644 --- a/client/ios/LayoutKit/LayoutKit/Blocks/BlockTooltip.swift +++ b/client/ios/LayoutKit/LayoutKit/Blocks/BlockTooltip.swift @@ -1,6 +1,11 @@ +import CommonCorePublic import CoreGraphics -import CommonCorePublic +#if os(iOS) +public typealias TooltipViewFactory = Variable +#else +public typealias TooltipViewFactory = Variable +#endif public struct BlockTooltip: Equatable { public enum Position: String, CaseIterable { @@ -20,19 +25,22 @@ public struct BlockTooltip: Equatable { public let duration: Duration public let offset: CGPoint public let position: Position + public let tooltipViewFactory: TooltipViewFactory? public init( id: String, block: Block, duration: Duration, offset: CGPoint, - position: BlockTooltip.Position + position: BlockTooltip.Position, + tooltipViewFactory: TooltipViewFactory? = nil ) { self.id = id self.block = block self.duration = duration self.offset = offset self.position = position + self.tooltipViewFactory = tooltipViewFactory } public static func ==(lhs: BlockTooltip, rhs: BlockTooltip) -> Bool { diff --git a/client/ios/LayoutKit/LayoutKit/Tooltips/TooltipManager.swift b/client/ios/LayoutKit/LayoutKit/Tooltips/TooltipManager.swift index e8b8a360f..3c41b8165 100644 --- a/client/ios/LayoutKit/LayoutKit/Tooltips/TooltipManager.swift +++ b/client/ios/LayoutKit/LayoutKit/Tooltips/TooltipManager.swift @@ -129,7 +129,11 @@ extension TooltipAnchorView { id: tooltip.id, duration: tooltip.duration, view: { - let tooltipView = tooltip.block.makeBlockView() + var tooltipView: VisibleBoundsTrackingView = tooltip.block.makeBlockView() + if let tooltipViewFactory = tooltip.tooltipViewFactory, + let view = tooltipViewFactory.value { + tooltipView = view + } let frame = tooltip.calculateFrame(targeting: bounds) tooltipView.frame = convert(frame, to: nil) return tooltipView