/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.documentation.treeprops

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.getTreeProp
import com.facebook.litho.kotlin.widget.Text

// start_example
class LeafKComponent() : KComponent() {

  override fun ComponentScope.render(): Component {
    val parentLogContext = getTreeProp<LogContext>()
    return Text(text = LogContext.append(parentLogContext, "leaf").toString())
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.documentation.treeprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.TreePropProvider
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useState

// start_example
class ParentComponentTreePropAsStateKComponent() : KComponent() {

  override fun ComponentScope.render(): Component? {
    val importantHelper = useState { ImportantHelper() }

    return TreePropProvider(ImportantHelper::class.java to importantHelper) {
      Column() { child(Text(text = "ImportantHelper can be used as State in render function")) }
    }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.documentation.treeprops

import android.net.Uri
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.TreePropProvider

// start_example
class ParentKComponent(private val prefetcher: Prefetcher, private val imageUri: Uri) :
    KComponent() {

  override fun ComponentScope.render(): Component? {
    return TreePropProvider(Prefetcher::class.java to prefetcher) { ChildKComponent(imageUri) }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.documentation.treeprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.TreePropProvider
import com.facebook.litho.core.height
import com.facebook.litho.dp
import com.facebook.litho.widget.collection.LazyList

// start_example
class RootKComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    return TreePropProvider(LogContext::class.java to LogContext("root")) {
      Column() {
        child(LeafKComponent())
        child(LazyList(style = Style.height(500.dp)) { child(TopGroupKComponent()) })
      }
    }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.documentation.treeprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.TreePropProvider
import com.facebook.litho.getTreeProp

// start_example
class TopGroupKComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val parentLogContext = getTreeProp<LogContext>()
    val topGroupLogContext = LogContext.append(parentLogContext, "top")

    return TreePropProvider(LogContext::class.java to topGroupLogContext) {
      Column {
        child(LeafKComponent())
        child(BottomGroupKComponent())
      }
    }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.java.lifecycle

import android.os.Bundle
import android.view.ViewGroup
import android.widget.LinearLayout
import com.facebook.litho.AOSPLithoLifecycleProvider
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoView
import com.facebook.samples.litho.NavigatableDemoActivity
import com.facebook.samples.litho.java.lifecycle.ConsoleView.LogRunnable
import java.util.Random
import java.util.concurrent.atomic.AtomicInteger

class LifecycleDelegateActivity : NavigatableDemoActivity() {

  private var lithoView: LithoView? = null
  private var consoleView: ConsoleView? = null
  private val consoleDelegateListener: ConsoleDelegateListener =
      object : ConsoleDelegateListener() {}
  private val delegateListener: DelegateListener =
      object : DelegateListener {
        override fun onDelegateMethodCalled(
            type: Int,
            thread: Thread,
            timestamp: Long,
            id: String
        ) {
          consoleView?.post(
              LogRunnable(
                  consoleView,
                  LifecycleDelegateLog.prefix(thread, timestamp, id),
                  LifecycleDelegateLog.log(type)))
        }

        override fun setRootComponent(isSync: Boolean) {
          if (lithoView != null) {
            val random = Random()
            val root =
                LifecycleDelegateComponent.create(ComponentContext(this@LifecycleDelegateActivity))
                    .id(atomicId.getAndIncrement().toString())
                    .key(random.nextInt().toString()) // Force to reset component.
                    .delegateListener(this)
                    .consoleDelegateListener(consoleDelegateListener)
                    .build()
            if (isSync) {
              lithoView?.setComponent(root)
            } else {
              lithoView?.setComponentAsync(root)
            }
          }
        }
      }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val parent = LinearLayout(this)
    parent.orientation = LinearLayout.VERTICAL
    parent.layoutParams =
        LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)

    // start_example_lifecycleprovider
    val lifecycleProvider = AOSPLithoLifecycleProvider(this)
    val componentContext = ComponentContext(this)
    lithoView =
        LithoView.create(
            this,
            LifecycleDelegateComponent.create(componentContext)
                .id(atomicId.getAndIncrement().toString())
                .delegateListener(delegateListener)
                .consoleDelegateListener(consoleDelegateListener)
                .build(),
            lifecycleProvider /* The LithoLifecycleProvider for this LithoView */)
    // end_example_lifecycleprovider
    val params1 =
        LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
    params1.weight = 1f
    lithoView?.layoutParams = params1
    parent.addView(lithoView)
    consoleView = ConsoleView(this)
    val params2 =
        LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
    params2.weight = 1f
    consoleView?.layoutParams = params2
    parent.addView(consoleView)
    setContentView(parent)
  }

  override fun onDestroy() {
    super.onDestroy()
    lithoView?.release()
  }

  companion object {
    private val atomicId = AtomicInteger(0)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.java.lifecycle

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.Button
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoLifecycleProvider
import com.facebook.litho.LithoLifecycleProviderDelegate
import com.facebook.litho.LithoView
import com.facebook.samples.litho.R
import java.util.concurrent.atomic.AtomicInteger

class LifecycleFragment : Fragment(), View.OnClickListener {

  private var lithoView: LithoView? = null
  private var consoleView: ConsoleView? = null
  private val consoleDelegateListener = ConsoleDelegateListener()
  private val delegateListener: DelegateListener =
      object : DelegateListener {
        override fun onDelegateMethodCalled(
            type: Int,
            thread: Thread,
            timestamp: Long,
            id: String
        ) {
          val prefix = LifecycleDelegateLog.prefix(thread, timestamp, id)
          val logRunnable =
              ConsoleView.LogRunnable(consoleView, prefix, LifecycleDelegateLog.log(type))
          consoleView?.post(logRunnable)
        }

        override fun setRootComponent(isSync: Boolean) = Unit
      }

  // start_example_lifecycleprovider
  private val delegate: LithoLifecycleProviderDelegate = LithoLifecycleProviderDelegate()

  override fun onClick(view: View) {

    // Replaces the current fragment with a new fragment
    replaceFragment()

    // inform the LithoView
    delegate.moveToLifecycle(LithoLifecycleProvider.LithoLifecycle.HINT_VISIBLE)
  }

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    val parent =
        inflater.inflate(R.layout.activity_fragment_transactions_lifecycle, container, false)
            as ViewGroup
    val c = ComponentContext(requireContext())
    lithoView =
        LithoView.create(
            c,
            getComponent(c),
            delegate /* The LithoLifecycleProvider delegate for this LithoView */)

    // end_example_lifecycleprovider
    val layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
    layoutParams.weight = 1f
    lithoView?.layoutParams = layoutParams
    consoleView = ConsoleView(requireContext())
    consoleView?.layoutParams = layoutParams
    parent.addView(consoleView)
    return parent
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val fragmentButton = view.findViewById<Button>(R.id.new_fragment_button)
    fragmentButton.text = "New Fragment"
    fragmentButton.setOnClickListener(this)
    val fragmentLithoView = view.findViewById<ViewGroup>(R.id.fragment_litho_view)
    fragmentLithoView.addView(lithoView)
  }

  private fun getComponent(c: ComponentContext): Component =
      Column.create(c)
          .child(
              LifecycleDelegateComponent.create(c)
                  .id(atomicId.getAndIncrement().toString())
                  .delegateListener(delegateListener)
                  .consoleDelegateListener(consoleDelegateListener)
                  .build())
          .build()

  private fun replaceFragment() {
    parentFragmentManager
        .beginTransaction()
        .replace(R.id.fragment_view, LifecycleFragment(), null)
        .addToBackStack(null)
        .commit()
  }

  companion object {
    private val atomicId = AtomicInteger(0)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.java.lifecycle

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoLifecycleProvider
import com.facebook.litho.LithoLifecycleProviderDelegate
import com.facebook.litho.LithoView
import com.facebook.litho.widget.Text
import com.facebook.samples.litho.R
import com.facebook.samples.litho.java.lifecycle.ConsoleView.LogRunnable
import com.facebook.yoga.YogaAlign
import java.util.concurrent.atomic.AtomicInteger

class ScreenSlidePageFragment : Fragment(R.layout.screen_slide_fragment) {

  private var lithoView: LithoView? = null
  private var consoleView: ConsoleView? = null
  private var wasVisible = false
  private var position = 0
  private val consoleDelegateListener = ConsoleDelegateListener()
  private val delegateListener: DelegateListener =
      object : DelegateListener {
        override fun onDelegateMethodCalled(
            type: Int,
            thread: Thread,
            timestamp: Long,
            id: String
        ) {
          consoleView?.post(
              LogRunnable(
                  consoleView,
                  LifecycleDelegateLog.prefix(thread, timestamp, id),
                  LifecycleDelegateLog.log(type)))
        }

        override fun setRootComponent(isSync: Boolean) = Unit
      }

  // start_example_lifecycleprovider
  private val delegate = LithoLifecycleProviderDelegate()

  override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    super.setUserVisibleHint(isVisibleToUser)
    if (wasVisible == isVisibleToUser) {
      return
    }
    if (isVisibleToUser) {
      wasVisible = true
      delegate.moveToLifecycle(LithoLifecycleProvider.LithoLifecycle.HINT_VISIBLE)
    } else {
      wasVisible = false
      delegate.moveToLifecycle(LithoLifecycleProvider.LithoLifecycle.HINT_INVISIBLE)
    }
  }

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View {
    val parent = inflater.inflate(R.layout.screen_slide_fragment, container, false) as ViewGroup
    val c = ComponentContext(requireContext())
    lithoView =
        LithoView.create(
            c,
            getComponent(c),
            delegate /* The LithoLifecycleProvider delegate for this LithoView */)

    // end_example_lifecycleprovider
    val params1 = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
    params1.weight = 1f
    lithoView?.layoutParams = params1
    parent.addView(lithoView)
    consoleView = ConsoleView(requireContext())
    val params2 = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
    params2.weight = 1f
    consoleView?.layoutParams = params2
    parent.addView(consoleView)
    return parent
  }

  private fun getComponent(c: ComponentContext): Component =
      Column.create(c)
          .child(Text.create(c).text(position.toString()).alignSelf(YogaAlign.CENTER))
          .child(
              LifecycleDelegateComponent.create(c)
                  .id(atomicId.getAndIncrement().toString())
                  .delegateListener(delegateListener)
                  .consoleDelegateListener(consoleDelegateListener)
                  .build())
          .build()

  private fun setPosition(position: Int) {
    this.position = position
  }

  companion object {
    private val atomicId = AtomicInteger(0)

    @JvmStatic
    fun newInstance(position: Int): ScreenSlidePageFragment {
      val fragment = ScreenSlidePageFragment()
      val args = Bundle()
      args.putInt("position", position)
      fragment.arguments = args
      fragment.setPosition(position)
      return fragment
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animatedapi

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.DynamicValue
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.animated.Animated
import com.facebook.litho.animated.Easing
import com.facebook.litho.animated.SpringConfig
import com.facebook.litho.animated.alpha
import com.facebook.litho.animated.scaleX
import com.facebook.litho.animated.scaleY
import com.facebook.litho.animated.translationY
import com.facebook.litho.animated.useBinding
import com.facebook.litho.core.height
import com.facebook.litho.core.margin
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.flexbox.alignSelf
import com.facebook.litho.useState
import com.facebook.litho.view.background
import com.facebook.litho.view.onClick
import com.facebook.litho.view.wrapInView
import com.facebook.samples.litho.kotlin.drawable.RoundedRect
import com.facebook.yoga.YogaAlign

class AnimatedComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val isScaleTransitionComplete = useState { false }
    val isYProgressCompleted = useState { false }
    val alpha = useBinding(0f)
    val alpha2 = useBinding(0f)
    val alpha3 = useBinding(0f)
    val alphaProgress = useBinding(1f)
    val animScale = useBinding(100f)
    val animScale2 = useBinding(110f)
    val scale = useBinding(animScale) { progress -> progress / 100 }
    val scale2 = useBinding(animScale2) { progress -> progress / 100 }
    val yProgress = useBinding(0f)
    val y2Progress = useBinding(150f)

    val animateOn =
        Animated.timing(
            target = y2Progress,
            to = 0f,
            duration = 2000,
            easing = Easing.bezier(0.85f, 0f, 0.15f, 1f))
    val animateOff =
        Animated.timing(
            target = yProgress,
            to = 150f,
            duration = 2000,
            easing = Easing.bezier(0.85f, 0f, 0.15f, 1f),
            animationFinishListener = {
              isYProgressCompleted.update(true)
              alphaProgress.set(if (alphaProgress.get() == 1f) 0.5f else 1f)
            })
    val animDelay =
        Animated.timing(
            target = DynamicValue(0f),
            to = 1f,
            duration = 1000,
            animationFinishListener = { isYProgressCompleted.update(false) })
    val staggerTopAnimation =
        Animated.stagger(
            200,
            Animated.timing(alpha, to = 1f),
            Animated.timing(alpha2, to = 1f),
            Animated.timing(alpha3, to = 1f))

    val blueBlockAnimation = Animated.loop(Animated.sequence(animateOff, animateOn, animDelay))

    val pulseAnimation =
        Animated.loop(
            Animated.delay(
                500,
                Animated.sequence(
                    Animated.spring(
                        animScale,
                        to = 110f,
                        SpringConfig(stiffness = 50f, dampingRatio = 0.5f),
                        animationFinishListener = {
                          isScaleTransitionComplete.update(!isScaleTransitionComplete.value)
                        }),
                    Animated.spring(
                        animScale,
                        to = 100f,
                        SpringConfig(stiffness = 50f, dampingRatio = 0.5f),
                        animationFinishListener = {
                          isScaleTransitionComplete.update(!isScaleTransitionComplete.value)
                        }))))

    return Column {
      child(
          Row(
              style =
                  Style.onClick {
                    blueBlockAnimation.start()
                    staggerTopAnimation.start()
                    pulseAnimation.start()
                  }) {
                child(
                    Column(
                        style =
                            Style.width(100.dp)
                                .height(150.dp)
                                .margin(all = 5.dp)
                                .background(RoundedRect(0xff99b3ff, 8.dp))))
                child(
                    Column(
                        style =
                            Style.width(100.dp)
                                .height(150.dp)
                                .margin(all = 5.dp)
                                .alpha(alpha)
                                .background(RoundedRect(0xffffd480, 8.dp))))
                child(
                    Column(
                        style =
                            Style.width(100.dp)
                                .height(150.dp)
                                .margin(all = 5.dp)
                                .alpha(alpha2)
                                .background(RoundedRect(0xffe699cc, 8.dp))))
                child(
                    Column(
                        style =
                            Style.width(100.dp)
                                .height(150.dp)
                                .margin(all = 5.dp)
                                .alpha(alpha3)
                                .background(RoundedRect(0xff9fdfbf, 8.dp))))
              })
      child(
          Row(
              style =
                  Style.width(150.dp)
                      .height(70.dp)
                      .margin(all = 50.dp)
                      .alignSelf(YogaAlign.CENTER)
                      .scaleX(
                          if (isScaleTransitionComplete.value) {
                            scale
                          } else {
                            scale2
                          })
                      .scaleY(
                          if (isScaleTransitionComplete.value) {
                            scale
                          } else {
                            scale2
                          })
                      .background(RoundedRect(0xffd9d9d9, 8.dp))) {
                child(
                    Row(
                        style =
                            Style.margin(30.dp)
                                .width(90.dp)
                                .height(20.dp)
                                .background(RoundedRect(0xff999999, 12.dp))))
              })
      child(
          Row(style = Style.alignSelf(YogaAlign.CENTER).wrapInView()) {
            child(
                Column(
                    style =
                        Style.width(50.dp)
                            .height(50.dp)
                            .margin(all = 5.dp)
                            .background(RoundedRect(0xff666699, 8.dp))))
            child(
                Column(
                    style =
                        Style.width(50.dp)
                            .height(50.dp)
                            .margin(all = 5.dp)
                            .translationY(
                                if (isYProgressCompleted.value) {
                                  y2Progress
                                } else {
                                  yProgress
                                })
                            .alpha(alphaProgress)
                            .background(RoundedRect(0xff666699, 8.dp))))
            child(
                Column(
                    style =
                        Style.width(50.dp)
                            .height(50.dp)
                            .margin(all = 5.dp)
                            .background(RoundedRect(0xff666699, 8.dp))))
            child(
                Column(
                    style =
                        Style.width(50.dp)
                            .height(50.dp)
                            .margin(all = 5.dp)
                            .background(RoundedRect(0xff666699, 8.dp))))
          })
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animatedbadge

import android.graphics.Color
import android.graphics.Typeface
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.ResourcesScope
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.flexbox.position
import com.facebook.litho.flexbox.positionType
import com.facebook.litho.key
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.background
import com.facebook.litho.view.onClick
import com.facebook.samples.litho.kotlin.drawable.RoundedRect
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaPositionType

class AnimatedBadgeKotlin : KComponent() {
  private val ANIMATION_DURATION = 300
  private val ANIMATOR = Transition.timing(ANIMATION_DURATION)
  private val TRANSITION_KEY_TEXT = "text"

  override fun ComponentScope.render(): Component? {
    val state = useState { 0 }
    val expanded1 = state.value == 1 || state.value == 2
    val expanded2 = state.value == 2 || state.value == 3

    useTransition(
        Transition.parallel<Transition.BaseTransitionUnitsBuilder>(
            Transition.allLayout().animator(ANIMATOR),
            Transition.create(Transition.TransitionKeyType.GLOBAL, TRANSITION_KEY_TEXT)
                .animate(AnimatedProperties.WIDTH)
                .appearFrom(0f)
                .disappearTo(0f)
                .animator(ANIMATOR),
            Transition.create(Transition.TransitionKeyType.GLOBAL, TRANSITION_KEY_TEXT)
                .animate(AnimatedProperties.ALPHA)
                .appearFrom(0f)
                .disappearTo(0f)
                .animator(ANIMATOR)))

    return Column(
        style = Style.padding(all = 8f.dp).onClick { state.update(state.value.plus(1).rem(4)) }) {
          child(Row(style = Style.margin(top = 8f.dp)) { child(buildComment1(expanded1)) })
          child(Row(style = Style.margin(top = 16f.dp)) { child(buildComment2(expanded2)) })
        }
  }

  private fun ResourcesScope.buildComment1(expanded: Boolean): Component =
      Column(style = Style.padding(all = 8f.dp).background(RoundedRect(0xFFDDDDDD, 20.dp))) {
        child(
            Row(alignItems = YogaAlign.CENTER) {
              child(Text("Cristobal Castilla", textSize = 16f.dp, textStyle = Typeface.BOLD))
              child(
                  Row(
                      style =
                          Style.margin(start = 8f.dp)
                              .padding(all = 3f.dp)
                              .background(RoundedRect(Color.WHITE, 12.dp)),
                      alignItems = YogaAlign.CENTER) {
                        child(
                            Column(
                                style =
                                    Style.height(18f.dp)
                                        .width(18f.dp)
                                        .background(RoundedRect(0xffffb74b, 9.dp))))
                        if (expanded) {
                          child(
                              key("text") {
                                Text(
                                    "Top Follower",
                                    style =
                                        Style.margin(left = 8f.dp)
                                            .transitionKey(
                                                context,
                                                TRANSITION_KEY_TEXT,
                                                Transition.TransitionKeyType.GLOBAL),
                                    textSize = 12f.dp,
                                    clipToBounds = true)
                              })
                        }
                        child(
                            Text(
                                "+1",
                                style = Style.margin(left = 8f.dp, right = 4f.dp),
                                textSize = 12f.dp,
                                textColor = Color.BLUE))
                      })
            })
        child(Text("So awesome!", textSize = 18f.dp))
      }

  private fun ResourcesScope.buildComment2(expanded: Boolean): Component =
      Column(style = Style.padding(all = 8f.dp).background(RoundedRect(0xFFDDDDDD, 20.dp))) {
        child(
            Row(alignItems = YogaAlign.CENTER) {
              child(Text("Cristobal Castilla", textSize = 16f.dp, textStyle = Typeface.BOLD))
              child(
                  Row(
                      style =
                          Style.width((if (expanded) 48f else 24f).dp)
                              .margin(start = 8f.dp)
                              .padding(all = 3f.dp)
                              .background(RoundedRect(Color.WHITE, 12.dp)),
                      alignItems = YogaAlign.CENTER) {
                        child(
                            Column(
                                style =
                                    Style.positionType(YogaPositionType.ABSOLUTE)
                                        .position(start = (if (expanded) 27 else 3).dp)
                                        .positionType(YogaPositionType.ABSOLUTE)
                                        .height(18f.dp)
                                        .width(18f.dp)
                                        .background(RoundedRect(0xFFB2CFE5, 9.dp))))
                        child(
                            Column(
                                style =
                                    Style.positionType(YogaPositionType.ABSOLUTE)
                                        .position(start = (if (expanded) 15 else 3).dp)
                                        .positionType(YogaPositionType.ABSOLUTE)
                                        .height(18f.dp)
                                        .width(18f.dp)
                                        .background(RoundedRect(0xFF4B8C61, 9.dp))))
                        child(
                            Column(
                                style =
                                    Style.height(18f.dp)
                                        .width(18f.dp)
                                        .background(RoundedRect(0xFFFFB74B, 9.dp))))
                      })
            })
        child(Text("So awesome!", textSize = 18f.dp))
      }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animatedcounter

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.animated.Animated
import com.facebook.litho.animated.AnimatedAnimation
import com.facebook.litho.animated.translationY
import com.facebook.litho.animated.useBinding
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.flexbox.alignSelf
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.onCleanup
import com.facebook.litho.sp
import com.facebook.litho.useEffect
import com.facebook.litho.useRef
import com.facebook.litho.useState
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaJustify

class AnimatingCounterRootComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val count = useState { 0 }
    return Column(alignContent = YogaAlign.CENTER) {
      child(
          Row(
              style = Style.alignSelf(YogaAlign.CENTER).flex(grow = 1f),
              justifyContent = YogaJustify.CENTER,
              alignItems = YogaAlign.CENTER) {
                child(
                    Text(
                        style = Style.padding(all = 8.dp).onClick { count.update { c -> c + 1 } },
                        text = "+"))
                child(AnimatingCounter(count = count.value))
                child(
                    Text(
                        style = Style.padding(all = 8.dp).onClick { count.update { c -> c - 1 } },
                        text = "-"))
              })
    }
  }
}

// start_example
class AnimatingCounter(private val count: Int) : KComponent() {
  override fun ComponentScope.render(): Component? {
    val animation = useRef<AnimatedAnimation?> { null }
    val translationY = useBinding(0f)

    useEffect(count) {
      // Animate the text to a Y-offset based on count
      val newAnimation = Animated.spring(translationY, to = count * 10.dp.toPixels().toFloat())
      newAnimation.start()
      animation.value = newAnimation

      onCleanup { animation.value?.cancel() }
    }

    return Text(style = Style.translationY(translationY), text = "$count", textSize = 24.sp)
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animatedcounter

import android.os.Bundle
import com.facebook.litho.LithoView
import com.facebook.samples.litho.NavigatableDemoActivity

class AnimatedCounterActivity : NavigatableDemoActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(LithoView.create(this, AnimatingCounterRootComponent()))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.eventHandlerWithReturn
import com.facebook.litho.key
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.common.DataDiffSection
import com.facebook.litho.sections.widget.RecyclerCollectionComponent
import com.facebook.litho.widget.ComponentRenderInfo
import com.facebook.litho.widget.RenderInfo

class ComposedAnimationsComponentKotlin : KComponent() {

  override fun ComponentScope.render(): Component? {
    return RecyclerCollectionComponent.create(context)
        .disablePTR(true)
        .section(
            DataDiffSection.create<Data>(SectionContext(context))
                .data(generateData(20))
                .renderEventHandler(eventHandlerWithReturn { e -> onRender(e.index) })
                .onCheckIsSameItemEventHandler(
                    eventHandlerWithReturn { e -> isSameItem(e.previousItem, e.nextItem) })
                .build())
        .build()
  }

  private fun onRender(index: Int): RenderInfo {
    val numDemos = 5
    // Keep alternating between demos
    val component: Component =
        when (index % numDemos) {
          0 -> key("footer") { StoryFooterComponent() }
          1 -> UpDownBlocksComponent()
          2 -> LeftRightBlocksComponent()
          3 -> OneByOneLeftRightBlocksComponent()
          4 -> LeftRightBlocksSequenceComponent()
          else -> throw RuntimeException("Bad index: $index")
        }
    return ComponentRenderInfo.create().component(component).build()
  }

  private fun isSameItem(previousItem: Data, nextItem: Data): Boolean =
      previousItem.number == nextItem.number

  private fun generateData(number: Int): List<Data> {
    val dummyData = mutableListOf<Data>()

    for (i in 0 until number) {
      dummyData.add(Data(i))
    }

    return dummyData
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

data class Data(val number: Int)


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import android.graphics.Color
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.BounceInterpolator
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.Transition.TransitionUnitsBuilder
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign

class LeftRightBlocksComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val left = useState { false }

    useTransition(
        Transition.parallel<TransitionUnitsBuilder>(
            Transition.create(Transition.TransitionKeyType.GLOBAL, "red")
                .animate(AnimatedProperties.X)
                .animator(Transition.timing(1000, AccelerateDecelerateInterpolator())),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "blue")
                .animate(AnimatedProperties.X)
                .animator(Transition.timing(1500)),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "green")
                .animate(AnimatedProperties.X)
                .animator(Transition.timing(1000, BounceInterpolator()))))

    return Column(
        alignItems = (if (left.value) YogaAlign.FLEX_START else YogaAlign.FLEX_END),
        style = Style.onClick { e -> left.update(!left.value) }) {
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#ee1111"))
                          .transitionKey(context, "red", Transition.TransitionKeyType.GLOBAL)))
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#1111ee"))
                          .transitionKey(context, "blue", Transition.TransitionKeyType.GLOBAL)))
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#11ee11"))
                          .transitionKey(context, "green", Transition.TransitionKeyType.GLOBAL)))
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.Transition.TransitionUnitsBuilder
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign

class LeftRightBlocksSequenceComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val left = useState { false }

    useTransition(
        Transition.sequence<TransitionUnitsBuilder>(
            Transition.create(Transition.TransitionKeyType.GLOBAL, "red")
                .animate(AnimatedProperties.X),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "blue")
                .animate(AnimatedProperties.X),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "green")
                .animate(AnimatedProperties.X)))
    return Column(
        alignItems = (if (left.value) YogaAlign.FLEX_START else YogaAlign.FLEX_END),
        style = Style.onClick { e -> left.update(!left.value) }) {
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#ee1111"))
                          .transitionKey(context, "red", Transition.TransitionKeyType.GLOBAL)))
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#1111ee"))
                          .transitionKey(context, "blue", Transition.TransitionKeyType.GLOBAL)))
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .width(40f.dp)
                          .backgroundColor(Color.parseColor("#11ee11"))
                          .transitionKey(context, "green", Transition.TransitionKeyType.GLOBAL)))
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign

