;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL

(ns app.main.ui.workspace.sidebar.options.menus.layout-item
  (:require-macros [app.main.style :as stl])
  (:require
   [app.common.data :as d]
   [app.common.schema :as sm]
   [app.common.types.shape.layout :as ctl]
   [app.main.data.workspace :as udw]
   [app.main.data.workspace.shape-layout :as dwsl]
   [app.main.data.workspace.tokens.application :as dwta]
   [app.main.features :as features]
   [app.main.refs :as refs]
   [app.main.store :as st]
   [app.main.ui.components.numeric-input :as deprecated-input]
   [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
   [app.main.ui.components.title-bar :refer [title-bar*]]
   [app.main.ui.ds.foundations.assets.icon :as i]
   [app.main.ui.icons :as deprecated-icon]
   [app.main.ui.workspace.sidebar.options.common :as soc]
   [app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
   [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
   [app.util.dom :as dom]
   [app.util.i18n :as i18n :refer [tr]]
   [rumext.v2 :as mf]))

(def layout-item-attrs
  [:layout-item-margin      ;; {:m1 0 :m2 0 :m3 0 :m4 0}
   :layout-item-margin-type ;; :simple :multiple
   :layout-item-h-sizing    ;; :fill :fix :auto
   :layout-item-v-sizing    ;; :fill :fix :auto
   :layout-item-max-h       ;; num
   :layout-item-min-h       ;; num
   :layout-item-max-w       ;; num
   :layout-item-min-w       ;; num
   :layout-item-align-self  ;; :start :end :center :stretch :baseline
   :layout-item-absolute
   :layout-item-z-index])

(defn- select-margins
  [m1? m2? m3? m4?]
  (st/emit! (udw/set-margins-selected {:m1 m1? :m2 m2? :m3 m3? :m4 m4?})))

(defn- select-margin
  [prop]
  (select-margins (= prop :m1) (= prop :m2) (= prop :m3) (= prop :m4)))

(mf/defc margin-simple*
  [{:keys [value on-change on-blur applied-tokens ids]}]
  (let [token-numeric-inputs
        (features/use-feature "tokens/numeric-input")

        m1 (:m1 value)
        m2 (:m2 value)
        m3 (:m3 value)
        m4 (:m4 value)

        m1 (when (and (not= value :multiple) (= m1 m3)) m1)
        m2 (when (and (not= value :multiple) (= m2 m4)) m2)

        token-applied-m1 (:m1 applied-tokens)
        token-applied-m2 (:m2 applied-tokens)
        token-applied-m3 (:m3 applied-tokens)
        token-applied-m4 (:m4 applied-tokens)

        token-applied-m1 (if (and (not= applied-tokens :multiple) (= token-applied-m1 token-applied-m3)) token-applied-m1
                             :multiple)

        token-applied-m2 (if (and (not= applied-tokens :multiple) (= token-applied-m2 token-applied-m4)) token-applied-m2
                             :multiple)

        m1-placeholder (if (and (not= value :multiple)
                                (= m1 m3)
                                (= token-applied-m1 token-applied-m3))
                         "--"
                         (tr "settings.multiple"))

        m2-placeholder (if (and (not= value :multiple)
                                (= m2 m4)
                                (= token-applied-m2 token-applied-m4))
                         "--"
                         (tr "settings.multiple"))

        on-focus
        (mf/use-fn
         (mf/deps select-margins)
         (fn [attr event]
           (case attr
             :m1 (select-margins true false true false)
             :m2 (select-margins false true false true))
           (dom/select-target event)))

        on-detach-token
        (mf/use-fn
         (mf/deps ids)
         (fn [token-name attr]
           (st/emit! (dwta/unapply-token {:token-name token-name
                                          :attributes #{attr}
                                          :shape-ids ids}))))

        on-detach-horizontal
        (mf/use-fn
         (mf/deps on-detach-token)
         (fn [token]
           (run! #(on-detach-token token %) [:m2 :m4])))

        on-detach-vertical
        (mf/use-fn
         (mf/deps on-detach-token)
         (fn [token]
           (run! #(on-detach-token token %) [:m1 :m3])))

        on-change'
        (mf/use-fn
         (mf/deps on-change ids)
         (fn [value attr]
           (soc/emit-value-or-token value
                                    #(on-change :simple attr %)
                                    ids
                                    (if (= :m1 attr) #{:m1 :m3} #{:m2 :m4}))))

        on-focus-m1
        (mf/use-fn (mf/deps on-focus) #(on-focus :m1))

        on-focus-m2
        (mf/use-fn (mf/deps on-focus) #(on-focus :m2))

        on-m1-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m1))

        on-m2-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m2))]

    [:div {:class (stl/css :margin-simple)}
     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m1-change
         :on-detach on-detach-vertical
         :class (stl/css :vertical-margin-wrapper)
         :on-blur on-blur
         :on-focus on-focus-m1
         :placeholder m1-placeholder
         :icon i/margin-top-bottom
         :attr :m1
         :default nil
         :input-type :vertical-margin
         :property "Vertical margin "
         :nillable true
         :applied-token token-applied-m1
         :value m1}]

       [:div {:class (stl/css :vertical-margin)
              :title "Vertical margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-top-bottom]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder m1-placeholder
                                             :data-name "m1"
                                             :on-focus on-focus-m1
                                             :on-change on-m1-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m1}]])

     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m2-change
         :on-detach on-detach-horizontal
         :on-blur on-blur
         :on-focus on-focus-m2
         :placeholder m2-placeholder
         :icon i/margin-left-right
         :class (stl/css :horizontal-margin-wrapper)
         :attr :m2
         :align :right
         :default nil
         :input-type :horizontal-margin
         :property "Horizontal margin"
         :nillable true
         :applied-token token-applied-m2
         :value m2}]

       [:div {:class (stl/css :horizontal-margin)
              :title "Horizontal margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-left-right]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder m2-placeholder
                                             :data-name "m2"
                                             :on-focus on-focus-m2
                                             :on-change on-m2-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m2}]])]))

(mf/defc margin-multiple*
  [{:keys [value on-change on-blur applied-tokens ids]}]
  (let [token-numeric-inputs
        (features/use-feature "tokens/numeric-input")

        m1     (:m1 value)
        m2     (:m2 value)
        m3     (:m3 value)
        m4     (:m4 value)

        applied-token-to-m1     (:m1 applied-tokens)
        applied-token-to-m2     (:m2 applied-tokens)
        applied-token-to-m3     (:m3 applied-tokens)
        applied-token-to-m4     (:m4 applied-tokens)

        on-detach-token
        (mf/use-fn
         (mf/deps ids)
         (fn [token-name attr]
           (st/emit! (dwta/unapply-token {:token-name token-name
                                          :attributes #{attr}
                                          :shape-ids ids}))))

        on-focus
        (mf/use-fn
         (mf/deps select-margin)
         (fn [attr event]
           (select-margin attr)
           (dom/select-target event)))

        on-focus-m1
        (mf/use-fn (mf/deps on-focus) #(on-focus :m1))

        on-focus-m2
        (mf/use-fn (mf/deps on-focus) #(on-focus :m2))

        on-focus-m3
        (mf/use-fn (mf/deps on-focus) #(on-focus :m3))

        on-focus-m4
        (mf/use-fn (mf/deps on-focus) #(on-focus :m4))

        on-change'
        (mf/use-fn
         (mf/deps on-change ids)
         (fn [value attr]
           (soc/emit-value-or-token value
                                    #(on-change :multiple attr %)
                                    ids
                                    #{attr})))

        on-m1-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m1))

        on-m2-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m2))

        on-m3-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m3))

        on-m4-change
        (mf/use-fn (mf/deps on-change') #(on-change' % :m4))]

    [:div {:class (stl/css :margin-multiple)}
     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m1-change
         :on-detach on-detach-token
         :on-blur on-blur
         :on-focus on-focus-m1
         :icon i/margin-top
         :class (stl/css :top-margin-wrapper)
         :default nil
         :attr :m1
         :input-type :vertical-margin
         :property "Top margin"
         :nillable true
         :applied-token applied-token-to-m1
         :value m1}]

       [:div {:class (stl/css :top-margin)
              :title "Top margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-top]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder "--"
                                             :data-name "m1"
                                             :on-focus on-focus-m1
                                             :on-change on-m1-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m1}]])
     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m2-change
         :on-detach on-detach-token
         :on-blur on-blur
         :on-focus on-focus-m2
         :icon i/margin-right
         :class (stl/css :right-margin-wrapper)
         :default nil
         :attr :m2
         :align :right
         :input-type :horizontal-margin
         :property "Right margin"
         :nillable true
         :applied-token applied-token-to-m2
         :value m2}]

       [:div {:class (stl/css :right-margin)
              :title "Right margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-right]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder "--"
                                             :data-name "m2"
                                             :on-focus on-focus-m2
                                             :on-change on-m2-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m2}]])

     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m3-change
         :on-detach on-detach-token
         :on-blur on-blur
         :on-focus on-focus-m3
         :icon i/margin-bottom
         :class (stl/css :bottom-margin-wrapper)
         :attr :m3
         :default nil
         :input-type :vertical-margin
         :property "Bottom margin"
         :nillable true
         :applied-token applied-token-to-m3
         :value m3}]

       [:div {:class (stl/css :bottom-margin)
              :title "Bottom margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-bottom]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder "--"
                                             :data-name "m3"
                                             :on-focus on-focus-m3
                                             :on-change on-m3-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m3}]])

     (if token-numeric-inputs
       [:> numeric-input-wrapper*
        {:on-change on-m4-change
         :on-detach on-detach-token
         :on-blur on-blur
         :on-focus on-focus-m4
         :icon i/margin-left
         :class (stl/css :left-margin-wrapper)
         :default nil
         :attr :m4
         :align :right
         :property "Left margin"
         :input-type :horizontal-margin
         :nillable true
         :applied-token applied-token-to-m4
         :value m4}]

       [:div {:class (stl/css :left-margin)
              :title "Left margin"}
        [:span {:class (stl/css :icon)}
         deprecated-icon/margin-left]
        [:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
                                             :placeholder "--"
                                             :data-name "m4"
                                             :on-focus on-focus-m4
                                             :on-change on-m4-change
                                             :on-blur on-blur
                                             :is-nillable true
                                             :value m4}]])]))

