浏览代码

enhance: auto-complete for property values

Tienson Qin 3 年之前
父节点
当前提交
7b74117d85

+ 13 - 13
src/main/frontend/commands.cljs

@@ -333,19 +333,19 @@
                            (+ current-pos i)))
                            (+ current-pos i)))
                        current-pos)
                        current-pos)
           orig-prefix (subs edit-content 0 current-pos)
           orig-prefix (subs edit-content 0 current-pos)
-          space? (when (and last-pattern orig-prefix)
-                   (let [s (when-let [last-index (string/last-index-of orig-prefix last-pattern)]
-                             (gp-util/safe-subs orig-prefix 0 last-index))]
-                     (not
-                      (or
-                       (and s
-                            (string/ends-with? s "(")
-                            (or (string/starts-with? last-pattern "((")
-                                (string/starts-with? last-pattern "[[")))
-                       (and s (string/starts-with? s "{{embed"))))))
-          space? (if (and space? (string/starts-with? last-pattern "#[["))
-                   false
-                   space?)
+          space? (let [space? (when (and last-pattern orig-prefix)
+                                (let [s (when-let [last-index (string/last-index-of orig-prefix last-pattern)]
+                                          (gp-util/safe-subs orig-prefix 0 last-index))]
+                                  (not
+                                   (or
+                                    (and s
+                                         (string/ends-with? s "(")
+                                         (or (string/starts-with? last-pattern "((")
+                                             (string/starts-with? last-pattern "[[")))
+                                    (and s (string/starts-with? s "{{embed"))))))]
+                   (if (and space? (string/starts-with? last-pattern "#[["))
+                     false
+                     space?))
           prefix (cond
           prefix (cond
                    (and backward-truncate-number (integer? backward-truncate-number))
                    (and backward-truncate-number (integer? backward-truncate-number))
                    (str (gp-util/safe-subs orig-prefix 0 (- (count orig-prefix) backward-truncate-number))
                    (str (gp-util/safe-subs orig-prefix 0 (- (count orig-prefix) backward-truncate-number))

+ 4 - 4
src/main/frontend/components/block.cljs

@@ -421,7 +421,9 @@
   [config page-name-in-block page-name redirect-page-name page-entity contents-page? children html-export? label]
   [config page-name-in-block page-name redirect-page-name page-entity contents-page? children html-export? label]
   (let [tag? (:tag? config)]
   (let [tag? (:tag? config)]
     [:a
     [:a
-     {:class (if tag? "tag" "page-ref")
+     {:class (cond-> (if tag? "tag" "page-ref")
+               (:property? config)
+               (str " page-property-key"))
       :data-ref page-name
       :data-ref page-name
       :on-mouse-down
       :on-mouse-down
       (fn [e]
       (fn [e]
@@ -1805,9 +1807,7 @@
   [config block k v]
   [config block k v]
   (let [date (and (= k :date) (date/get-locale-string (str v)))]
   (let [date (and (= k :date) (date/get-locale-string (str v)))]
     [:div
     [:div
-     [:a.page-property-key
-      {:href (rfe/href :page {:name (name k)})}
-      (name k)]
+     (page-cp (assoc config :property? true) {:block/name (name k)})
      [:span.mr-1 ":"]
      [:span.mr-1 ":"]
      (cond
      (cond
        (int? v)
        (int? v)

+ 34 - 7
src/main/frontend/components/editor.cljs

@@ -237,22 +237,47 @@
     (when input
     (when input
       (let [current-pos (cursor/pos input)
       (let [current-pos (cursor/pos input)
             edit-content (state/sub [:editor/content id])
             edit-content (state/sub [:editor/content id])
-            q (or
-               (when (>= (count edit-content) current-pos)
-                 (subs edit-content pos current-pos))
-               "")
+            q (:searching-property (editor-handler/get-searching-property input))
             matched-properties (editor-handler/get-matched-properties q)
             matched-properties (editor-handler/get-matched-properties q)
+            q-property (string/replace (string/lower-case q) #"\s+" "-")
             non-exist-handler (fn [_state]
             non-exist-handler (fn [_state]
-                                ((editor-handler/property-on-chosen-handler id q) nil))]
+                                ((editor-handler/property-on-chosen-handler id q-property) nil))]
         (ui/auto-complete
         (ui/auto-complete
          matched-properties
          matched-properties
-         {:on-chosen (editor-handler/property-on-chosen-handler id q)
+         {:on-chosen (editor-handler/property-on-chosen-handler id q-property)
           :on-enter non-exist-handler
           :on-enter non-exist-handler
-          :empty-placeholder [:div.px-4.py-2.text-sm (str "Create a new property: " q)]
+          :empty-placeholder [:div.px-4.py-2.text-sm (str "Create a new property: " q-property)]
           :header [:div.px-4.py-2.text-sm.font-medium "Matched properties: "]
           :header [:div.px-4.py-2.text-sm.font-medium "Matched properties: "]
           :item-render (fn [property] property)
           :item-render (fn [property] property)
           :class       "black"})))))
           :class       "black"})))))
 
 
+(rum/defc property-value-search < rum/reactive
+  {:will-unmount (fn [state] (reset! editor-handler/*selected-text nil) state)}
+  [id]
+  (let [pos (:pos (:pos (state/get-editor-action-data)))
+        property (:property (state/get-editor-action-data))
+        input (gdom/getElement id)]
+    (when (and input
+               (not (string/blank? property)))
+      (let [current-pos (cursor/pos input)
+            edit-content (state/sub [:editor/content id])
+            start-idx (string/last-index-of (subs edit-content 0 current-pos) "::")
+            q (or
+               (when (>= current-pos (+ start-idx 2))
+                 (subs edit-content (+ start-idx 2) current-pos))
+               "")
+            matched-values (editor-handler/get-matched-property-values property q)
+            non-exist-handler (fn [_state]
+                                ((editor-handler/property-value-on-chosen-handler id q) nil))]
+        (ui/auto-complete
+         matched-values
+         {:on-chosen (editor-handler/property-value-on-chosen-handler id q)
+          :on-enter non-exist-handler
+          :empty-placeholder [:div.px-4.py-2.text-sm (str "Create a new property value: " q)]
+          :header [:div.px-4.py-2.text-sm.font-medium "Matched property values: "]
+          :item-render (fn [property-value] property-value)
+          :class       "black"})))))
+
 (rum/defcs input < rum/reactive
 (rum/defcs input < rum/reactive
   (rum/local {} ::input-value)
   (rum/local {} ::input-value)
   (mixins/event-mixin
   (mixins/event-mixin
@@ -515,6 +540,8 @@
       (= :property-search action)
       (= :property-search action)
       (animated-modal "property-search" (property-search id) true)
       (animated-modal "property-search" (property-search id) true)
 
 
+      (= :property-value-search action)
+      (animated-modal "property-value-search" (property-value-search id) true)
 
 
       (= :datepicker action)
       (= :datepicker action)
       (animated-modal "date-picker" (datetime-comp/date-picker id format nil) false)
       (animated-modal "date-picker" (datetime-comp/date-picker id format nil) false)

+ 21 - 3
src/main/frontend/db/model.cljs

@@ -1348,14 +1348,32 @@
                        :where
                        :where
                        [_ :block/properties ?p]]
                        [_ :block/properties ?p]]
                      (conn/get-db))
                      (conn/get-db))
-        properties (remove (fn [m] (or (empty? m)
-                                       (and (= 1 (count m))
-                                            (= :id (first (keys m)))))) properties)]
+        properties (remove (fn [m] (empty? m)) properties)]
     (->> (map keys properties)
     (->> (map keys properties)
          (apply concat)
          (apply concat)
          distinct
          distinct
+         (remove #{:id})
          sort)))
          sort)))
 
 
+(defn get-property-values
+  [property]
+  (let [pred (fn [_db properties]
+               (get properties property))]
+    (->>
+     (d/q
+       '[:find [?property-val ...]
+         :in $ ?pred
+         :where
+         [_ :block/properties ?p]
+         [(?pred $ ?p) ?property-val]]
+       (conn/get-db)
+       pred)
+     (map (fn [x] (if (coll? x) x [x])))
+     (apply concat)
+     (remove string/blank?)
+     (distinct)
+     (sort))))
+
 (defn get-template-by-name
 (defn get-template-by-name
   [name]
   [name]
   (when (string? name)
   (when (string? name)

+ 42 - 7
src/main/frontend/handler/editor.cljs

@@ -1620,6 +1620,10 @@
   [q]
   [q]
   (search/property-search q))
   (search/property-search q))
 
 
+(defn get-matched-property-values
+  [property q]
+  (search/property-value-search property q))
+
 (defn get-matched-commands
 (defn get-matched-commands
   [input]
   [input]
   (try
   (try
@@ -1848,6 +1852,15 @@
       (and (= last-input-char last-prev-input-char commands/colon)
       (and (= last-input-char last-prev-input-char commands/colon)
            (or (nil? prev-prev-input-char)
            (or (nil? prev-prev-input-char)
                (= prev-prev-input-char "\n")))
                (= prev-prev-input-char "\n")))
+      (do
+        (cursor/move-cursor-backward input 2)
+        (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
+        (state/set-editor-show-property-search! true))
+
+      (and
+       (not= :property-search (state/get-editor-action))
+       (or (wrapped-by? input "" "::")
+           (wrapped-by? input "\n" "::")))
       (do
       (do
         (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
         (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
         (state/set-editor-show-property-search! true))
         (state/set-editor-show-property-search! true))
@@ -2054,17 +2067,39 @@
     (insert-template! element-id db-id
     (insert-template! element-id db-id
                       {:replace-empty-target? true})))
                       {:replace-empty-target? true})))
 
 
+(defn get-searching-property
+  [input]
+  (let [value (.-value input)
+        pos (util/get-selection-start input)
+        postfix (subs value pos)
+        end-index (when-let [idx (string/index-of postfix "::")]
+                    (+ (max 0 (count (subs value 0 pos))) idx))
+        start-index (or (when-let [p (string/last-index-of (subs value 0 pos) "\n")]
+                          (inc p))
+                        0)]
+    {:end-index end-index
+     :searching-property (when (and start-index end-index (>= end-index start-index))
+                           (subs value start-index end-index))}))
+
 (defn property-on-chosen-handler
 (defn property-on-chosen-handler
   [element-id q]
   [element-id q]
   (fn [property]
   (fn [property]
     (when-let [input (gdom/getElement element-id)]
     (when-let [input (gdom/getElement element-id)]
-      (let [value (.-value input)
-            pos (util/get-selection-start input)
-            last-index (string/last-index-of (subs value 0 pos) "::")
-            last-pattern (subs value last-index pos)]
-        (commands/insert! element-id (str (or property q) ":: ")
-                          {:last-pattern last-pattern})
-        (state/clear-editor-action!)))))
+      (let [{:keys [end-index searching-property]} (get-searching-property input)]
+        (cursor/move-cursor-to input (+ end-index 2))
+        (commands/insert! element-id (str (or property q) "::")
+                          {:last-pattern (str searching-property "::")})
+        (state/set-editor-action! :property-value-search)
+        (state/set-editor-action-data! {:property (or property q)
+                                        :pos (cursor/pos input)})))))
+
+(defn property-value-on-chosen-handler
+  [element-id q]
+  (fn [property-value]
+    (when-let [input (gdom/getElement element-id)]
+      (commands/insert! element-id (or property-value q)
+                        {:last-pattern q}))
+    (state/clear-editor-action!)))
 
 
 (defn parent-is-page?
 (defn parent-is-page?
   [{{:block/keys [parent page]} :data :as node}]
   [{{:block/keys [parent page]} :data :as node}]

+ 5 - 3
src/main/frontend/handler/page.cljs

@@ -242,7 +242,7 @@
 
 
 (defn- replace-property-ref!
 (defn- replace-property-ref!
   [content old-name new-name]
   [content old-name new-name]
-  (let [new-name (keyword (string/replace (string/lower-case new-name) #"\s+" "_"))
+  (let [new-name (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
         old-property (str old-name "::")
         old-property (str old-name "::")
         new-property (str (name new-name) "::")]
         new-property (str (name new-name) "::")]
     (util/replace-ignore-case content old-property new-property)))
     (util/replace-ignore-case content old-property new-property)))
@@ -274,7 +274,7 @@
                        (replace-old-page! f old-name new-name))
                        (replace-old-page! f old-name new-name))
 
 
                      (and (keyword f) (= (name f) old-name))
                      (and (keyword f) (= (name f) old-name))
-                     (keyword (string/replace (string/lower-case new-name) #"\s+" "_"))
+                     (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
 
 
                      :else
                      :else
                      f))
                      f))
@@ -380,6 +380,7 @@
                                   {:block/uuid       uuid
                                   {:block/uuid       uuid
                                    :block/content    content
                                    :block/content    content
                                    :block/properties properties
                                    :block/properties properties
+                                   :block/properties-order (map first properties)
                                    :block/refs (rename-update-block-refs! (:block/refs block) (:db/id page) (:db/id to-page))
                                    :block/refs (rename-update-block-refs! (:block/refs block) (:db/id page) (:db/id to-page))
                                    :block/path-refs (rename-update-block-refs! (:block/path-refs block) (:db/id page) (:db/id to-page))})))) blocks)
                                    :block/path-refs (rename-update-block-refs! (:block/path-refs block) (:db/id page) (:db/id to-page))})))) blocks)
                       (remove nil?))]
                       (remove nil?))]
@@ -568,7 +569,8 @@
           (rename-namespace-pages! repo old-name new-name))
           (rename-namespace-pages! repo old-name new-name))
         (rename-nested-pages old-name new-name))
         (rename-nested-pages old-name new-name))
       (when (string/blank? new-name)
       (when (string/blank? new-name)
-        (notification/show! "Please use a valid name, empty name is not allowed!" :error)))))
+        (notification/show! "Please use a valid name, empty name is not allowed!" :error)))
+    (ui-handler/re-render-root!)))
 
 
 (defn- split-col-by-element
 (defn- split-col-by-element
   [col element]
   [col element]

+ 13 - 1
src/main/frontend/search.cljs

@@ -172,7 +172,19 @@
        (if (string/blank? q)
        (if (string/blank? q)
          properties
          properties
          (let [result (fuzzy-search properties q :limit limit)]
          (let [result (fuzzy-search properties q :limit limit)]
-          (vec result)))))))
+           (vec result)))))))
+
+(defn property-value-search
+  ([property q]
+   (property-value-search property q 10))
+  ([property q limit]
+   (let [q (clean-str q)
+         result (db-model/get-property-values (keyword property))]
+     (when (seq result)
+       (if (string/blank? q)
+         result
+         (let [result (fuzzy-search result q :limit limit)]
+           (vec result)))))))
 
 
 (defn sync-search-indice!
 (defn sync-search-indice!
   [repo tx-report]
   [repo tx-report]

+ 0 - 1
src/main/frontend/state.cljs

@@ -588,7 +588,6 @@
 
 
 (defn set-editor-action!
 (defn set-editor-action!
   [value]
   [value]
-  (js/console.trace)
   (set-state! :editor/action value))
   (set-state! :editor/action value))
 
 
 (defn set-editor-action-data!
 (defn set-editor-action-data!

+ 10 - 8
src/main/frontend/util/text.cljs

@@ -86,19 +86,21 @@
 
 
 (defn get-string-all-indexes
 (defn get-string-all-indexes
   "Get all indexes of `value` in the string `s`."
   "Get all indexes of `value` in the string `s`."
-  [s value]
-  (loop [acc []
-         i 0]
-    (if-let [i (string/index-of s value i)]
-      (recur (conj acc i) (+ i (count value)))
-      acc)))
+  [s value before?]
+  (if (= value "")
+    (if before? [0] [(dec (count s))])
+    (loop [acc []
+          i 0]
+     (if-let [i (string/index-of s value i)]
+       (recur (conj acc i) (+ i (count value)))
+       acc))))
 
 
 (defn wrapped-by?
 (defn wrapped-by?
   "`pos` must be wrapped by `before` and `and` in string `value`, e.g. ((a|b))"
   "`pos` must be wrapped by `before` and `and` in string `value`, e.g. ((a|b))"
   [value pos before end]
   [value pos before end]
-  (let [before-matches (->> (get-string-all-indexes value before)
+  (let [before-matches (->> (get-string-all-indexes value before true)
                             (map (fn [i] [i :before])))
                             (map (fn [i] [i :before])))
-        end-matches (->> (get-string-all-indexes value end)
+        end-matches (->> (get-string-all-indexes value end false)
                          (map (fn [i] [i :end])))
                          (map (fn [i] [i :end])))
         indexes (sort-by first (concat before-matches end-matches [[pos :between]]))
         indexes (sort-by first (concat before-matches end-matches [[pos :between]]))
         ks (map second indexes)
         ks (map second indexes)