private const val TRANSITION_KEY_RED = "red"
private const val TRANSITION_KEY_BLUE = "blue"
private const val TRANSITION_KEY_GREEN = "green"
private val ALL_TRANSITION_KEYS =
    arrayOf(TRANSITION_KEY_RED, TRANSITION_KEY_BLUE, TRANSITION_KEY_GREEN)

class OneByOneLeftRightBlocksComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val state = useState { 0 }

    val redLeft = state.value == 0 || state.value == 4 || state.value == 5
    val blueLeft = state.value == 0 || state.value == 1 || state.value == 5
    val greenLeft = state.value == 0 || state.value == 1 || state.value == 2

    useTransition(
        Transition.create(Transition.TransitionKeyType.GLOBAL, *ALL_TRANSITION_KEYS)
            .animate(AnimatedProperties.X, AnimatedProperties.Y, AnimatedProperties.ALPHA))

    return Column(style = Style.onClick { state.update { prevValue -> (prevValue + 1) % 6 } }) {
      child(
          Column(alignItems = (if (redLeft) YogaAlign.FLEX_START else YogaAlign.FLEX_END)) {
            child(
                Row(
                    style =
                        Style.height(40f.dp)
                            .width(40f.dp)
                            .backgroundColor(Color.parseColor("#ee1111"))
                            .transitionKey(
                                context, TRANSITION_KEY_RED, Transition.TransitionKeyType.GLOBAL)))
          })

      child(
          Column(alignItems = (if (blueLeft) YogaAlign.FLEX_START else YogaAlign.FLEX_END)) {
            child(
                Row(
                    style =
                        Style.height(40f.dp)
                            .width(40f.dp)
                            .backgroundColor(Color.parseColor("#1111ee"))
                            .transitionKey(
                                context, TRANSITION_KEY_BLUE, Transition.TransitionKeyType.GLOBAL)))
          })
      child(
          Column(alignItems = (if (greenLeft) YogaAlign.FLEX_START else YogaAlign.FLEX_END)) {
            child(
                Row(
                    style =
                        Style.height(40f.dp)
                            .width(40f.dp)
                            .backgroundColor(Color.parseColor("#11ee11"))
                            .transitionKey(
                                context,
                                TRANSITION_KEY_GREEN,
                                Transition.TransitionKeyType.GLOBAL)))
          })
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.animation.DimensionValue
import com.facebook.litho.core.height
import com.facebook.litho.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.core.width
import com.facebook.litho.core.widthPercent
import com.facebook.litho.dp
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sp
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.litho.view.testKey
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaJustify

class StoryFooterComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val commentText = useState { false }
    useTransition(
        Transition.parallel(
            Transition.create(Transition.TransitionKeyType.GLOBAL, "comment_editText")
                .animate(AnimatedProperties.ALPHA)
                .appearFrom(0f)
                .disappearTo(0f)
                .animate(AnimatedProperties.X)
                .appearFrom(DimensionValue.widthPercentageOffset(-50f))
                .disappearTo(DimensionValue.widthPercentageOffset(-50f)),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "cont_comment")
                .animate(AnimatedProperties.ALPHA)
                .appearFrom(0f)
                .disappearTo(0f),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "icon_like", "icon_share")
                .animate(AnimatedProperties.X),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "text_like", "text_share")
                .animate(AnimatedProperties.ALPHA)
                .appearFrom(0f)
                .disappearTo(0f)
                .animate(AnimatedProperties.X)
                .appearFrom(DimensionValue.widthPercentageOffset(50f))
                .disappearTo(DimensionValue.widthPercentageOffset(50f))))
    if (!commentText.value) {
      return Row(style = Style.backgroundColor(Color.WHITE).height(56f.dp)) {
        child(
            Row(
                alignItems = YogaAlign.CENTER,
                justifyContent = YogaJustify.CENTER,
                style =
                    Style.widthPercent(33.3f)
                        .onClick { commentText.update(!commentText.value) }
                        .testKey("like_button")
                        .transitionKey(context, "icon_like", Transition.TransitionKeyType.GLOBAL)) {
                  child(
                      Column(
                          style =
                              Style.height(24f.dp)
                                  .width(24f.dp)
                                  .backgroundColor(Color.RED)
                                  .transitionKey(
                                      context, "icon_like", Transition.TransitionKeyType.GLOBAL)))
                  child(
                      Text(
                          textSize = 16f.sp,
                          text = "Like",
                          style =
                              Style.transitionKey(
                                      context, "text_like", Transition.TransitionKeyType.GLOBAL)
                                  .margin(left = 8f.dp)))
                })
        child(
            Row(
                alignItems = YogaAlign.CENTER,
                justifyContent = YogaJustify.CENTER,
                style =
                    Style.transitionKey(
                            context, "cont_comment", Transition.TransitionKeyType.GLOBAL)
                        .widthPercent(33.3f)) {
                  child(
                      Column(style = Style.height(24f.dp).width(24f.dp).backgroundColor(Color.RED)))
                  child(
                      Text(textSize = 16f.sp, text = "Comment", style = Style.margin(left = 8f.dp)))
                })
        child(
            Row(
                alignItems = YogaAlign.CENTER,
                justifyContent = YogaJustify.CENTER,
                style = Style.widthPercent(33.3f)) {
                  child(
                      Column(
                          style =
                              Style.transitionKey(
                                      context, "icon_share", Transition.TransitionKeyType.GLOBAL)
                                  .height(24f.dp)
                                  .width(24f.dp)
                                  .backgroundColor(Color.RED)))
                  child(
                      Text(
                          textSize = 16f.sp,
                          text = "Share",
                          style =
                              Style.transitionKey(
                                      context, "text_share", Transition.TransitionKeyType.GLOBAL)
                                  .margin(left = 8f.dp)))
                })
      }
    } else {
      return Row(style = Style.backgroundColor(Color.WHITE).height(56f.dp)) {
        child(
            Row(
                alignItems = YogaAlign.CENTER,
                justifyContent = YogaJustify.CENTER,
                style =
                    Style.onClick { commentText.update(!commentText.value) }
                        .padding(horizontal = 16f.dp)
                        .testKey("like_button")) {
                  child(
                      Column(
                          style =
                              Style.transitionKey(
                                      context, "icon_like", Transition.TransitionKeyType.GLOBAL)
                                  .height(24f.dp)
                                  .width(24f.dp)
                                  .backgroundColor(Color.RED)))
                })
        child(
            Column(
                style =
                    Style.flex(grow = 1f)
                        .transitionKey(
                            context, "comment_editText", Transition.TransitionKeyType.GLOBAL)) {
                  child(Text(text = "Input here", textSize = 16f.sp))
                })
        child(
            Row(
                alignItems = YogaAlign.CENTER,
                style =
                    Style.transitionKey(context, "cont_share", Transition.TransitionKeyType.GLOBAL)
                        .onClick { commentText.update(!commentText.value) }
                        .padding(all = 16f.dp)
                        .backgroundColor(0xFF0000FF.toInt())) {
                  child(
                      Column(
                          style =
                              Style.transitionKey(
                                      context, "icon_share", Transition.TransitionKeyType.GLOBAL)
                                  .height(24f.dp)
                                  .width(24f.dp)
                                  .backgroundColor(Color.RED)))
                })
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.animationcomposition

import android.graphics.Color
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.Transition.TransitionUnitsBuilder
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.dp
import com.facebook.litho.flexbox.flex
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign

class UpDownBlocksComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val top = useState { false }
    useTransition(
        Transition.stagger<TransitionUnitsBuilder>(
            200,
            Transition.create(Transition.TransitionKeyType.GLOBAL, "red")
                .animate(AnimatedProperties.Y),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "blue")
                .animate(AnimatedProperties.Y),
            Transition.create(Transition.TransitionKeyType.GLOBAL, "green")
                .animate(AnimatedProperties.Y)))
    return Row(
        alignItems = (if (top.value) YogaAlign.FLEX_START else YogaAlign.FLEX_END),
        style = Style.height(200f.dp).onClick { e -> top.update(!top.value) }) {
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .flex(grow = 1f)
                          .backgroundColor(Color.parseColor("#ee1111"))
                          .transitionKey(context, "red", Transition.TransitionKeyType.GLOBAL)))

          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .flex(grow = 1f)
                          .backgroundColor(Color.parseColor("#1111ee"))
                          .transitionKey(context, "blue", Transition.TransitionKeyType.GLOBAL)))
          child(
              Row(
                  style =
                      Style.height(40f.dp)
                          .flex(grow = 1f)
                          .backgroundColor(Color.parseColor("#11ee11"))
                          .transitionKey(context, "green", Transition.TransitionKeyType.GLOBAL)))
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.bounds

