mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
refactor(ui): refactor all of the classical datepicker codes with the shui calendar
This commit is contained in:
@@ -219,6 +219,14 @@
|
||||
&.as-heading {
|
||||
@apply flex w-full;
|
||||
}
|
||||
|
||||
&:has(.dsl-query) {
|
||||
@apply flex flex-row w-full;
|
||||
}
|
||||
|
||||
.dsl-query {
|
||||
@apply w-full;
|
||||
}
|
||||
}
|
||||
|
||||
.block-control, .block-control:hover {
|
||||
|
||||
@@ -1,214 +1,3 @@
|
||||
/*----------------------------------------------------------------------------------------
|
||||
Stylesheet for re-com.date Date Picker variants inline-picker & dropdown-picker
|
||||
Day8 variation loosely based on:
|
||||
Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
|
||||
Licensed under the Apache License v2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Built for http://www.improvely.com
|
||||
http://eternicode.github.io/bootstrap-datepicker
|
||||
----------------------------------------------------------------------------------------*/
|
||||
|
||||
.noselect {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.datepicker.single .calendar {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.datepicker .calendar {
|
||||
display: none;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.datepicker .calendar.single .calendar-date {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.datepicker .calendar th,
|
||||
.datepicker .calendar td {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.datepicker .calendar-date {
|
||||
border: 1px solid #ddd;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker .calendar-time {
|
||||
text-align: center;
|
||||
margin: 8px auto 0 auto;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 20px;
|
||||
padding: 10px;
|
||||
line-height: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 27px;
|
||||
height: 26px;
|
||||
max-width: 27px;
|
||||
max-height: 26px;
|
||||
min-width: 27px;
|
||||
min-height: 26px;
|
||||
padding: 4px;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.datepicker td.off {
|
||||
padding: 4px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.datepicker td.disabled {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.datepicker th.disabled {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.datepicker td.available:hover,
|
||||
.datepicker th.available:hover {
|
||||
@apply rounded bg-gray-07 cursor-pointer;
|
||||
}
|
||||
|
||||
.datepicker td.in-range {
|
||||
background: #ebf4f8;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.datepicker td.start-date {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.datepicker td.end-date {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.datepicker td.start-date.end-date {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker td.active,
|
||||
.datepicker td.active:hover {
|
||||
@apply bg-accent/90 border border-accent text-accent-foreground;
|
||||
}
|
||||
|
||||
/* Introduced by Day8 from http://eternicode.github.io/bootstrap-datepicker */
|
||||
.datepicker td.today,
|
||||
.datepicker td.today:hover {
|
||||
background-color: #ffcd70;
|
||||
border-color: #f59e00;
|
||||
border-radius: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.datepicker th.day-enabled,
|
||||
label.day-enabled {
|
||||
font-weight: normal;
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.datepicker th.selectable {
|
||||
@apply font-normal text-accent;
|
||||
}
|
||||
|
||||
.datepicker th.day-disabled {
|
||||
font-weight: normal;
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.datepicker td.week,
|
||||
.datepicker th.week {
|
||||
font-size: 80%;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.datepicker th.month {
|
||||
width: auto;
|
||||
font-size: 14px;
|
||||
color: var(--ls-title-text-color);
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
cursor: pointer;
|
||||
height: 32px;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.dropdown-button.activator {
|
||||
width: 40px;
|
||||
color: #777;
|
||||
/* background-color: #F7F7F7 */
|
||||
}
|
||||
|
||||
.table-condensed > thead > tr > th,
|
||||
.table-condensed > tbody > tr > th,
|
||||
.table-condensed > tfoot > tr > th,
|
||||
.table-condensed > thead > tr > td,
|
||||
.table-condensed > tbody > tr > td,
|
||||
.table-condensed > tfoot > tr > td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dark-theme .datepicker {
|
||||
background: var(--ls-secondary-background-color);
|
||||
}
|
||||
|
||||
.dark-theme .datepicker th.day-disabled,
|
||||
.dark-theme .datepicker th.disabled,
|
||||
.dark-theme .datepicker td.disabled,
|
||||
.dark-theme .datepicker td.off {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dark-theme .datepicker th.day-enabled,
|
||||
.dark-theme label.day-enabled {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.datepicker tr:nth-child(odd),
|
||||
.datepicker tr:nth-child(even),
|
||||
.dark-theme .datepicker tr:nth-child(odd),
|
||||
.dark-theme .datepicker tr:nth-child(even) {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.datepicker th,
|
||||
.datepicker tr,
|
||||
.datepicker td,
|
||||
.dark-theme .datepicker th,
|
||||
.dark-theme .datepicker tr,
|
||||
.dark-theme .datepicker td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#time-repeater {
|
||||
input.form-input, select.form-select {
|
||||
@apply h-8 mt-0;
|
||||
|
||||
@@ -18,13 +18,6 @@
|
||||
:repeater {}})
|
||||
(defonce *timestamp (atom default-timestamp-value))
|
||||
|
||||
(defn- date->goog-date
|
||||
[d]
|
||||
(cond
|
||||
(some->> d (instance? js/Date))
|
||||
(goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))
|
||||
:else d))
|
||||
|
||||
(defonce *show-time? (atom false))
|
||||
(rum/defc time-input < rum/reactive
|
||||
[default-value]
|
||||
@@ -97,7 +90,7 @@
|
||||
[e]
|
||||
(when e (util/stop e))
|
||||
(let [{:keys [repeater] :as timestamp} @*timestamp
|
||||
date (-> (:date-picker/date @state/state) date->goog-date)
|
||||
date (-> (:date-picker/date @state/state) date/js-date->goog-date)
|
||||
timestamp (assoc timestamp :date (or date (t/today)))
|
||||
kind (if (= "w" (:duration repeater)) "++" ".+")
|
||||
timestamp (assoc-in timestamp [:repeater :kind] kind)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[frontend.components.select :as component-select]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[logseq.shui.ui :as shui]
|
||||
[frontend.search :as search]
|
||||
[frontend.mixins :as mixins]
|
||||
[logseq.graph-parser.db :as gp-db]
|
||||
@@ -74,50 +75,58 @@
|
||||
:aria-label "Full text search"
|
||||
:on-change #(reset! *input-value (util/evalue %))}]))
|
||||
|
||||
(defonce *shown-datepicker (atom nil))
|
||||
(defonce *between-dates (atom {}))
|
||||
(rum/defcs datepicker < rum/reactive
|
||||
(rum/local nil ::input-value)
|
||||
{:init (fn [state]
|
||||
(when (:auto-focus (last (:rum/args state)))
|
||||
(reset! *shown-datepicker (first (:rum/args state))))
|
||||
state)
|
||||
:will-unmount (fn [state]
|
||||
(swap! *between-dates dissoc (first (:rum/args state)))
|
||||
state)}
|
||||
(rum/local nil ::input-value)
|
||||
{:will-unmount (fn [state]
|
||||
(swap! *between-dates dissoc (first (:rum/args state)))
|
||||
state)}
|
||||
[state id placeholder {:keys [auto-focus]}]
|
||||
(let [*input-value (::input-value state)
|
||||
show? (= id (rum/react *shown-datepicker))]
|
||||
(let [*input-value (::input-value state)]
|
||||
[:div.ml-4
|
||||
[:input.query-builder-datepicker.form-input.block.sm:text-sm.sm:leading-5
|
||||
{:auto-focus (or auto-focus false)
|
||||
:placeholder placeholder
|
||||
:aria-label placeholder
|
||||
:value @*input-value
|
||||
:on-click #(reset! *shown-datepicker id)}]
|
||||
(when show?
|
||||
(ui/datepicker nil {:on-change (fn [_e date]
|
||||
(let [journal-date (date/journal-name date)]
|
||||
(reset! *input-value journal-date)
|
||||
(reset! *shown-datepicker nil)
|
||||
(swap! *between-dates assoc id journal-date)))}))]))
|
||||
:value (some-> @*input-value (first))
|
||||
:on-focus (fn [^js e]
|
||||
(js/setTimeout
|
||||
#(shui/popup-show! (.-target e)
|
||||
(let [select-handle! (fn [^js d]
|
||||
(let [gd (date/js-date->goog-date d)
|
||||
journal-date (date/js-date->journal-title gd)]
|
||||
(reset! *input-value [journal-date d])
|
||||
(swap! *between-dates assoc id journal-date))
|
||||
(shui/popup-hide!))]
|
||||
(shui/calendar
|
||||
{:mode "single"
|
||||
:initial-focus true
|
||||
:selected (some-> @*input-value (second))
|
||||
:on-select select-handle!
|
||||
:on-day-key-down (fn [^js d _ ^js e]
|
||||
(when (= "Enter" (.-key e))
|
||||
(select-handle! d)
|
||||
(util/stop e)))}))
|
||||
{:id :query-datepicker
|
||||
:align :start}) 16))}]]))
|
||||
|
||||
(rum/defcs between <
|
||||
(rum/local nil ::start)
|
||||
(rum/local nil ::end)
|
||||
[state {:keys [tree loc] :as opts}]
|
||||
[:div.between-date {:on-pointer-down (fn [e] (util/stop-propagation e))}
|
||||
[:div.between-date.p-4 {:on-pointer-down (fn [e] (util/stop-propagation e))}
|
||||
[:div.flex.flex-row
|
||||
[:div.font-medium.mt-2 "Between: "]
|
||||
(datepicker :start "Start date" (merge opts {:auto-focus true}))
|
||||
(datepicker :end "End date" opts)]
|
||||
(ui/button "Submit"
|
||||
:on-click (fn []
|
||||
(let [{:keys [start end]} @*between-dates]
|
||||
(when (and start end)
|
||||
(let [clause [:between [:page-ref start] [:page-ref end]]]
|
||||
(append-tree! tree opts loc clause)
|
||||
(reset! *between-dates {}))))))])
|
||||
[:p.pt-2
|
||||
(ui/button "Submit"
|
||||
:on-click (fn []
|
||||
(let [{:keys [start end]} @*between-dates]
|
||||
(when (and start end)
|
||||
(let [clause [:between [:page-ref start] [:page-ref end]]]
|
||||
(append-tree! tree opts loc clause)
|
||||
(reset! *between-dates {}))))))]])
|
||||
|
||||
(rum/defc property-select
|
||||
[*mode *property]
|
||||
@@ -266,21 +275,19 @@
|
||||
(append-tree! *tree opts loc [(keyword value)])
|
||||
|
||||
:else
|
||||
(do (reset! *mode value)
|
||||
((:toggle-fn opts)))))
|
||||
(reset! *mode value)))
|
||||
{:input-default-placeholder "Add filter/operator"})])]))
|
||||
|
||||
(rum/defc add-filter
|
||||
[*find *tree loc clause]
|
||||
(ui/dropdown
|
||||
(fn [{:keys [toggle-fn]}]
|
||||
[:a.flex.add-filter {:title "Add clause"
|
||||
:on-click toggle-fn}
|
||||
(ui/icon "plus" {:style {:font-size 20}})])
|
||||
(fn [{:keys [toggle-fn]}]
|
||||
(picker *find *tree loc clause {:toggle-fn toggle-fn}))
|
||||
{:modal-class (util/hiccup->class
|
||||
"origin-top-right.absolute.left-0.mt-2.ml-2.rounded-md.shadow-lg")}))
|
||||
[:a.flex.add-filter
|
||||
{:title "Add clause"
|
||||
:on-click (fn [^js e]
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn [{:keys [id]}]
|
||||
(picker *find *tree loc clause {:toggle-fn #(shui/popup-hide! id)}))
|
||||
{}))}
|
||||
(ui/icon "plus" {:style {:font-size 20}})])
|
||||
|
||||
(declare clauses-group)
|
||||
|
||||
|
||||
@@ -172,6 +172,13 @@
|
||||
[date]
|
||||
(journal-name (tc/to-local-date date)))
|
||||
|
||||
(defn js-date->goog-date
|
||||
[d]
|
||||
(cond
|
||||
(some->> d (instance? js/Date))
|
||||
(goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))
|
||||
:else d))
|
||||
|
||||
(comment
|
||||
(def default-formatter (tf/formatter "MMM do, yyyy"))
|
||||
(def zh-formatter (tf/formatter "YYYY年MM月dd日"))
|
||||
|
||||
@@ -238,10 +238,6 @@
|
||||
@current-idx))
|
||||
(on-chosen-open-link (nth matched @current-idx) false))))))
|
||||
|
||||
;; date-picker
|
||||
;; TODO: find a better way
|
||||
(def *internal-model (rum/cursor state/state :date-picker/date))
|
||||
|
||||
(defn- non-edit-input?
|
||||
[]
|
||||
(when-let [elem js/document.activeElement]
|
||||
@@ -255,42 +251,6 @@
|
||||
(or (non-edit-input?)
|
||||
(util/select? elem))))
|
||||
|
||||
(defn- inc-date [date n] (plus date (days n)))
|
||||
|
||||
(defn- inc-week [date n] (plus date (weeks n)))
|
||||
|
||||
(defn shortcut-complete
|
||||
[state e]
|
||||
(let [{:keys [on-change deadline-or-schedule?]} (last (:rum/args state))]
|
||||
(when (and on-change
|
||||
(not (input-or-select?)))
|
||||
(when-not deadline-or-schedule?
|
||||
(on-change e @*internal-model)))))
|
||||
|
||||
(defn shortcut-prev-day
|
||||
[_state e]
|
||||
(when-not (input-or-select?)
|
||||
(util/stop e)
|
||||
(swap! *internal-model inc-date -1)))
|
||||
|
||||
(defn shortcut-next-day
|
||||
[_state e]
|
||||
(when-not (input-or-select?)
|
||||
(util/stop e)
|
||||
(swap! *internal-model inc-date 1)))
|
||||
|
||||
(defn shortcut-prev-week
|
||||
[_state e]
|
||||
(when-not (input-or-select?)
|
||||
(util/stop e)
|
||||
(swap! *internal-model inc-week -1)))
|
||||
|
||||
(defn shortcut-next-week
|
||||
[_state e]
|
||||
(when-not (input-or-select?)
|
||||
(util/stop e)
|
||||
(swap! *internal-model inc-week 1)))
|
||||
|
||||
(defn toggle-cards!
|
||||
[]
|
||||
(if (and (= :srs (:modal/id @state/state)) (:modal/show? @state/state))
|
||||
|
||||
@@ -51,25 +51,7 @@
|
||||
;; * :file-graph? - Optional boolean to identify a command to only be run in file graphs
|
||||
;; and warned gracefully in db graphs
|
||||
(def ^:large-vars/data-var all-built-in-keyboard-shortcuts
|
||||
;; BUG: Actually, "enter" is registered by mixin behind a "when inputing" guard
|
||||
;; So this setting item does not cover all cases.
|
||||
;; See-also: frontend.components.datetime/time-repeater
|
||||
{:date-picker/complete {:binding "enter"
|
||||
:fn ui-handler/shortcut-complete}
|
||||
|
||||
:date-picker/prev-day {:binding "left"
|
||||
:fn ui-handler/shortcut-prev-day}
|
||||
|
||||
:date-picker/next-day {:binding "right"
|
||||
:fn ui-handler/shortcut-next-day}
|
||||
|
||||
:date-picker/prev-week {:binding ["up" "ctrl+p"]
|
||||
:fn ui-handler/shortcut-prev-week}
|
||||
|
||||
:date-picker/next-week {:binding ["down" "ctrl+n"]
|
||||
:fn ui-handler/shortcut-next-week}
|
||||
|
||||
:pdf/previous-page {:binding "alt+p"
|
||||
{:pdf/previous-page {:binding "alt+p"
|
||||
:fn pdf-utils/prev-page}
|
||||
|
||||
:pdf/next-page {:binding "alt+n"
|
||||
@@ -953,11 +935,6 @@
|
||||
:auto-complete/shift-complete
|
||||
:auto-complete/meta-complete
|
||||
:auto-complete/open-link
|
||||
:date-picker/prev-day
|
||||
:date-picker/next-day
|
||||
:date-picker/prev-week
|
||||
:date-picker/next-week
|
||||
:date-picker/complete
|
||||
:git/commit
|
||||
:dev/show-block-data
|
||||
:dev/show-block-ast
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
[frontend.rum :as r]
|
||||
[frontend.state :as state]
|
||||
[frontend.storage :as storage]
|
||||
[frontend.ui.date-picker]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.cursor :as cursor]
|
||||
[goog.dom :as gdom]
|
||||
@@ -583,8 +582,6 @@
|
||||
(when empty-placeholder
|
||||
empty-placeholder))]))
|
||||
|
||||
(def datepicker frontend.ui.date-picker/date-picker)
|
||||
|
||||
(defn toggle
|
||||
([on? on-click] (toggle on? on-click false))
|
||||
([on? on-click small?]
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
(ns ^:no-doc frontend.ui.date-picker
|
||||
(:require [cljs-time.core :refer [after? before? day day-of-week days first-day-of-the-month minus month months plus year]]
|
||||
[cljs-time.format :refer [formatter unparse]]
|
||||
[frontend.modules.shortcut.core :as shortcut]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util :refer [deref-or-value now->utc]]
|
||||
[rum.core :as rum]))
|
||||
|
||||
;; Adapted from re-com date-picker
|
||||
|
||||
;; TODO: add left, right, up, down, enter bindings
|
||||
|
||||
;; Loosely based on ideas: https://github.com/dangrossman/bootstrap-daterangepicker
|
||||
|
||||
;; --- cljs-time facades ------------------------------------------------------
|
||||
|
||||
(def ^:const month-format (formatter "MMMM yyyy"))
|
||||
|
||||
(def ^:const week-format (formatter "ww"))
|
||||
|
||||
(defn- month-label [date] (unparse month-format date))
|
||||
|
||||
(defn- dec-month [date] (minus date (months 1)))
|
||||
|
||||
(defn- inc-month [date] (plus date (months 1)))
|
||||
|
||||
(defn- inc-date [date n] (plus date (days n)))
|
||||
|
||||
(defn previous
|
||||
"If date fails pred, subtract period until true, otherwise answer date"
|
||||
;; date - a date object that satisfies cljs-time.core/DateTimeProtocol.
|
||||
;; If omitted, use now->utc, which returns a goog.date.UtcDateTime version of now with time removed.
|
||||
;; pred - can be one of cljs-time.predicate e.g. sunday? but any valid pred is supported.
|
||||
;; period - a period which will be subtracted see cljs-time.core periods
|
||||
;; Note: If period and pred do not represent same granularity, some steps may be skipped
|
||||
; e.g Pass a Wed date, specify sunday? as pred and a period (days 2) will skip one Sunday.
|
||||
([pred]
|
||||
(previous pred (now->utc)))
|
||||
([pred date]
|
||||
(previous pred date (days 1)))
|
||||
([pred date period]
|
||||
(if (pred date)
|
||||
date
|
||||
(recur pred (minus date period) period))))
|
||||
|
||||
(defn- =date [date1 date2]
|
||||
(and
|
||||
(= (year date1) (year date2))
|
||||
(= (month date1) (month date2))
|
||||
(= (day date1) (day date2))))
|
||||
|
||||
(defn- <=date [date1 date2]
|
||||
(or (=date date1 date2) (before? date1 date2)))
|
||||
|
||||
(defn- >=date [date1 date2]
|
||||
(or (=date date1 date2) (after? date1 date2)))
|
||||
|
||||
(def ^:private days-vector
|
||||
[{:key :Mo :short-name "M" :name "MON"}
|
||||
{:key :Tu :short-name "T" :name "TUE"}
|
||||
{:key :We :short-name "W" :name "WED"}
|
||||
{:key :Th :short-name "T" :name "THU"}
|
||||
{:key :Fr :short-name "F" :name "FRI"}
|
||||
{:key :Sa :short-name "S" :name "SAT"}
|
||||
{:key :Su :short-name "S" :name "SUN"}])
|
||||
|
||||
(defn- rotate
|
||||
[n coll]
|
||||
(let [c (count coll)]
|
||||
(take c (drop (mod n c) (cycle coll)))))
|
||||
|
||||
(defn- is-day-pred [d]
|
||||
#(= (day-of-week %) (inc d)))
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
|
||||
(def *internal-model (rum/cursor state/state :date-picker/date))
|
||||
|
||||
(defn- main-div-with
|
||||
[table-div class style attr]
|
||||
[:div.rc-datepicker-wrapper
|
||||
[:div {:style {:border-radius 4}}
|
||||
[:div (merge
|
||||
{:class (str "rc-datepicker datepicker noselect " class)
|
||||
:style (merge {:font-size "13px"
|
||||
:position "static"}
|
||||
style)}
|
||||
attr)
|
||||
table-div]]])
|
||||
|
||||
(rum/defc table-thead
|
||||
"Answer 2 x rows showing month with nav buttons and days NOTE: not internationalized"
|
||||
[display-month {show-weeks? :show-weeks? minimum :minimum maximum :maximum start-of-week :start-of-week}]
|
||||
(let [prev-date (dec-month display-month)
|
||||
minimum (deref-or-value minimum)
|
||||
maximum (deref-or-value maximum)
|
||||
prev-enabled? (if minimum (after? prev-date (dec-month minimum)) true)
|
||||
next-date (inc-month display-month)
|
||||
next-enabled? (if maximum (before? next-date maximum) true)
|
||||
template-row (if show-weeks? [:tr [:th]] [:tr])]
|
||||
[:thead
|
||||
(conj template-row
|
||||
[:th {:class (str "prev " (if prev-enabled? "available selectable" "disabled"))
|
||||
:style {:padding "0px"}
|
||||
:on-click #(when prev-enabled? (reset! *internal-model prev-date))}
|
||||
[:span.font-bold "<"]]
|
||||
[:th {:class "month" :col-span "5"} (month-label display-month)]
|
||||
[:th {:class (str "next " (if next-enabled? "available selectable" "disabled"))
|
||||
:style {:padding "0px"}
|
||||
:on-click #(when next-enabled? (reset! *internal-model next-date))}
|
||||
[:span.font-bold ">"]])
|
||||
(conj template-row
|
||||
(for [day (rotate start-of-week days-vector)]
|
||||
^{:key (:key day)} [:th {:class "day-enabled"} (str (:name day))]))]))
|
||||
|
||||
(defn- table-td
|
||||
[date focus-month selected today {minimum :minimum maximum :maximum :as attributes} disabled? on-change]
|
||||
;;following can be simplified and terse
|
||||
(let [minimum (deref-or-value minimum)
|
||||
maximum (deref-or-value maximum)
|
||||
enabled-min (if minimum (>=date date minimum) true)
|
||||
enabled-max (if maximum (<=date date maximum) true)
|
||||
enabled-day (and enabled-min enabled-max)
|
||||
disabled-day? (if enabled-day
|
||||
(not ((:selectable-fn attributes) date))
|
||||
true)
|
||||
classes (cond disabled? "off"
|
||||
disabled-day? "off"
|
||||
(= focus-month (month date)) "available"
|
||||
:else "available off")
|
||||
classes (cond (and selected (=date selected date)) (str classes " active start-date end-date")
|
||||
(and today (=date date today)) (str classes " today")
|
||||
:else classes)
|
||||
on-click (fn [e]
|
||||
(when-not (or disabled? disabled-day?)
|
||||
(reset! *internal-model date)
|
||||
(on-change e date)))]
|
||||
[:td {:class classes
|
||||
:on-click on-click} (day date)]))
|
||||
|
||||
(defn- week-td [date]
|
||||
[:td {:class "week"} (unparse week-format date)])
|
||||
|
||||
(defn- table-tr
|
||||
"Return 7 columns of date cells from date inclusive"
|
||||
[date focus-month selected attributes disabled? on-change]
|
||||
; {:pre [(sunday? date)]}
|
||||
(let [table-row (if (:show-weeks? attributes) [:tr (week-td date)] [:tr])
|
||||
row-dates (map #(inc-date date %) (range 7))
|
||||
today (when (:show-today? attributes) (now->utc))]
|
||||
(into table-row (map #(table-td % focus-month selected today attributes disabled? on-change) row-dates))))
|
||||
|
||||
(rum/defc table-tbody
|
||||
"Return matrix of 6 rows x 7 cols table cells representing 41 days from start-date inclusive"
|
||||
[display-month selected attributes disabled? on-change]
|
||||
(let [start-of-week (:start-of-week attributes)
|
||||
current-start (previous (is-day-pred start-of-week) display-month)
|
||||
focus-month (month display-month)
|
||||
row-start-dates (map #(inc-date current-start (* 7 %)) (range 6))]
|
||||
(into [:tbody] (map #(table-tr % focus-month selected attributes disabled? on-change) row-start-dates))))
|
||||
|
||||
(defn- configure
|
||||
"Augment passed attributes with extra info/defaults"
|
||||
[attributes]
|
||||
(let [selectable-fn (if (-> attributes :selectable-fn fn?)
|
||||
(:selectable-fn attributes)
|
||||
(constantly true))]
|
||||
(merge attributes {:selectable-fn selectable-fn})))
|
||||
|
||||
|
||||
(rum/defc date-picker < rum/reactive
|
||||
{:init (fn [state]
|
||||
(reset! *internal-model (first (:rum/args state)))
|
||||
state)}
|
||||
(shortcut/mixin :shortcut.handler/date-picker false)
|
||||
[_model {:keys [on-change disabled? start-of-week class style attr]
|
||||
:or {start-of-week (state/get-start-of-week)} ;; Default to Sunday
|
||||
:as args}]
|
||||
(let [internal-model (util/react *internal-model)
|
||||
display-month (first-day-of-the-month (or internal-model (now->utc)))
|
||||
props-with-defaults (merge args {:start-of-week start-of-week})
|
||||
configuration (configure props-with-defaults)]
|
||||
(main-div-with
|
||||
[:table.table-auto {:class "table-condensed"}
|
||||
(table-thead display-month configuration)
|
||||
(table-tbody display-month internal-model configuration disabled? on-change)]
|
||||
class
|
||||
style
|
||||
attr)))
|
||||
@@ -650,12 +650,7 @@
|
||||
;; Commands are nested for now to stay in sync with the shortcuts system.
|
||||
;; Other languages should not nest keys under :commands
|
||||
:commands
|
||||
{:date-picker/complete "Date picker: Choose selected day"
|
||||
:date-picker/prev-day "Date picker: Select previous day"
|
||||
:date-picker/next-day "Date picker: Select next day"
|
||||
:date-picker/prev-week "Date picker: Select previous week"
|
||||
:date-picker/next-week "Date picker: Select next week"
|
||||
:pdf/previous-page "Pdf: Previous page of current pdf doc"
|
||||
{:pdf/previous-page "Pdf: Previous page of current pdf doc"
|
||||
:pdf/next-page "Pdf: Next page of current pdf doc"
|
||||
:pdf/close "Pdf: Close current pdf doc"
|
||||
:pdf/find "Pdf: Search text of current pdf doc"
|
||||
|
||||
@@ -76,63 +76,7 @@
|
||||
(is (= (m+ 3 5) 8))
|
||||
(is (= @actual-ops 4))
|
||||
(is (= (m+ 3 5) 8))
|
||||
(is (= @actual-ops 4))))
|
||||
|
||||
(testing "memoize-last nested mapping test"
|
||||
(let [actual-ops (atom 0)
|
||||
flatten-f (util/memoize-last (fn [& args]
|
||||
(swap! actual-ops inc) ;; side effect for counting
|
||||
(apply #'shortcut-data-helper/flatten-bindings-by-id (conj (vec args) nil true))))
|
||||
target (atom {:part1 {:date-picker/complete {:binding "enter"
|
||||
:fn "ui-handler/shortcut-complete"}
|
||||
:date-picker/prev-day {:binding "left"
|
||||
:fn "ui-handler/shortcut-prev-day"}}
|
||||
:part2 {:date-picker/next-day {:binding "right"
|
||||
:fn "ui-handler/shortcut-next-day"}
|
||||
:date-picker/prev-week {:binding ["up" "ctrl+p"]
|
||||
:fn "ui-handler/shortcut-prev-week"}}})]
|
||||
(is (= (flatten-f @target) {:date-picker/complete "enter"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]}))
|
||||
(is (= @actual-ops 1))
|
||||
(is (= (flatten-f @target) {:date-picker/complete "enter"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]}))
|
||||
(is (= @actual-ops 1))
|
||||
;; edit value
|
||||
(swap! target assoc-in [:part1 :date-picker/complete :binding] "tab")
|
||||
(is (= (flatten-f @target) {:date-picker/complete "tab"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]}))
|
||||
(is (= @actual-ops 2))
|
||||
(is (= (flatten-f @target) {:date-picker/complete "tab"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]}))
|
||||
(is (= @actual-ops 2))
|
||||
(is (= (flatten-f @target) {:date-picker/complete "tab"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]}))
|
||||
(is (= @actual-ops 2))
|
||||
;; edit key
|
||||
(swap! target assoc :part3 {:date-picker/next-week {:binding "down"
|
||||
:fn "ui-handler/shortcut-next-week"}})
|
||||
(is (= (flatten-f @target) {:date-picker/complete "tab"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]
|
||||
:date-picker/next-week "down"}))
|
||||
(is (= @actual-ops 3))
|
||||
(is (= (flatten-f @target) {:date-picker/complete "tab"
|
||||
:date-picker/prev-day "left"
|
||||
:date-picker/next-day "right"
|
||||
:date-picker/prev-week ["up" "ctrl+p"]
|
||||
:date-picker/next-week "down"}))
|
||||
(is (= @actual-ops 3)))))
|
||||
(is (= @actual-ops 4)))))
|
||||
|
||||
(deftest test-media-format-from-input
|
||||
(testing "predicate file type from ext (html5 supported)"
|
||||
|
||||
Reference in New Issue
Block a user