Extract auto-complete component

pull/645/head
Tienson Qin 2020-05-04 11:04:08 +08:00
parent 9b9f07c0f0
commit 34a3f5de83
5 changed files with 131 additions and 76 deletions

View File

@ -1,7 +1,8 @@
(ns frontend.commands
(:require [frontend.util :as util]
[frontend.state :as state]
[clojure.string :as string]))
[clojure.string :as string]
[goog.dom :as gdom]))
(defn ->page-reference
[page]
@ -18,7 +19,7 @@
["Date Picker" [[:date/pick]]]
["Page Reference" [[:editor/input "[[]]"]
[:editor/cursor-back 2]
[:search :page]]]
[:editor/search-page]]]
["Link" nil]
["Upload a file" nil]]
;; Allow user to modify or extend, should specify how to extend.
@ -40,3 +41,29 @@
(if (string/blank? result)
nil
result))))
(defmulti handle-step first)
(defmethod handle-step :editor/input [[_ append-value]]
(when-let [edit-content (state/get-edit-content)]
(let [new-value (util/replace-last "/" edit-content (str append-value))]
(state/set-edit-content! new-value))))
(defmethod handle-step :editor/cursor-back [[_ n]]
(when-let [input-id (state/get-edit-input-id)]
(when-let [current-input (gdom/getElement input-id)]
(util/cursor-move-back current-input n))))
(defmethod handle-step :editor/search-page [[_]]
(when-let [input-id (state/get-edit-input-id)]
(when-let [current-input (gdom/getElement input-id)]
;; (util/cursor-move-back current-input n)
)))
(defmethod handle-step :default [[type & _args]]
(prn "No handler for step: " type))
(defn handle-steps
[vector]
(doseq [step vector]
(handle-step step)))

View File

