Tienson Qin 1 год назад
Родитель
Сommit
c5c0f5d822

+ 4 - 1
deps/db/src/logseq/db/frontend/property/type.cljs

@@ -17,7 +17,7 @@
 
 (def user-built-in-property-types
   "Valid property types for users in order they appear in the UI"
-  [:default :number :date :checkbox :url :node])
+  [:default :number :date :datetime :checkbox :url :node])
 
 (def closed-value-property-types
   "Valid property :type for closed values"
@@ -141,6 +141,9 @@
    :date     [:fn
               {:error/message "should be a journal date"}
               date?]
+   :datetime [:fn
+              {:error/message "should be a datetime"}
+              number?]
    :checkbox boolean?
    :url      [:fn
               {:error/message "should be a URL"}

+ 3 - 3
scripts/src/logseq/tasks/dev/db_and_file_graphs.clj

@@ -94,13 +94,13 @@
                        (map str)
                        (into [;; e.g. block/properties :title
                               "block/properties :"
-                              "block/name"
+                              ;; "block/name"
                               ;; anything org mode
                               "org"
                               "pre-block"
                               ;; TODO: rename split-namespace?
                               ;; "namespace"
-                              "db/get-page"
+                              ;; "db/get-page"
                               "/page-name-sanity-lc"]))
         res (apply shell {:out :string :continue true}
                    "git grep -E" (str "(" (string/join "|" file-concepts) ")")
@@ -110,7 +110,7 @@
       (println (:out res))
       (System/exit 1))))
 
