/*
 * 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.collection

import android.os.Handler
import android.os.Looper
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.core.height
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Progress
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useCached
import com.facebook.litho.useState
import com.facebook.litho.widget.collection.LazyList
import com.facebook.litho.widget.collection.OnNearCallback
import com.facebook.yoga.YogaAlign

class PaginationCollectionKComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    val paginatedData = useCached {
      PagedDataWithDelay(data = (0..100).map { Item(it, "$it") }, pageSize = 40)
    }
    val list = useState { paginatedData.next() }
    return PagedExample(
        PaginatedList(
            list = list.value,
            fetchNextPage = {
              paginatedData.fetchDelayed { newData -> list.update { it + newData } }
            },
            hasNextPage = paginatedData.hasNext))
  }
}

class PaginatedList<T>(val list: List<T>, val fetchNextPage: () -> Unit, val hasNextPage: Boolean)

class Item(val id: Int, val text: String)

// start_example
class PagedExample(private val pagedList: PaginatedList<Item>) : KComponent() {
  override fun ComponentScope.render(): Component =
      LazyList(
          onNearEnd = OnNearCallback { if (pagedList.hasNextPage) pagedList.fetchNextPage() },
      ) {
        // Add the retrieved items
        children(items = pagedList.list, id = { it.id }) { Text(it.text) }

        // Optionally add a progress spinner
        if (pagedList.hasNextPage) {
          child(ProgressSpinner())
        }
      }
}
// end_example

// A progress spinner centered in a Column
class ProgressSpinner : KComponent() {
  override fun ComponentScope.render(): Component =
      Column(alignItems = YogaAlign.CENTER) {
        child(component = Progress(style = Style.height(50.dp).height(50.dp)))
      }
}

// A paged datasource with a simulated network delay
class PagedDataWithDelay<T>(data: List<T>, pageSize: Int) {
  private val pagedData = data.chunked(pageSize).iterator()
  private var isFetching = false
  val hasNext
    get() = pagedData.hasNext()

  fun next(): List<T> = pagedData.next()

  fun fetchDelayed(callback: (newData: List<T>) -> Unit) {
    if (isFetching || !hasNext) return
    isFetching = true
    Handler(Looper.getMainLooper())
        .postDelayed(
            {
              callback.invoke(next())
              isFetching = false
            },
            simulatedNetworkDelayMs)
  }

  companion object {
    const val simulatedNetworkDelayMs = 1000L
  }
}


/*
 * 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.collection

import android.graphics.Color
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import androidx.annotation.ColorInt
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useState
import com.facebook.litho.widget.collection.LazyCollectionController
import com.facebook.litho.widget.collection.LazyList

// start_example
class PullToRefreshCollectionKComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    val deck = useState { (0..51).map { Card(it) }.shuffled() }
    val lazyCollectionController = useState { LazyCollectionController() }.value
    return LazyList(
        lazyCollectionController = lazyCollectionController,
        onPullToRefresh = {
          deck.update { it.shuffled() }
          lazyCollectionController.setRefreshing(false)
        },
    ) {
      children(items = deck.value, id = { it.index }) { Text(it.styledText) }
    }
  }
}
// end_example

class Card(val suit: Suit, val value: Int) {

  enum class Suit(val symbol: Char, @ColorInt val color: Int) {
    SPADES('â™ ', Color.BLACK),
    HEARTS('â™¥', Color.RED),
    CLUBS('â™£', Color.BLACK),
    DIAMONDS('â™¦', Color.RED),
  }

  constructor(index: Int) : this(Suit.values()[index / 13], (index % 13) + 1)

  init {
    if (value !in 1..13) {
      throw IllegalArgumentException()
    }
  }

  val valueText
    get() =
        when (value) {
          1 -> "A"
          11 -> "J"
          12 -> "Q"
          13 -> "K"
          else -> "$value"
        }

  val styledText
    get(): CharSequence {
      val spannable = SpannableString("$valueText ${suit.symbol}")
      spannable.setSpan(
          ForegroundColorSpan(suit.color), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
      return spannable
    }

  val index
    get() = (suit.ordinal * 13) + (value - 1)
}


/*
 * 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.collection

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.core.padding
import com.facebook.litho.dp
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useState
import com.facebook.litho.widget.SmoothScrollAlignmentType
import com.facebook.litho.widget.collection.LazyCollectionController
import com.facebook.litho.widget.collection.LazyList

class ScrollToCollectionKComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    val controller = useState { LazyCollectionController() }.value
    return Column(style = Style.padding(16.dp)) {
      child(
          Row {
            child(Button("First") { controller.scrollToIndex(0) })
            child(Button("Position 10") { controller.scrollToIndex(10) })
            child(
                Button("50 to center") {
                  controller.smoothScrollToIndex(
                      50, smoothScrollAlignmentType = SmoothScrollAlignmentType.SNAP_TO_CENTER)
                })
            child(Button("End") { controller.smoothScrollToId("End") })
          })
      child(
          LazyList(
              lazyCollectionController = controller,
              style = Style.flex(grow = 1f),
          ) {
            children(items = (0..99), id = { it }) { Text("$it ") }
            child(id = "End", component = Text("End"))
          })
    }
  }
}


/*
 * 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.collection

import com.facebook.litho.ClickEvent
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.EmptyComponent as MyComponent
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.eventHandler
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useCallback
import com.facebook.litho.useState
import com.facebook.litho.view.onClick
import com.facebook.litho.widget.Text
import com.facebook.litho.widget.collection.LazyList

// start_simple_group
class SimpleGroupMigration(private val title: String) : KComponent() {
  override fun ComponentScope.render(): Component = LazyList {
    // Add SingleComponentSection components as children:
    child(Text(title))
    child(MyComponent())
  }
}
// end_simple_group

class Model(val id: String, val field1: String, val field2: String)

// start_list
class ListMigration(private val data: List<Model>) : KComponent() {
  override fun ComponentScope.render(): Component = LazyList {
    // Add DataDiffSection contents as children with ids
    children(items = data, id = { it.id }) { Text("${it.field1} ${it.field2}") }
  }
}
// end_list

// start_event_handler
class EventHandlerMigration : KComponent() {
  override fun ComponentScope.render(): Component {
    val onClick = useCallback { _: ClickEvent -> println("Hello World!") }
    return LazyList {
      // Using Style.onClick(..)
      child(Text("Say Hello", style = Style.onClick(action = onClick)))

      // Or using the Spec api with eventHandler(..)
      child(Text.create(context).text("Say Hello").clickHandler(eventHandler(onClick)).build())
    }
  }
}
// end_event_handler

// start_state
class StateMigration : KComponent() {
  override fun ComponentScope.render(): Component {
    val counter = useState { 0 }
    val onClick = useCallback { _: ClickEvent -> counter.update { it + 1 } }
    return LazyList {
      child(Text("Increment ${counter.value}", style = Style.onClick(action = onClick)))
    }
  }
}
// end_state


/*
 * 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.collection

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.core.padding
import com.facebook.litho.dp
import com.facebook.litho.drawableRes
import com.facebook.litho.kotlin.widget.Image
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useCallback
import com.facebook.litho.useState
import com.facebook.litho.view.onClick
import com.facebook.litho.widget.collection.LazyList
import com.facebook.yoga.YogaJustify

class SelectionCollectionKComponent : KComponent() {

  private val items = listOf("O-Ren Ishii", "Vernita Green", "Budd", "Elle Driver", "Bill")

  override fun ComponentScope.render(): Component {
    val selected = useState { setOf<String>() }

    fun isSelected(itemIndex: String): Boolean = selected.value.contains(itemIndex)

    fun isAllSelected(): Boolean = selected.value.size == items.size

    val selectItemClickCallback = useCallback { item: String ->
      selected.update(
          selected.value
              .toMutableSet()
              .apply { if (isSelected(item)) remove(item) else add(item) }
              .toSet())
    }

    val selectAllClickCallback = useCallback { _: String ->
      selected.update(if (isAllSelected()) emptySet() else items.toSet())
    }

    return LazyList {
      val isAllSelected = isAllSelected()
      child(deps = arrayOf(isAllSelected)) {
        Selectable(
            item = "Select All", selected = isAllSelected, onRowClick = selectAllClickCallback)
      }

      children(items = items, id = { it }) {
        val selected = isSelected(it)
        Selectable(item = it, selected = selected, onRowClick = selectItemClickCallback)
      }

      child(
          Text(
              selected.value.joinToString(prefix = "Selected: "),
              textColor = Color.DKGRAY,
              style = Style.padding(horizontal = 20.dp, vertical = 10.dp)))
    }
  }
}

class Selectable(val item: String, val selected: Boolean, val onRowClick: (String) -> Unit) :
    KComponent() {

  override fun ComponentScope.render(): Component =
      Row(
          justifyContent = YogaJustify.SPACE_BETWEEN,
          style =
              Style.padding(horizontal = 20.dp, vertical = 10.dp).onClick { onRowClick(item) }) {
            child(Text(item))
            child(
                Image(
                    drawableRes(
                        if (selected) android.R.drawable.checkbox_on_background
                        else android.R.drawable.checkbox_off_background)))
          }
}


/*
 * 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.collection

import android.graphics.Color
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.widget.collection.LazyGrid

class SpanCollectionKComponent : KComponent() {

  override fun ComponentScope.render(): Component {

    return LazyGrid(
        columns = 4,
    ) {
      child(isFullSpan = true, component = Text("Full Span", backgroundColor = Color.WHITE))
      children(items = (0..20), id = { it }) { Text("$it") }
      child(spanSize = 2, component = Text("Span 2", backgroundColor = Color.WHITE))
      children(items = (21..40), id = { it }) { Text("$it") }
      child(spanSize = 3, component = Text("Span 2", backgroundColor = Color.WHITE))
      children(items = (21..40), id = { it }) { Text("$it") }
      child(Text("Default Span", backgroundColor = Color.WHITE))
      children(items = (61..80), id = { it }) { Text("$it") }
    }
  }
}


/*
 * 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.collection

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.Dimen
import com.facebook.litho.KComponent
import com.facebook.litho.ResourcesScope
import com.facebook.litho.Style
import com.facebook.litho.core.height
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.px
import com.facebook.litho.view.background
import com.facebook.litho.widget.collection.LazyStaggeredGrid
import com.facebook.samples.litho.kotlin.drawable.RoundedRect

class StaggeredGridCollectionExample : KComponent() {

  private val colors = listOf(Color.RED, Color.BLUE, Color.GREEN, Color.MAGENTA)
  private val baseHeightDp = 25.dp

  override fun ComponentScope.render(): Component =
      LazyStaggeredGrid(spans = 3) { children(items = (0..30), id = { it }) { item(it) } }

  private fun colorForIndex(index: Int): Int = colors[index % 4]

  private fun ResourcesScope.heightForIndex(index: Int): Dimen =
      (((index % 4) + 1) * baseHeightDp.toPixels()).px

  private fun ResourcesScope.item(index: Int): Component =
      Column(style = Style.padding(all = 5.dp)) {
        child(
            Column(
                style =
                    Style.background(RoundedRect(colorForIndex(index), 10.dp))
                        .height(heightForIndex(index))))
      }
}


/*
 * 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.collection

import android.graphics.Color
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.widget.collection.LazyList

class StickyHeaderCollectionKComponent : KComponent() {

  override fun ComponentScope.render(): Component = LazyList {
    child(isSticky = true, component = Text("Sticky Title 1", backgroundColor = Color.WHITE))
    children(items = (0..20), id = { it }) { Text("$it") }
    child(isSticky = true, component = Text("Sticky Title 2", backgroundColor = Color.WHITE))
    children(items = (21..40), id = { it }) { Text("$it") }
    child(isSticky = false, component = Text("Not sticky Title 3", backgroundColor = Color.WHITE))
    children(items = (41..60), id = { it }) { Text("$it") }
    child(isSticky = true, component = Text("Sticky Title 4", backgroundColor = Color.WHITE))
    children(items = (61..80), id = { it }) { Text("$it") }
  }
}


/*
 * 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.collection

import android.graphics.Color
import android.graphics.Typeface
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.colorRes
import com.facebook.litho.core.padding
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.useState
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick
import com.facebook.litho.widget.TextAlignment
import com.facebook.litho.widget.collection.LazyList
import com.facebook.samples.litho.R

class TabsCollectionKComponent : KComponent() {

  enum class Tab(val title: String) {
    Tab1("Tab1"),
    Tab2("Tab2"),
    Tab3("Tab3"),
  }

  override fun ComponentScope.render(): Component {
    val selectedTab = useState { Tab.Tab1 }

    return LazyList {
      child(tabBar(selectedTab))

      when (selectedTab.value) {
        Tab.Tab1 -> {
          child(id = "tab1_tile", component = Text("Tab1 Title"))
          child(id = "tab1_subtitle", component = Text("Tab1 Subtitle"))
        }
        Tab.Tab2 -> {
          child(id = "tab2_tile", component = Text("Tab2 Title"))
          child(id = "tab2_subtitle", component = Text("Tab2 Subtitle"))
        }
        Tab.Tab3 -> {
          child(id = "tab3_tile", component = Text("Tab3 Title"))
          child(id = "tab3_subtitle", component = Text("Tab3 Subtitle"))
        }
      }
    }
  }

  private fun ResourcesScope.tabBar(selectedTab: State<Tab>): Component = Row {
    Tab.values().forEach { tab ->
      val isSelected = tab == selectedTab.value
      child(
          Text(
              tab.title.uppercase(),
              textSize = 16.sp,
              alignment = TextAlignment.CENTER,
              textColor = if (isSelected) colorRes(R.color.primaryColor) else Color.DKGRAY,
              textStyle = if (isSelected) Typeface.BOLD else Typeface.NORMAL,
              style =
                  Style.padding(all = 16.dp)
                      .flex(grow = 1f)
                      .backgroundColor(
                          if (isSelected) colorRes(R.color.colorPrimaryLightBg) else Color.WHITE)
                      .onClick { selectedTab.update { tab } }))
    }
  }
}


/*
 * 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.documentation

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.alpha
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick

class AlphaTransitionKComponent : KComponent() {

  // component_start
  override fun ComponentScope.render(): Component {
    val isHalfAlpha = useState { false }
    useTransition(Transition.create(SQUARE_KEY).animate(AnimatedProperties.ALPHA))
    return Column(style = Style.onClick { isHalfAlpha.update { !it } }) {
      child(
          Row(
              style =
                  Style.transitionKey(context, SQUARE_KEY, Transition.TransitionKeyType.GLOBAL)
                      .backgroundColor(Color.YELLOW)
                      .width(80.dp)
                      .height(80.dp)
                      .alpha(if (isHalfAlpha.value) 0.5f else 1.0f)))
    }
  }
  // component_end
}

private const val SQUARE_KEY: String = "square"


/*
 * 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.documentation

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.drawableRes
import com.facebook.litho.kotlin.widget.Image
import com.facebook.litho.useState
import com.facebook.litho.view.onClick

// start_example
class CheckboxComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val isChecked = useState { false }

    return Column(style = Style.onClick { isChecked.update { currValue -> !currValue } }) {
      child(
          Image(
              drawable =
                  drawableRes(
                      if (isChecked.value) {
                        android.R.drawable.checkbox_on_background
                      } else {
                        android.R.drawable.checkbox_off_background
                      })))
    }
  }
}
// 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.documentation

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.kotlinStyle
import com.facebook.litho.view.alpha

// start_combine_style_example
class OuterTextComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    return InnerTextComponent(style = Style.margin(all = 8.dp))
  }
}

class InnerTextComponent(private val style: Style? = null) : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Text(
        style = Style.padding(all = 8.dp).alpha(.5f) + style,
        text = "I accept style from a parent!")
  }
}
// end_combine_style_example
// start_combine_java_kotlin_style_example
class OuterStyleKComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val style = Style.margin(all = 8.dp)
    return OuterStyleComponent.create(context).kotlinStyle(style).build()
  }
}
// end_combine_java_kotlin_style_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.documentation

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.kotlin.widget.Text
import com.facebook.litho.view.onClick

// start_example
class ClickEventComponent(
    private val onButtonClicked: ((String) -> Unit),
) : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Column {
      child(Text(text = "OK", style = Style.onClick { onButtonClicked("OK clicked") }))
      child(Text(text = "Cancel", style = Style.onClick { onButtonClicked("Cancel clicked") }))
    }
  }
}
// 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.documentation

import android.graphics.Color.YELLOW
import android.util.Log
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.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick

// start_example
class EventComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Column(
        style = Style.onClick { onClick() }.width(80.dp).height(80.dp).backgroundColor(YELLOW))
  }
}

private fun onClick() {
  Log.d("EventComponent", "click")
}
// 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.documentation

import com.facebook.litho.ClickEvent
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.common.SingleComponentSection
import com.facebook.litho.widget.Text

// start_example
@GroupSectionSpec
object EventHandlerSectionSpec {

  @OnCreateChildren
  fun onCreateChildren(c: SectionContext): Children =
      Children.create()
          .child(
              SingleComponentSection.create(c)
                  .component(
                      Text.create(c)
                          .text("Say Hello")
                          .clickHandler(EventHandlerSection.onClick(c))
                          .build()))
          .build()

  @OnEvent(ClickEvent::class)
  fun onClick(c: SectionContext) {
    println("Hello World!")
  }
}
// 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.documentation

import android.graphics.Typeface
import android.widget.ImageView
import androidx.annotation.DrawableRes
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.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.drawableRes
import com.facebook.litho.flexbox.aspectRatio
import com.facebook.litho.kotlin.widget.Image
import com.facebook.litho.kotlin.widget.Text
import com.facebook.yoga.YogaAlign

// start_example
class FlexboxComponent(
    private val username: String,
    @DrawableRes private val avatarRes: Int,
    @DrawableRes private val imageRes: Int
) : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Column {
      child(
          Row(alignItems = YogaAlign.CENTER, style = Style.padding(all = 8.dp)) {
            child(
                Image(
                    drawable = drawableRes(avatarRes),
                    style = Style.width(36.dp).height(36.dp).margin(start = 4.dp, end = 8.dp)))
            child(Text(text = username, textStyle = Typeface.BOLD))
          })
      child(
          Image(
              drawable = drawableRes(imageRes),
              scaleType = ImageView.ScaleType.CENTER_CROP,
              style = Style.aspectRatio(1f)))
    }
  }
}
// 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.documentation

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.view.onClick

// start_simple_example
class HelloComponent(private val name: String) : KComponent() {

  override fun ComponentScope.render(): Component? {
    return Text(text = "Hello $name!")
  }
}
// end_simple_example

fun log(s: String) = Unit

// start_styled_example
class StyledHelloComponent(private val style: Style? = null, private val name: String) :
    KComponent() {

  override fun ComponentScope.render(): Component? {
    return Text(style = style, text = "Hello $name!")
  }
}

val componentWithOnClick =
    StyledHelloComponent(style = Style.onClick { log("clicked!") }, name = "Common Props")
// end_styled_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.documentation

import android.view.inputmethod.EditorInfo
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.Handle
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.eventHandlerWithReturn
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useEffect
import com.facebook.litho.useRef
import com.facebook.litho.useState
import com.facebook.litho.widget.TextInput

// start_example
class TemperatureConvertor : KComponent() {
  override fun ComponentScope.render(): Component {
    // Singe hoisted state
    val temperatureCelsius = useState { 0.0 }

    return Column {
      child(
          TemperatureInput(
              value = temperatureCelsius.value,
              scale = TemperatureScale.Celsius,
              onReturn = { newTemp -> temperatureCelsius.update(newTemp) }))
      child(
          TemperatureInput(
              value = toFahrenheit(temperatureCelsius.value),
              scale = TemperatureScale.Fahrenheit,
              onReturn = { newTemp -> temperatureCelsius.update(toCelsius(newTemp)) }))
    }
  }
}
// end_example

fun toFahrenheit(degreesCelsius: Double): Double = degreesCelsius * 9 / 5 + 32

fun toCelsius(degreesFahrenheit: Double): Double = (degreesFahrenheit - 32) * 5 / 9

enum class TemperatureScale(val unit: Char) {
  Celsius('C'),
  Fahrenheit('F');

  val degreeUnit: String = "Â°$unit"
}

/** User input for a temperature, including the unit */
class TemperatureInput(
    val value: Double,
    val scale: TemperatureScale,
    private val onReturn: ((Double) -> Unit)? = null
) : KComponent() {
  override fun ComponentScope.render(): Component = Row {
    child(DoubleInput(value, onReturn))
    child(Text("${scale.degreeUnit}"))
  }
}

