datetime.cljs 6.7 KB

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