diff --git a/doc/improvements.md b/doc/improvements.md new file mode 100644 index 0000000..e4d49d5 --- /dev/null +++ b/doc/improvements.md @@ -0,0 +1,8 @@ + +### Merged cells +Merged cells are a bit hackily implemented. They are not really merged at the grid level and exist mainly on the presentation layer. +All cells under a merged cell should maybe evaluate to the same value. +Right now a merged cell can be set and evaluated separately which can cause +a cell under a merged cell to have a different value. + + diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 34ecee0..f20eb27 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -33,8 +33,9 @@ day8.re-frame.tracing.trace-enabled? true}}} :release {:build-options {:ns-aliases - {day8.re-frame.tracing day8.re-frame.tracing-stubs}}} - :modules {:main {:entries [bean.ui.main]}}} + {day8.re-frame.tracing day8.re-frame.tracing-stubs}}} + :modules {:main {:entries [bean.ui.main] + :init-fn bean.ui.main/init}}} :test {:target :node-test :output-to "tests.js" :ns-regexp "-test$"}}} diff --git a/src/bean/ui/db.cljs b/src/bean/ui/db.cljs index 0efad11..a0263d8 100644 --- a/src/bean/ui/db.cljs +++ b/src/bean/ui/db.cljs @@ -7,7 +7,11 @@ (vec (for [_ (range num-rows)] (vec (map (fn [_] "") (range num-cols))))) - "")) + "add:{x+y} +inc:{x+1} +sum:{x.reduce({x + y})} +count:{x.reduce(inc 0)} +concatt:{x.concat(y)}")) (def Cell [:map diff --git a/src/bean/ui/events.cljs b/src/bean/ui/events.cljs index 745ebbf..2f62df5 100644 --- a/src/bean/ui/events.cljs +++ b/src/bean/ui/events.cljs @@ -1,13 +1,14 @@ (ns bean.ui.events - (:require [bean.grid :as grid] + (:require [bean.code :as code] + [bean.code-errors :as code-errors] [bean.frames :as frames] - [bean.ui.provenance :as provenance] + [bean.grid :as grid] [bean.ui.db :as db] + [bean.ui.paste :as paste] + [bean.ui.provenance :as provenance] + [bean.ui.util :as util] [re-frame.core :as rf] - [reagent.core :as rc] - [bean.code :as code] - [bean.code-errors :as code-errors] - [bean.ui.util :as util])) + [reagent.core :as rc])) (rf/reg-event-db ::initialize-db @@ -48,6 +49,25 @@ (:start (get-in db [:ui :grid :selection])) addressed-cells)))) +(rf/reg-fx + ::copy-to-clipboard + (fn [html plain-text] + (.write (.-clipboard js/navigator) + [(new js/ClipboardItem + #js {"text/html" (new js/Blob [html] {:type "text/html"}) + "text/plain" (new js/Blob [plain-text] {:type "text/plain"})})]))) + +(defn debug [x] + (prn x) x) + +(rf/reg-event-fx + ::copy-selection + (fn copy-selection [{:keys [db]}] + (let [selection (get-in db [:ui :grid :selection])] + {:fx [[::copy-to-clipboard + (debug (paste/selection->html selection (:sheet db))) + (paste/selection->plain-text selection (:sheet db))]]}))) + (rf/reg-event-fx ::merge-cells (fn merge-cells [{:keys [db]} [_ area]] diff --git a/src/bean/ui/main.cljs b/src/bean/ui/main.cljs index 09f652e..1eb8de6 100644 --- a/src/bean/ui/main.cljs +++ b/src/bean/ui/main.cljs @@ -1,17 +1,22 @@ (ns bean.ui.main - (:require [bean.ui.views.root :as root] - [bean.ui.events :as events] + (:require [bean.ui.events :as events] + [bean.ui.views.sheet :as sheet] [bean.ui.routes :as routes] + [bean.ui.views.root :as root] [re-frame.core :as rf] [reagent.dom :as r])) (defn ^:dev/after-load main* [] - (routes/start) + (routes/start) (r/render [root/routed] (.getElementById js/document "app")) (rf/dispatch [::events/reload-bindings])) +(defn init [] + (.addEventListener js/window "paste" (fn [e] (sheet/handle-paste e))) + (.addEventListener js/window "copy" (fn [e] (sheet/handle-copy e)))) + (defn ^:export main [] (rf/dispatch-sync [::events/initialize-db]) (main*)) diff --git a/src/bean/ui/paste.cljs b/src/bean/ui/paste.cljs index cadf384..1665012 100644 --- a/src/bean/ui/paste.cljs +++ b/src/bean/ui/paste.cljs @@ -2,6 +2,7 @@ (:require [bean.ui.util :as util] [clojure.string :as string] [hickory.core :as hickory] + [hickory.render :as hr] [hickory.convert :as hc] [hickory.select :as hs])) @@ -10,6 +11,13 @@ (def sample3 "
fwefwe
fwefwef
fwefwe
") (def sample4 "
September 4, 2003September 2, 2004
8Advanced Battle53September 9, 2004September 29, 2005
9Battle FrontierBattle Frontierfwfwefwef47October 6, 2005September 14, 2006
10Diamond and Pearl52September 28, 2006October 25, 2007
") +(defn css-to-map [css-str] + (->> (string/split css-str #";") + (map string/trim) + (map #(map string/trim (string/split % #":"))) + (map (fn [[k v]] [(keyword k) v])) + (into {}))) + (defn inner-text [hiccup-form] (-> (map #(cond @@ -33,15 +41,44 @@ (string/replace ">" ">") (string/replace """ "\""))) -(defn hiccup-cell->cell [cell merge-until] - {:content (or (inner-text cell) "") - :style (when (or (= (first cell) :th) - (re-find #"bold" (or (:style (second cell)) ""))) - {:bold true}) +(defn hiccup-cell->cell [hiccup-cell merge-until] + (let [style (css-to-map (:style (second hiccup-cell)))] + {:content (or + (:data-bean-content (second hiccup-cell)) + (inner-text hiccup-cell) "") + :style (merge + (when (or (= (first hiccup-cell) :th) + (= (:font-weight style) "bold")) + {:bold true}) + (when-let [bg (:background style)] {:background bg})) ;; this is a weird place to sneak "merge"-ing cells but ;; passing it in from here for now to the grid so I can do everything in the ;; in the same function. - :merge-until merge-until}) + :merge-until merge-until})) + +(defn merged-with-another? [cell] + (and (get-in cell [:style :merged-with]) + (not (get-in cell [:style :merged-until])))) + +(defn cell->hiccup-cell [cell [r c]] + (let [[mr mc] (get-in cell [:style :merged-until])] + (when-not (merged-with-another? cell) + [:td + (merge + {:data-bean-content (:content cell) + :style (string/join ";" + [(when (get-in cell [:style :bold]) + "font-weight: bold") + (when-let [bg (get-in cell [:style :background])] + (str "background: " (util/color-int->hex bg)))])} + (when mc {:colspan (str (inc (- mc c)))}) + (when mr {:rowspan (str (inc (- mr r)))})) + (:representation cell)]))) + +(defn hiccup-matrix->html [matrix] + (hr/hiccup-to-html + [[:body {} [:table {} + (into [:tbody {}] matrix)]]])) (defn hickory-table->cells [hickory-table] (loop [hiccup-cells (->> @@ -94,3 +131,17 @@ (defn parse-plaintext [e] (plain-text->cells (.getData (.-clipboardData e) "text"))) + +(defn selection->html + [{:keys [start end]} sheet] + (->> (util/addresses-matrix start end) + (util/map-on-matrix #(cell->hiccup-cell (get-in (:grid sheet) %) %)) + (map #(into [] (remove nil? (into [:tr {}] %)))) + hiccup-matrix->html)) + +(defn selection->plain-text + [{:keys [start end]} sheet] + (->> (util/addresses-matrix start end) + (util/map-on-matrix #(get-in (:grid sheet) (conj % :content))) + (map #(string/join "\t" %)) + (string/join "\n"))) diff --git a/src/bean/ui/views/code.cljs b/src/bean/ui/views/code.cljs index dedaf97..b7a445b 100644 --- a/src/bean/ui/views/code.cljs +++ b/src/bean/ui/views/code.cljs @@ -34,4 +34,9 @@ :content-editable "" :on-change #(rf/dispatch [::events/update-code (.-value (.-target %))]) :spell-check false - :default-value (code/get-code @sheet)}]]])) + :default-value (code/get-code @sheet)} + "add:{x+y} + inc:{x+1} + sum:{x.reduce({x + y})} + count:{x.reduce(inc 0)} + concatt:{x.concat(y)}"]]])) diff --git a/src/bean/ui/views/sheet.cljs b/src/bean/ui/views/sheet.cljs index 6504538..942c443 100644 --- a/src/bean/ui/views/sheet.cljs +++ b/src/bean/ui/views/sheet.cljs @@ -841,5 +841,5 @@ (rf/dispatch [::events/paste-addressed-cells pasted-table]) (rf/dispatch [::events/paste-addressed-cells (paste/parse-plaintext e)]))) -(.addEventListener js/window "paste" handle-paste) -;; (.removeEventListener js/window "paste" handle-paste) +(defn handle-copy [_] + (rf/dispatch [::events/copy-selection]))