/** User input for a Double */
class DoubleInput(val double: Double, private val onReturn: ((Double) -> Unit)? = null) :
    KComponent() {
  override fun ComponentScope.render(): Component? {
    val handle = useRef { Handle() }.value

    useEffect(double) {
      TextInput.setText(context, handle, "$double")
      null
    }

    return TextInput.create(context)
        .handle(handle)
        .inputType(
            EditorInfo.TYPE_CLASS_NUMBER or
                EditorInfo.TYPE_NUMBER_FLAG_SIGNED or
                EditorInfo.TYPE_NUMBER_FLAG_DECIMAL)
        .editorActionEventHandler(
            eventHandlerWithReturn {
              it.view.text
                  .toString()
                  .toDoubleOrNull()
                  ?.takeIf { it != double }
                  ?.let { onReturn?.invoke(it) }
              true
            })
        .flexGrow(1f)
        .build()
  }
}


/*
 * 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.documentation

import com.facebook.litho.annotations.FromEvent
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.common.DataDiffSection
import com.facebook.litho.sections.common.OnCheckIsSameContentEvent
import com.facebook.litho.sections.common.OnCheckIsSameItemEvent
import com.facebook.litho.sections.common.RenderEvent
import com.facebook.litho.widget.ComponentRenderInfo
import com.facebook.litho.widget.RenderInfo
import com.facebook.litho.widget.Text

// start_data_type
class Model(val id: String, val field1: String, val field2: String)
// end_data_type

// start_example
@GroupSectionSpec
object ListSectionSpec {

  @OnCreateChildren
  fun onCreateChildren(c: SectionContext, @Prop data: List<Model>): Children =
      Children.create()
          .child(
              DataDiffSection.create<Model>(c)
                  .data(data)
                  .renderEventHandler(ListSection.onRenderItem(c))
                  .onCheckIsSameItemEventHandler(ListSection.onCheckIsSameItem(c))
                  .onCheckIsSameContentEventHandler(ListSection.onCheckIsSameContent(c)))
          .build()

  @OnEvent(RenderEvent::class)
  fun onRenderItem(c: SectionContext, @FromEvent model: Model): RenderInfo =
      ComponentRenderInfo.create()
          // highlight-start
          .component(Text.create(c).text("${model.field1} ${model.field2}").build())
          // highlight-end
          .build()

  @OnEvent(OnCheckIsSameItemEvent::class)
  fun onCheckIsSameItem(
      c: SectionContext,
      @FromEvent previousItem: Model,
      @FromEvent nextItem: Model
  ): Boolean =
      // highlight-start
      previousItem.id == nextItem.id
  // highlight-end

  @OnEvent(OnCheckIsSameContentEvent::class)
  fun onCheckIsSameContent(
      sectionContext: SectionContext,
      @FromEvent previousItem: Model,
      @FromEvent nextItem: Model
  ): Boolean = previousItem.field1 == nextItem.field1 && previousItem.field2 == nextItem.field2
}
// end_example


// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

package com.facebook.samples.litho.kotlin.documentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.facebook.litho.AOSPLithoLifecycleProvider
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.LithoView
import com.facebook.litho.kotlin.widget.Text

// start_example
class LithoViewCreationFragment : Fragment() {

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View? {
    return LithoView.create(requireContext(), SampleComponent(), AOSPLithoLifecycleProvider(this))
  }
}
// end_example
private class SampleComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Text("Hello")
  }
}


/*
 * 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.documentation

import android.content.Context
import android.widget.Toast
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.useRef
import com.facebook.litho.visibility.onVisible

// start_useref_example
class LogOnceComponent : KComponent() {
  override fun ComponentScope.render(): Component {
    val hasLoggedVisible = useRef<Boolean> { false }

    return Text(
        style =
            Style.onVisible {
              // onVisible executes on the main thread, so it's safe to read/write hasLoggedVisible
              if (!hasLoggedVisible.value) {
                doLogVisible(androidContext)
                hasLoggedVisible.value = true
              }
            },
        text = "I'll let you know when I'm visible, but only once!")
  }
}
// end_useref_example

fun doLogVisible(c: Context) {
  Toast.makeText(c, "I'm visible!", Toast.LENGTH_SHORT).show()
}


/*
 * 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.documentation

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Size
import com.facebook.litho.SizeSpec
import com.facebook.litho.SizeSpec.UNSPECIFIED
import com.facebook.litho.drawableRes
import com.facebook.litho.kotlin.widget.Image
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sp
import com.facebook.samples.litho.R

// start_example
class LongTextReplacerKComponent(private val widthSpec: Int, private val heightSpec: Int) :
    KComponent() {

  override fun ComponentScope.render(): Component {
    val textComponent = Text(textSize = 16.sp, text = "Some text to measure")

    val textOutputSize = Size()

    textComponent.measure(
        context,
        SizeSpec.makeSizeSpec(0, UNSPECIFIED),
        SizeSpec.makeSizeSpec(0, UNSPECIFIED),
        textOutputSize)

    // Small component to use in case textComponent doesnâ€™t fit within
    // the current layout.
    val imageComponent = Image(drawable = drawableRes(R.drawable.ic_launcher))

    // Assuming SizeSpec.getMode(widthSpec) == EXACTLY or AT_MOST.
    val layoutWidth = SizeSpec.getSize(widthSpec)
    val textFits = textOutputSize.width <= layoutWidth

    return if (textFits) textComponent else imageComponent
  }
}
// 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.documentation

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec

// start_example
@LayoutSpec
object LongTextReplacerWrapperComponentSpec {

  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(c: ComponentContext, widthSpec: Int, heightSpec: Int): Component {
    return LongTextReplacerKComponent(widthSpec, heightSpec)
  }
}
// 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.documentation

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.Style
import com.facebook.litho.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.eventHandlerWithReturn
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.common.DataDiffSection
import com.facebook.litho.sections.common.RenderEvent
import com.facebook.litho.sections.widget.RecyclerCollectionComponent
import com.facebook.litho.widget.ComponentRenderInfo
import com.facebook.litho.widget.RenderInfo

// start_example
class SectionComponent(private val words: List<String>) : KComponent() {
  override fun ComponentScope.render(): Component? {
    return Column(style = Style.width(80.dp).height(80.dp)) {
      child(
          RecyclerCollectionComponent(
              section =
                  DataDiffSection.create<String>(SectionContext(context))
                      .data(words)
                      .renderEventHandler(eventHandlerWithReturn { event -> onRender(event) })
                      .build()))
    }
  }

  private fun ResourcesScope.onRender(event: RenderEvent<String>): RenderInfo {
    return ComponentRenderInfo.create().component(Text(text = event.model)).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.documentation

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.Style
import com.facebook.litho.Transition
import com.facebook.litho.core.margin
import com.facebook.litho.dp
import com.facebook.litho.transition.useTransition
import com.facebook.litho.useState
import com.facebook.litho.view.onClick
import com.facebook.litho.widget.SolidColor
import com.facebook.yoga.YogaAlign

class SimpleAllLayoutTransitionKComponent : KComponent() {

  // layout_start
  override fun ComponentScope.render(): Component {
    val toRight = useState { false }
    return Column(
        style = Style.margin(all = 10.dp).onClick { toRight.update { !it } },
        alignItems = if (toRight.value) YogaAlign.FLEX_END else YogaAlign.FLEX_START) {
          child(SolidColor.create(context).color(Color.YELLOW).widthDip(80f).heightDip(80f).build())
        }
  }
  // layout_end
}

class SimpleAllLayoutTransitionKComponentV2 : KComponent() {

  override fun ComponentScope.render(): Component {
    val toRight = useState { false }
    // transition_start
    useTransition(Transition.allLayout())
    // transition_end
    return Column(
        style = Style.margin(all = 10.dp).onClick { toRight.update { !it } },
        alignItems = if (toRight.value) YogaAlign.FLEX_END else YogaAlign.FLEX_START) {
          child(SolidColor.create(context).color(Color.YELLOW).widthDip(80f).heightDip(80f).build())
        }
  }
}


/*
 * 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.documentation

import com.facebook.litho.EmptyComponent as MyComponent
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.common.SingleComponentSection
import com.facebook.litho.widget.Text

// start_example
@GroupSectionSpec
object SimpleGroupSectionSpec {

  @OnCreateChildren
  fun onCreateChildren(c: SectionContext, @Prop title: String): Children =
      Children.create()
          // highlight-start
          .child(SingleComponentSection.create(c).component(Text.create(c).text(title).build()))
          .child(SingleComponentSection.create(c).component(MyComponent()))
          // highlight-end
          .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.documentation

import com.facebook.litho.ClickEvent
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.State
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.common.SingleComponentSection
import com.facebook.litho.widget.Text

// start_example
@GroupSectionSpec
object StateSectionSpec {

  @OnCreateInitialState
  fun createInitialState(c: SectionContext, counter: StateValue<Int>) {
    counter.set(0)
  }

  @OnCreateChildren
  fun onCreateChildren(c: SectionContext, @State counter: Int): Children =
      Children.create()
          .child(
              SingleComponentSection.create(c)
                  .component(
                      Text.create(c)
                          .text("Increment: $counter")
                          .clickHandler(StateSection.onClick(c))
                          .build()))
          .build()

  @OnEvent(ClickEvent::class)
  fun onClick(c: SectionContext) {
    StateSection.incrementCounter(c)
  }

  @OnUpdateState
  fun incrementCounter(counter: StateValue<Int>) {
    counter.set((counter.get() ?: 0) + 1)
  }
}
// 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.documentation

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.alpha
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick

// start_example
class TransitionComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val isHalfAlpha = useState { false }
    useTransition(Transition.create("square").animate(AnimatedProperties.ALPHA))

    return Column(style = Style.onClick { isHalfAlpha.update(!isHalfAlpha.value) }) {
      child(
          Row(
              style =
                  Style.backgroundColor(Color.YELLOW)
                      .width(80.dp)
                      .height(80.dp)
                      .alpha(
                          if (isHalfAlpha.value) {
                            0.5f
                          } else {
                            1.0f
                          })
                      .transitionKey(context, "square")))
    }
  }
}
// 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.documentation

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.kotlin.widget.Text
import com.facebook.litho.useCached
import com.facebook.litho.useState
import com.facebook.litho.view.onClick

// start_example
class UseCachedWithDependencyComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val number = useState { 1 }

    val expensiveValue = useCached(number) { expensiveRepeatFunc("hello", number.value) }

    return Column(style = Style.onClick { number.update { n -> n + 1 } }) {
      child(Text(text = expensiveValue))
    }
  }

  companion object {
    private fun expensiveRepeatFunc(prefix: String, num: Int = 20): String {
      return StringBuilder().apply { repeat(num) { append(prefix) } }.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.kotlin.documentation

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.kotlin.widget.Text
import com.facebook.litho.useCached
import com.facebook.litho.useState
import com.facebook.litho.view.onClick

// start_example
class UseCachedWithoutDependencyComponent : KComponent() {
  override fun ComponentScope.render(): Component? {
    val number = useState { 1 }

    val expensiveValue = useCached {
      // state isn't declared as a dependency, so initial state value will always be used
      expensiveRepeatFunc("hello", number.value)
    }

    return Column(style = Style.onClick { number.update { n -> n + 1 } }) {
      child(Text(text = expensiveValue))
    }
  }
  // end_example
  companion object {
    private fun expensiveRepeatFunc(prefix: String, num: Int = 20): String {
      return StringBuilder().apply { repeat(num) { append(prefix) } }.toString()
    }
  }
}


/*
 * 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.documentation

import android.util.Log
import android.view.View
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.kotlin.widget.Text
import com.facebook.litho.visibility.onInvisible
import com.facebook.litho.visibility.onVisibilityChanged
import com.facebook.litho.visibility.onVisible

// start_example
class VisibilityHandlingExampleComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    return Column(
        style =
            Style.onVisible { event ->
                  // If the handler was set on a component which mounts content then the
                  // event.content will be a reference to the mounted content.
                  if (event.content is View) {
                    log("Visible", "View")
                  } else {
                    log("Visible", "Drawable")
                  }
                }
                .onInvisible { log("Invisible", "null") }) {
          child(Text("hello world"))
          child(
              Row(
                  style =
                      Style.onVisibilityChanged { event ->
                        if (event.percentVisibleHeight > 50) {
                          Log.d(
                              "visibility-changed",
                              "View is mostly visible now. With: " +
                                  "\ntop: ${event.visibleTop}" +
                                  "\nleft: ${event.visibleLeft}" +
                                  "\nvisible width: ${event.visibleWidth}" +
                                  "\nvisible height: ${event.visibleHeight}" +
                                  "\npercentage visible height: ${event.percentVisibleHeight}" +
                                  "\npercentage visible width: ${event.percentVisibleWidth}")
                        }
                      }) {
                    child(Text("This is an example."))
                  })
        }
  }

  fun log(type: String, content: String) {
    Log.d("visibility", "Visibility callback: $type content: $content")
  }
}
// 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.drawable

import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RoundRectShape
import androidx.annotation.ColorInt
import androidx.annotation.Px
import com.facebook.litho.Dimen
import com.facebook.litho.ResourcesScope

private fun RoundedRect(@ColorInt fillColor: Int, @Px cornerRadiusPx: Float): Drawable =
    ShapeDrawable(RoundRectShape(FloatArray(8) { cornerRadiusPx }, null, null)).apply {
      paint.color = fillColor
    }

/** Create a Rectangle drawable with rounded corners */
fun ResourcesScope.RoundedRect(@ColorInt fillColor: Int, cornerRadius: Dimen): Drawable =
    RoundedRect(fillColor, cornerRadius.toPixels(resourceResolver).toFloat())

