Skip to content

Commit

Permalink
[#16377] feat: add calendar component in quo2 preview
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsen-ghafouri committed Jul 26, 2023
1 parent 54747d4 commit b2ec274
Show file tree
Hide file tree
Showing 23 changed files with 536 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
(ns quo2.components.calendar.--tests--.calendar-component-spec
(:require [quo2.components.calendar.calendar.view :as calendar]
[test-helpers.component :as h]
[cljs-time.core :as t]))

(def start-date (t/date-time (t/year (t/now)) (t/month (t/now)) 5))
(def end-date (t/date-time (t/plus start-date (t/days 2))))

(h/describe "calendar component"
(h/test "default render of calendar component"
(h/render
[calendar/calendar
{:start-date start-date
:end-date end-date}])
(-> (h/expect (h/query-by-translation-text "Mo"))
(h/is-truthy)))

(h/test "should call on-change with selected date on first click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/calendar
{:start-date nil
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (t/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date nil})))

(h/test "should call on-change with start and end date on second click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/calendar
{:start-date start-date :end-date nil :on-change on-change}])
(h/fire-event :press (h/query-by-text (str (t/day end-date))))
(h/was-called-with on-change {:start-date start-date :end-date end-date})))

(h/test "should reset the dates on third click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/calendar
{:start-date start-date
:end-date end-date
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (t/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date nil})))

(h/test "should reset dates when start date is clicked again"
(let [on-change (h/mock-fn)]
(h/render
[calendar/calendar
{:start-date start-date
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (t/day start-date))))
(h/was-called-with on-change {:start-date nil :end-date nil})))


(h/test "should assign start and end date correctly when upper range selected first"
(let [on-change (h/mock-fn)]
(h/render
[calendar/calendar
{:start-date end-date
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (t/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date end-date}))))
7 changes: 7 additions & 0 deletions src/quo2/components/calendar/calendar/days_grid/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(ns quo2.components.calendar.calendar.days-grid.style)

(def container-days
{:flex-grow 1
:margin-top 4
:margin-horizontal 8
:overflow :hidden})
66 changes: 66 additions & 0 deletions src/quo2/components/calendar/calendar/days_grid/utils.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
(ns quo2.components.calendar.calendar.days-grid.utils
(:require
[utils.number :as number-utils]
[cljs-time.core :as t]))

(defn- day-of-week
[date]
(let [day (t/day-of-week date)]
(mod day 7)))

(defn- add-days
[date days]
(t/plus date (t/days days)))

(defn day-grid
[year month]
(let [year (number-utils/parse-int year)
month (number-utils/parse-int month)
first-day (t/date-time year month 1)
start-day (add-days first-day (- 0 (day-of-week first-day)))
end-day (add-days start-day 34)]
(loop [days []
current-day start-day]
(if (t/after? current-day end-day)
days
(recur (conj days current-day) (add-days current-day 1))))))

(defn get-day-state
[day today year month start-date end-date]
(cond
(and start-date (t/equal? day start-date)) :selected
(and end-date (t/equal? day end-date)) :selected
(and (= (t/year day) (t/year today))
(= (t/month day) (t/month today))
(= (t/day day) (t/day today))) :today
(and (= (t/year day) year) (= (t/month day) month)) :default
:else :disabled))

(defn update-range
[day start-date end-date]
(let [new-state (cond
(and start-date end-date) {:start-date day :end-date nil}
(and start-date (t/equal? day start-date)) {:start-date nil :end-date nil}
(and end-date (t/equal? day end-date)) {:start-date nil :end-date nil}
(nil? start-date) {:start-date day :end-date nil}
(nil? end-date) {:start-date start-date :end-date day}
:else {:start-date start-date
:end-date end-date})]
(if (and (:start-date new-state)
(:end-date new-state)
(t/after? (:start-date new-state) (:end-date new-state)))
{:start-date (:end-date new-state) :end-date (:start-date new-state)}
new-state)))

(defn in-range?
[day start-date end-date]
(and start-date end-date (t/after? day start-date) (t/before? day end-date)))

(defn get-in-range-pos
[day start-date end-date]
(cond
(or (nil? start-date) (nil? end-date)) nil
(and start-date (t/equal? day start-date)) :start
(and end-date (t/equal? day end-date)) :end
(in-range? day start-date end-date) :middle
:else nil))
43 changes: 43 additions & 0 deletions src/quo2/components/calendar/calendar/days_grid/view.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
(ns quo2.components.calendar.calendar.days-grid.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[cljs-time.core :as t]
[quo2.components.calendar.calendar.days-grid.utils :as utils]
[quo2.components.calendar.calendar-day.view :as calendar-day]
[quo2.components.calendar.calendar.days-grid.style :as style]))

(defn render-day
[{:keys [year month day selection-range on-press]}]
(let [today (t/now)
start-date (:start-date selection-range)
end-date (:end-date selection-range)
state (utils/get-day-state day today year month start-date end-date)
in-range (utils/get-in-range-pos day start-date end-date)
on-press #(on-press (t/date-time day))]
[calendar-day/calendar-day
{:state state
:in-range in-range
:on-press on-press}
(str (t/day day))]))

(defn- days-grid-internal
[{:keys [year month on-change start-date end-date]}]
(let [on-day-press (fn [day]
(let [new-selection (utils/update-range day start-date end-date)]
(on-change new-selection)))]
[rn/view
{:style style/container-days}
[rn/flat-list
{:data (utils/day-grid year month)
:key-fn str
:numColumns 7
:content-container-style {:margin-horizontal -2}
:render-fn (fn [item]
(render-day
{:year year
:month month
:day item
:on-press on-day-press
:selection-range {:start-date start-date :end-date end-date}}))}]]))

(def days-grid (theme/with-theme days-grid-internal))
16 changes: 16 additions & 0 deletions src/quo2/components/calendar/calendar/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(ns quo2.components.calendar.calendar.style
(:require
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]))

(defn container
[]
{:flex-direction :row
:height 270
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
:border-radius 12
:border-width 1
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40)})

(def container-main
{:flex-grow 1})
25 changes: 25 additions & 0 deletions src/quo2/components/calendar/calendar/utils.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
(ns quo2.components.calendar.calendar.utils
(:require [utils.datetime :as dt]
[utils.number :as number-utils]
[clojure.string :as string]))

(defn generate-years
[current-year]
(let [current-year current-year]
(reverse (vec (range (- current-year 100) (+ current-year 1))))))

(defn current-year
[]
(-> (dt/now)
dt/timestamp->year-month-day-date
(string/split #"-")
first
number-utils/parse-int))

(defn current-month
[]
(-> (dt/now)
dt/timestamp->year-month-day-date
(string/split #"-")
second
number-utils/parse-int))
41 changes: 41 additions & 0 deletions src/quo2/components/calendar/calendar/view.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
(ns quo2.components.calendar.calendar.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[reagent.core :as reagent]
[utils.number :as number-utils]
[quo2.components.calendar.calendar.utils :as utils]
[quo2.components.calendar.calendar.style :as style]
[quo2.components.calendar.calendar.years-list.view :as years-list]
[quo2.components.calendar.calendar.days-grid.view :as days-grid]
[quo2.components.calendar.calendar.weekdays-header.view :as weekdays-header]
[quo2.components.calendar.calendar-month.view :as calendar-month]))

(defn- calendar-internal
[]
(let [selected-year (reagent/atom (utils/current-year))
selected-month (reagent/atom (utils/current-month))
on-change-year #(reset! selected-year %)
on-change-month (fn [new-date]
(reset! selected-year (number-utils/parse-int (:year new-date)))
(reset! selected-month (number-utils/parse-int (:month new-date))))]
(fn [{:keys [on-change start-date end-date]}]
[rn/view
{:style (style/container)}
[years-list/years-list-view
{:on-change-year on-change-year
:year @selected-year}]
[rn/view
{:style style/container-main}
[calendar-month/calendar-month
{:year @selected-year
:month @selected-month
:on-change on-change-month}]
[weekdays-header/weekdays-header]
[days-grid/days-grid
{:year @selected-year
:month @selected-month
:start-date start-date
:end-date end-date
:on-change on-change}]]])))

(def calendar (theme/with-theme calendar-internal))
22 changes: 22 additions & 0 deletions src/quo2/components/calendar/calendar/weekdays_header/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(ns quo2.components.calendar.calendar.weekdays-header.style
(:require [quo2.foundations.typography :as typography]
[quo2.foundations.colors :as colors]))

(def container-weekday-row
{:flex-direction :row
:justify-content :space-between
:padding-horizontal 8})

(def container-weekday
{:width 32
:height 30
:padding-top 2
:justify-content :center
:align-items :center})

(defn text-weekdays
[]
(-> typography/paragraph-2
(merge typography/font-medium)
(merge {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})))

21 changes: 21 additions & 0 deletions src/quo2/components/calendar/calendar/weekdays_header/view.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(ns quo2.components.calendar.calendar.weekdays-header.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[utils.datetime :as dt]
[utils.i18n :as i18n]
[quo2.components.calendar.calendar.weekdays-header.style :as style]))

(defn- weekdays-header-internal
[]
[rn/view
{:style style/container-weekday-row}
(doall
(for [name dt/weekday-names]
[rn/view
{:style style/container-weekday
:key name}
[rn/text
{:style (style/text-weekdays)}
(str (i18n/label name))]]))])

(def weekdays-header (theme/with-theme weekdays-header-internal))
35 changes: 35 additions & 0 deletions src/quo2/components/calendar/calendar/years_list/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
(ns quo2.components.calendar.calendar.years-list.style
(:require
[quo2.foundations.colors :as colors]))

(defn gradient-start-color
[]
(colors/theme-colors colors/white colors/neutral-90))

(defn gradient-end-color
[]
(colors/theme-colors colors/white-opa-0 colors/neutral-100-opa-0))

(def gradient-view
{:position :absolute
:height 50
:border-top-left-radius 12
:top 0
:left 0
:right 0})

(defn container-years
[]
{:border-width 1
:overflow :hidden
:padding-left 8
:padding-right 7
:padding-vertical 8
:margin-left -1
:margin-top -1
:margin-bottom -1
:border-style :dashed
:border-top-left-radius 12
:border-bottom-left-radius 12
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)})

Loading

0 comments on commit b2ec274

Please sign in to comment.