(mf/defc margin-section*
  {::mf/private true
   ::mf/expect-props #{:value :type :on-type-change :on-change :applied-tokens :ids}}
  [{:keys [type on-type-change] :as props}]
  (let [type       (d/nilv type :simple)
        on-blur    (mf/use-fn
                    (mf/deps select-margins)
                    #(select-margins false false false false))
        props      (mf/spread-props props {:on-blur on-blur})

        on-type-change'
        (mf/use-fn
         (mf/deps type on-type-change)
         (fn [_]
           (if (= type :multiple)
             (on-type-change :simple)
             (on-type-change :multiple))))]

    (mf/with-effect []
      (fn [] (on-blur)))

    [:div {:class (stl/css :margin-row)}
     [:div {:class (stl/css :inputs-wrapper)}
      (cond
        (= type :simple)
        [:> margin-simple* props]

        (= type :multiple)
        [:> margin-multiple* props])]

     [:button {:class (stl/css-case
                       :margin-mode true
                       :selected (= type :multiple))
               :title "Margin - multiple"
               :on-click on-type-change'}
      deprecated-icon/margin]]))

(mf/defc element-behaviour-horizontal
  {::mf/props :obj
   ::mf/private true}
  [{:keys [^boolean is-auto ^boolean has-fill value on-change]}]
  [:div {:class (stl/css-case
                 :horizontal-behaviour true
                 :one-element (and (not has-fill) (not is-auto))
                 :two-element (or has-fill is-auto)
                 :three-element (and has-fill is-auto))}
   [:& radio-buttons
    {:selected  (d/name value)
     :decode-fn keyword
     :on-change on-change
     :name      "flex-behaviour-h"}

    [:& radio-button
     {:value "fix"
      :icon  i/fixed-width
      :title "Fix width"
      :id    "behaviour-h-fix"}]

    (when has-fill
      [:& radio-button
       {:value "fill"
        :icon  i/fill-content
        :title "Width 100%"
        :id    "behaviour-h-fill"}])
    (when is-auto
      [:& radio-button
       {:value "auto"
        :icon  i/hug-content
        :title "Fit content (Horizontal)"
        :id    "behaviour-h-auto"}])]])

(mf/defc element-behaviour-vertical
  {::mf/props :obj
   ::mf/private true}
  [{:keys [^boolean is-auto ^boolean has-fill value on-change]}]
  [:div {:class (stl/css-case
                 :vertical-behaviour true
                 :one-element (and (not has-fill) (not is-auto))
                 :two-element (or has-fill is-auto)
                 :three-element (and has-fill is-auto))}
   [:& radio-buttons
    {:selected  (d/name value)
     :decode-fn keyword
     :on-change on-change
     :name      "flex-behaviour-v"}
    [:& radio-button
     {:value      "fix"
      :icon       i/fixed-width
      :icon-class (stl/css :rotated)
      :title      "Fix height"
      :id         "behaviour-v-fix"}]

    (when has-fill
      [:& radio-button
       {:value      "fill"
        :icon       i/fill-content
        :icon-class (stl/css :rotated)
        :title      "Height 100%"
        :id         "behaviour-v-fill"}])
    (when is-auto
      [:& radio-button
       {:value      "auto"
        :icon       i/hug-content
        :icon-class (stl/css :rotated)
        :title      "Fit content (Vertical)"
        :id         "behaviour-v-auto"}])]])

(mf/defc align-self-row
  {::mf/props :obj}
  [{:keys [^boolean is-col value on-change]}]
  [:& radio-buttons {:selected (d/name value)
                     :decode-fn keyword
                     :on-change on-change
                     :name "flex-align-self"
                     :allow-empty true}
   [:& radio-button {:value "start"
                     :icon  (get-layout-flex-icon :align-self :start is-col)
                     :title "Align self start"
                     :id     "align-self-start"}]
   [:& radio-button {:value "center"
                     :icon  (get-layout-flex-icon :align-self :center is-col)
                     :title "Align self center"
                     :id    "align-self-center"}]
   [:& radio-button {:value "end"
                     :icon  (get-layout-flex-icon :align-self :end is-col)
                     :title "Align self end"
                     :id    "align-self-end"}]])

(def ^:private schema:layout-item-props-schema
  [:map
   [:layout-item-margin
    {:optional true}
    [:map
     [:m1 {:optional true} [:or :float :int]]
     [:m2 {:optional true} [:or :float :int]]
     [:m3 {:optional true} [:or :float :int]]
     [:m4 {:optional true} [:or :float :int]]]]

   [:layout-item-margin-type {:optional true} :keyword]

   [:layout-item-h-sizing {:optional true} :keyword]
   [:layout-item-v-sizing {:optional true} :keyword]

   [:layout-item-min-w {:optional true} [:or :float :int]]
   [:layout-item-max-w {:optional true} [:or :float :int]]
   [:layout-item-min-h {:optional true} [:or :float :int]]
   [:layout-item-max-h {:optional true} [:or :float :int]]])

(def ^:private schema:layout-size-constraints
  [:map
   [:values schema:layout-item-props-schema]
   [:applied-tokens [:maybe [:map-of :keyword :string]]]
   [:ids [::sm/vec ::sm/uuid]]])

(mf/defc layout-size-constraints*
  {::mf/private true
   ::mf/schema (sm/schema schema:layout-size-constraints)}
  [{:keys [values ids applied-tokens] :as props}]
  (let [token-numeric-inputs
        (features/use-feature "tokens/numeric-input")

        v-sizing
        (:layout-item-v-sizing values)

        min-w (get values :layout-item-min-w)

        max-w (get values :layout-item-max-w)

        min-h (get values :layout-item-min-h)

        max-h (get values :layout-item-max-h)

        applied-token-to-min-w (get applied-tokens :layout-item-min-w)

        applied-token-to-max-w (get applied-tokens :layout-item-max-w)

        applied-token-to-min-h (get applied-tokens :layout-item-min-h)

        applied-token-to-max-h (get applied-tokens :layout-item-max-h)

        on-detach-token
        (mf/use-fn
         (mf/deps ids)
         (fn [token-name attr]
           (st/emit! (dwta/unapply-token {:token-name token-name
                                          :attributes #{attr}
                                          :shape-ids ids}))))

        on-size-change
        (mf/use-fn
         (mf/deps ids)
         (fn [value attr]
           (soc/emit-value-or-token value
                                    #(st/emit! (dwsl/update-layout-child ids {attr %}))
                                    ids
                                    #{attr})))

        on-layout-item-min-w-change
        (mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-w))

        on-layout-item-max-w-change
        (mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-w))

        on-layout-item-min-h-change
        (mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-h))

        on-layout-item-max-h-change
        (mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-h))]

    [:section {:class (stl/css :advanced-options)
               :aria-label "Layout item size constraints"}
     (when (= (:layout-item-h-sizing values) :fill)
       [:div {:class (stl/css :horizontal-fill)}
        (if token-numeric-inputs
          [:> numeric-input-wrapper*
           {:on-change on-layout-item-min-w-change
            :on-detach on-detach-token
            :min 0
            :attr :layout-item-min-w
            :property (tr "workspace.options.layout-item.layout-item-min-w")
            :text-icon "MIN W"
            :input-type :min-width
            :nillable true
            :applied-token applied-token-to-min-w
            :tooltip-class (stl/css :tooltip-wrapper)
            :value min-w}]

          [:div {:class (stl/css :layout-item-min-w)
                 :title (tr "workspace.options.layout-item.layout-item-min-w")}

           [:span {:class (stl/css :icon-text)} "MIN W"]
           [:> deprecated-input/numeric-input*
            {:class (stl/css :numeric-input)
             :no-validate true
             :min 0
             :data-wrap true
             :placeholder "--"
             :data-attr "layout-item-min-w"
             :on-focus dom/select-target
             :on-change on-layout-item-min-w-change
             :value (get values :layout-item-min-w)
             :is-nillable true}]])

        (if token-numeric-inputs
          [:> numeric-input-wrapper*
           {:on-change on-layout-item-max-w-change
            :on-detach on-detach-token
            :text-icon "MAX W"
            :min 0
            :align :right
            :input-type :max-width
            :attr :layout-item-max-w
            :property (tr "workspace.options.layout-item.layout-item-max-w")
            :nillable true
            :tooltip-class (stl/css :tooltip-wrapper)
            :applied-token applied-token-to-max-w
            :value max-w}]

          [:div {:class (stl/css :layout-item-max-w)
                 :title (tr "workspace.options.layout-item.layout-item-max-w")}
           [:span {:class (stl/css :icon-text)} "MAX W"]
           [:> deprecated-input/numeric-input*
            {:class (stl/css :numeric-input)
             :no-validate true
             :min 0
             :data-wrap true
             :placeholder "--"
             :data-attr "layout-item-max-w"
             :on-focus dom/select-target
             :on-change on-layout-item-max-w-change
             :value (get values :layout-item-max-w)
             :is-nillable true}]])])

     (when (= v-sizing :fill)
       [:div {:class (stl/css :vertical-fill)}
        (if token-numeric-inputs
          [:> numeric-input-wrapper*
           {:on-change on-layout-item-min-h-change
            :on-detach on-detach-token
            :text-icon "MIN H"
            :input-type :max-height
            :min 0
            :attr :layout-item-min-h
            :property (tr "workspace.options.layout-item.layout-item-min-h")
            :nillable true
            :tooltip-class (stl/css :tooltip-wrapper)

            :applied-token applied-token-to-min-h
            :value min-h}]

          [:div {:class (stl/css :layout-item-min-h)
                 :title (tr "workspace.options.layout-item.layout-item-min-h")}
           [:span {:class (stl/css :icon-text)} "MIN H"]
           [:> deprecated-input/numeric-input*
            {:class (stl/css :numeric-input)
             :no-validate true
             :min 0
             :data-wrap true
             :placeholder "--"
             :data-attr "layout-item-min-h"
             :on-focus dom/select-target
             :on-change on-layout-item-min-h-change
             :value (get values :layout-item-min-h)
             :is-nillable true}]])

        (if token-numeric-inputs
          [:> numeric-input-wrapper*
           {:on-change on-layout-item-max-h-change
            :on-detach on-detach-token
            :min 0
            :text-icon "MAX H"
            :align :right
            :input-type :max-height
            :attr :layout-item-max-h
            :property (tr "workspace.options.layout-item.layout-item-max-h")
            :nillable true
            :tooltip-class (stl/css :tooltip-wrapper)
            :applied-token applied-token-to-max-h
            :value max-h}]

          [:div {:class (stl/css :layout-item-max-h)
                 :title (tr "workspace.options.layout-item.layout-item-max-h")}

           [:span {:class (stl/css :icon-text)} "MAX H"]
           [:> deprecated-input/numeric-input*
            {:class (stl/css :numeric-input)
             :no-validate true
             :min 0
             :data-wrap true
             :placeholder "--"
             :data-attr "layout-item-max-h"
             :on-focus dom/select-target
             :on-change on-layout-item-max-h-change
             :value (get values :layout-item-max-h)
             :is-nillable true}]])])]))

(defn- check-layout-item-menu-props
  [old-props new-props]
  (let [old-values (unchecked-get old-props "values")
        new-values (unchecked-get new-props "values")]
    (and (identical? (unchecked-get old-props "ids")
                     (unchecked-get new-props "ids"))
         (identical? (unchecked-get old-props "type")
                     (unchecked-get new-props "type"))
         (identical? (unchecked-get old-props "isLayoutChild")
                     (unchecked-get new-props "isLayoutChild"))
         (identical? (unchecked-get old-props "isLayoutContainer")
                     (unchecked-get new-props "isLayoutContainer"))
         (identical? (unchecked-get old-props "isGridParent")
                     (unchecked-get new-props "isGridParent"))
         (identical? (unchecked-get old-props "isFlexParent")
                     (unchecked-get new-props "isFlexParent"))
         (identical? (unchecked-get old-props "isGridLayout")
                     (unchecked-get new-props "isGridLayout"))
         (identical? (unchecked-get old-props "isFlexLayout")
                     (unchecked-get new-props "isFlexLayout"))
         (identical? (unchecked-get old-props "appliedTokens")
                     (unchecked-get new-props "appliedTokens"))
         (identical? (get old-values :layout-item-margin)
                     (get new-values :layout-item-margin))
         (identical? (get old-values :layout-item-margin-type)
                     (get new-values :layout-item-margin-type))
         (identical? (get old-values :layout-item-h-sizing)
                     (get new-values :layout-item-h-sizing))
         (identical? (get old-values :layout-item-v-sizing)
                     (get new-values :layout-item-v-sizing))
         (identical? (get old-values :layout-item-max-h)
                     (get new-values :layout-item-max-h))
         (identical? (get old-values :layout-item-min-h)
                     (get new-values :layout-item-min-h))
         (identical? (get old-values :layout-item-max-w)
                     (get new-values :layout-item-max-w))
         (identical? (get old-values :layout-item-min-w)
                     (get new-values :layout-item-min-w))
         (identical? (get old-values :layout-item-align-self)
                     (get new-values :layout-item-align-self))
         (identical? (get old-values :layout-item-absolute)
                     (get new-values :layout-item-absolute))
         (identical? (get old-values :layout-item-z-index)
                     (get new-values :layout-item-z-index)))))

(mf/defc layout-item-menu*
  {::mf/wrap [#(mf/memo' % check-layout-item-menu-props)]}
  [{:keys [ids values
           ^boolean is-layout-child
           ^boolean is-layout-container
           ^boolean is-grid-parent
           ^boolean is-flex-parent
           ^boolean is-flex-layout
           ^boolean is-grid-layout
           applied-tokens]}]

  (let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
        selection-parents  (mf/deref selection-parents*)

        ^boolean
        is-absolute?       (:layout-item-absolute values)

        ^boolean
        is-col?            (every? ctl/col? selection-parents)

        ^boolean
        is-layout-child?   (and is-layout-child (not is-absolute?))

        state*             (mf/use-state true)
        open?              (deref state*)

        toggle-content     (mf/use-fn #(swap! state* not))
        has-content?       (or is-layout-child?
                               is-flex-parent
                               is-grid-parent
                               is-layout-container)

        ;; Align self
        align-self         (:layout-item-align-self values)
        h-sizing           (:layout-item-h-sizing values)
        v-sizing           (:layout-item-v-sizing values)

        title
        (cond
          (and is-layout-container
               is-flex-layout
               (not is-layout-child?))
          "Flex board"

          (and is-layout-container
               is-grid-layout
               (not is-layout-child?))
          "Grid board"

          (and is-layout-container
               (not is-layout-child?))
          "Layout board"

          is-flex-parent
          "Flex element"

          is-grid-parent
          "Grid element"

          :else
          "Layout element")

        on-align-self-change
        (mf/use-fn
         (mf/deps ids align-self)
         (fn [value]
           (if (= align-self value)
             (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
             (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value})))))

        ;; Margin
        on-margin-type-change
        (mf/use-fn
         (mf/deps ids)
         (fn [type]
           (st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type}))))

        on-margin-change
        (mf/use-fn
         (mf/deps ids)
         (fn [type prop val]
           (cond
             (and (= type :simple) (= prop :m1))
             (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m3 val}}))

             (and (= type :simple) (= prop :m2))
             (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}}))

             :else
             (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))))

        ;; Behaviour
        on-behaviour-h-change
        (mf/use-fn
         (mf/deps ids)
         (fn [value]
           (st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))))

        on-behaviour-v-change
        (mf/use-fn
         (mf/deps ids)
         (fn [value]
           (st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))

        ;; Position
        on-change-position
        (mf/use-fn
         (mf/deps ids)
         (fn [value]
           (when (= value :static)
             (st/emit! (dwsl/update-layout-child ids {:layout-item-z-index nil})))
           (st/emit! (dwsl/update-layout-child ids {:layout-item-absolute (= value :absolute)}))))

        ;; Z Index
        on-change-z-index
        (mf/use-fn
         (mf/deps ids)
         (fn [value]
           (st/emit! (dwsl/update-layout-child ids {:layout-item-z-index value}))))]

    [:section {:class (stl/css :element-set)
               :aria-label "Layout item section"}
     [:div {:class (stl/css :element-title)}
      [:> title-bar* {:collapsable  has-content?
                      :collapsed    (not open?)
                      :on-collapsed toggle-content
                      :title        title
                      :class        (stl/css-case :title-spacing-layout-element true
                                                  :title-spacing-empty (not has-content?))}]]
     (when open?
       [:div {:class (stl/css :flex-element-menu)}
        (when (or is-layout-child? is-absolute?)
          [:div {:class (stl/css :position-row)}
           [:div {:class (stl/css :position-options)}
            [:& radio-buttons {:selected (if is-absolute? "absolute" "static")
                               :decode-fn keyword
                               :on-change on-change-position
                               :name "layout-style"
                               :wide true}
             [:& radio-button {:value "static"
                               :id :static-position}]
             [:& radio-button {:value "absolute"
                               :id :absolute-position}]]]

           [:div {:class (stl/css :z-index-wrapper)
                  :title "z-index"}

            [:span {:class (stl/css :icon-text)} "Z"]
            [:> deprecated-input/numeric-input*
             {:class (stl/css :numeric-input)
              :placeholder "--"
              :on-focus #(dom/select-target %)
              :on-change #(on-change-z-index %)
              :is-nillable true
              :value (:layout-item-z-index values)}]]])

        [:div {:class (stl/css :behavior-row)}
         [:div {:class (stl/css-case
                        :behaviour-menu true
                        :wrap (and ^boolean is-layout-child?
                                   ^boolean is-layout-container))}
          [:& element-behaviour-horizontal
           {:is-auto is-layout-container
            :has-fill is-layout-child?
            :value (:layout-item-h-sizing values)
            :on-change on-behaviour-h-change}]
          [:& element-behaviour-vertical
           {:is-auto is-layout-container
            :has-fill is-layout-child?
            :value (:layout-item-v-sizing values)
            :on-change on-behaviour-v-change}]]]

        (when (and is-layout-child? is-flex-parent)
          [:div {:class (stl/css :align-row)}
           [:& align-self-row {:is-col is-col?
                               :value align-self
                               :on-change on-align-self-change}]])

        (when is-layout-child?
          [:> margin-section* {:value (:layout-item-margin values)
                               :type (:layout-item-margin-type values)
                               :on-type-change on-margin-type-change
                               :applied-tokens applied-tokens
                               :ids ids
                               :on-change on-margin-change}])

        (when (or (= h-sizing :fill)
                  (= v-sizing :fill))
          [:> layout-size-constraints* {:ids ids
                                        :values values
                                        :applied-tokens applied-tokens}])])]))
