mirror of
https://github.com/plausible/analytics.git
synced 2024-12-27 19:47:26 +03:00
f464ceae88
* Revert "Remove site pins for now"
This reverts commit 5eccf4eaf6
.
* Implement basic site pin schema level logic within user specific preferences
* Add vertical ellipsis menu markup
* Implement basic changesets for user preferences
* Implement pin toggling
* Try to fix pin sorting
* Implement pin toggling in LV
* Adjust moduledocs for new schema(s)
* Remove unnecessary `distinct` from query
* Use `button` for pin/unpin action
* Generalize preference setting
* Rename schema and fields for clarity
* Rename `list_type` -> `entry_type`
* Safeguard setting options
* Test `set_option/4` and `toggle_pin/2`
* Add test for listing pinned sites via `Sites.list`
* Disallow pinning sites outside page explicitly
* Test pinning in LV
* Test conditional rendering of site settings in /sites
* Remove unnecessary TODO comment
* Safeguard `Sites.set_option/4` against invalid user/site combo
* Handle pinned sites in dashboard site picker
* Clear flashes upon (un)pinning sites
* Update CHANGELOG
* Prevent blinking of hamburger menu items on first paint
* Highlight hamburger handle on hover in /sites
* Start showing hotkeys in site picker again
* Sort pinned sites in the order they were pinned
* Update sites list order immediately after pin/unpin toggle
* Refactor and split `Sites.list/3`, extracting `Sites.list_with_invitations/3`
* Cap number of pinned sites at 9 per user
* First pass on visual indication of site cards (dis)appearing
* Apply ellipsis gradient+shadow on card hover
* Fix responsive padding of site cards
* Sort by invitations first, pinned sites second and then the rest
* Revert "Apply ellipsis gradient+shadow on card hover"
This reverts commit 0608796612639030ccbb12df639709f78edc1434.
* Apply more subtle hover effect on the ellipsis menu
* Make error and success flash LV boxes use separate component containers
* Promote `pinned_at` in table migration to a column
* Switch logic to using `pinned_at` as a standard schema field
* Refactor `Sites.list*` getting rid of subquery (h/t @ukutaht)
* Remove migration which is already merged upstream
---------
Co-authored-by: Adam Rutkowski <hq@mtod.org>
225 lines
6.0 KiB
Elixir
225 lines
6.0 KiB
Elixir
defmodule PlausibleWeb.Components.Generic do
|
|
@moduledoc """
|
|
Generic reusable components
|
|
"""
|
|
use Phoenix.Component
|
|
|
|
attr(:type, :string, default: "button")
|
|
attr(:class, :string, default: "")
|
|
attr(:disabled, :boolean, default: false)
|
|
attr(:rest, :global)
|
|
|
|
slot(:inner_block)
|
|
|
|
def button(assigns) do
|
|
~H"""
|
|
<button
|
|
type={@type}
|
|
disabled={@disabled}
|
|
class={[
|
|
"inline-flex items-center justify-center gap-x-2 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:bg-gray-400",
|
|
@class
|
|
]}
|
|
{@rest}
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
</button>
|
|
"""
|
|
end
|
|
|
|
attr(:href, :string, required: true)
|
|
attr(:class, :string, default: "")
|
|
attr(:rest, :global)
|
|
|
|
slot(:inner_block)
|
|
|
|
def button_link(assigns) do
|
|
~H"""
|
|
<.link
|
|
href={@href}
|
|
class={[
|
|
"inline-flex items-center justify-center gap-x-2 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:bg-gray-400",
|
|
@class
|
|
]}
|
|
{@rest}
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
</.link>
|
|
"""
|
|
end
|
|
|
|
attr(:slug, :string, required: true)
|
|
|
|
def docs_info(assigns) do
|
|
~H"""
|
|
<a href={"https://plausible.io/docs/#{@slug}"} rel="noreferrer" target="_blank">
|
|
<Heroicons.information_circle class="text-gray-400 w-6 h-6 absolute top-0 right-0 text-gray-400" />
|
|
</a>
|
|
"""
|
|
end
|
|
|
|
attr(:title, :string, default: "Notice")
|
|
attr(:size, :atom, default: :sm)
|
|
attr(:rest, :global)
|
|
slot(:inner_block)
|
|
|
|
def notice(assigns) do
|
|
~H"""
|
|
<div class="rounded-md bg-yellow-50 dark:bg-yellow-100 p-4" {@rest}>
|
|
<div class="flex">
|
|
<div :if={@size !== :xs} class="flex-shrink-0">
|
|
<svg
|
|
class="h-5 w-5 text-yellow-400"
|
|
viewBox="0 0 20 20"
|
|
fill="currentColor"
|
|
aria-hidden="true"
|
|
>
|
|
<path
|
|
fill-rule="evenodd"
|
|
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
|
|
clip-rule="evenodd"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3
|
|
:if={@size !== :xs}
|
|
class={"text-#{@size} font-medium text-yellow-800 dark:text-yellow-900 mb-2"}
|
|
>
|
|
<%= @title %>
|
|
</h3>
|
|
<div class={"text-#{@size} text-yellow-700 dark:text-yellow-800"}>
|
|
<p>
|
|
<%= render_slot(@inner_block) %>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
attr :id, :any, default: nil
|
|
attr :href, :string, required: true
|
|
attr :new_tab, :boolean, default: false
|
|
attr :class, :string, default: ""
|
|
slot :inner_block
|
|
|
|
def styled_link(assigns) do
|
|
~H"""
|
|
<.unstyled_link
|
|
new_tab={@new_tab}
|
|
href={@href}
|
|
class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-500 dark:hover:text-indigo-600"
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
</.unstyled_link>
|
|
"""
|
|
end
|
|
|
|
slot :button, required: true do
|
|
attr :class, :string
|
|
end
|
|
|
|
slot :panel, required: true do
|
|
attr :class, :string
|
|
end
|
|
|
|
def dropdown(assigns) do
|
|
~H"""
|
|
<div class="flex justify-center">
|
|
<div
|
|
x-data="dropdown"
|
|
x-on:keydown.escape.prevent.stop="close($refs.button)"
|
|
x-on:focusin.window="! $refs.panel.contains($event.target) && close()"
|
|
x-id="['dropdown-button']"
|
|
class="relative"
|
|
>
|
|
<button
|
|
x-ref="button"
|
|
x-on:click="toggle()"
|
|
x-bind:aria-expanded="open"
|
|
x-bind:aria-controls="$id('dropdown-button')"
|
|
type="button"
|
|
class={List.first(@button).class}
|
|
>
|
|
<%= render_slot(List.first(@button)) %>
|
|
</button>
|
|
<div
|
|
x-ref="panel"
|
|
x-show="open"
|
|
x-transition.origin.top.left
|
|
x-on:click.outside="close($refs.button)"
|
|
x-on:click="onPanelClick"
|
|
x-bind:id="$id('dropdown-button')"
|
|
style="display: none;"
|
|
class={List.first(@panel).class}
|
|
>
|
|
<%= render_slot(List.first(@panel)) %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
attr :href, :string, required: true
|
|
attr :new_tab, :boolean, default: false
|
|
slot :inner_block, required: true
|
|
|
|
def dropdown_link(assigns) do
|
|
~H"""
|
|
<.unstyled_link
|
|
new_tab={@new_tab}
|
|
href={@href}
|
|
class="w-full justify-between text-gray-700 dark:text-gray-300 block px-3.5 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-100"
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
</.unstyled_link>
|
|
"""
|
|
end
|
|
|
|
attr :href, :string, required: true
|
|
attr :new_tab, :boolean, default: false
|
|
attr :class, :string, default: ""
|
|
attr :id, :any, default: nil
|
|
attr :rest, :global
|
|
slot :inner_block
|
|
|
|
def unstyled_link(assigns) do
|
|
if assigns[:new_tab] do
|
|
assigns = assign(assigns, :icon_class, icon_class(assigns))
|
|
|
|
~H"""
|
|
<.link
|
|
id={@id}
|
|
class={[
|
|
"inline-flex items-center gap-x-0.5",
|
|
@class
|
|
]}
|
|
href={@href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
{@rest}
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
<Heroicons.arrow_top_right_on_square class={["opacity-60", @icon_class]} />
|
|
</.link>
|
|
"""
|
|
else
|
|
~H"""
|
|
<.link class={@class} href={@href}>
|
|
<%= render_slot(@inner_block) %>
|
|
</.link>
|
|
"""
|
|
end
|
|
end
|
|
|
|
defp icon_class(link_assigns) do
|
|
if String.contains?(link_assigns[:class], "text-sm") do
|
|
["w-3 h-3"]
|
|
else
|
|
["w-4 h-4"]
|
|
end
|
|
end
|
|
end
|