Fork me on GitHub
#clojurescript
<
2024-04-18
>
Benson05:04:54

Hi, I'm trying to implement a popover from scratch with the behaviour of clicking the trigger element to open, and clicking outside of the popover to close. I feel like I'm not doing it fundamentally correctly as I'm running into a lot of issues. My implementation idea is: On click of popover trigger, I create a new div element and attach it to the document body, then i use reagent.dom/render to render my popover component into it. My popover component is created using reagent.core/create-class, using component-will-mount/unmount to add and remove the event listener mousedown which checks if there is a click outside of the popover in order to close it. It also uses component-did-mount to grab some data about the popover size and translate it to the anchor position. The issues I'm having are: ā€¢ When scrolling in another context (modal for example), the popover stays in place as the document itself is not scrolling. ā€¢ When resizing the popover, the positioning becomes incorrect due to the size / position of the anchor and containers changing i'm guessing. I feel like I have some fundamental misunderstanding in how to properly implement something like this in clojurescript. It feels wrong using all these dom manipulation functions when reagent was designed so we don't do stuff like this.

thheller06:04:01

this likely has more to do with html/css than cljs. what styles do you use to position the elements?

thheller06:04:38

you also likely can simplify that a lot by not using reagent.dom/render to render the elements

thheller06:04:30

dunno exactly how to use them via reagent but should be easy

Gabriele Lippi07:04:09

Hello, it's not fully supported yet (Firefox should implement it in v125) but there's also an official HTML element for this: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover

Benson09:04:27

@U05224H0W I'm using position: absolute and transform: translate() to position the popover, the coordinates being initially where the trigger element is. It seems this solution only works for the initial position and for when the document itself is scrolled, and not correctly when the page is resized / the element moves somewhere else as the popover is attached to the body instead of as a direct child of the trigger element. I'll have a look createPortal, it looks like a promising solution.

Benson09:04:54

@UHJMSFSKC looks good since i'm primarily supporting chrome, i'll take a look at this as well, cheers.

thheller09:04:59

absolute position does not update dynamically, so you maybe need to reposition. see ResizeObserver

Benson10:04:11

Thanks for the suggestions. I've gotten everything working much better with above solutions, i've solved the local scrolling context issue by subscribing to all scroll events in the document and updating the position of the popover dynamically (i'm not sure if that's the best thing to do, but it seems most existing popovers do something similar?).

thheller10:04:05

well there is also position: fixed

vraid21:04:02

Does anyone have a go-to simple plotting library for clojurescript, or is it better to use a javascript one? (And if so, which one?)

isak22:04:11

Vega is pretty good. Easy to use from CLJS, since the API is just data.

vraid00:04:22

I'll check it out, thanks!

vraid00:04:35

I get the impression that Hanami comes as a full-fledged application layer, whereas i just want to include a few plots in an existing application. But maybe the examples are deceptive?

vraid00:04:44

@U08JKUHA9 do you run Vega without any wrappers? If so, do you know of any example code out there?

isak14:04:10

@U0552GV2X32 yea, here is one:

(ns app.charting
  (:require
   ["vega" :refer (View) :as vega]
   ["vega-lite" :as vega-lite]
   ["vega-embed" :as vega-embed :refer (embed)]
   [reagent.core :as reagent]))

(def vega-embed-opts {"actions" false})

(defn resize-and-run-vega-view [container-el vega-view]
  (let [padding (.padding vega-view)
        padding-x (+ (.-left padding) (.-right padding))
        padding-y (+ (.-top padding) (.-bottom padding))]
    (.width vega-view (- (.-clientWidth container-el) padding-x))
    (.height vega-view (- (.-clientHeight container-el) padding-y))
    (.runAsync vega-view)))

(defn vega-chart-inner [_]
  (let [chart-el (atom nil)
        vega-view-atom (atom nil)
        destroy-chart! (fn []
                         (when-some [^js vega-view @vega-view-atom]
                           (.finalize vega-view)
                           (reset! vega-view-atom nil)))
        render-chart! (fn [this]
                        (let [el ^js @chart-el
                              {:keys [chart-spec renderer]
                               :or   {renderer "svg"}
                               :as   props} (reagent/props this)]
                          (-> (embed el (clj->js chart-spec) (clj->js vega-embed-opts))
                            (.then (fn [^js result]
                                     (let [vega-view (.-view result)]
                                       (reset! vega-view-atom vega-view)
                                       (resize-and-run-vega-view el vega-view)))))))]
    (reagent/create-class
      {:reagent-render         (fn [_]
                                 [:div.chart.w-full.h-full
                                  {:ref #(reset! chart-el %)}])

       :component-did-mount    (fn [this]
                                 (render-chart! this))

       :component-will-unmount (fn [this]
                                 (destroy-chart!))

       :component-did-update   (fn [this]
                                 (destroy-chart!)
                                 (render-chart! this))})))

(defn vega-chart [vega-spec]
  [vega-chart-inner {:chart-spec vega-spec}])
Example chart specs you can find here: https://vega.github.io/vega/examples/

vraid19:04:09

@U08JKUHA9 thanks, i got it working. Exactly what i need šŸ™‚

šŸ‘ 1
vraid23:04:24

Although this isn't quite the behaviour i expect when plotting a diagonal of integers

vraid02:04:09

With oz the same plot renders correctly

isak14:04:44

Oh strange, haven't seen it draw incorrectly before

vraid16:04:28

I think it's a resizing bug, but not sure what exactly causes it. Different sizes would give me different alignments of points and axes/grid. TIcks and tooltips for bar chart were also slightly off. I'm glad oz fixed it so that i didn't have to šŸ˜¬

šŸ‘Œ 1