-(defn- validate-db-concepts-not-in-file
+;; (defn- validate-db-concepts-not-in-file
   []
   (let [db-concepts
         ;; from logseq.db.frontend.schema

+ 3 - 3
src/main/frontend/components/datetime.cljs

@@ -149,7 +149,7 @@
         deadline-or-schedule? (and current-command
                                 (contains? #{"deadline" "scheduled"}
                                   (string/lower-case current-command)))
-        _date (state/sub :date-picker/date)
+        date (state/sub :date-picker/date)
         select-handler! (fn [^js d]
                           (let [gd (goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))
                                 journal (date/js-date->journal-title gd)]
@@ -167,10 +167,10 @@
      ;; inline container
      [:div.border-red-500
       (ui/nlp-calendar
-        {:mode "single"
+       {:mode "single"
          :initial-focus true
          :show-week-number false
-         :selected _date
+         :selected date
          :on-select select-handler!
          :on-day-key-down (fn [^js d _ ^js e]
                             (when (= "Enter" (.-key e))

+ 3 - 1
src/main/frontend/components/property.cljs

@@ -183,6 +183,7 @@
                (case type
                  :number "number"
                  :date "calendar"
+                 :datetime "calendar"
                  :checkbox "checkbox"
                  :url "link"
                  :page "page"
@@ -435,6 +436,7 @@
                                  (:block/page (first v))))
                         (contains? #{:default} type))
             date? (= type :date)
+            datetime? (= type :datetime)
             checkbox? (= type :checkbox)
             property-key-cp' (property-key-cp block property (assoc (select-keys opts [:class-schema?])
                                                                     :block? block?
@@ -443,7 +445,7 @@
         [:div {:class (cond
                         (and (= (:db/ident property) :logseq.property.class/properties) (seq v))
                         "property-pair !flex flex-col"
-                        (or date? checkbox?)
+                        (or date? datetime? checkbox?)
                         "property-pair items-center"
                         :else
                         "property-pair items-start")}

+ 75 - 50
src/main/frontend/components/property/value.cljs

@@ -31,7 +31,8 @@
             [frontend.search :as search]
             [goog.functions :refer [debounce]]
             [frontend.handler.route :as route-handler]
-            [frontend.components.title :as title]))
+            [frontend.components.title :as title]
+            [cljs-time.coerce :as tc]))
 
 (rum/defc property-empty-btn-value
   [property & opts]
@@ -190,7 +191,7 @@
                    (shui/dialog-close!)
                    (state/set-editor-action! nil)
                    state)}
-  [state id {:keys [on-change value del-btn? on-delete]}]
+  [state id {:keys [datetime? on-change value del-btn? on-delete]}]
   (let [*ident (::identity state)
         initial-day (or (some-> value (.getTime) (js/Date.)) (js/Date.))
         initial-month (when value
@@ -202,87 +203,111 @@
             (let [gd (goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))]
               (let [journal (date/js-date->journal-title gd)]
                 (p/do!
-                 (when-not (db/get-case-page journal)
+                 (when-not (db/get-page journal)
                    (page-handler/<create! journal {:redirect? false
                                                    :create-first-block? false}))
                  (when (fn? on-change)
-                   (on-change (db/get-case-page journal)))
+                   (let [value (if datetime? (tc/to-long d) (db/get-page journal))]
+                     (on-change value)))
                  (shui/popup-hide! id)
                  (ui/hide-popups-until-preview-popup!)
                  (shui/dialog-close!))))))]
     (ui/nlp-calendar
      (cond->
       {:initial-focus true
+       :datetime? datetime?
        :selected initial-day
        :id @*ident
        :del-btn? del-btn?
        :on-delete on-delete
-       :on-day-click select-handler!}
+       :on-select select-handler!}
        initial-month
        (assoc :default-month initial-month)))))
 
 (rum/defc date-picker
-  [value {:keys [on-change on-delete del-btn? editing? multiple-values? other-position?]}]
+  [value {:keys [datetime? on-change on-delete del-btn? editing? multiple-values? other-position?]}]
   (let [*trigger-ref (rum/use-ref nil)
-        page value
-        title (when page (:block/title page))
-        value' (when title
-                 (js/Date. (date/journal-title->long title)))
+        value' (cond
+                 (map? value)
+                 (js/Date. (date/journal-title->long (:block/title value)))
+
+                 (number? value)
+                 (js/Date. value)
+
+                 :else
+                 (js/Date.))
         content-fn (fn [{:keys [id]}] (calendar-inner id
-                                        {:on-change on-change :value value'
-                                         :del-btn? del-btn? :on-delete on-delete}))
+                                                      {:on-change on-change
+                                                       :value value'
+                                                       :del-btn? del-btn?
+                                                       :on-delete on-delete
+                                                       :datetime? datetime?}))
         open-popup! (fn [e]
                       (when-not (or (util/meta-key? e) (util/shift-key? e))
                         (util/stop e)
                         (when-not config/publishing?
                           (shui/popup-show! (.-target e) content-fn
-                            {:align "start" :auto-focus? true}))))]
+                                            {:align "start" :auto-focus? true}))))]
     (rum/use-effect!
-      (fn []
-        (when editing?
-          (js/setTimeout
-            #(some-> (rum/deref *trigger-ref)
-               (.click)) 32)))
-      [editing?])
+     (fn []
+       (when editing?
+         (js/setTimeout
+          #(some-> (rum/deref *trigger-ref)
+                   (.click)) 32)))
+     [editing?])
 
     (if multiple-values?
       (shui/button
-        {:class "jtrigger h-6 empty-btn"
-         :ref *trigger-ref
-         :variant :text
-         :size :sm
-         :on-click open-popup!}
-        (ui/icon "calendar-plus" {:size 16}))
+       {:class "jtrigger h-6 empty-btn"
+        :ref *trigger-ref
+        :variant :text
+        :size :sm
+        :on-click open-popup!}
+       (ui/icon "calendar-plus" {:size 16}))
       (shui/trigger-as
-        :div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
-        {:tabIndex 0
-         :class "jtrigger min-h-[24px]"                     ; FIXME: min-h-6 not works
-         :ref *trigger-ref
-         :on-click open-popup!}
-        (if page
-          (when-let [page-cp (state/get-component :block/page-cp)]
-            (rum/with-key
-              (page-cp {:disable-preview? true
-                        :meta-click? other-position?} page)
-              (:db/id page)))
-          (property-empty-btn-value nil))))))
+       :div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
+       {:tabIndex 0
+        :class "jtrigger min-h-[24px]"                     ; FIXME: min-h-6 not works
+        :ref *trigger-ref
+        :on-click open-popup!}
+       (cond
+         (map? value)
+         (when-let [page-cp (state/get-component :block/page-cp)]
+           (rum/with-key
+             (page-cp {:disable-preview? true
+                       :meta-click? other-position?} value)
+             (:db/id value)))
+
+         (number? value)
+         (when-let [date (js/Date. value)]
+           [:div.flex.flex-row.gap-1
+            (when-let [page-cp (state/get-component :block/page-cp)]
+             (let [page-title (date/js-date->journal-title date)]
+               (page-cp {:disable-preview? true}
+                        {:block/name page-title})))
+            [:span (str (.getHours date) ":" (.getMinutes date))]])
+
+         :else
+         (property-empty-btn-value nil))))))
 
 (rum/defc property-value-date-picker
   [block property value opts]
   (let [multiple-values? (db-property/many? property)
-        repo (state/get-current-repo)]
+        repo (state/get-current-repo)
+        datetime? (= :datetime (get-in property [:block/schema :type]))]
     (date-picker value
-      (merge opts
-        {:multiple-values? multiple-values?
-         :on-change (fn [page]
-                      (property-handler/set-block-property! repo (:block/uuid block)
-                        (:db/ident property)
-                        (:db/id page)))
-         :del-btn? (some-> value (:block/title) (boolean))
-         :on-delete (fn []
-                      (property-handler/set-block-property! repo (:block/uuid block)
-                        (:db/ident property) nil)
-                      (shui/popup-hide!))}))))
+                 (merge opts
+                        {:datetime? datetime?
+                         :multiple-values? multiple-values?
+                         :on-change (fn [value]
+                                      (property-handler/set-block-property! repo (:block/uuid block)
+                                                                            (:db/ident property)
+                                                                            (if (map? value) (:db/id value) value)))
+                         :del-btn? (some-> value (:block/title) (boolean))
+                         :on-delete (fn []
+                                      (property-handler/set-block-property! repo (:block/uuid block)
+                                                                            (:db/ident property) nil)
+                                      (shui/popup-hide!))}))))
 
 (defn- <create-page-if-not-exists!
   [block property classes page]
@@ -857,7 +882,7 @@
                                select-opts
                                (assoc opts :editing? editing?)))
         (case type
-          :date
+          (:date :datetime)
           (property-value-date-picker block property value (merge opts {:editing? editing?}))
 
           :checkbox

+ 3 - 3
src/main/frontend/components/views.cljs

@@ -169,12 +169,12 @@
 
         [{:id :block/created-at
           :name (t :page/created-at)
-          :type :date-time
+          :type :datetime
           :header header-cp
           :cell timestamp-cell-cp}
          {:id :block/updated-at
           :name (t :page/updated-at)
-          :type :date-time
+          :type :datetime
           :header header-cp
           :cell timestamp-cell-cp}])
        (remove nil?)))