/**
 * Create a Rectangle drawable with rounded corners. Kotlin resolves numbers >= 0x80000000 as a
 * Long. This is a convenience function that accepts [fillColor] as a Long.
 */
fun ResourcesScope.RoundedRect(@ColorInt fillColor: Long, cornerRadius: Dimen): Drawable =
    RoundedRect(fillColor.toInt(), cornerRadius)


/*
 * 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.errors

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

class ErrorHandlingKotlinActivity : NavigatableDemoActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(
        LithoView.create(
            this, ErrorRootComponent.create(ComponentContext(this)).dataModels(DATA).build()))
  }

  companion object {
    private val DATA =
        listOf(
            ListRow("First Title", "First Subtitle"),
            ListRow("Second Title", "Second Subtitle"),
            ListRow("Third Title", "Third Subtitle"),
            ListRow("Fourth Title", "Fourth Subtitle"),
            ListRow("Fifth Title", "Fifth Subtitle"),
            ListRow("Sixth Title", "Sixth Subtitle"),
            ListRow("Seventh Title", "Seventh Subtitle"))
  }
}


/*
 * 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.errors

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.FromEvent
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.common.DataDiffSection
import com.facebook.litho.sections.common.RenderEvent
import com.facebook.litho.sections.widget.RecyclerCollectionComponent
import com.facebook.litho.widget.ComponentRenderInfo
import com.facebook.litho.widget.RenderInfo
import com.facebook.yoga.YogaEdge

@LayoutSpec
object ErrorRootComponentSpec {

  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop dataModels: List<ListRow>): Component =
      RecyclerCollectionComponent.create(c)
          .disablePTR(true)
          .section(
              DataDiffSection.create<ListRow>(SectionContext(c))
                  .data(dataModels)
                  .renderEventHandler(ErrorRootComponent.onRender(c))
                  .build())
          .paddingDip(YogaEdge.TOP, 8f)
          .build()

  @OnEvent(RenderEvent::class)
  fun onRender(c: ComponentContext, @FromEvent model: ListRow): RenderInfo =
      ComponentRenderInfo.create().component(model.createComponent(c)).build()
}


/*
 * 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.errors

import android.graphics.Typeface
import android.util.Log
import androidx.annotation.ColorInt
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.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.utils.StacktraceHelper
import com.facebook.litho.view.backgroundColor
import com.facebook.litho.view.onClick

/**
 * Renders a throwable as a text with a title and provides a touch callback that logs the throwable
 * with WTF level.
 */
