diff --git a/web/package.json b/web/package.json index bd13c7a33a..dea9158f78 100644 --- a/web/package.json +++ b/web/package.json @@ -18,7 +18,6 @@ "clean": "/usr/bin/rm -rf target; /usr/bin/rm -rf ../resources/static/js/compiled; /usr/bin/rm -rf ../resources/static/js/cljs-runtime;" }, "dependencies": { - "caret-pos": "^1.2.2", "diff": "^4.0.2", "react": "^16.12.0", "react-dom": "^16.12.0", diff --git a/web/src/main/frontend/caret_pos.js b/web/src/main/frontend/caret_pos.js index 13e33a1977..2b71efa892 100644 --- a/web/src/main/frontend/caret_pos.js +++ b/web/src/main/frontend/caret_pos.js @@ -1,23 +1,2 @@ -export var getCaretRange = function (element) { - var caretRange = ""; - var doc = element.ownerDocument || element.document; - var win = doc && (doc.defaultView || doc.parentWindow); - var sel; - if (typeof win.getSelection != "undefined") { - sel = win.getSelection(); - if (sel.rangeCount > 0) { - var range = win.getSelection().getRangeAt(0); - var preCaretRange = range.cloneRange(); - preCaretRange.selectNodeContents(element); - preCaretRange.setEnd(range.endContainer, range.endOffset); - caretRange = preCaretRange.toString(); - } - } else if ( (sel = doc.selection) && sel.type != "Control") { - var textRange = sel.createRange(); - var preCaretTextRange = doc.body.createTextRange(); - preCaretTextRange.moveToElementText(element); - preCaretTextRange.setEndPoint("EndToEnd", textRange); - caretRange = preCaretTextRange.text; - } - return caretRange; -}; +// Built from https://github.com/deshiknaves/caret-pos without editable support +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t["caret-pos"]={})}(this,function(t){"use strict";function s(e,t){var o,n,r,i=document.createElement("div");return o=t,n={position:"absolute",left:-9999,top:0,zIndex:-2e3},"TEXTAREA"===e.tagName&&u.push("width"),u.forEach(function(t){n[t]=getComputedStyle(e)[t]}),r=n,Object.keys(r).forEach(function(t){i.style[t]=r[t]}),i.innerHTML=o,e.parentNode.insertBefore(i,e.nextSibling),{rect:function(){var t=i.ownerDocument.getElementById("caret-position-marker"),e={left:t.offsetLeft,top:t.offsetTop,height:t.offsetHeight};return i.parentNode.removeChild(i),e}}}function f(t){var e=0|`|"|&/g,"?").replace(/\r\n|\r|\n/g,"
")}!d.customPos&&0!==d.customPos||(t=d.customPos);var o=void 0===t?u():t,n=l.value.slice(0,o),r=l.value.slice(o),i=''+e(n)+"";i+='|',i+=''+e(r)+"";var f=s(l,i).rect();return f.pos=u(),f};return{getPos:u,setPos:function(t){return l.setSelectionRange(t,t),l},getOffset:function(t){var e=r(l),o=n(t);return{top:e.top+o.top+d.document.body.scrollTop,left:e.left+o.left+d.document.body.scrollLeft,height:o.height}},getPosition:n}}function d(t,e){return o(t,e)}var u=["borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle","borderTopWidth","boxSizing","fontFamily","fontSize","fontWeight","height","letterSpacing","lineHeight","marginBottom","marginLeft","marginRight","marginTop","outlineWidth","overflow","overflowX","overflowY","paddingBottom","paddingLeft","paddingRight","paddingTop","textAlign","textOverflow","textTransform","whiteSpace","wordBreak","wordWrap"],e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};t.position=function(t,e,o){var n=2 0) { + var range = win.getSelection().getRangeAt(0); + var preCaretRange = range.cloneRange(); + preCaretRange.selectNodeContents(element); + preCaretRange.setEnd(range.endContainer, range.endOffset); + caretRange = preCaretRange.toString(); + } + } else if ( (sel = doc.selection) && sel.type != "Control") { + var textRange = sel.createRange(); + var preCaretTextRange = doc.body.createTextRange(); + preCaretTextRange.moveToElementText(element); + preCaretTextRange.setEndPoint("EndToEnd", textRange); + caretRange = preCaretTextRange.text; + } + return caretRange; +}; diff --git a/web/src/main/frontend/commands.cljs b/web/src/main/frontend/commands.cljs index bf01a69113..771e161ba8 100644 --- a/web/src/main/frontend/commands.cljs +++ b/web/src/main/frontend/commands.cljs @@ -55,10 +55,7 @@ (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) - ))) + (state/set-editor-show-page-search true)) (defmethod handle-step :default [[type & _args]] (prn "No handler for step: " type)) diff --git a/web/src/main/frontend/components/editor.cljs b/web/src/main/frontend/components/editor.cljs index bab75ed967..80f9565c40 100644 --- a/web/src/main/frontend/components/editor.cljs +++ b/web/src/main/frontend/components/editor.cljs @@ -50,6 +50,34 @@ :left left :width 400}}))) +(rum/defc page-search < rum/reactive + [id] + (let [{:keys [top left pos]} (rum/react *slash-caret-pos) + current-pos (:pos (util/get-caret-pos (gdom/getElement id))) + edit-content (state/sub :edit-content) + q (subs edit-content (inc pos) current-pos) + matched-pages (when-not (string/blank? q) + (let [pages (db/get-pages (state/get-current-repo))] + (filter + (fn [page] + (string/index-of + (string/lower-case page) + (string/lower-case q))) + pages)))] + (ui/auto-complete + matched-pages + (fn [chosen] + (let [new-value (util/format "%s [[%s%s" + (subs edit-content 0 (dec (dec pos))) + chosen + (subs edit-content current-pos))] + (handler/editor-set-new-value! new-value)) + (state/set-editor-show-page-search false)) + {:style {:top (+ top 20) + :left left + :width 400}} + :empty-div [:div.text-gray-500.pl-4.pr-4 "Search for a page"]))) + (defn get-state [state] (let [[_ {:keys [on-hide dummy?]} id] (:rum/args state) @@ -114,11 +142,20 @@ last-command (commands/get-command-input edit-content)] (or (and (= \/ (last edit-content)) - (= " " (nth edit-content (- (count edit-content) 2))) + (or + (and + (>= (count edit-content) 2) + (= " " (nth edit-content (- (count edit-content) 2)))) + (= edit-content "/")) commands/commands-map) (and last-command (commands/get-matched-commands last-command))))) +(defn in-auto-complete? + [input] + (or (seq (get-matched-commands input)) + (state/get-editor-show-page-search))) + (rum/defc box < rum/reactive (mixins/event-mixin (fn [state] @@ -136,11 +173,11 @@ { ;; up 38 (fn [state e] - (when-not (seq (get-matched-commands input)) + (when-not (in-auto-complete? input) (on-up-down state e true))) ;; down 40 (fn [state e] - (when-not (seq (get-matched-commands input)) + (when-not (in-auto-complete? input) (on-up-down state e false))) ;; backspace @@ -178,7 +215,8 @@ [content {:keys [on-hide dummy?] :or {dummy? false}} id] (let [value (state/sub :edit-content) - show-commands? (rum/react *show-commands)] + show-commands? (rum/react *show-commands) + show-page-search? (state/sub :editor/show-page-search?)] [:div.editor {:style {:position "relative" :display "flex" :flex "1 1 0%"}} @@ -197,4 +235,11 @@ {:class-names "fade" :timeout {:enter 500 :exit 300}} - (commands id)))])) + (commands id))) + + (when show-page-search? + (ui/css-transition + {:class-names "fade" + :timeout {:enter 500 + :exit 300}} + (page-search id)))])) diff --git a/web/src/main/frontend/db.cljs b/web/src/main/frontend/db.cljs index 2fb5b61234..41d9b212aa 100644 --- a/web/src/main/frontend/db.cljs +++ b/web/src/main/frontend/db.cljs @@ -211,7 +211,8 @@ :where [?page :page/name ?page-name]] (get-conn repo false)) - (rum/react) + ;; (rum/react) + deref (map first) distinct)) diff --git a/web/src/main/frontend/handler.cljs b/web/src/main/frontend/handler.cljs index 109c6820b2..c7146c1f0e 100644 --- a/web/src/main/frontend/handler.cljs +++ b/web/src/main/frontend/handler.cljs @@ -660,6 +660,13 @@ (when-let [current-input (gdom/getElement input-id)] (util/move-cursor-to-end current-input)))))) +(defn editor-set-new-value! + [new-value] + (state/set-edit-content! new-value) + (when-let [input-id (state/get-edit-input-id)] + (when-let [current-input (gdom/getElement input-id)] + (util/move-cursor-to-end current-input)))) + (defn insert-image! [image-url] ;; (let [content (state/get-edit-content) diff --git a/web/src/main/frontend/state.cljs b/web/src/main/frontend/state.cljs index 46b4655047..22dc3780c0 100644 --- a/web/src/main/frontend/state.cljs +++ b/web/src/main/frontend/state.cljs @@ -34,6 +34,8 @@ :github/contents {} :config {} + + :editor/show-page-search? false })) (defn sub @@ -151,3 +153,10 @@ (defn set-edit-input-id! [id] (set-state! :edit-input-id id)) + +(defn set-editor-show-page-search + [value] + (set-state! :editor/show-page-search? value)) +(defn get-editor-show-page-search + [] + (get @state :editor/show-page-search?)) diff --git a/web/src/main/frontend/ui.cljs b/web/src/main/frontend/ui.cljs index 9702c50121..391002dbef 100644 --- a/web/src/main/frontend/ui.cljs +++ b/web/src/main/frontend/ui.cljs @@ -182,7 +182,6 @@ body) (rum/defcs auto-complete < - (rum/local nil ::matched) (rum/local 0 ::current-idx) (mixins/event-mixin (fn [state] @@ -213,13 +212,13 @@ (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)] + [state matched on-chosen div-option & {:keys [empty-div]}] + (let [current-idx (get state ::current-idx)] + [:div.absolute.rounded-md.shadow-lg + div-option + [:div.py-1.rounded-md.bg-white.shadow-xs + (if (seq matched) + (for [[idx item] (medley/indexed matched)] (rum/with-key (menu-link {:style (merge @@ -233,4 +232,6 @@ (let [option (nth matched @current-idx)] (on-chosen option)))} item) - idx))]]))) + idx)) + (when empty-div + empty-div))]])) diff --git a/web/src/main/frontend/util.cljs b/web/src/main/frontend/util.cljs index 2c01b2d73d..c16ed9000d 100644 --- a/web/src/main/frontend/util.cljs +++ b/web/src/main/frontend/util.cljs @@ -7,7 +7,7 @@ [cljs-bean.core :as bean] [clojure.string :as string] ["/frontend/caret_pos" :as caret-pos] - ["caret-pos" :as caret] + ["/frontend/caret_range" :as caret-range] [goog.string :as gstring] [goog.string.format] [dommy.core :as d])) @@ -270,7 +270,7 @@ (apply f args)) threshold)))))) -(def caret-range caret-pos/getCaretRange) +(def caret-range caret-range/getCaretRange) (defn set-caret-pos! [input pos] @@ -278,7 +278,7 @@ (defn get-caret-pos [input] - (bean/->clj ((gobj/get caret "position") input))) + (bean/->clj ((gobj/get caret-pos "position") input))) (defn minimize-html [s] diff --git a/web/yarn.lock b/web/yarn.lock index b9c8c87b05..2e0faa03c8 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -253,11 +253,6 @@ caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001030: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz#76f1bdd39e19567b855302f65102d9a8aaad5930" integrity sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg== -caret-pos@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/caret-pos/-/caret-pos-1.2.2.tgz#b2bac513eaf3680201df38d0de22e8a0d5d0eace" - integrity sha512-C+z3AZU3a/V+YxK+ZvM+fSLs9rRGPAg9ZbuchTfAz572BiT76GOm6H4padNnSf5qKAKLjt0vlm1zJLEN/ftApg== - chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"