Browse Source

enhance(ux): functionality for the dropdown property editor

charlie 1 year ago
parent
commit
082511cf83

+ 4 - 4
src/main/frontend/components/property.css

@@ -312,14 +312,14 @@ a.control-link {
       @apply flex items-center gap-1 font-normal opacity-90;
       @apply flex items-center gap-1 font-normal opacity-90;
     }
     }
 
 
-    > small {
-      @apply flex items-center opacity-50;
+    > label {
+      @apply text-xs flex items-center opacity-40;
     }
     }
 
 
     &.disabled {
     &.disabled {
       @apply opacity-70 select-none cursor-not-allowed;
       @apply opacity-70 select-none cursor-not-allowed;
 
 
-      > small {
+      > label {
         @apply flex items-center gap-1 opacity-40 relative top-[2px] right-[1px];
         @apply flex items-center gap-1 opacity-40 relative top-[2px] right-[1px];
       }
       }
     }
     }
@@ -350,7 +350,7 @@ a.control-link {
     }
     }
 
 
     > .ui__input:last-child {
     > .ui__input:last-child {
-      @apply !h-[29px];
+      @apply !h-[29px] px-2;
     }
     }
   }
   }
 }
 }

+ 66 - 24
src/main/frontend/components/property_v2.cljs

@@ -29,27 +29,70 @@
     (db-property-handler/upsert-closed-value! (:db/ident property) item)
     (db-property-handler/upsert-closed-value! (:db/ident property) item)
     (re-init-commands! property)))
     (re-init-commands! property)))
 
 
