Просмотр исходного кода

enhance: use shadcn form components for import

Part of LOG-3235. Also adds inline client validation for required name field.
Form components look better, easier to add more inputs and there are
many examples to learn from
Gabriel Horner 1 год назад
Родитель
Сommit
28e2ca1ce5
2 измененных файлов с 168 добавлено и 171 удалено
  1. 167 170
      src/main/frontend/components/imports.cljs
  2. 1 1
      src/main/frontend/routes.cljs

+ 167 - 170
src/main/frontend/components/imports.cljs

@@ -29,6 +29,7 @@
             [rum.core :as rum]
             [logseq.shui.ui :as shui]
             [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.form.core :as form-core]
             [lambdaisland.glogi :as log]
             [logseq.db.frontend.validate :as db-validate]
             [logseq.db :as ldb]))
@@ -165,86 +166,82 @@
                  {:on-click on-submit})]]))
 
 (rum/defc import-file-graph-dialog
-  [initial-name on-graph-name-confirmed]
-  (let [[graph-input set-graph-input!] (rum/use-state initial-name)
-        [tag-classes-input set-tag-classes-input!] (rum/use-state "")
-        [convert-all-tags-input set-convert-all-tags-input!] (rum/use-state false)
-        [property-classes-input set-property-classes-input!] (rum/use-state "")
-        [property-parent-classes-input set-property-parent-classes-input!] (rum/use-state "")
-        on-submit #(do (on-graph-name-confirmed
-                        {:graph-name graph-input
-                         :tag-classes tag-classes-input
-                         :convert-all-tags? convert-all-tags-input
-                         :property-classes property-classes-input
-                         :property-parent-classes property-parent-classes-input})
-                       (shui/dialog-close!))]
-    [:div.container
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.leading-6.font-medium
-        "New graph name:"]]]
-     (shui/input
-      {:class "my-2 mb-4"
-       :auto-focus true
-       :default-value graph-input
-       :on-change (fn [e]
-                    (set-graph-input! (util/evalue e)))
-       :on-key-down (fn [e]
-                      (when (= "Enter" (util/ekey e))
-                        (on-submit)))})
-
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.leading-6.font-medium
-        "(Optional) Tags to import as new tags:"]
-       [:div
-        [:span.text-sm.mr-2 "Import all tags"]
-        (shui/checkbox {:default-checked convert-all-tags-input
-                        :on-checked-change set-convert-all-tags-input!})]
-       [:div
-        [:span.text-sm "Import only these tags. "]
-        [:span.text-xs "Tags are case insensitive and separated by commas"]]]]
-     (shui/input
-      {:class "my-2 mb-4"
-       :default-value tag-classes-input
-       :disabled convert-all-tags-input
-       :on-change (fn [e]
-                    (set-tag-classes-input! (util/evalue e)))
-       :on-key-down (fn [e]
-                      (when (= "Enter" (util/ekey e))
-                        (on-submit)))})
-
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.leading-6.font-medium
-        "(Optional) Properties whose values are imported as new tags e.g. 'type':"]
-       [:span.text-xs
-        "Properties are case insensitive and separated by commas"]]]
-     (shui/input
-      {:class "my-2 mb-4"
-       :default-value property-classes-input
-       :on-change (fn [e]
-                    (set-property-classes-input! (util/evalue e)))
-       :on-key-down (fn [e]
-                      (when (= "Enter" (util/ekey e))
-                        (on-submit)))})
-
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.leading-6.font-medium
-        "(Optional) Properties whose values are imported as parents of new tags e.g. 'parent':"]
-       [:span.text-xs
-        "Properties are case insensitive and separated by commas"]]]
-     (shui/input
-      {:class "my-2 mb-4"
-       :default-value property-parent-classes-input
-       :on-change (fn [e]
-                    (set-property-parent-classes-input! (util/evalue e)))
-       :on-key-down (fn [e]
-                      (when (= "Enter" (util/ekey e))
-                        (on-submit)))})
-
-     (shui/button {:size :sm :on-click on-submit} "Submit")]))
+  [initial-name on-submit-fn]
+  [:div.border.p-6.rounded.bg-gray-01.mt-4
+   (let [form-ctx (form-core/use-form
+                   {:defaultValues {:graph-name initial-name
+                                    :convert-all-tags false
+                                    :tag-classes ""
+                                    :property-classes ""
+                                    :property-parent-classes ""}
+                    :yupSchema (-> (.object form-core/yup)
+                                   (.shape #js {:graph-name (-> (.string form-core/yup) (.required))})
+                                   (.required))})
+         handle-submit (:handleSubmit form-ctx)
+         on-submit-valid (handle-submit
+                          (fn [^js e]
+                            ;; (js/console.log "[form] submit: " e (js->clj e))
+                            (on-submit-fn (js->clj e :keywordize-keys true))
+                            (shui/dialog-close!)))
+        [convert-all-tags-input set-convert-all-tags-input!] (rum/use-state false)]
+
+     (shui/form-provider form-ctx
+       [:form
+        {:on-submit on-submit-valid}
+
+        (shui/form-field {:name "graph-name"}
+                         (fn [field error]
+                           (shui/form-item
+                            (shui/form-label "New graph name")
+                            (shui/form-control
+                             (shui/input (merge {:placeholder "Graph name"} field)))
+                            (when error
+                              (shui/form-description
+                               [:b.text-red-800 (:message error)])))))
+
+        (shui/form-field {:name "convert-all-tags"}
+                         (fn [field]
+                           (shui/form-item
+                            {:class "pt-3 flex justify-start items-center space-x-3 space-y-0 my-3 pr-3"}
+                            (shui/form-label "Import all tags")
+                            (shui/form-control
+                             (shui/checkbox {:checked (:value field)
+                                             :on-checked-change (fn [e]
+                                                                  ((:onChange field) e)
+                                                                  (set-convert-all-tags-input! (not convert-all-tags-input)))})))))
+
+        (shui/form-field {:name "tag-classes"}
+                         (fn [field _error]
+                           (shui/form-item
+                            {:class "pt-3"}
+                            (shui/form-label "Import specific tags")
+                            (shui/form-control
+                             (shui/input (merge field
+                                                {:placeholder "tag 1, tag 2" :disabled convert-all-tags-input})))
+                            (shui/form-description
+                             "Tags are case insensitive"))))
+
+        (shui/form-field {:name "property-classes"}
+                         (fn [field _error]
+                           (shui/form-item
+                            {:class "pt-3"}
+                            (shui/form-label "Import additional tags from property values")
+                            (shui/form-control
+                             (shui/input (merge {:placeholder "e.g. type"} field)))
+                            (shui/form-description
+                             "Properties are case insensitive and separated by commas"))))
+
+        (shui/form-field {:name "property-parent-classes"}
+                         (fn [field _error]
+                           (shui/form-item
+                            {:class "pt-3"}
+                            (shui/form-label "Import tag parents from property values")
+                            (shui/form-control
+                             (shui/input (merge {:placeholder "e.g. parent"} field)))
+                            (shui/form-description
+                             "Properties are case insensitive and separated by commas"))))
+
+        (shui/button {:type "submit" :class "right-0 mt-3"} "Submit")]))])
 
 (defn- counts-from-entities
   [entities]
@@ -313,7 +310,7 @@
                    (fs/write-file! repo repo-dir (:path file) content {:skip-transact? true})))))))
 
 (defn- import-file-graph
-  [*files {:keys [graph-name tag-classes convert-all-tags? property-classes property-parent-classes]} config-file]
+  [*files {:keys [graph-name tag-classes convert-all-tags property-classes property-parent-classes]} config-file]
   (state/set-state! :graph/importing :file-graph)
   (state/set-state! [:graph/importing-state :current-page] "Config files")
   (p/let [start-time (t/now)
@@ -324,7 +321,7 @@
                    :tag-classes (set (string/split tag-classes #",\s*"))
                    :property-classes (set (string/split property-classes #",\s*"))
                    :property-parent-classes (set (string/split property-parent-classes #",\s*"))
-                   :convert-all-tags? convert-all-tags?
+                   :convert-all-tags? convert-all-tags
                    ;; common options
                    :notify-user show-notification
                    :set-ui-state state/set-state!
@@ -355,23 +352,24 @@
     (finished-cb)))
 
 (defn import-file-to-db-handler
-  "Import from a graph folder as a DB-based graph.
-
-- Page name, journal name creation"
-  [ev _opts]
-  (let [^js file-objs (array-seq (.-files (.-target ev)))
-        original-graph-name (string/replace (.-webkitRelativePath (first file-objs)) #"/.*" "")
-        import-graph-fn (fn [user-inputs]
-                          (let [files (->> file-objs
-                                           (map #(hash-map :file-object %
-                                                           :path (path/trim-dir-prefix original-graph-name (.-webkitRelativePath %))))
-                                           (remove #(and (not (string/starts-with? (:path %) "assets/"))
+  "Import from a graph folder as a DB-based graph"
+  [ev opts]
+  (let [^js file-objs (if ev (array-seq (.-files (.-target ev))) #js [])
+        original-graph-name (if (first file-objs)
+                              (string/replace (.-webkitRelativePath (first file-objs)) #"/.*" "")
+                              "")
+        import-graph-fn (or (:import-graph-fn opts)
+                            (fn [user-inputs]
+                              (let [files (->> file-objs
+                                               (map #(hash-map :file-object %
+                                                               :path (path/trim-dir-prefix original-graph-name (.-webkitRelativePath %))))
+                                               (remove #(and (not (string/starts-with? (:path %) "assets/"))
                                                          ;; TODO: Update this when supporting more formats as this aggressively excludes most formats
-                                                         (fs-util/ignored-path? original-graph-name (.-webkitRelativePath (:file-object %))))))]
-                            (if-let [config-file (first (filter #(= (:path %) "logseq/config.edn") files))]
-                              (import-file-graph files user-inputs config-file)
-                              (notification/show! "Import failed as the file 'logseq/config.edn' was not found for a Logseq graph."
-                                                  :error))))]
+                                                             (fs-util/ignored-path? original-graph-name (.-webkitRelativePath (:file-object %))))))]
+                                (if-let [config-file (first (filter #(= (:path %) "logseq/config.edn") files))]
+                                  (import-file-graph files user-inputs config-file)
+                                  (notification/show! "Import failed as the file 'logseq/config.edn' was not found for a Logseq graph."
+                                                      :error)))))]
     (shui/dialog-open!
      #(import-file-graph-dialog original-graph-name
                                 (fn [{:keys [graph-name] :as user-inputs}]
@@ -379,9 +377,6 @@
                                     (repo/invalid-graph-name? graph-name)
                                     (repo/invalid-graph-name-warning)
 
-                                    (string/blank? graph-name)
-                                    (notification/show! "Empty graph name." :error)
-
                                     (repo-handler/graph-already-exists? graph-name)
                                     (notification/show! "Please specify another name as another graph with this name already exists!" :error)
 
@@ -426,72 +421,74 @@
      (import-indicator importing?)
      (when-not importing?
        (setups/setups-container
-         :importer
-         [:article.flex.flex-col.items-center.importer.py-16.px-8
-          [:section.c.text-center
-           [:h1 (t :on-boarding/importing-title)]
-           [:h2 (t :on-boarding/importing-desc)]]
-          [:section.d.md:flex.flex-col
-           [:label.action-input.flex.items-center.mx-2.my-2
-            [:span.as-flex-center [:i (svg/logo 28)]]
-            [:span.flex.flex-col
-             [[:strong "SQLite"]
-              [:small (t :on-boarding/importing-sqlite-desc)]]]
-            [:input.absolute.hidden
-             {:id "import-sqlite-db"
-              :type "file"
-              :on-change (fn [e]
-                           (shui/dialog-open!
-                             #(set-graph-name-dialog e {:sqlite? true})))}]]
-
-           (when (or (util/electron?) util/web-platform?)
-             [:label.action-input.flex.items-center.mx-2.my-2
-              [:span.as-flex-center [:i (svg/logo 28)]]
-              [:span.flex.flex-col
-               [[:strong "File to DB graph"]
-                [:small "Import a file-based Logseq graph folder into a new DB graph"]]]
-              [:input.absolute.hidden
-               {:id "import-file-graph"
-                :type "file"
-                :webkitdirectory "true"
-                :on-change (debounce (fn [e]
-                                       (import-file-to-db-handler e {}))
-                             1000)}]])
-
-           (when (and (util/electron?) support-file-based?)
-             [:label.action-input.flex.items-center.mx-2.my-2
-              [:span.as-flex-center [:i (svg/logo 28)]]
-              [:span.flex.flex-col
-               [[:strong "EDN / JSON"]
-                [:small (t :on-boarding/importing-lsq-desc)]]]
-              [:input.absolute.hidden
-               {:id "import-lsq"
-                :type "file"
-                :on-change lsq-import-handler}]])
-
-           (when (and (util/electron?) support-file-based?)
-             [:label.action-input.flex.items-center.mx-2.my-2
-              [:span.as-flex-center [:i (svg/roam-research 28)]]
-              [:div.flex.flex-col
-               [[:strong "RoamResearch"]
-                [:small (t :on-boarding/importing-roam-desc)]]]
-              [:input.absolute.hidden
-               {:id "import-roam"
-                :type "file"
-                :on-change roam-import-handler}]])
-
-           (when (and (util/electron?) support-file-based?)
-             [:label.action-input.flex.items-center.mx-2.my-2
-              [:span.as-flex-center.ml-1 (ui/icon "sitemap" {:size 26})]
-              [:span.flex.flex-col
-               [[:strong "OPML"]
-                [:small (t :on-boarding/importing-opml-desc)]]]
-
-              [:input.absolute.hidden
-               {:id "import-opml"
-                :type "file"
-                :on-change opml-import-handler}]])]
-
-          (when (= "picker" (:from query-params))
-            [:section.e
-             [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])]))]))
+        :importer
+        [:article.flex.flex-col.items-center.importer.py-16.px-8
+         [:section.c.text-center
+          [:h1 (t :on-boarding/importing-title)]
+          [:h2 (t :on-boarding/importing-desc)]]
+         [:section.d.md:flex.flex-col
+          [:label.action-input.flex.items-center.mx-2.my-2
+           [:span.as-flex-center [:i (svg/logo 28)]]
+           [:span.flex.flex-col
+            [[:strong "SQLite"]
+             [:small (t :on-boarding/importing-sqlite-desc)]]]
+           [:input.absolute.hidden
+            {:id "import-sqlite-db"
+             :type "file"
+             :on-change (fn [e]
+                          (shui/dialog-open!
+                           #(set-graph-name-dialog e {:sqlite? true})))}]]
+
+          (when (or (util/electron?) util/web-platform?)
+            [:label.action-input.flex.items-center.mx-2.my-2
+             [:span.as-flex-center [:i (svg/logo 28)]]
+             [:span.flex.flex-col
+              [[:strong "File to DB graph"]
+               [:small "Import a file-based Logseq graph folder into a new DB graph"]]]
+             ;; Test form style changes
+             #_[:a.button {:on-click #(import-file-to-db-handler nil {:import-graph-fn js/alert})} "Open"]
+             [:input.absolute.hidden
+              {:id "import-file-graph"
+               :type "file"
+               :webkitdirectory "true"
+               :on-change (debounce (fn [e]
+                                      (import-file-to-db-handler e {}))
+                                    1000)}]])
+
+          (when (and (util/electron?) support-file-based?)
+            [:label.action-input.flex.items-center.mx-2.my-2
+             [:span.as-flex-center [:i (svg/logo 28)]]
+             [:span.flex.flex-col
+              [[:strong "EDN / JSON"]
+               [:small (t :on-boarding/importing-lsq-desc)]]]
+             [:input.absolute.hidden
+              {:id "import-lsq"
+               :type "file"
+               :on-change lsq-import-handler}]])
+
+          (when (and (util/electron?) support-file-based?)
+            [:label.action-input.flex.items-center.mx-2.my-2
+             [:span.as-flex-center [:i (svg/roam-research 28)]]
+             [:div.flex.flex-col
+              [[:strong "RoamResearch"]
+               [:small (t :on-boarding/importing-roam-desc)]]]
+             [:input.absolute.hidden
+              {:id "import-roam"
+               :type "file"
+               :on-change roam-import-handler}]])
+
+          (when (and (util/electron?) support-file-based?)
+            [:label.action-input.flex.items-center.mx-2.my-2
+             [:span.as-flex-center.ml-1 (ui/icon "sitemap" {:size 26})]
+             [:span.flex.flex-col
+              [[:strong "OPML"]
+               [:small (t :on-boarding/importing-opml-desc)]]]
+
+             [:input.absolute.hidden
+              {:id "import-opml"
+               :type "file"
+               :on-change opml-import-handler}]])]
+
+         (when (= "picker" (:from query-params))
+           [:section.e
+            [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])]))]))

+ 1 - 1
src/main/frontend/routes.cljs

@@ -12,7 +12,7 @@
             [frontend.extensions.zotero :as zotero]
             [frontend.components.bug-report :as bug-report]
             [frontend.components.user.login :as login]
-            [logseq.shui.demo2 :as shui]
+            [logseq.shui.demo :as shui]
             [frontend.components.imports :as imports]
             [frontend.config :as config]
             [logseq.db :as ldb]