class KDebugComponent(private val message: String, private val throwable: Throwable) :
    KComponent() {

  private val TAG = "KDebugComponent"

  @ColorInt private val DARK_RED_FRAME = 0xffcd4928.toInt()
  @ColorInt private val LIGHT_RED_BACKGROUND = 0xfffcece9.toInt()
  @ColorInt private val LIGHT_GRAY_TEXT = 0xff606770.toInt()

  override fun ComponentScope.render(): Component? {
    Log.e(TAG, message, throwable)

    return Column(
        style =
            Style.onClick { onClick(message, throwable) }
                .padding(all = 1.dp)
                .backgroundColor(DARK_RED_FRAME)) {
          child(
              Text(
                  style = Style.padding(all = 4.dp).backgroundColor(LIGHT_RED_BACKGROUND),
                  text = message,
                  textSize = 16.dp))
          child(
              Text(
                  style = Style.padding(all = 4.dp).backgroundColor(LIGHT_RED_BACKGROUND),
                  text = StacktraceHelper.formatStacktrace(throwable),
                  textSize = 12.dp,
                  textColor = LIGHT_GRAY_TEXT,
                  typeface = Typeface.MONOSPACE))
        }
  }

  private fun onClick(message: String, throwable: Throwable) {
    Log.wtf(TAG, message, throwable)
  }
}