+(defn- loop-focusable-elements!
+  ([^js cnt] (loop-focusable-elements! cnt
+               ".ui__button:not([disabled]), .ui__input, .ui__textarea"))
+  ([^js cnt selectors]
+   (when-let [els (some-> cnt (.querySelectorAll selectors) (seq))]
+     (let [active js/document.activeElement
+           current-idx (.indexOf els active)
+           total-len (count els)
+           to-idx (cond
+                    (or (= -1 current-idx)
+                      (= total-len (inc current-idx)))
+                    0
+                    :else
+                    (inc current-idx))]
+       (some-> els (nth to-idx) (.focus))))))
 
 
 (rum/defc name-edit-pane
 (rum/defc name-edit-pane
-  [property]
-  (let [title (:block/title property)
-        icon (:logseq.property/icon property)
-        *input-ref (rum/use-ref nil)]
-    [:div.ls-property-name-edit-pane
+  [property {:keys [set-sub-open!]}]
+  (let [*form-data (rum/use-ref {:icon (:logseq.property/icon property)
+                                 :title (or (:block/title property) "")
+                                 :description (or (:logseq.property/description property) "")})
+        [form-data, set-form-data!] (rum/use-state (rum/deref *form-data))
+        *el (rum/use-ref nil)
+        *input-ref (rum/use-ref nil)
+        title (util/trim-safe (:title form-data))
+        description (util/trim-safe (:description form-data))]
+
+    (rum/use-effect!
+      (fn []
+        (js/setTimeout #(some-> (rum/deref *el) (.focus)) 32))
+      [])
+
+    [:div.ls-property-name-edit-pane.outline-none
+     {:on-key-down (fn [^js e] (when (= "Tab" (.-key e))
+                                 (loop-focusable-elements! (rum/deref *el))))
+      :tab-index -1
+      :ref *el}
      [:div.flex.items-center.input-wrap
      [:div.flex.items-center.input-wrap
-      (icon-component/icon-picker icon {:on-chosen (fn [_e icon]
-                                                     (db-property-handler/upsert-property!
-                                                       (:db/ident property)
-                                                       (:block/schema property)
-                                                       {:properties {:logseq.property/icon icon}}))
-                                        :popup-opts {:align "start"}
-                                        :empty-label "?"})
+      (icon-component/icon-picker (:icon form-data)
+        {:on-chosen (fn [_e icon] (set-form-data! (assoc form-data :icon icon)))
+         :popup-opts {:align "start"}
+         :empty-label "?"})
+      (shui/input {:ref *input-ref :size "sm" :default-value title :placeholder "name"
+                   :on-change (fn [^js e] (set-form-data! (assoc form-data :title (util/trim-safe (util/evalue e)))))})]
+     [:div.pt-2 (shui/textarea {:placeholder "description" :default-value description
+                                :on-change (fn [^js e] (set-form-data! (assoc form-data :description (util/trim-safe (util/evalue e)))))})]
 
 
-      (shui/input {:ref *input-ref :size "sm" :default-value title})]
-     [:div.pt-2 (shui/textarea {:placeholder "description"})]
-     [:div.pt-2.flex.justify-end
-      (shui/button {:size "sm" :disabled true
-                    :variant :secondary} "Save")]]))
+     (let [dirty? (not= (rum/deref *form-data) form-data)]
+       [:div.pt-2.flex.justify-end
+        (shui/button {:size "sm" :disabled (not dirty?)
+                      :variant (if dirty? :default :secondary)
+                      :on-click (fn []
+                                  (when (string/blank? title)
+                                    (some-> (rum/deref *input-ref) (.focus))
+                                    (throw (js/Error. "property name is empty")))
+
+                                  (-> (db-property-handler/upsert-property!
+                                        (:db/ident property)
+                                        (:block/schema property)
+                                        {:property-name title
+                                         :properties {:logseq.property/icon (:icon form-data)}})
+                                    (p/then #(set-sub-open! false))
+                                    (p/catch #(shui/toast! (str %) :error))))}
+          "Save")])]))
 
 
 (rum/defc base-edit-form
 (rum/defc base-edit-form
   [own-property block]
   [own-property block]
@@ -141,19 +184,19 @@
             (shui/switch {:id id2 :size "sm" :default-checked toggle-checked?
             (shui/switch {:id id2 :size "sm" :default-checked toggle-checked?
                           :disabled disabled? :on-click #(util/stop-propagation %)
                           :disabled disabled? :on-click #(util/stop-propagation %)
                           :on-checked-change (or on-toggle-checked-change identity)})]
                           :on-checked-change (or on-toggle-checked-change identity)})]
-           [:small [:span desc]
+           [:label [:span desc]
             (when disabled? (shui/tabler-icon "forbid-2" {:size 15}))]))])))
             (when disabled? (shui/tabler-icon "forbid-2" {:size 15}))]))])))
 
 
 (rum/defc choice-item-content
 (rum/defc choice-item-content
   [property block]
   [property block]
   (let [delete-choice! (fn []
   (let [delete-choice! (fn []
                          (p/do!
                          (p/do!
-                          (db-property-handler/delete-closed-value! (:db/id property) (:db/id block))
-                          (re-init-commands! property)))
+                           (db-property-handler/delete-closed-value! (:db/id property) (:db/id block))
+                           (re-init-commands! property)))
         update-icon! (fn [icon]
         update-icon! (fn [icon]
                        (property-handler/set-block-property!
                        (property-handler/set-block-property!
-                        (state/get-current-repo) (:block/uuid block) :logseq.property/icon
-                        (select-keys icon [:id :type :color])))
+                         (state/get-current-repo) (:block/uuid block) :logseq.property/icon
+                         (select-keys icon [:id :type :color])))
         icon (:logseq.property/icon block)
         icon (:logseq.property/icon block)
         value (db-property/closed-value-content block)]
         value (db-property/closed-value-content block)]
 
 
@@ -230,11 +273,10 @@
                                 (or property-type :default))
                                 (or property-type :default))
         icon (:logseq.property/icon property)
         icon (:logseq.property/icon property)
         icon (when icon [:span.float-left.w-4.h-4.overflow-hidden.leading-4.relative
         icon (when icon [:span.float-left.w-4.h-4.overflow-hidden.leading-4.relative
-                         {:class "top-[1px]"}
                          (icon-component/icon icon {:size 15})])]
                          (icon-component/icon icon {:size 15})])]
     [:<>
     [:<>
      (dropdown-editor-menuitem {:icon :edit :title "Property name" :desc [:span.flex.items-center.gap-1 icon title]
      (dropdown-editor-menuitem {:icon :edit :title "Property name" :desc [:span.flex.items-center.gap-1 icon title]
-                                :submenu-content (fn [] (name-edit-pane property))})
+                                :submenu-content (fn [ops] (name-edit-pane property ops))})
      (dropdown-editor-menuitem {:icon :hash :title "Property type" :desc (str property-type-label) :disabled? true})
      (dropdown-editor-menuitem {:icon :hash :title "Property type" :desc (str property-type-label) :disabled? true})
 
 
      (when enable-closed-values? (empty? (:property/schema.classes property))
      (when enable-closed-values? (empty? (:property/schema.classes property))