datetime.cljs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. (ns frontend.components.datetime
  2. (:require [rum.core :as rum]
  3. [frontend.util :as util :refer [profile]]
  4. [frontend.components.svg :as svg]
  5. [frontend.date :as date]
  6. [frontend.state :as state]
  7. [frontend.handler.repeated :as repeated]
  8. [frontend.handler.editor :as editor-handler]
  9. [cljs-time.core :as t]
  10. [frontend.ui :as ui]
  11. [frontend.commands :as commands]
  12. [clojure.string :as string]))
  13. (defonce default-timestamp-value {:date nil
  14. :time ""
  15. :repeater {}})
  16. (defonce *timestamp (atom default-timestamp-value))
  17. (defonce *show-time? (atom false))
  18. (rum/defc time-input < rum/reactive
  19. [default-value]
  20. (let [show? (rum/react *show-time?)]
  21. (if (or show? (not (string/blank? default-value)))
  22. [:div.flex.flex-row {:style {:height 32}}
  23. [:input#time.form-input
  24. {:style {:width 240}
  25. :default-value default-value
  26. :on-change (fn [event]
  27. (util/stop event)
  28. (let [value (util/evalue event)]
  29. (swap! *timestamp assoc :time value)))}]
  30. [:a.ml-2.self-center {:on-click (fn []
  31. (reset! *show-time? false)
  32. (swap! *timestamp assoc :time nil))}
  33. svg/close]]
  34. [:a.text-sm {:on-click (fn []
  35. (reset! *show-time? true)
  36. (let [{:keys [hour minute]} (date/get-local-date)
  37. result (str hour ":" (util/zero-pad minute))]
  38. (swap! *timestamp assoc :time result)))}
  39. "Add time"])))
  40. (defonce *show-repeater? (atom false))
  41. (rum/defc repeater-cp < rum/reactive
  42. [{:keys [num duration kind]}]
  43. (let [show? (rum/react *show-repeater?)]
  44. (if (or show? (and num duration kind))
  45. [:div.flex.flex-row.justify-center {:style {:height 32}}
  46. [:div.block.text-medium.mr-2.mt-1 {:style {:width 110}}
  47. "Every"]
  48. [:input#repeater-num.form-input.mt-1
  49. {:style {:width 48}
  50. :default-value num
  51. :on-change (fn [event]
  52. (let [value (util/evalue event)]
  53. (swap! *timestamp assoc-in [:repeater :num] value)))}]
  54. (ui/select
  55. (mapv
  56. (fn [item]
  57. (if (= (:label item) duration)
  58. (assoc item :selected "selected")
  59. item))
  60. [{:label "h"}
  61. {:label "d"}
  62. {:label "w"}
  63. {:label "m"}
  64. {:label "y"}])
  65. (fn [value]
  66. (swap! *timestamp assoc-in [:repeater :duration] value)))
  67. [:a.ml-2.self-center {:on-click (fn []
  68. (reset! *show-repeater? false)
  69. (swap! *timestamp assoc :repeater {}))}
  70. svg/close]]
  71. [:a.text-sm {:on-click (fn []
  72. (reset! *show-repeater? true)
  73. (swap! *timestamp assoc :repeater
  74. {:kind ".+"
  75. :num 1
  76. :duration "d"}))}
  77. "Add repeater"])))
  78. (defn clear-timestamp!
  79. []
  80. (reset! *timestamp default-timestamp-value)
  81. (reset! *show-time? false)
  82. (reset! *show-repeater? false))
  83. (rum/defc time-repeater < rum/reactive
  84. []
  85. (let [{:keys [date time repeater] :as timestamp} (rum/react *timestamp)
  86. timestamp (if date
  87. timestamp
  88. (assoc timestamp :date (t/today)))
  89. kind (if (= "w" (:duration repeater)) "++" ".+")
  90. timestamp (assoc-in timestamp [:repeater :kind] kind)
  91. text (repeated/timestamp-map->text timestamp)]
  92. [:div.py-1.px-4 {:style {:min-width 300}}
  93. [:p.text-sm.opacity-50.font-medium.mt-4 "Time:"]
  94. (time-input time)
  95. [:p.text-sm.opacity-50.font-medium.mt-4 "Repeater:"]
  96. (repeater-cp repeater)
  97. [:p.mt-4
  98. (ui/button "Submit"
  99. :on-click (fn [e]
  100. (util/stop e)
  101. (let [block-data (state/get-timestamp-block)]
  102. (let [{:keys [block typ show?]} block-data
  103. block-id (or (:block/uuid (state/get-edit-block))
  104. (:block/uuid block))
  105. typ (or @commands/*current-command typ)]
  106. (editor-handler/set-block-timestamp! block-id
  107. typ
  108. text)
  109. (state/clear-edit!)
  110. (when show?
  111. (reset! show? false))))
  112. (clear-timestamp!)
  113. (state/set-editor-show-date-picker! false)))]]))
  114. (rum/defc date-picker < rum/reactive
  115. {:init (fn [state]
  116. (let [ts (last (:rum/args state))]
  117. (if ts
  118. (reset! *timestamp ts)
  119. (reset! *timestamp {:date (if (and ts (:date ts))
  120. (:date ts)
  121. (t/today))
  122. :time ""
  123. :repeater {}})))
  124. state)
  125. :will-unmount (fn [state]
  126. (clear-timestamp!)
  127. state)}
  128. [id format ts]
  129. (let [current-command @commands/*current-command
  130. deadline-or-schedule? (and current-command
  131. (contains? #{"deadline" "scheduled"}
  132. (string/lower-case current-command)))
  133. date (get @*timestamp :date)]
  134. (when (state/sub :editor/show-date-picker?)
  135. [:div#date-time-picker.flex.flex-row {:on-click (fn [e] (util/stop e))
  136. :on-mouse-down (fn [e] (.stopPropagation e))}
  137. (ui/datepicker
  138. date
  139. {:deadline-or-schedule? deadline-or-schedule?
  140. :on-change
  141. (fn [e date]
  142. (util/stop e)
  143. (let [date (t/to-default-time-zone date)
  144. journal (date/journal-name date)]
  145. (when-not deadline-or-schedule?
  146. ;; similar to page reference
  147. (editor-handler/insert-command! id
  148. (util/format "[[%s]]" journal)
  149. format
  150. nil)
  151. (state/set-editor-show-date-picker! false)
  152. (reset! commands/*current-command nil))
  153. (swap! *timestamp assoc :date date)))})
  154. (when deadline-or-schedule?
  155. (time-repeater))])))