import android.graphics.Color
import android.graphics.Typeface
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.ResourcesScope
import com.facebook.litho.Row
import com.facebook.litho.State
import com.facebook.litho.Style
import com.facebook.litho.Transition
import com.facebook.litho.animation.AnimatedProperties
import com.facebook.litho.core.height
import com.facebook.litho.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sp
import com.facebook.litho.transition.transitionKey
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaJustify

private const val ANIMATION_TIMING = 1000
private const val TRANSITION_KEY_CONTAINER_AFFECTED_CHILDREN = "container_affected_children"
private const val TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_START = "child_affected_children_start"
private const val TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_MIDDLE = "child_affected_children_middle"
private const val TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_END = "child_affected_children_end"
private const val TRANSITION_KEY_CONTAINER_AFFECTED_SIBLINGS = "container_affected_siblings"
private const val TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_START = "child_affected_siblings_start"
private const val TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_END = "child_affected_siblings_end"
private const val TRANSITION_KEY_CONTAINER_AFFECTED_PARENT = "container_affected_parent"
private const val TRANSITION_KEY_CHILD_AFFECTED_PARENT = "child_affected_parent"
private const val TRANSITION_KEY_CONTAINER_ALL_TOGETHER_GLOBAL = "container_all_together_global"
private const val TRANSITION_KEY_CONTAINER_ALL_TOGETHER_TOP = "container_all_together_top"
private const val TRANSITION_KEY_CONTAINER_ALL_TOGETHER_BOTTOM = "container_all_together_bottom"
private const val TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_START = "child_all_together_top_start"
private const val TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_END = "child_all_together_top_end"
private const val TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_START = "child_all_together_bottom_start"
private const val TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_END = "child_all_together_bottom_end"