/*
 * 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.errors

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.core.margin
import com.facebook.litho.dp
import com.facebook.litho.useErrorBoundary
import com.facebook.litho.useState

// start_example
class KErrorBoundary(private val childComponent: Component) : KComponent() {

  override fun ComponentScope.render(): Component? {
    val errorState = useState<Exception?> { null }
    useErrorBoundary { exception: Exception -> errorState.update(exception) }

    errorState.value?.let {
      return Column(style = Style.margin(all = 16.dp)) {
        child(KDebugComponent(message = "KComponent's Error Boundary", throwable = it))
      }
    }

    return childComponent
  }
}
// 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.errors

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import java.lang.Exception

// start_example
class KIncorrectErrorHandlingComponent : KComponent() {

  override fun ComponentScope.render(): Component {
    // This implementation won't work - you need to implement error boundary with useErrorBoundary
    // hook instead.
    return try {
      // PossiblyCrashingSubTitleComponent can crash
      PossiblyCrashingSubTitleComponent.create(context).subtitle("example").build()
    } catch (e: Exception) {
      // error handling code
      KDebugComponent(message = "Error Component", throwable = e)
    }
  }
}
// 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.errors

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.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.widget.Card

class KListRow(private val row: ListRow) : KComponent() {

  override fun ComponentScope.render(): Component? {
    return Column(style = Style.padding(vertical = 8.dp, horizontal = 32.dp)) {
      child(
          Card.create(context)
              .content(
                  Column(style = Style.margin(all = 32.dp)) {
                    child(TitleComponent.create(context).title(row.title).build())
                    child(
                        PossiblyCrashingSubTitleComponent.create(context)
                            .subtitle(row.subtitle)
                            .build())
                  })
              .build())
    }
  }
}


/*
 * 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.errors

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext

data class ListRow(val title: String, val subtitle: String) {
  fun createComponent(c: ComponentContext): Component = KErrorBoundary(KListRow(this))
}


/*
 * 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.errors

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ResType.STRING
import com.facebook.litho.widget.Text

@LayoutSpec
object PossiblyCrashingSubTitleComponentSpec {

  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop(resType = STRING) subtitle: String): Component {
    if (Math.random() >= 0.7) {
      throw RuntimeException("Oh no, a random error!")
    }

    return Text.create(c).text(subtitle).textSizeDip(18f).build()
  }
}


/*
 * 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.errors

import android.graphics.Typeface.BOLD
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ResType.STRING
import com.facebook.litho.widget.Text
import com.facebook.yoga.YogaEdge

@LayoutSpec
object TitleComponentSpec {

  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop(resType = STRING) title: String): Component =
      Text.create(c)
          .text(title)
          .textStyle(BOLD)
          .textSizeDip(24f)
          .positionDip(YogaEdge.BOTTOM, 16f)
          .build()
}


/*
 * 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.gettingstarted

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.widget.collection.LazyList

private val ONE_TO_TEN =
    listOf("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten")

class BasicList : KComponent() {
  override fun ComponentScope.render(): Component = LazyList {
    child(Text(text = "Header"))
    ONE_TO_TEN.forEach { child(id = it, component = Text(it)) }
    child(Text(text = "Footer"))
  }
}


/*
 * 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.gettingstarted

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.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sp
import com.facebook.litho.useState
import com.facebook.litho.view.onClick
import com.facebook.litho.view.viewId
import com.facebook.samples.litho.R

/**
 * Text component which will display a greeting with the given name.
 *
 * @param name the name to display in the greeting message
 * @constructor Instantiates a new BootcampText component
 * @property name the name that will be displayed in the greeting message
 */