@@ -618,7 +618,7 @@
        (case (get-in property [:block/schema :type])
          (:default :url :node)
          [:text-contains :text-not-contains]
-         :date
+         (:date :datetime)
          [:date-before :date-after]
          :number
          [:number-gt :number-lt :number-gte :number-lte :between]

+ 66 - 24
src/main/frontend/ui.cljs

@@ -1176,11 +1176,11 @@
 (defn single-calendar
   [{:keys [del-btn? on-delete on-select on-day-click] :as opts}]
   (shui/calendar
-    (merge
-      {:mode "single"
-       :caption-layout "dropdown-buttons"
-     :fromYear 1899
-     :toYear 2099
+   (merge
+    {:mode "single"
+     :caption-layout "dropdown-buttons"
+     :fromYear 1000
+     :toYear 3000
      :components (cond-> {:Dropdown #(date-year-month-select (bean/bean %))}
                    del-btn? (assoc :Head #(DelDateButton on-delete)))
      :class-names {:months "" :root (when del-btn? "has-del-btn")}
@@ -1190,26 +1190,68 @@
                             (on-select' d))))}
     opts)))
 
-(defn nlp-calendar
-  [opts]
-  [:div.flex.flex-col.gap-2
-   (single-calendar opts)
+(defn- get-current-hh-mm
+  []
+  (let [current-time-s (first (.split (.toTimeString (js/Date.)) " "))]
+    (subs current-time-s 0 (- (count current-time-s) 3))))
+
+(rum/defc time-picker
+  [{:keys [on-change default-value]}]
+  [:div.flex.flex-row.items-center.gap-2.mx-3.mb-3
    (shui/input
-    {:type "text"
-     :placeholder "e.g. Next week"
-     :class "mx-3 mb-3"
-     :style {:width "initial"
-             :tab-index -1}
-     :auto-complete (if (util/chrome?) "chrome-off" "off")
-     :on-mouse-down util/stop-propagation
-     :on-key-down (fn [e]
-                    (when (= "Enter" (util/ekey e))
-                      (let [value (util/evalue e)]
-                        (when-not (string/blank? value)
-                          (when-let [result (date/nld-parse value)]
-                            (when-let [date (doto (goog.date.DateTime.) (.setTime (.getTime result)))]
-                              (let [on-select' (or (:on-select opts) (:on-day-click opts))]
-                                (on-select' date))))))))})])
+    {:id "time-picker"
+     :type "time"
+     :class "!py-0 !w-max !h-8"
+     :default-value (or default-value "00:00")
+     :on-blur (fn [e]
+                (on-change (util/evalue e)))})
+   (shui/button
+    {:variant :ghost
+     :size :sm
+     :class "text-muted-foreground"
+     :on-click (fn []
+                 (let [value (get-current-hh-mm)]
+                   (set! (.-value (gdom/getElement "time-picker")) value)
+                   (on-change value)))}
+    "Use current time")])
+
+(rum/defc nlp-calendar
+  [{:keys [selected on-select] :as opts}]
+  (let [on-select' (if (:datetime? opts)
+                     (fn [date value]
+                       (let [value (or (and (string? value) value)
+                                       (.-value (gdom/getElement "time-picker")))]
+                         (let [[h m] (string/split value ":")]
+                           (when selected
+                             (.setHours date h m 0))
+                           (on-select date))))
+                     on-select)]
+    [:div.flex.flex-col.gap-2
+     (single-calendar (assoc opts :on-select on-select'))
+     (when (:datetime? opts)
+       (time-picker (cond->
+                     {:on-change (fn [value] (on-select' selected value))}
+                      selected
+                      (assoc :default-value (str (util/zero-pad (.getHours selected))
+                                                 ":"
+                                                 (util/zero-pad (.getMinutes selected)))))))
+
+     (shui/input
+      {:type "text"
+       :placeholder "e.g. Next week"
+       :class "mx-3 mb-3"
+       :style {:width "initial"
+               :tab-index -1}
+       :auto-complete (if (util/chrome?) "chrome-off" "off")
+       :on-mouse-down util/stop-propagation
+       :on-key-down (fn [e]
+                      (when (= "Enter" (util/ekey e))
+                        (let [value (util/evalue e)]
+                          (when-not (string/blank? value)
+                            (when-let [result (date/nld-parse value)]
+                              (when-let [date (doto (goog.date.DateTime.) (.setTime (.getTime result)))]
+                                (let [on-select' (or (:on-select opts) (:on-day-click opts))]
+                                  (on-select' date))))))))})]))
 
 (comment
   (rum/defc emoji-picker