class BoundsAnimationComponent : KComponent() {
  override fun ComponentScope.render(): Component {
    val autoBoundsTransitionEnabled = useState { false }
    val isAffectedChildrenExpanded = useState { true }
    val isAffectedSiblingsExpanded = useState { true }
    val isAffectedParentExpanded = useState { true }
    val isAllTogetherExpanded = useState { false }

    val transitionKeys =
        if (autoBoundsTransitionEnabled.value) {
          arrayOf(
              TRANSITION_KEY_CONTAINER_AFFECTED_CHILDREN,
              TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_START,
              TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_MIDDLE,
              TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_END,
              TRANSITION_KEY_CONTAINER_AFFECTED_SIBLINGS,
              TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_START,
              TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_END,
              TRANSITION_KEY_CONTAINER_AFFECTED_PARENT,
              TRANSITION_KEY_CHILD_AFFECTED_PARENT,
              TRANSITION_KEY_CONTAINER_ALL_TOGETHER_GLOBAL,
              TRANSITION_KEY_CONTAINER_ALL_TOGETHER_TOP,
              TRANSITION_KEY_CONTAINER_ALL_TOGETHER_BOTTOM,
              TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_START,
              TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_END,
              TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_START,
              TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_END)
        } else {
          arrayOf(
              TRANSITION_KEY_CONTAINER_AFFECTED_CHILDREN,
              TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_END,
              TRANSITION_KEY_CHILD_AFFECTED_PARENT,
              TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_END)
        }

    useTransition(
        Transition.parallel<Transition.BaseTransitionUnitsBuilder>(
            Transition.create(Transition.TransitionKeyType.GLOBAL, *transitionKeys)
                .animate(AnimatedProperties.WIDTH, AnimatedProperties.X)
                .animator(Transition.timing(ANIMATION_TIMING))))

    return Column(
        alignItems = YogaAlign.CENTER,
        style = Style.backgroundColor(Color.WHITE).padding(vertical = 8.dp)) {
          child(
              Text(
                  text = "ABT " + if (autoBoundsTransitionEnabled.value) "enabled" else "disabled",
                  textSize = 20.sp,
                  textStyle = Typeface.BOLD,
                  textColor = Color.BLACK,
                  style =
                      Style.onClick {
                        autoBoundsTransitionEnabled.update(autoBoundsTransitionEnabled.value.not())
                      }))
          child(
              Text(
                  text = "Affected Children",
                  textSize = 20.sp,
                  style = Style.margin(vertical = 8.dp)))
          child(getAffectedChildren(isAffectedChildrenExpanded))
          child(
              Text(
                  text = "Affected Siblings",
                  textSize = 20.sp,
                  style = Style.margin(vertical = 8.dp)))
          child(getAffectedSiblings(isAffectedSiblingsExpanded))
          child(
              Text(
                  text = "Affected Parent",
                  textSize = 20.sp,
                  style = Style.margin(vertical = 8.dp)))
          child(getAffectedParents(isAffectedParentExpanded))
          child(getAllTogether(isAllTogetherExpanded))
        }
  }

