Browse Source

ux: multiple values editor

Tienson Qin 3 years ago
parent
commit
52cbf9b784

+ 97 - 36
src/main/frontend/components/property.cljs

@@ -13,7 +13,8 @@
             [frontend.search :as search]
             [frontend.components.search.highlight :as highlight]
             [frontend.components.svg :as svg]
-            [frontend.modules.shortcut.core :as shortcut]))
+            [frontend.modules.shortcut.core :as shortcut]
+            [medley.core :as medley]))
 
 (defn- add-property
   [entity k *new-property?]
@@ -71,27 +72,85 @@
      (when (and @*show-close? (not ref-property?))
        [:div.absolute.top-0.right-0
         [:a.fade-link.fade-in.py-2.px-1
-         {:title "Remove the property from this page"
+         {:title "Remove this property"
           :on-click (fn [_e]
                       (property-handler/delete-property! entity property-id))}
          (ui/icon "x")]])]))
 
-(rum/defc property-value < rum/reactive
-  [entity property k v k' {:keys [inline-text editor-box]}]
+(rum/defcs property-value < rum/reactive
+  [state entity property k v k' {:keys [inline-text editor-box page-cp]}]
   (let [block (assoc entity :editing-property property)
         dom-id (str "ls-property-" k)
         editor-id (str "property-" (:db/id entity) "-" k')
-        editing? (state/sub [:editor/editing? editor-id])]
-    (if editing?
+        editing? (state/sub [:editor/editing? editor-id])
+        schema (:block/property-schema property)
+        edit-fn (fn [editor-id id v]
+                  (let [cursor-range (util/caret-range (gdom/getElement (or id dom-id)))]
+                    (state/set-editing! editor-id v block cursor-range)
+                    (js/setTimeout
+                     (fn []
+                       (state/set-editor-action-data! {:property (:block/original-name property)
+                                                       :pos 0})
+                       (state/set-editor-action! :property-value-search))
+                     100)))
+        multiple-values? (:multiple-values? schema)
+        type (:type schema)
+        object? (= type "object")]
+    (cond
+      multiple-values?
+      (let [v' (if (coll? v) v (when v [v]))
+            editor-id' (str editor-id (count v'))
+            new-editing? (state/sub [:editor/editing? editor-id'])]
+        [:div.flex.flex-1.flex-col
+         [:div.flex.flex-1.flex-col
+          (for [[idx item] (medley/indexed v')]
+            (let [dom-id' (str dom-id "-" idx)
+                  editor-id' (str editor-id idx)
+                  editing? (state/sub [:editor/editing? editor-id'])]
+              (if editing?
+                (editor-box {:format :markdown
+                             :block block} editor-id' {})
+                [:div.flex.flex-1.property-value-content
+                 {:id dom-id'
+                  :on-click (fn [] (edit-fn editor-id' dom-id' item))}
+                 (if object?
+                   (page-cp {} {:block/name (util/page-name-sanity-lc item)})
+                   (inline-text {} :markdown (str v)))])))
+
+          (let [fv (first v')]
+            (when (and (not new-editing?)
+                       fv
+                       (or (and (string? fv) (not (string/blank? fv)))
+                           (and (not (string? fv)) (some? fv))))
+             [:div.rounded-sm.ml-1
+              {:on-click (fn []
+                           (edit-fn (str editor-id (count v')) nil ""))}
+              [:div.flex.flex-row
+               [:div.block {:style {:height      20
+                                    :width       20}}
+                [:a.add-button-link.block {:title "Add another value"
+                                           :style {:margin-left -4}}
+                 (ui/icon "circle-plus")]]]]))]
+         (when new-editing?
+           (editor-box {:format :markdown
+                        :block block} editor-id' {}))])
+
+      editing?
       (editor-box {:format :markdown
                    :block block} editor-id {})
+
+      :else
       [:div.flex.flex-1.property-value-content
        {:id dom-id
         :on-click (fn []
-                    (let [cursor-range (util/caret-range (gdom/getElement dom-id))]
-                      (state/set-editing! editor-id (str v) block cursor-range)))}
-       (when-not (string/blank? (str v))
-         (inline-text {} :markdown (str v)))])))
+                    (edit-fn editor-id nil v))}
+       (cond
+         (and (= type "date") (string/blank? v))
+         [:div "TBD (date icon)"]
+
+         :else
+         (when-not (string/blank? (str v))
+           (inline-text {} :markdown (str v))))])))
 
 (rum/defcs properties-area <
   (rum/local false ::new-property?)
@@ -99,34 +158,36 @@
   [state entity properties refs-properties {:keys [page-cp inline-text]}]
   (let [*new-property? (::new-property? state)
         editor-box (state/get-component :editor/box)
-        ref-keys (set (keys refs-properties))]
+        ref-keys (set (keys refs-properties))
+        page? (:block/name entity)]
     [:div.ls-properties-area
      (when (seq properties)
-       [:table.table-auto.m-0
-        [:tbody
-         (for [[k v] properties]
-           (when-let [property (db/pull [:block/uuid k])]
-             (when-let [k' (:block/original-name property)]
-               (let [ref-property? (contains? ref-keys k)]
-                 [:tr
-                  [:td.property-key.p-0 {:style {:width 160}} ;FIXME: auto responsive
-                   (property-key entity k' page-cp k ref-property?)]
-
-                  [:td.property-value.p-0
-                   (property-value entity property k v k' {:inline-text inline-text
-                                                           :editor-box editor-box})]]))))]])
-
-     (if @*new-property?
-       (property-input entity *new-property?)
-       [:div.flex-1.flex-col.rounded-sm
-        {:on-click (fn []
-                     (reset! *new-property? true))}
-        [:div.flex.flex-row
-         [:div.block {:style {:height      20
-                              :width       20}}
-          [:a.add-button-link.block {:title "Add another property"
-                                     :style {:margin-left -4}}
-           (ui/icon "circle-plus")]]]])]))
+       [:div
+        (for [[k v] properties]
+          (when-let [property (db/pull [:block/uuid k])]
+            (when-let [k' (:block/original-name property)]
+              (let [ref-property? (contains? ref-keys k)]
+                [:div.grid.grid-cols-4.gap-1
+                 [:div.property-key.col-span-1
+                  (property-key entity k' page-cp k ref-property?)]
+
+                 [:div.col-span-3
+                  (property-value entity property k v k' {:page-cp page-cp
+                                                          :inline-text inline-text
+                                                          :editor-box editor-box})]]))))])
+
+     (when page?
+       (if @*new-property?
+         (property-input entity *new-property?)
+         [:div.flex-1.flex-col.rounded-sm
+          {:on-click (fn []
+                       (reset! *new-property? true))}
+          [:div.flex.flex-row
+           [:div.block {:style {:height      20
+                                :width       20}}
+            [:a.add-button-link.block {:title "Add another property"
+                                       :style {:margin-left -4}}
+             (ui/icon "circle-plus")]]]]))]))
 
 (defn properties
   [entity block-components-m]

+ 10 - 9
src/main/frontend/components/property.css

@@ -1,16 +1,13 @@
-.property-value {
-    @apply px-1 rounded-sm;
-}
-
-.property-value:hover {
-  background: var(--ls-secondary-background-color);
-}
-
 .property-value-content {
+  @apply px-1 rounded-sm;
   cursor: text;
   min-height: 24px;
 }
 
+.property-value-content:hover {
+    background: var(--ls-secondary-background-color);
+}
+
 .ls-properties-area {
     .add-button-link {
         opacity: 0.5;
@@ -19,10 +16,14 @@
     tr:nth-child(even), tr:nth-child(odd) {
         background: none;
     }
+
+    .editor-inner {
+        @apply px-1;
+    }
 }
 
 input.simple-input {
-    padding-left: 0;
+    @apply px-1;
     border-radius: 0;
     border: none;
     border-bottom: 1px solid;

+ 1 - 0
src/main/frontend/components/property/schema.cljs

@@ -33,6 +33,7 @@
                   [{:label "Any" :value "any"}
                    {:label "Number" :value "number"}
                    {:label "Date" :value "date"}
+                   {:label "Checkbox" :value "checkbox"}
                    {:label "Url" :value "url"}
                    {:label "Object" :value "object"}])]
     (property-item

+ 24 - 9
src/main/frontend/handler/property.cljs

@@ -62,11 +62,15 @@
 (defn- extract-refs
   [entity properties]
   (let [property-values (->>
-                         (map (fn [v]
-                                (if (coll? v)
-                                  v
-                                  [v]))
-                           (vals properties))
+                         properties
+                         (map (fn [[k v]]
+                                (let [schema (:block/property-schema (db/pull [:block/uuid k]))
+                                      object? (= (:type schema) "object")
+                                      f (if object? page-ref/->page-ref identity)]
+                                  (->> (if (coll? v)
+                                         v
+                                         [v])
+                                       (map f)))))
                          (apply concat)
                          (filter string?))
         block-text (string/join " "
@@ -111,7 +115,7 @@
       "url" (if (gp-util/url? value)
               [true value]
               [false "invalid URL"])
-      "object" (let [page-name (page-ref/get-page-name value)]
+      "object" (if-let [page-name (page-ref/get-page-name value)]
                  [true page-name]
                  [false "invalid Object"]))))
 
@@ -120,11 +124,22 @@
   (when (not= property-id (:block/uuid entity))
     (when-let [property (db/pull [:block/uuid property-id])]
       (let [schema (:block/property-schema property)
-            [success? property-value-or-error] (validate schema property-value)]
+            [success? property-value-or-error] (validate schema property-value)
+            multiple-values? (:multiple-values? schema)
+            object? (= "object" (:type schema))]
         (if success?
           (let [properties (:block/properties entity)
-                properties' (assoc properties property-id property-value-or-error)
-                refs (extract-refs entity properties')]
+                property-value (get properties property-id)
+                value (if multiple-values?
+                        (conj (if (coll? property-value) (set property-value) #{property-value}) property-value-or-error)
+                        property-value-or-error)
+                refs (extract-refs entity (assoc properties property-id value))
+                value (if (and multiple-values? object?)
+                        (->> (map util/page-name-sanity-lc value)
+                             (remove string/blank?)
+                             (set))
+                        value)
+                properties' (assoc properties property-id value)]
             (outliner-tx/transact!
               {:outliner-op :save-block}
               (outliner-core/save-block!