class ClickableText(private val name: String) : KComponent() {
  override fun ComponentScope.render(): Component {
    val clickCounter = useState { 0 }

    return Column {
      child(
          Text(
              style = Style.margin(top = 16.dp, horizontal = 16.dp),
              text = "Hello $name!",
              textSize = 20.sp,
          ))
      child(
          Text(
              style =
                  Style.onClick { clickCounter.update { it + 1 } }
                      .viewId(R.id.sample)
                      .padding(all = 16.dp),
              text = "I have been clicked ${clickCounter.value} times.",
              textSize = 20.sp,
          ))
    }
  }
}


/*
 * 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.gettingstartedsolution

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.core.margin
import com.facebook.litho.dp
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.widget.collection.LazyList

/**
 * Components which consists in a vertical list with all characters from the given sentence.
 *
 * @property text the sentence to be displayed vertically
 */
class VerticalSpeller(private val text: String) : KComponent() {
  override fun ComponentScope.render(): Component {
    return LazyList(style = Style.flex(grow = 1f)) {
      child(Text(text = "Spelling of $text"))
      // Using index as id is recommended here as we may have the same character twice in the text
      text.forEachIndexed { index, character ->
        child(
            id = index,
            component = Text(style = Style.margin(horizontal = 16.dp), text = character.toString()))
      }
    }
  }
}