  private fun ResourcesScope.getAffectedChildren(isExpanded: State<Boolean>): Component =
      Row(
          style =
              Style.transitionKey(
                      context,
                      TRANSITION_KEY_CONTAINER_AFFECTED_CHILDREN,
                      Transition.TransitionKeyType.GLOBAL)
                  .height((60 + 2 * 8).dp)
                  .width((3 * 60 / (if (isExpanded.value) 1 else 2) + 4 * 8).dp)
                  .padding(all = 8.dp)
                  .backgroundColor(Color.YELLOW)
                  .onClick { isExpanded.update(isExpanded.value.not()) }) {
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_START,
                                Transition.TransitionKeyType.GLOBAL)
                            .flex(1f)
                            .backgroundColor(Color.RED)))
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_MIDDLE,
                                Transition.TransitionKeyType.GLOBAL)
                            .flex(1f)
                            .backgroundColor(Color.RED)
                            .margin(horizontal = 8.dp)))
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_CHILDREN_END,
                                Transition.TransitionKeyType.GLOBAL)
                            .flex(1f)
                            .backgroundColor(Color.RED)))
          }

  private fun ResourcesScope.getAffectedSiblings(isExpanded: State<Boolean>): Component =
      Row(
          style =
              Style.transitionKey(
                      context,
                      TRANSITION_KEY_CONTAINER_AFFECTED_SIBLINGS,
                      Transition.TransitionKeyType.GLOBAL)
                  .height((60 + 2 * 8).dp)
                  .width((3 * 60 + 4 * 8).dp)
                  .padding(all = 8.dp)
                  .backgroundColor(Color.LTGRAY)
                  .onClick { isExpanded.update(isExpanded.value.not()) }) {
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_START,
                                Transition.TransitionKeyType.GLOBAL)
                            .flex(1f)
                            .backgroundColor(Color.RED)))
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_SIBLINGS_END,
                                Transition.TransitionKeyType.GLOBAL)
                            .flex(if (isExpanded.value) 2f else 1f)
                            .backgroundColor(Color.YELLOW)
                            .margin(start = 8.dp)))
          }

  private fun ResourcesScope.getAffectedParents(isExpanded: State<Boolean>): Component =
      Row(
          justifyContent = YogaJustify.CENTER,
          style =
              Style.transitionKey(
                      context,
                      TRANSITION_KEY_CONTAINER_AFFECTED_PARENT,
                      Transition.TransitionKeyType.GLOBAL)
                  .height((60 + 2 * 8).dp)
                  .padding(all = 8.dp)
                  .backgroundColor(Color.LTGRAY)
                  .onClick { isExpanded.update(isExpanded.value.not()) }) {
            child(
                Column(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CHILD_AFFECTED_PARENT,
                                Transition.TransitionKeyType.GLOBAL)
                            .width((60 * if (isExpanded.value) 2 else 1).dp)
                            .backgroundColor(Color.YELLOW)))
          }

  private fun ResourcesScope.getAllTogether(isExpanded: State<Boolean>): Component =
      Column(
          style =
              Style.transitionKey(
                      context,
                      TRANSITION_KEY_CONTAINER_ALL_TOGETHER_GLOBAL,
                      Transition.TransitionKeyType.GLOBAL)
                  .margin(top = 24.dp)
                  .height((60 * 2 + 3 * 8).dp)
                  .padding(all = 8.dp)
                  .backgroundColor(Color.LTGRAY)
                  .onClick { isExpanded.update(isExpanded.value.not()) }) {
            child(
                Row(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CONTAINER_ALL_TOGETHER_TOP,
                                Transition.TransitionKeyType.GLOBAL)
                            .height(60.dp)
                            .padding(all = 6.dp)
                            .backgroundColor(Color.GRAY)) {
                      child(
                          Column(
                              style =
                                  Style.transitionKey(
                                          context,
                                          TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_START,
                                          Transition.TransitionKeyType.GLOBAL)
                                      .margin(end = 6.dp)
                                      .flex(1f)
                                      .backgroundColor(Color.RED)))
                      child(
                          Column(
                              style =
                                  Style.transitionKey(
                                          context,
                                          TRANSITION_KEY_CHILD_ALL_TOGETHER_TOP_END,
                                          Transition.TransitionKeyType.GLOBAL)
                                      .flex(1f)
                                      .backgroundColor(Color.RED)))
                    })
            child(
                Row(
                    style =
                        Style.transitionKey(
                                context,
                                TRANSITION_KEY_CONTAINER_ALL_TOGETHER_BOTTOM,
                                Transition.TransitionKeyType.GLOBAL)
                            .height(60.dp)
                            .padding(all = 6.dp)
                            .margin(top = 8.dp)
                            .backgroundColor(Color.GRAY)) {
                      child(
                          Column(
                              style =
                                  Style.transitionKey(
                                          context,
                                          TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_START,
                                          Transition.TransitionKeyType.GLOBAL)
                                      .margin(end = 6.dp)
                                      .width(100.dp)
                                      .backgroundColor(Color.RED)))
                      child(
                          Column(
                              style =
                                  Style.transitionKey(
                                          context,
                                          TRANSITION_KEY_CHILD_ALL_TOGETHER_BOTTOM_END,
                                          Transition.TransitionKeyType.GLOBAL)
                                      .flex(1f)
                                      .width((if (isExpanded.value) 200 else 100).dp)
                                      .backgroundColor(Color.YELLOW)))
                    })
          }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.animated.alpha
