diff options
-rw-r--r-- | src/frontend/tubo/events.cljs | 1 | ||||
-rw-r--r-- | src/frontend/tubo/layout/events.cljs | 81 | ||||
-rw-r--r-- | src/frontend/tubo/layout/subs.cljs | 25 | ||||
-rw-r--r-- | src/frontend/tubo/layout/views.cljs | 78 | ||||
-rw-r--r-- | src/frontend/tubo/subs.cljs | 1 | ||||
-rw-r--r-- | src/frontend/tubo/views.cljs | 10 |
6 files changed, 168 insertions, 28 deletions
diff --git a/src/frontend/tubo/events.cljs b/src/frontend/tubo/events.cljs index f0edf47..ca3d864 100644 --- a/src/frontend/tubo/events.cljs +++ b/src/frontend/tubo/events.cljs @@ -11,6 +11,7 @@ [tubo.channel.events] [tubo.comments.events] [tubo.kiosks.events] + [tubo.layout.events] [tubo.main-player.events] [tubo.modals.events] [tubo.navigation.events] diff --git a/src/frontend/tubo/layout/events.cljs b/src/frontend/tubo/layout/events.cljs new file mode 100644 index 0000000..5b968e6 --- /dev/null +++ b/src/frontend/tubo/layout/events.cljs @@ -0,0 +1,81 @@ +(ns tubo.layout.events + (:require + [nano-id.core :refer [nano-id]] + [re-frame.core :as rf] + [clojure.string :as str])) + +(rf/reg-event-db + :layout/show-bg-overlay + (fn [db [_ {:keys [on-click] :as data} remain-open?]] + (assoc db + :layout/bg-overlay + (assoc data + :show? true + :on-click #(do (when on-click (on-click)) + (when-not remain-open? + (rf/dispatch [:layout/hide-bg-overlay]))))))) + +(rf/reg-event-db + :layout/hide-bg-overlay + (fn [db _] + (assoc-in db [:layout/bg-overlay :show?] false))) + +(rf/reg-event-fx + :layout/show-mobile-tooltip + (fn [{:keys [db]} [_ data]] + {:db (assoc db :layout/mobile-tooltip (assoc data :show? true)) + :fx [[:dispatch [:layout/register-tooltip {:id (:id data)}]] + [:dispatch [:layout/show-bg-overlay {:extra-classes ["z-30"]}]]]})) + +(defn default-tooltip-data + [] + {:id (nano-id) + :destroy-on-click-out? true}) + +(rf/reg-event-db + :layout/register-tooltip + (fn [db [_ data]] + (let [full-data (merge (default-tooltip-data) data)] + (assoc-in db [:layout/tooltips (:id data)] full-data)))) + +(rf/reg-event-fx + :layout/destroy-tooltip-by-id + (fn [{:keys [db]} [_ id]] + {:db (update db :layout/tooltips dissoc id)})) + +(rf/reg-event-db + :layout/destroy-tooltips-by-ids + (fn [db [_ ids]] + (update db :layout/tooltips #(apply dissoc % ids)))) + +(defonce tooltip-controller-class-prefix "tooltip-controller-") + +(defn find-tooltip-controller-class-in-node + [node] + (some->> (.-className node) + (re-find (re-pattern (str tooltip-controller-class-prefix + "([\\w\\-]+)"))) + (first))) + +(defn find-tooltip-controller-class + [node] + (or (find-tooltip-controller-class-in-node node) + (some-> (.-parentNode node) + (find-tooltip-controller-class)))) + +(rf/reg-event-fx + :layout/destroy-tooltips-on-click-out + (fn [{:keys [db]} [_ clicked-node]] + (when (seq (:layout/tooltips db)) + (let [clicked-controller (some-> + (find-tooltip-controller-class clicked-node) + (str/split tooltip-controller-class-prefix) + (second)) + tooltip-ids (->> (:layout/tooltips db) + (vals) + (filter :destroy-on-click-out?) + (map :id) + (set))] + {:fx [[:dispatch + [:layout/destroy-tooltips-by-ids + (disj tooltip-ids clicked-controller)]]]})))) diff --git a/src/frontend/tubo/layout/subs.cljs b/src/frontend/tubo/layout/subs.cljs new file mode 100644 index 0000000..87d6b27 --- /dev/null +++ b/src/frontend/tubo/layout/subs.cljs @@ -0,0 +1,25 @@ +(ns tubo.layout.subs + (:require + [re-frame.core :as rf])) + +(rf/reg-sub + :layout/bg-overlay + (fn [db] + (:layout/bg-overlay db))) + +(rf/reg-sub + :layout/mobile-tooltip + (fn [db] + (:layout/mobile-tooltip db))) + +(rf/reg-sub + :layout/tooltips + (fn [db] + (:layout/tooltips db))) + +(rf/reg-sub + :layout/tooltip-by-id + (fn [] + (rf/subscribe [:layout/tooltips])) + (fn [tooltips [_ id]] + (get tooltips id))) diff --git a/src/frontend/tubo/layout/views.cljs b/src/frontend/tubo/layout/views.cljs index 6537765..77bcf56 100644 --- a/src/frontend/tubo/layout/views.cljs +++ b/src/frontend/tubo/layout/views.cljs @@ -1,6 +1,7 @@ (ns tubo.layout.views (:require [clojure.string :as str] + [nano-id.core :refer [nano-id]] [re-frame.core :as rf] [reitit.frontend.easy :as rfe] [reagent.core :as r] @@ -38,13 +39,15 @@ {:class classes :style {:color service-color}}]]) -(defn focus-overlay - [on-click active? transparent? extra-classes] - [:div.w-full.fixed.min-h-screen.right-0.top-0.transition-all.delay-75.ease-in-out.z-20 - {:class (conj extra-classes (when-not transparent? "bg-black")) - :style {:visibility (when-not active? "hidden") - :opacity (if active? "0.5" "0")} - :on-click on-click}]) +(defn background-overlay + [] + (when-let [{:keys [show?] :as overlay} @(rf/subscribe [:layout/bg-overlay])] + [:div.w-full.fixed.min-h-screen.right-0.top-0.z-20 + {:class (conj (:extra-classes overlay) + (when-not (:transparent? overlay) "bg-black")) + :style {:visibility (when-not show? "hidden") + :opacity (if show? "0.5" "0")} + :on-click (:on-click overlay)}])) (defn content-container [& children] @@ -138,7 +141,7 @@ [:option.dark:bg-neutral-900.border-none {:value option :key i} option])]]) -(defn menu-item +(defn tooltip-item [{:keys [label icon on-click link] :as item}] (let [content [:<> (when icon @@ -160,27 +163,50 @@ :class (str/join " " classes)} (if (vector? item) item content)]))) -(defn menu - [active? items & {:keys [extra-classes]}] +(defn tooltip + [items & {:keys [extra-classes]}] (when-not (empty? (remove nil? items)) - [:ul.xs:absolute.bg-neutral-100.dark:bg-neutral-800.rounded-t.rounded-b.z-20.flex.flex-col.text-neutral-800.dark:text-white.shadow.shadow-neutral-400.dark:shadow-neutral-900.bottom-2.left-2.right-2.fixed - {:class (conj extra-classes (when-not active? "hidden"))} + [:ul.absolute.bg-neutral-100.dark:bg-neutral-800.rounded-t.rounded-b.flex.flex-col.text-neutral-800.dark:text-white.shadow.shadow-neutral-400.dark:shadow-neutral-900.z-30 + {:class (conj extra-classes)} (for [[i item] (map-indexed vector (remove nil? items))] - ^{:key i} [menu-item item])])) + ^{:key i} [tooltip-item item])])) -(defn popover-menu - [!menu-active? items & - {:keys [menu-classes extra-classes] - :or {extra-classes [:p-3] - menu-classes ["xs:bottom-auto" "xs:left-auto" "xs:right-auto"]}}] - [:div.flex.items-center - [focus-overlay #(reset! !menu-active? false) @!menu-active? true - ["bg-black" "xs:bg-transparent"]] - [:button.focus:outline-none.xs:relative - {:on-click #(reset! !menu-active? (not @!menu-active?)) - :class extra-classes} - [:i.fa-solid.fa-ellipsis-vertical] - [menu @!menu-active? items :extra-classes menu-classes]]]) +(defn mobile-tooltip + [] + (let [{:keys [id items show?]} @(rf/subscribe [:layout/mobile-tooltip]) + tooltip-data (rf/subscribe [:layout/tooltip-by-id id])] + (when @tooltip-data + [:div.xs:hidden + {:class (str "tooltip-controller-" id) + :on-click #(do (rf/dispatch [:layout/destroy-tooltip-by-id id]) + (rf/dispatch [:layout/hide-bg-overlay]))} + (when-not (empty? (remove nil? items)) + [:ul.bg-neutral-100.dark:bg-neutral-800.rounded-t.rounded-b.z-30.flex.flex-col.text-neutral-800.dark:text-white.shadow.shadow-neutral-400.dark:shadow-neutral-900.bottom-4.left-2.right-2.fixed + {:class (when-not show? "hidden")} + (for [[i item] (map-indexed vector (remove nil? items))] + ^{:key i} [tooltip-item item])])]))) + +(defn popover + [] + (let [tooltip-id (nano-id) + tooltip-data (rf/subscribe [:layout/tooltip-by-id tooltip-id])] + (fn [items & + {:keys [extra-classes tooltip-classes] :or {extra-classes ["p-3"]}}] + [:div.flex.items-center.tooltip-controller + {:class (str "tooltip-controller-" tooltip-id)} + [:button.focus:outline-none.relative.hidden.xs:block + {:on-click #(if @tooltip-data + (rf/dispatch [:layout/destroy-tooltip-by-id tooltip-id]) + (rf/dispatch [:layout/register-tooltip {:id tooltip-id}])) + :class extra-classes} + [:i.fa-solid.fa-ellipsis-vertical] + (when @tooltip-data + [tooltip items :extra-classes tooltip-classes])] + [:button.focus:outline-none.relative.xs:hidden + {:on-click #(rf/dispatch [:layout/show-mobile-tooltip + {:items items :id tooltip-id}]) + :class extra-classes} + [:i.fa-solid.fa-ellipsis-vertical]]]))) (defn accordeon [{:keys [label on-open open? left-icon right-button]} & content] diff --git a/src/frontend/tubo/subs.cljs b/src/frontend/tubo/subs.cljs index 8c0575f..1a11893 100644 --- a/src/frontend/tubo/subs.cljs +++ b/src/frontend/tubo/subs.cljs @@ -6,6 +6,7 @@ [tubo.bookmarks.subs] [tubo.channel.subs] [tubo.kiosks.subs] + [tubo.layout.subs] [tubo.main-player.subs] [tubo.modals.subs] [tubo.navigation.subs] diff --git a/src/frontend/tubo/views.cljs b/src/frontend/tubo/views.cljs index 188c90b..074596e 100644 --- a/src/frontend/tubo/views.cljs +++ b/src/frontend/tubo/views.cljs @@ -2,6 +2,7 @@ (:require [re-frame.core :as rf] [tubo.bg-player.views :as bg-player] + [tubo.layout.views :as layout] [tubo.main-player.views :as main-player] [tubo.modals.views :as modals] [tubo.navigation.views :as navigation] @@ -12,9 +13,14 @@ [] (let [current-match @(rf/subscribe [:navigation/current-match]) dark-theme? @(rf/subscribe [:dark-theme])] - [:div {:class (when dark-theme? :dark)} - [:div.font-nunito-sans.min-h-screen.h-full.relative.flex.flex-col.dark:text-white.bg-neutral-100.dark:bg-neutral-900 [modals/modal] + [:div + {:class (when dark-theme? :dark) + :on-click #(rf/dispatch [:layout/destroy-tooltips-on-click-out + (.. % -target)])} + [:div.font-nunito-sans.min-h-screen.h-full.relative.flex.flex-col.dark:text-white.bg-neutral-100.dark:bg-neutral-900.z-10 + [layout/background-overlay] + [layout/mobile-tooltip] [navigation/navbar current-match] [notifications/notifications-panel] [:div.flex.flex-col.flex-auto.justify-between.relative |