Skip to content

Instantly share code, notes, and snippets.

@cblavier
Created January 9, 2021 10:55
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save cblavier/0e227de6fd1dfa00814b88642cdcb2a9 to your computer and use it in GitHub Desktop.
Save cblavier/0e227de6fd1dfa00814b88642cdcb2a9 to your computer and use it in GitHub Desktop.
Responsive Phoenix LiveView
const Hooks = { ViewportResizeHooks}
const connectLiveSocket = () => {
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content')
const liveSocket = new LiveSocket('/my_app/live', Socket, {
params: {
_csrf_token: csrfToken,
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
},
hooks: Hooks
})
liveSocket.connect()
return liveSocket
}
defmodule MyApp.SomeLiveView.Show do
use MyApp, :live_view
use ViewportHelpers
def render(assigns) do
render_for_device(SomeView, "show.html", assigns)
end
def mount(params, session, socket) do
{:ok,
socket
|> assign_device_kind()
}
end
end
defmodule ViewportHelpers do
alias Phoenix.{LiveView, View}
# in sync with css/shared/_media_queries.scss
@mobile_max_width 480
@desktop_kind :desktop
@mobile_kind :mobile
defmacro __using__(_) do
quote do
import ViewportHelpers
def handle_event("viewport_resize", viewport, socket) do
device_kind = viewport |> Map.get("width") |> device_kind_for_width()
{:noreply, LiveView.assign(socket, device_kind: device_kind)}
end
end
end
def assign_device_kind(socket) do
device_kind =
socket.private
|> get_in([:connect_params, "viewport", "width"])
|> device_kind_for_width()
LiveView.assign(socket, device_kind: device_kind)
end
def render_for_device(module, template, assigns = %{device_kind: device_kind}) do
template = String.replace(template, ".html", ".#{device_kind}.html")
View.render(module, template, assigns)
end
def render_for_device(module, template, assigns) do
View.render(module, template, assigns)
end
def device_kind_for_width(width) when is_integer(width) and width <= @mobile_max_width do
@mobile_kind
end
def device_kind_for_width(_width), do: @desktop_kind
end
import _ from 'lodash'
let resizeHandler
export const ViewportResizeHooks = {
mounted () {
// Direct push of current window size to properly update view
this.pushResizeEvent()
resizeHandler = _.debounce(() => {
this.pushResizeEvent()
}, 100)
window.addEventListener('resize', resizeHandler)
},
pushResizeEvent () {
this.pushEvent('viewport_resize', {
width: window.innerWidth,
height: window.innerHeight
})
},
turbolinksDisconnected () {
window.removeEventListener('resize', resizeHandler)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment