(ns microtables-frontend.views.sheet (:require [microtables-frontend.events :as events] [microtables-frontend.utils.coordinates :as coords] [re-frame.core :as re-frame])) ;TODO: expand selection/highlight mode ; - directional controls ; - arrow keys, enter, tab, shift-enter, shift-tab ; - enter key triggers editing (?) ; - special controls ; - expand selection ; - move selection ; - delete selection (neighbouring cells displaced in) ; - clear selection (cells become blank) ; - continue selection (intelligent sequence continuation) ; - common functions (sum, avg, etc) ; - display numeric answer in menu ; - offer to put the answer (in formula form) in a cell (to be chosen by clicking) (defn cell [c r col-idx maxrow data] (let [datum (get-in data [c r]) touch-device? (pos? (.. js/navigator -maxTouchPoints))] ^{:key (str c r)} [:td [:input (cond-> {:id (str c r) :class (if (= (:view datum) :highlighted) "highlighted" "") :value (if (= (get datum :view nil) :value) (get datum :value "") (get datum :error (get datum :display (get datum :value ""))));TODO: add "highlight" display mode (possibly just a css class) :on-change #(re-frame/dispatch [::events/edit-cell-value c r (.. % -target -value)]) :on-focus #(re-frame/dispatch [::events/movement-enter-cell c r]) :on-blur #(re-frame/dispatch [::events/movement-leave-cell c r]) :on-keyPress #(when (= (.. % -which) 13) (if (.. % -shiftKey) (re-frame/dispatch [::events/press-shift-enter-in-cell c r]) (re-frame/dispatch [::events/press-enter-in-cell c r])))} ; column-major tabindex on touch devices so "Next" moves down instead of right touch-device? (assoc :tab-index (+ (* col-idx maxrow) r)))]])) (defn row [r cols maxrow data] ^{:key (str "row-" r)} [:tr (cons ^{:key (str "row-head-" r)} [:th (str r)] (map-indexed #(cell %2 r %1 maxrow data) cols))]) (defn header-row [cols] ^{:key "header"} [:tr (cons ^{:key "corner"} [:th] (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))]) (defn sheet [data] [:table {:id "main-table"} [:tbody ;TODO: figure out appropriate starting values for maxrow and maxcol (maybe keep them intentionally small) ;TODO: figure out movement (maybe allow scroll overflow) (let [maxrow 20;(coords/highest-row data) maxcol "G";(coords/highest-col data) cols (take-while (partial not= (coords/next-letter maxcol)) coords/col-letters)] (cons (header-row cols) (map #(row % cols maxrow data) (range 1 (inc maxrow)))))]])