Jarzka / stylefy

Clojure(Script) library for styling user interface components with ease.
MIT License
316 stars 10 forks source link

Auto-generated class not created/found when using iframes #62

Open light-matters opened 2 years ago

light-matters commented 2 years ago

The bug is if I embed a react (reagent) element into an iframe object then the styles I set with stylefy don't seem to work, even if the actions do.

For instance, if I create a series of elements of the form

[:div (stylefy/use-style {:margin "0 50px"
                            ::stylefy/mode
                            {:hover {:transform "scale(1.1, 1.1)"}}}
                           {:on-click #(rf/dispatch
                                        [::ui-events/...])
                            :on-aux-click #(rf/dispatch
                                            [::ui-events/...])
                            :on-context-menu #(js/window.open (...))})

   word]

then the margin and hover options are ignored and yet all of the on-... events work as expected. Looking at the html elements with an inspector, I think the problem is that the automatic stylefy classes are not being generated in this case. The elements are created with a specific stylefy label (e.g. "_stylefy_1431804467") but the corresponding class file doesn't seem to exist.

Any temporary workarounds or suggestions would be much appreciated.

Thanks!

Jarzka commented 2 years ago

Can you give an example how you embed Reagent component into an iframe object?

I tested embedding a whole HTML page running stylefy, and it worked fine.

light-matters commented 2 years ago

Ah, I wasn't expecting you to reply so soon! Thanks.

Mmm, the plot thickens then; you give me hope that there could be an easy fix. I can give you a summary of sorts. So, the general task is to embed clickable text over a video. A pseudo-random site that I was using to test this was videa.hu, where the main video is hidden behind an iframe.

I haven't released the source for this project yet but I can summarise the relevant code. Here is the mounting code, along with associated auxiliary functions.

(let [element (ensure-textboxes video-element)]
    (rdom/render [#(ui-textbox/textboxes)] element))
(defn ensure-textboxes [video-element]
  (let [id "content-textboxes"
        element (get-element id)]
    (if-not element
      (let [element (.insertAdjacentElement video-element
                                            "afterend"
                                            (.createElement js/document "div"))]
        (.setAttribute element "id" id)
        element)
      element)))
(defn textbox-target []
  (let [text @(rf/subscribe [::ui-subs/text-current])
        words-user @(rf/subscribe [::ui-subs/user-words])
        words-with-punc (string/split text #" ")
        words-clickable (mapv (comp #(colour-word-div words-user %)
                                    div<-word) words-with-punc)
        container [:div#text-box-target
                   props-target]]

    (do (reduce (fn [x y] (conj x y))
                container
                words-clickable))))
(defn div<-word [word]
  [:div (stylefy/use-style {:margin "0 50px"
                            ::stylefy/mode
                            {:hover {:transform "scale(1.1, 1.1)"}}}
                           {:on-click #(rf/dispatch
                                        [::ui-events/toggle-word ...])
                            :on-aux-click #(rf/dispatch
                                            [::ui-events/learn-word ...])
                            :on-context-menu #(js/window.open ...)})

   word])

Where [#(ui-textbox/textboxes)] is just a container for the textbox in question:

[:div#text-tracks {}
     (textbox-target)
     (textbox-native)].
Jarzka commented 2 years ago

I was able to repeat your problem. I inserted a custom HTML element inside an iframe and then embeded a Reagent component inside of it. The autogenerated CSS class was then inserted in the head section of the parent page, and of course, it does not affect the styles inside the iframe from there.

To fix this, we would need to tell stylefy to insert the autogenerated CSS class somewhere else in the DOM, inside the iframe in this case. I think it would be possible to achieve, but I'm not sure if it is worth implementing. To me, this problem sounds like a rare corner case.

A simple workaround is to use regular HTML inline styles. Those should always work inside iframes (as long as you do not need media queries or some other CSS features that are not supported in inline styles).

light-matters commented 2 years ago

Thank you for having a look at it. Once I realised that it involved iframes, I did wonder if it was more of a feature request than a bug. I can completely understand if it's not a common enough problem to implement.

Just for posterity, I'll say that for me the class doesn't seem to be generated at all. At least I can't seem to find it in the browser inspector. Maybe I can generate it manually though and copy it across for my purposes. Unfortunately, inline doesn't work for me as I want certain effects like hover etc.

Thanks again for stylefy though, it's proving to be pretty convenient.

Jarzka commented 2 years ago

Just for posterity, I'll say that for me the class doesn't seem to be generated at all. At least I can't seem to find it in the browser inspector.

That sounds weird. If stylefy is running in the parent document, the autogenerated CSS should be inserted there.

Maybe I can generate it manually though and copy it across for my purposes. Unfortunately, inline doesn't work for me as I want certain effects like hover etc.

Just keep in mind that stylefy gives no guarantees that the autogenerated class names won't change in future updates. This has happened a few times since the library was created (there were bugs in the hashing logic) and I have mentioned it in the changelog. If you want to be sure, you could probably attach your own class name along with your own CSS code into the iframe to solve this corner case.