/*
 * 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.lithography

import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoView
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.widget.RecyclerCollectionComponent
import com.facebook.samples.litho.NavigatableDemoActivity
import com.facebook.samples.litho.kotlin.lithography.data.DataFetcher
import com.facebook.samples.litho.kotlin.lithography.data.DecadeViewModel
import com.facebook.samples.litho.kotlin.lithography.data.Model
import com.facebook.samples.litho.kotlin.lithography.sections.LithoFeedSection
import kotlin.LazyThreadSafetyMode.NONE

class LithographyKotlinActivity : NavigatableDemoActivity() {

  private val c by lazy(NONE) { ComponentContext(this) }
  private val lithoView by lazy(NONE) { LithoView(c) }
  private val fetcher by lazy(NONE) { DataFetcher(viewModel.model) }
  private val viewModel by lazy(NONE) { ViewModelProvider(this).get(DecadeViewModel::class.java) }

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

    viewModel.model.observe(this, Observer { setList(it) })
    setContentView(lithoView)
  }

  private fun setList(model: Model?) {
    model?.let { lithoView.setComponentAsync(createRecyclerComponent(it)) }
  }

  private fun createRecyclerComponent(model: Model): Component =
      RecyclerCollectionComponent.create(c)
          .section(
              LithoFeedSection.create(SectionContext(c))
                  .decades(model.decades)
                  .fetcher(fetcher)
                  .loading(model.isLoading)
                  .build())
          .disablePTR(true)
          .build()
}


/*
 * 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.lithography.components

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.core.padding
import com.facebook.litho.dp
import com.facebook.litho.drawableColor
import com.facebook.litho.view.background

class ActionsComponent(val style: Style? = null) : KComponent() {

  override fun ComponentScope.render(): Component? {
    return Row(style = Style.padding(2.dp).background(drawableColor(0xddffffff)) + style) {
      child(FavouriteButton())
    }
  }
}


/*
 * 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.lithography.components

import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Row
import com.facebook.litho.Style
import com.facebook.litho.core.height
import com.facebook.litho.core.margin
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.drawableColor
import com.facebook.litho.flexbox.flex
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.px
import com.facebook.litho.sp
import com.facebook.litho.view.background
import com.facebook.samples.litho.kotlin.lithography.data.Decade
import com.facebook.yoga.YogaAlign.CENTER

class DecadeSeparator(val decade: Decade) : KComponent() {
  override fun ComponentScope.render() =
      Row(alignItems = CENTER, style = Style.padding(16.dp).background(drawableColor(0xFFFAFAFA))) {
        child(Row(style = Style.height(1.px).flex(grow = 1f).background(drawableColor(0xFFAAAAAA))))
        child(
            Text(
                text = "${decade.year}",
                textSize = 14.sp,
                textColor = 0xFFAAAAAA.toInt(),
                style = Style.margin(horizontal = 10.dp).flex(shrink = 0f)))
        child(Row(style = Style.height(1.px).flex(grow = 1f).background(drawableColor(0xFFAAAAAA))))
      }
}


/*
 * 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.lithography.components

import android.R.drawable.star_off
import android.R.drawable.star_on
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.core.height
import com.facebook.litho.core.width
import com.facebook.litho.dp
import com.facebook.litho.drawableRes
import com.facebook.litho.useState
import com.facebook.litho.view.background
import com.facebook.litho.view.onClick

class FavouriteButton : KComponent() {
  override fun ComponentScope.render(): Component {
    val isFavourite = useState { false }
    val star = drawableRes(if (isFavourite.value) star_on else star_off)

    return Row(
        style =
            Style.width(32.dp).height(32.dp).background(star).onClick {
              isFavourite.update { isFavourite -> !isFavourite }
            })
  }
}


/*
 * 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.lithography.components

import com.facebook.litho.Column
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Card
import com.facebook.samples.litho.kotlin.lithography.data.Artist

class FeedItemCard(val artist: Artist) : KComponent() {
  override fun ComponentScope.render() =
      Column(style = Style.padding(horizontal = 16.dp, vertical = 8.dp)) {
        child(Card { FeedItemComponent(artist = artist) })
      }
}


/*
 * 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.lithography.components

import android.graphics.Typeface.BOLD
import android.widget.LinearLayout
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.Style
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.drawableColor
import com.facebook.litho.flexbox.aspectRatio
import com.facebook.litho.flexbox.position
import com.facebook.litho.flexbox.positionType
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.widget.ListRecyclerConfiguration
import com.facebook.litho.sections.widget.RecyclerCollectionComponent
import com.facebook.litho.sp
import com.facebook.litho.view.background
import com.facebook.litho.widget.SnapUtil
import com.facebook.samples.litho.kotlin.lithography.data.Artist
import com.facebook.samples.litho.kotlin.lithography.sections.ImagesSection
import com.facebook.yoga.YogaPositionType

class FeedItemComponent(val artist: Artist) : KComponent() {
  override fun ComponentScope.render() =
      Column() {
        child(
            Column() {
              child(imageBlock(artist))
              child(
                  Text(
                      text = artist.name,
                      style =
                          Style.position(start = 4.dp, bottom = 4.dp)
                              .positionType(YogaPositionType.ABSOLUTE)
                              .padding(horizontal = 6.dp)
                              .background(drawableColor(0xddffffff)),
                      textSize = 24.sp,
                      textStyle = BOLD))
              child(
                  ActionsComponent(
                      style =
                          Style.position(top = 4.dp, end = 4.dp)
                              .positionType(YogaPositionType.ABSOLUTE)))
            })
        child(FooterComponent(text = artist.biography))
      }
}

private val recyclerConfiguration =
    ListRecyclerConfiguration.create()
        .orientation(LinearLayout.HORIZONTAL)
        .snapMode(SnapUtil.SNAP_TO_CENTER)
        .build()

private fun ResourcesScope.imageBlock(artist: Artist): Component =
    when (artist.images.size) {
      1 -> SingleImageComponent(imageUri = artist.images[0], imageAspectRatio = 2f)
      else -> imageRecycler(artist)
    }

private fun ResourcesScope.imageRecycler(artist: Artist): Component =
    RecyclerCollectionComponent.create(context)
        .recyclerConfiguration(recyclerConfiguration)
        .section(ImagesSection.create(SectionContext(context)).images(artist.images).build())
        .aspectRatio(2f)
        .build()


/*
 * 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.lithography.components

import android.graphics.Color.GRAY
import android.graphics.Typeface.ITALIC
import com.facebook.litho.Column
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.Style
import com.facebook.litho.core.padding
import com.facebook.litho.dp
import com.facebook.litho.kotlin.widget.Text

class FooterComponent(val text: String) : KComponent() {
  override fun ComponentScope.render() =
      Column(style = Style.padding(8.dp)) {
        child(Text(text = text, textColor = GRAY, textStyle = ITALIC))
      }
}