import com.facebook.litho.animated.background
import com.facebook.litho.animated.elevation
import com.facebook.litho.animated.rotation
import com.facebook.litho.animated.scaleX
import com.facebook.litho.animated.scaleY
import com.facebook.litho.animated.translationX
import com.facebook.litho.animated.useBinding
import com.facebook.litho.core.height
import com.facebook.litho.core.padding
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.flexbox.alignSelf
import com.facebook.yoga.YogaAlign

class AllCommonDynamicPropsKComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val scale = useBinding(1f)
    val alpha = useBinding(1f)
    val translation = useBinding(1f)
    val rotation = useBinding(0f)
    val elevation = useBinding(1f)

    val background = useBinding(ColorDrawable(Color.RED))
    val fgColor = useBinding(Color.GREEN)

    val square =
        Column(
            style =
                Style.width(100.dp)
                    .height(100.dp)
                    // .foregroundColor(fgColor)
                    .background(background)
                    .alignSelf(YogaAlign.CENTER)
                    .scaleX(scale)
                    .scaleY(scale)
                    .alpha(alpha)
                    .translationX(translation)
                    .rotation(rotation)
                    .elevation(elevation))

    return Column(style = Style.padding(all = 20.dp)) {
      child(SeekBar(initialValue = 1f, label = "Alpha") { alpha.set(it) })
      child(
          SeekBar(
              initialValue = .5f,
              label = "Scale",
              onProgressChanged = { scale.set(evaluate(it, .75f, 1.25f)) }))
      child(
          SeekBar(
              initialValue = .5f,
              label = "Translation",
              onProgressChanged = { translation.set(evaluate(it, -100f, 100f)) }))
      child(
          SeekBar(
              initialValue = 0f,
              label = "Rotation",
              onProgressChanged = { rotation.set(evaluate(it, 0f, 360f)) }))
      child(
          SeekBar(
              initialValue = 0f,
              label = "background",
              onProgressChanged = {
                background.set(
                    ColorDrawable(Color.HSVToColor(floatArrayOf(evaluate(it, 0f, 360f), 1f, 1f))))
              }))
      child(
          SeekBar(
              initialValue = 0f,
              label = "foreground Color",
              onProgressChanged = {
                fgColor.set(Color.HSVToColor(floatArrayOf(evaluate(it, 0f, 360f), 1f, 1f)))
              }))
      child(
          SeekBar(
              initialValue = 0f,
              label = "Elevation",
              onProgressChanged = { elevation.set(evaluate(it, 0f, 10f)) }))

      child(square)
    }
  }

  fun evaluate(fraction: Float, start: Float, end: Float): Float = start + fraction * (end - start)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import com.facebook.litho.ClickEvent
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.DynamicValue
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.State
import com.facebook.litho.eventHandler
import com.facebook.litho.widget.Text
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaEdge
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference

// start_example
@LayoutSpec
object AnimateDynamicPropsComponentSpec {

  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      time: StateValue<DynamicValue<Long>>,
      animator: StateValue<AtomicReference<ValueAnimator?>>,
  ) {
    time.set(DynamicValue<Long>(0))
    animator.set(AtomicReference<ValueAnimator?>(null))
  }

  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @State time: DynamicValue<Long>,
      @State animator: AtomicReference<ValueAnimator?>,
  ): Component? {
    val startAnimator: (ClickEvent) -> Unit = {
      animator.get()?.let { it.cancel() }
      animator.set(
          ValueAnimator.ofInt(0, TimeUnit.HOURS.toMillis(12).toInt()).apply {
            duration = 2000
            interpolator = AccelerateDecelerateInterpolator()
            addUpdateListener { time.set((it.animatedValue as Int).toLong()) }
          })
      animator.get()?.start()
    }

    return Column.create(c)
        .paddingDip(YogaEdge.ALL, 20f)
        .alignItems(YogaAlign.CENTER)
        .child(
            Text.create(c)
                .text("Click to Start Animation")
                .clickHandler(eventHandler(startAnimator)))
        .child(
            ClockFace.create(c)
                .time(time)
                .widthDip(200f)
                .heightDip(200f)
                .marginDip(YogaEdge.TOP, 20f)
                .build())
        .build()
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import com.facebook.litho.ClickEvent
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.animated.useBinding
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useRef
import com.facebook.litho.view.onClick
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaEdge
import java.util.concurrent.TimeUnit

// start_example
class AnimateDynamicPropsKComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    val time = useBinding(0L)
    val animator = useRef<ValueAnimator?> { null }

    val startAnimator: (ClickEvent) -> Unit = {
      animator.value?.cancel()
      animator.value =
          ValueAnimator.ofInt(0, TimeUnit.HOURS.toMillis(12).toInt()).apply {
            duration = 2000
            interpolator = AccelerateDecelerateInterpolator()
            addUpdateListener { time.set((it.animatedValue as Int).toLong()) }
          }
      animator.value?.start()
    }

    return Column(alignItems = YogaAlign.CENTER, style = Style.padding(all = 20.dp)) {
      child(Text("Click to Start Animation", style = Style.onClick(action = startAnimator)))
      child(
          ClockFace.create(context)
              .time(time)
              .widthDip(200f)
              .heightDip(200f)
              .marginDip(YogaEdge.TOP, 20f)
              .build())
    }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import android.content.Context
import com.facebook.litho.DynamicValue
import com.facebook.litho.MeasureScope
import com.facebook.litho.MountableComponent
import com.facebook.litho.MountableComponentScope
import com.facebook.litho.MountableRenderResult
import com.facebook.litho.SimpleMountable
import com.facebook.litho.Style
import com.facebook.rendercore.MeasureResult

class ClockFaceMountable(private val time: DynamicValue<Long>, private val style: Style? = null) :
    MountableComponent() {

  override fun MountableComponentScope.render(): MountableRenderResult {
    time.bindTo(0L) { view: SimpleClockView, value -> view.time = value }

    return MountableRenderResult(Mountable(), style)
  }

  private class Mountable : SimpleMountable<SimpleClockView>(RenderType.VIEW) {

    override fun createContent(context: Context): SimpleClockView = SimpleClockView(context)

    override fun mount(c: Context, content: SimpleClockView, layoutData: Any?) = Unit

    override fun unmount(c: Context, content: SimpleClockView, layoutData: Any?) = Unit

    override fun MeasureScope.measure(widthSpec: Int, heightSpec: Int): MeasureResult =
        fromSpecs(widthSpec, heightSpec)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import android.content.Context
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnBindDynamicValue
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.Prop

// start_example
@MountSpec
object ClockFaceSpec {

  @OnCreateMountContent
  fun onCreateMountContent(androidContext: Context): SimpleClockView {
    return SimpleClockView(androidContext)
  }

  @OnBindDynamicValue
  fun onBindTime(simpleClockView: SimpleClockView, @Prop(dynamic = true) time: Long) {
    simpleClockView.time = time
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.DynamicValue
import com.facebook.litho.StateValue
import com.facebook.litho.animated.alpha
import com.facebook.litho.animated.scaleX
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.State
import com.facebook.litho.flexbox.alignSelf
import com.facebook.samples.litho.R
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaEdge
import com.facebook.yoga.YogaJustify

// start_example
@LayoutSpec
object CommonDynamicPropsComponentSpec {

  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      alpha: StateValue<DynamicValue<Float>>,
      scale: StateValue<DynamicValue<Float>>,
  ) {
    alpha.set(DynamicValue<Float>(1f))
    scale.set(DynamicValue<Float>(1f))
  }

  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @State alpha: DynamicValue<Float>,
      @State scale: DynamicValue<Float>,
  ): Component {
    val square =
        Column.create(c)
            .widthDip(100f)
            .heightDip(100f)
            .backgroundRes(R.color.primaryColor)
            .alignSelf(YogaAlign.CENTER)
            .scaleX(scale)
            .scaleY(scale)
            .alpha(alpha)
            .build()

    return Column.create(c)
        .justifyContent(YogaJustify.SPACE_BETWEEN)
        .paddingDip(YogaEdge.ALL, 20f)
        .child(
            SeekBar.create(c).heightDip(14f).widthPercent(100f).initialValue(1f).onProgressChanged {
              alpha.set(it)
            })
        .child(square)
        .child(
            SeekBar.create(c).heightDip(14f).widthPercent(100f).initialValue(1f).onProgressChanged {
              scale.set(it)
            })
        .build()
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.animated.alpha
import com.facebook.litho.animated.scaleX
import com.facebook.litho.animated.scaleY
import com.facebook.litho.animated.useBinding
import com.facebook.litho.colorRes
import com.facebook.litho.core.height
import com.facebook.litho.core.padding
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.flexbox.alignSelf
import com.facebook.litho.view.backgroundColor
import com.facebook.samples.litho.R
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaJustify

// start_example
class CommonDynamicPropsKComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    val scale = useBinding(1f)
    val alpha = useBinding(1f)

    val square =
        Column(
            style =
                Style.width(100.dp)
                    .height(100.dp)
                    .backgroundColor(colorRes(R.color.primaryColor))
                    .alignSelf(YogaAlign.CENTER)
                    .scaleX(scale)
                    .scaleY(scale)
                    .alpha(alpha))

    return Column(justifyContent = YogaJustify.SPACE_BETWEEN, style = Style.padding(all = 20.dp)) {
      child(SeekBar(onProgressChanged = { alpha.set(it) }))
      child(square)
      child(SeekBar(onProgressChanged = { scale.set(it) }))
    }
  }
}
// end_example


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.samples.litho.kotlin.animations.dynamicprops

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.DynamicValue
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.State
import com.facebook.yoga.YogaAlign
import com.facebook.yoga.YogaEdge
import java.util.concurrent.TimeUnit

// start_example
@LayoutSpec
object CustomDynamicPropsComponentSpec {

  @OnCreateInitialState
  fun onCreateInitialState(c: ComponentContext, time: StateValue<DynamicValue<Long>>) {
    time.set(DynamicValue<Long>(0))
  }

  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State time: DynamicValue<Long>): Component =
      Column.create(c)
          .alignItems(YogaAlign.CENTER)
          .paddingDip(YogaEdge.ALL, 20f)
          .child(
              SeekBar.create(c)
                  .heightDip(14f)
                  .widthPercent(100f)
                  .initialValue(1f)
                  .onProgressChanged { time.set((it * TimeUnit.HOURS.toMillis(12)).toLong()) })
          .child(
              ClockFace.create(c)
                  .time(time)
                  .widthDip(200f)
                  .heightDip(200f)
                  .marginDip(YogaEdge.TOP, 20f)
                  .build())
          .build()
}
// end_example