Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/js/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function DashboardStats({
onLiveNavigate as EventListener
)
}
}, [navigate])
}, [onLiveNavigate])

const statsBoxClass =
'relative min-h-[436px] w-full mt-5 p-4 flex flex-col bg-white dark:bg-gray-900 shadow-sm rounded-md md:min-h-initial md:h-27.25rem md:w-[calc(50%-10px)] md:ml-[10px] md:mr-[10px] first:ml-0 last:mr-0'
Expand Down
57 changes: 57 additions & 0 deletions assets/js/liveview/dashboard_root.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Hook widget delegating navigation events to and from React.
* Necessary to emulate navigation events in LiveView with pushState
* manipulation disabled.
*/

import { buildHook } from './hook_builder'

export default buildHook({
initialize() {
this.url = window.location.href

this.addListener('click', document.body, (e) => {
const type = e.target.dataset.type || null

if (type === 'dashboard-link') {
this.url = e.target.href
const uri = new URL(this.url)
// Domain is dropped from URL prefix, because that's what react-dom-router
// expects.
const path = '/' + uri.pathname.split('/').slice(2).join('/')
this.el.dispatchEvent(
new CustomEvent('dashboard:live-navigate', {
bubbles: true,
detail: { path: path, search: uri.search }
})
)

this.pushEvent('handle_dashboard_params', { url: this.url })

e.preventDefault()
}
})

// Browser back and forward navigation triggers that event.
this.addListener('popstate', window, () => {
if (this.url !== window.location.href) {
this.pushEvent('handle_dashboard_params', {
url: window.location.href
})
}
})

// Navigation events triggered from liveview are propagated via this
// handler.
this.addListener('dashboard:live-navigate-back', window, (e) => {
if (
typeof e.detail.search === 'string' &&
this.url !== window.location.href
) {
this.pushEvent('handle_dashboard_params', {
url: window.location.href
})
}
})
}
})
44 changes: 44 additions & 0 deletions assets/js/liveview/dashboard_tabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Hook widget for optimistic loading of tabs and
* client-side persistence of selection using localStorage.
*/

import { buildHook } from './hook_builder'

function getDomain(url) {
const uri = typeof url === 'object' ? url : new URL(url)
return uri.pathname.split('/')[1]
}

export default buildHook({
initialize() {
const domain = getDomain(window.location.href)

this.addListener('click', this.el, (e) => {
const button = e.target.closest('button')
const tab = button && button.dataset.tab

if (tab) {
const label = button.dataset.label
const storageKey = button.dataset.storageKey
const activeClasses = button.dataset.activeClasses
const inactiveClasses = button.dataset.inactiveClasses
const title = this.el
.closest('[data-tile]')
.querySelector('[data-title]')

title.innerText = label

this.el.querySelectorAll(`button[data-tab] span`).forEach((s) => {
s.className = inactiveClasses
})

button.querySelector('span').className = activeClasses

if (storageKey) {
localStorage.setItem(`${storageKey}__${domain}`, tab)
}
}
})
}
})
53 changes: 53 additions & 0 deletions assets/js/liveview/hook_builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export function buildHook({ initialize, cleanup }) {
cleanup = cleanup || function () {}

return {
mounted() {
this.initialize()
},

updated() {
this.initialize()
},

reconnected() {
this.initialize()
},

destroyed() {
this.cleanup()
},

initialize() {
this.cleanup()
initialize.bind(this)()
},

cleanup() {
this.removeListeners()
cleanup.bind(this)()
},

addListener(eventName, listener, callback) {
this.listeners = this.listeners || []

listener.addEventListener(eventName, callback)

this.listeners.push({
element: listener,
event: eventName,
callback: callback
})
},

removeListeners() {
if (this.listeners) {
this.listeners.forEach((l) => {
l.element.removeEventListener(l.event, l.callback)
})

this.listeners = null
}
}
}
}
156 changes: 0 additions & 156 deletions assets/js/liveview/live_dashboard.js

This file was deleted.

5 changes: 3 additions & 2 deletions assets/js/liveview/live_socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import 'phoenix_html'
import { Socket } from 'phoenix'
import { LiveSocket } from 'phoenix_live_view'
import { Modal, Dropdown } from 'prima'
import LiveDashboard from './live_dashboard'
import DashboardRoot from './dashboard_root'
import DashboardTabs from './dashboard_tabs.js'
import topbar from 'topbar'
/* eslint-enable import/no-unresolved */

Expand All @@ -21,7 +22,7 @@ let disablePushStateFlag = document.querySelector(
)
let domain = document.querySelector("meta[name='dashboard-domain']")
if (csrfToken && websocketUrl) {
let Hooks = { Modal, Dropdown, LiveDashboard }
let Hooks = { Modal, Dropdown, DashboardRoot, DashboardTabs }
Hooks.Metrics = {
mounted() {
this.handleEvent('send-metrics', ({ event_name }) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible_web/live/components/dashboard/base.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule PlausibleWeb.Components.Dashboard.Base do
~H"""
<.link
data-type="dashboard-link"
href={@url}
patch={@url}
{@rest}
>
{render_slot(@inner_block)}
Expand Down
27 changes: 4 additions & 23 deletions lib/plausible_web/live/components/dashboard/tile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,18 @@ defmodule PlausibleWeb.Components.Dashboard.Tile do
slot :inner_block, required: true

def tile(assigns) do
assigns = assign(assigns, :update_mode, if(assigns.connected?, do: "ignore", else: "replace"))

~H"""
<div data-tile id={@id}>
<div data-tile class="w-full flex justify-between h-full">
<div id={@id <> "-title"} class="flex gap-x-1" phx-update={@update_mode}>
<div class="w-full flex justify-between h-full">
<div id={@id <> "-title"} class="flex gap-x-1" phx-update="ignore">
<h3 data-title class="font-bold dark:text-gray-100">{@title}</h3>
</div>

<div
:if={@tabs != []}
id={@id <> "-tabs"}
phx-update={@update_mode}
phx-hook="LiveDashboard"
data-widget="tabs"
phx-update="ignore"
phx-hook="DashboardTabs"
class="flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2 items-baseline"
>
{render_slot(@tabs)}
Expand All @@ -42,22 +39,6 @@ defmodule PlausibleWeb.Components.Dashboard.Tile do
"""
end

attr :id, :string, required: true
slot :inner_block, required: true

def tabs(assigns) do
~H"""
<div
id={@id}
phx-hook="LiveDashboard"
data-widget="tabs"
class="flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2 items-baseline"
>
{render_slot(@inner_block)}
</div>
"""
end

attr :label, :string, required: true
attr :value, :string, required: true
attr :active, :string, required: true
Expand Down
Loading
Loading