<commit_before>(ns foosball.editable
  (:require-macros [cljs.core.async.macros :refer [go-loop]])
  (:require [cljs.core.async :as async :refer [put! chan alts!]]
            [goog.dom :as gdom]
            [om.core :as om :include-macros true]
            [sablono.core :as html :refer-macros [html]]
            [foosball.console :refer-macros [debug error]]))

(defn notify-change [chan type val]
  (put! chan [type val]))

(defn on-change [chan owner e]
  (let [val (-> e .-target .-value)]
    (om/set-state! owner :current-value val)
    (notify-change chan ::change val)))

(defn begin-edit [chan owner val]
  (om/set-state! owner :editing true)
  (notify-change chan ::focus val))

(defn end-edit [chan owner val]
  (om/set-state! owner :editing false)
  (notify-change chan ::blur val))

(defn component-keyboard-event [ev]
  (let [keycode (.-keyCode ev)
        codes   {13 :enter
                 38 :up
                 40 :down}
        key-kw  (get codes keycode)
        modifiers (->> (map (fn [is? key] (when is? key))
                            [(.-ctrlKey ev) (.-shiftKey ev) (.-altKey ev) (.-metaKey ev)]
                            [:ctrl :shift :alt :meta])
                       (filter identity)
                       set)]
    (when key-kw {:key key-kw :modifiers modifiers})))

(defn editable
  [data owner {:keys [value-fn         ;; fn to apply on editable data to get str (default: identity)
                      placeholder      ;; placeholder text in input field
                      input-classes    ;; additional classes for input (seq of kw or str)
                      input-props      ;; additional properties for the input element
                      change-ch        ;; channel where changes are put to, as [type value] tuples,
                                        ; where type in #{:editable/focus :editable/change :editable/blur}
                      ] :as opts}]
  (reify
    om/IInitState
    (init-state [_]
      {:editing false
       :current-value ((or value-fn identity) data)})

    om/IRenderState
    (render-state [_ {:keys [editing current-value]}]
      (let [defaults   {:type "text"
                        :ref  "input"}
            must-haves {:class       (mapv name input-classes)
                        :value       current-value
                        :placeholder placeholder
                        :on-change   (fn [e] (on-change change-ch owner e))
                        :on-key-down (fn [e] (when (om/get-state owner :editing)
                                              (let [comp-kb-ev (component-keyboard-event e)]
                                                (when (= (:key comp-kb-ev) :enter)
                                                  (.blur (om/get-node owner "input"))))))
                        :on-blur     (fn [e] (when (om/get-state owner :editing)
                                              (end-edit change-ch owner current-value)))
                        :on-focus    (fn [e] (begin-edit change-ch owner current-value))}]
        (html [:input.form-control
               (merge defaults
                      input-props
                      must-haves)])))))<commit_msg>changes editable to use defcomponentk<commit_after>(ns foosball.editable
  (:require-macros [cljs.core.async.macros :refer [go-loop]])
  (:require [cljs.core.async :as async :refer [put! chan alts!]]
            [goog.dom :as gdom]
            [om.core :as om :include-macros true]
            [om-tools.core :refer-macros [defcomponentk]]
            [sablono.core :as html :refer-macros [html]]
            [foosball.console :refer-macros [debug error]]))

(defn notify-change [chan type val]
  (put! chan [type val]))

(defn on-change [chan owner e]
  (let [val (-> e .-target .-value)]
    (om/set-state! owner :current-value val)
    (notify-change chan ::change val)))

(defn begin-edit [chan owner val]
  (om/set-state! owner :editing true)
  (notify-change chan ::focus val))

(defn end-edit [chan owner val]
  (om/set-state! owner :editing false)
  (notify-change chan ::blur val))

(defn component-keyboard-event [ev]
  (let [keycode (.-keyCode ev)
        codes   {13 :enter
                 38 :up
                 40 :down}
        key-kw  (get codes keycode)
        modifiers (->> (map (fn [is? key] (when is? key))
                            [(.-ctrlKey ev) (.-shiftKey ev) (.-altKey ev) (.-metaKey ev)]
                            [:ctrl :shift :alt :meta])
                       (filter identity)
                       set)]
    (when key-kw {:key key-kw :modifiers modifiers})))

(defcomponentk editable
  [data owner
   [:opts
    {value-fn identity} ;; fn to apply on editable data to get str
    {placeholder nil}   ;; placeholder text in input field
    {input-classes nil} ;; additional classes for input (seq of kw or str)
    {input-props nil}   ;; additional properties for the input element
    change-ch           ;; channel where changes are put to, as [type value] tuples,
                        ;; where type in #{:editable/focus :editable/change :editable/blur}
    :as opts]]
  (init-state [_]
    {:editing false
     :current-value ((or value-fn identity) data)})

  (render-state [_ {:keys [editing current-value]}]
    (let [defaults   {:type "text"
                      :ref  "input"}
          must-haves {:class       (mapv name input-classes)
                      :value       current-value
                      :placeholder placeholder
                      :on-change   (fn [e] (on-change change-ch owner e))
                      :on-key-down (fn [e] (when (om/get-state owner :editing)
                                            (let [comp-kb-ev (component-keyboard-event e)]
                                              (when (= (:key comp-kb-ev) :enter)
                                                (.blur (om/get-node owner "input"))))))
                      :on-blur     (fn [e] (when (om/get-state owner :editing)
                                            (end-edit change-ch owner current-value)))
                      :on-focus    (fn [e] (begin-edit change-ch owner current-value))}]
      (html [:input.form-control
             (merge defaults
                    input-props
                    must-haves)]))))
{'fraction_non_alphanumeric': 0.12498007333014506, 'fraction_numerical': 0.001912960306073649, 'mean_word_length': 1.5483346872461414, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5301761', 'n_tokens_mistral': 1842, 'n_tokens_neox': 1753, 'n_words': 545}