@ -15,55 +15,40 @@
[medley.core :as medley]))
(defonce *show-commands (atom false))
(defonce *matched-commands (atom commands/commands-map))
(def *matched-commands (atom nil))
(defonce *slash-caret-pos (atom nil))
(defonce *command-current-idx (atom 0))
(defn- append-command!
[id command-output]
(handler/append-command! command-output)
(cond
;; replace string
(string? command-output)
(handler/append-command! command-output)
;; steps
(vector? command-output)
(commands/handle-steps command-output)
:else
nil)
(.focus (gdom/getElement id))
(reset! *show-commands false))
(rum/defc commands < rum/reactive
{:will-mount (fn [state]
(reset! *command-current-idx 0)
(reset! *matched-commands commands/commands-map)
state)}
[id]
(let [{:keys [top left]} (rum/react *slash-caret-pos)
command-current-idx (rum/react *command-current-idx)]
[:div.absolute.rounded-md.shadow-lg
matched (rum/react *matched-commands)]
(ui/auto-complete
(map first matched)
(fn [chosen]
(append-command! id (get (into {} matched) chosen)))
{:style {:top (+ top 20)
:left left
:width 400}}
[:div.py-1.rounded-md.bg-white.shadow-xs
(for [[idx [name handler]] (medley/indexed (rum/react *matched-commands))]
(rum/with-key
(ui/menu-link
{:style (merge
{:padding "6px"}
(when (= command-current-idx idx)
{:background-color "rgb(213, 218, 223)"}))
:class "initial-color"
:tab-index 0
:on-click (fn [e]
(util/stop e)
(cond
;; replace string
(string? handler)
(handler/append-command! handler)
;; steps
(vector? handler)
nil
:else
nil)
(.focus (gdom/getElement id))
(reset! *show-commands false))}
name)
name))]]))
:width 400}})))
(defn get-state
[state]
@ -129,6 +114,7 @@
last-command (commands/get-command-input edit-content)]
(or
(and (= \/ (last edit-content))
(= " " (nth edit-content (- (count edit-content) 2)))
commands/commands-map)
(and last-command
(commands/get-matched-commands last-command)))))
@ -150,38 +136,22 @@
{
;; up
38 (fn [state e]
(if (seq (get-matched-commands input))
(do
(util/stop e)
(when (>= @*command-current-idx 1)
(swap! *command-current-idx dec)))
(when-not (seq (get-matched-commands input))
(on-up-down state e true)))
;; down
40 (fn [state e]
(if-let [matched (seq (get-matched-commands input))]
(do
(util/stop e)
(let [total (count matched)]
(if (>= @*command-current-idx (dec total))
(reset! *command-current-idx 0)
(swap! *command-current-idx inc))))
(when-not (seq (get-matched-commands input))
(on-up-down state e false)))
;; backspace
8 (fn [state e] (on-backspace state e))
;; enter
13 (fn [state e]
(let [matched-commands (get-matched-commands input)]
(when (seq matched-commands)
(util/stop e)
(append-command! input-id (last (nth matched-commands @*command-current-idx))))))}
8 (fn [state e] (on-backspace state e))}
nil)
(mixins/on-key-up
state
{191 (fn [state e]
(reset! *show-commands true)
(reset! *slash-caret-pos (util/get-caret-pos input)))}
(when-let [matched-commands (seq (get-matched-commands input))]
(reset! *show-commands true)
(reset! *slash-caret-pos (util/get-caret-pos input))))}
(fn [e key-code]
(when (not= key-code 191)
(let [matched-commands (get-matched-commands input)]

View File

@ -505,11 +505,9 @@
(defn periodically-pull-and-push
[repo-url {:keys [pull-now?]
:or {pull-now? true}}]
;; (when-not config/dev?
;; (periodically-pull repo-url pull-now?)
;; (periodically-push-tasks repo-url))
(periodically-pull repo-url pull-now?)
(periodically-push-tasks repo-url))
(when-not config/dev?
(periodically-pull repo-url pull-now?)
(periodically-push-tasks repo-url)))
(defn edit-journal!
[journal]
@ -637,11 +635,6 @@
pos (if dummy? (+ 3 pos) pos)]
(util/set-caret-pos! node pos))))))
(defn move-cursor-to-end [input]
(let [n (count (.-value input))]
(set! (.-selectionStart input) n)
(set! (.-selectionEnd input) n)))
(defn remove-slash!
[]
(when-let [edit-content (state/get-edit-content)]
@ -656,7 +649,7 @@
(state/set-edit-content! new-value)
(when-let [input-id (state/get-edit-input-id)]
(when-let [current-input (gdom/getElement input-id)]
(move-cursor-to-end current-input))))))
(util/move-cursor-to-end current-input))))))
(defn append-command!
[append-value]
@ -665,7 +658,7 @@
(state/set-edit-content! new-value)
(when-let [input-id (state/get-edit-input-id)]
(when-let [current-input (gdom/getElement input-id)]
(move-cursor-to-end current-input))))))
(util/move-cursor-to-end current-input))))))
(defn insert-image!
[image-url]
@ -676,7 +669,7 @@
;; ]
;; (state/set-edit-content! new-content)
;; (set! (.-value node) new-content)
;; (move-cursor-to-end node))
;; (util/move-cursor-to-end node))
)
(defn search

View File

@ -8,7 +8,8 @@
[frontend.state :as state]
[clojure.string :as string]
[goog.object :as gobj]
[goog.dom :as gdom]))
[goog.dom :as gdom]
[medley.core :as medley]))
(defonce transition-group (r/adapt-class TransitionGroup))
(defonce css-transition (r/adapt-class CSSTransition))
@ -72,10 +73,10 @@
(let [class "inline-flex.items-center.px-3.py-2.border.border-transparent.text-sm.leading-4.font-medium.rounded-md.text-white.bg-indigo-600.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.active:bg-indigo-700.transition.ease-in-out.duration-150.mt-1"
class (if background (string/replace class "indigo" background) class)]
[:button
{:type "button"
:class (util/hiccup->class class)
:on-click on-click}
text]))
{:type "button"
:class (util/hiccup->class class)
:on-click on-click}
text]))
(rum/defc notification-content
[state content status]
@ -179,3 +180,57 @@
[state body {:keys [on-load]
:as opts}]
body)
(rum/defcs auto-complete <
(rum/local nil ::matched)
(rum/local 0 ::current-idx)
(mixins/event-mixin
(fn [state]
(mixins/on-key-down
state
{
;; up
38 (fn [_ e]
(let [current-idx (get state ::current-idx)]
(util/stop e)
(when (>= @current-idx 1)
(swap! current-idx dec))))
;; down
40 (fn [state e]
(let [current-idx (get state ::current-idx)
matched (first (:rum/args state))]
(util/stop e)
(let [total (count matched)]
(if (>= @current-idx (dec total))
(reset! current-idx 0)
(swap! current-idx inc)))))
;; enter
13 (fn [state e]
(let [current-idx (get state ::current-idx)
matched (first (:rum/args state))
on-chosen (nth (:rum/args state) 1)]
(util/stop e)
(on-chosen (nth matched @current-idx))))}
nil)))
[state matched on-chosen div-option]
(when (seq matched)
(let [current-idx (get state ::current-idx)]
[:div.absolute.rounded-md.shadow-lg
div-option
[:div.py-1.rounded-md.bg-white.shadow-xs
(for [[idx item] (medley/indexed matched)]
(rum/with-key
(menu-link
{:style (merge
{:padding "6px"}
(when (= @current-idx idx)
{:background-color "rgb(213, 218, 223)"}))
:class "initial-color"
:tab-index 0
:on-click (fn [e]
(util/stop e)
(let [option (nth matched @current-idx)]
(on-chosen option)))}
item)
idx))]])))

View File

@ -417,3 +417,13 @@
(when-let [last-index (string/last-index-of s pattern)]
(str (subs s 0 last-index)
new-value)))
(defn move-cursor-to-end [input]
(let [n (count (.-value input))]
(set! (.-selectionStart input) n)
(set! (.-selectionEnd input) n)))
(defn cursor-move-back [input n]
(let [total (count (.-value input))]
(set! (.-selectionStart input) (- total 2))
(set! (.-selectionEnd input) (- total 2))))