Skip to content

Instantly share code, notes, and snippets.

@not-in-stock
Last active August 10, 2021 14:26
Show Gist options
  • Select an option

  • Save not-in-stock/c53dd9f2f47f56547ecaa57bdcdea3e0 to your computer and use it in GitHub Desktop.

Select an option

Save not-in-stock/c53dd9f2f47f56547ecaa57bdcdea3e0 to your computer and use it in GitHub Desktop.
(ns not-in-stock.components.scrollbars
(:require [clojure.walk :as walk]
goog.object
[medley.core :as medley]
["react-custom-scrollbars" :default ReactCustomScrollbars]
[reagent.core :as r]
[superstring.core :as str]))
(defn- lisp-keyword [kw]
(keyword (str/lisp-case kw)))
(defn- js->clj-kw [x]
(walk/prewalk
(fn [node]
(if (map-entry? node)
[(lisp-keyword (key node)) (val node)]
node))
(js->clj-kw x)))
(defn- wrap-on-update [f]
(fn [values]
(f (js->clj-kw values))))
(defn- transform-props [props]
(medley/update-existing props :on-update wrap-on-update))
(def ^:private impl
(r/adapt-react-class ReactCustomScrollbars))
(defn- bottom-shadow-opacity [values shadow-height]
(let [{:keys [scroll-top scroll-height client-height]} values
double-height (* 2 shadow-height)
bottom-scroll-top (- scroll-height client-height)]
(* (/ 1 double-height)
(- bottom-scroll-top
(max scroll-top
(- bottom-scroll-top double-height))))))
(defn- top-shadow-opacity [values shadow-height]
(let [{:keys [scroll-top]} values
double-height (* 2 shadow-height)]
(* (/ 1 double-height)
(min scroll-top
double-height))))
(defn- shadow-gradient [color direction]
(str "linear-gradient(" direction ", " color " 0%, rgba(0, 0, 0, 0) 100%)"))
(defn- shadow-bottom [color height *values]
(let [values @*values]
[:div {:style {:opacity (bottom-shadow-opacity values height)
:position :absolute
:bottom 0
:left 0
:right 0
:height height
:background (shadow-gradient color "to top")}}]))
(defn- shadow-top [color height *values]
(let [values @*values]
[:div {:style {:opacity (top-shadow-opacity values height)
:position :absolute
:top 0
:left 0
:right 0
:height height
:background (shadow-gradient color "to bottom")}}]))
(defn- transform-shadow-props [props]
(-> props
(update :color #(or % "grey"))
(update :height #(or % 10))))
(defn- shadow []
(let [this (r/current-component)
props (transform-shadow-props (r/props this))
{:keys [top bottom color height *values]} props
children (r/children this)]
(cond-> [:div {:style {:position :relative}}]
top (conj [shadow-top color height *values])
:always (into children)
bottom (conj [shadow-bottom color height *values]))))
(defn- wrap-shadow-on-update [f on-update-fn]
(fn [values]
(when f (f values))
(on-update-fn values)))
(def ^:private ref-fns
["scrollTop"
"scrollLeft"
"scrollToTop"
"scrollToBottom"
"scrollToLeft"
"scrollToRight"
"getScrollLeft"
"getScrollTop"
"getScrollWidth"
"getScrollHeight"
"getClientWidth"
"getClientHeight"
"getValues"])
(def ^:private ref-fn-name->kw
(into {} (for [ref-fn ref-fns]
[ref-fn (keyword (str/lisp-case ref-fn))])))
(defn- component->ref-fns [component]
(into {} (for [ref-fn ref-fns]
[(ref-fn-name->kw ref-fn) (goog.object/get component ref-fn)])))
(defn- deep-merge [& maps]
(if (every? #(or (nil? %)
(map? %)) maps)
(apply merge-with deep-merge maps)
(last maps)))
(defn- transform-shadow-scrollbars-props [props on-update-fn *scroll-bar-ref]
(-> props
(update :on-update wrap-shadow-on-update on-update-fn)
(deep-merge {:style {:position :relative}
:ref #(reset! *scroll-bar-ref (component->ref-fns %))})))
(defn simple-scrollbars []
(let [this (r/current-component)
props (r/props this)]
(into [impl (transform-props props)]
(r/children this))))
(defn scrollbars []
(let [*values (r/atom [])
*scrollbar-fns (atom nil)]
(fn []
(let [this (r/current-component)
props (r/props this)
{shadow-props :shadow
:keys [on-component-update on-component-mount]} props]
(if (some? shadow-props)
[shadow (assoc shadow-props :*values *values)
(into [(with-meta simple-scrollbars
{:component-did-mount #(when on-component-update
(on-component-update @*scrollbar-fns))
:component-did-update #(when on-component-update
(on-component-mount @*scrollbar-fns))})
(-> props
(dissoc :shadow :on-component-update :on-component-mount)
(transform-shadow-scrollbars-props #(reset! *values %)
*scrollbar-fns))]
(r/children this))]
(into [simple-scrollbars props]
(r/children this)))))))
@not-in-stock
Copy link
Author

not-in-stock commented Jul 17, 2020

[components/scrollbars {:shadow {:top true
                                 :bottom true
                                 :height 20
                                 :color "white"}
                        :style {:height "246px"
                                :position :relative}}
 [:div.very-long-div "long div content"]]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment