create gists
This commit is contained in:
parent
79a17290d5
commit
43a8412f06
90 changed files with 1777 additions and 2107 deletions
93
lib/zoeyscomputer_web/components/code_block.ex
Normal file
93
lib/zoeyscomputer_web/components/code_block.ex
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
defmodule ZoeyscomputerWeb.CodeBlock do
|
||||
use Phoenix.Component
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
@moduledoc """
|
||||
A code block component with syntax highlighting using Shiki.
|
||||
|
||||
## Features:
|
||||
- Syntax highlighting with Shiki
|
||||
- Optional line numbers
|
||||
- Optional highlighted lines
|
||||
- Optional title
|
||||
- Copy button
|
||||
- Catppuccin theme styling
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Renders a code block with syntax highlighting.
|
||||
|
||||
## Examples
|
||||
|
||||
<.code_block
|
||||
code="def hello, do: :world"
|
||||
language="elixir"
|
||||
title="Example Code"
|
||||
line_numbers={true}
|
||||
highlighted_lines={[1, 3]}
|
||||
/>
|
||||
|
||||
## Options
|
||||
* `:code` - Required. The code string to highlight
|
||||
* `:language` - Required. Programming language for syntax highlighting
|
||||
* `:title` - Optional. Title displayed above the code block
|
||||
* `:line_numbers` - Optional. Show line numbers (default: false)
|
||||
* `:highlighted_lines` - Optional. List of line numbers to highlight
|
||||
"""
|
||||
attr :code, :string, required: true
|
||||
attr :language, :string, required: true
|
||||
attr :title, :string, default: nil
|
||||
attr :line_numbers, :boolean, default: false
|
||||
attr :highlighted_lines, :list, default: []
|
||||
|
||||
def code_block(assigns) do
|
||||
# Calculate the number of lines for line numbers
|
||||
assigns = assign(assigns, :num_lines, String.split(assigns.code, "\n") |> length())
|
||||
|
||||
~H"""
|
||||
<div class="relative ctp-bg-base rounded-lg overflow-hidden">
|
||||
<%= if @title do %>
|
||||
<div class="ctp-bg-mantle px-4 py-2 border-b ctp-border-surface0">
|
||||
<h3 class="ctp-text-text text-sm font-medium"><%= @title %></h3>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="relative">
|
||||
<%= if @line_numbers do %>
|
||||
<div class="absolute left-0 top-0 bottom-0 ctp-bg-crust w-12 flex flex-col items-end pr-2 py-4 ctp-text-surface2 select-none">
|
||||
<%= for line_num <- 1..@num_lines do %>
|
||||
<span class={[
|
||||
"text-sm leading-6",
|
||||
line_num in @highlighted_lines && "ctp-text-mauve font-medium"
|
||||
]}>
|
||||
<%= line_num %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class={["overflow-x-auto", @line_numbers && "pl-12"]}>
|
||||
<pre
|
||||
class="p-4"
|
||||
id={"code-block-#{System.unique_integer()}"}
|
||||
phx-hook="CodeBlockHook"
|
||||
data-code={@code}
|
||||
data-language={@language}
|
||||
data-highlighted-lines={Jason.encode!(@highlighted_lines)}
|
||||
><code class="text-sm"><%= @code %></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctp-bg-mantle px-4 py-2 border-t ctp-border-surface0 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class="ctp-text-subtext0 hover:ctp-text-text text-sm"
|
||||
phx-click={JS.dispatch("clipcopy", detail: %{text: @code})}
|
||||
>
|
||||
Copy code
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
@ -1,29 +1,3 @@
|
|||
<%!-- <header class="px-4 sm:px-6 lg:px-8"> --%>
|
||||
<%!-- <div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm"> --%>
|
||||
<%!-- <div class="flex items-center gap-4"> --%>
|
||||
<%!-- <a href="/"> --%>
|
||||
<%!-- <img src={~p"/images/logo.svg"} width="36" /> --%>
|
||||
<%!-- </a> --%>
|
||||
<%!-- <p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6"> --%>
|
||||
<%!-- v<%= Application.spec(:phoenix, :vsn) %> --%>
|
||||
<%!-- </p> --%>
|
||||
<%!-- </div> --%>
|
||||
<%!-- <div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900"> --%>
|
||||
<%!-- <a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700"> --%>
|
||||
<%!-- @elixirphoenix --%>
|
||||
<%!-- </a> --%>
|
||||
<%!-- <a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700"> --%>
|
||||
<%!-- GitHub --%>
|
||||
<%!-- </a> --%>
|
||||
<%!-- <a --%>
|
||||
<%!-- href="https://hexdocs.pm/phoenix/overview.html" --%>
|
||||
<%!-- class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80" --%>
|
||||
<%!-- > --%>
|
||||
<%!-- Get Started <span aria-hidden="true">→</span> --%>
|
||||
<%!-- </a> --%>
|
||||
<%!-- </div> --%>
|
||||
<%!-- </div> --%>
|
||||
<%!-- </header> --%>
|
||||
<main class="bg-ctp-base px-4 py-20 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<.flash_group flash={@flash} />
|
||||
|
|
|
|||
|
|
@ -8,20 +8,37 @@
|
|||
<%= assigns[:page_title] || "" %>
|
||||
</.live_title>
|
||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||
<link phx-track-statuc rel="stylesheet" href={~p"/fonts/Iosevka.css"} />
|
||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||
</script>
|
||||
</head>
|
||||
<body class="ctp-mocha bg-ctp-base text-ctp-overlay0">
|
||||
<body class="ctp-mocha bg-ctp-base text-base text-ctp-overlay0">
|
||||
<%= if @current_user do %>
|
||||
<div class="flex w-full">
|
||||
<div class="p-4 grow">
|
||||
<div class="p-4">
|
||||
<.link
|
||||
class="text-ctp-mauve font-bold hover:border-b-2 border-ctp-mauve"
|
||||
class="text-ctp-mauve text-sm font-bold hover:border-b-2 border-ctp-mauve"
|
||||
navigate={~p"/"}
|
||||
>
|
||||
zoey
|
||||
</.link>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<.link
|
||||
class="text-ctp-pink text-sm hover:border-b-2 border-ctp-pink"
|
||||
navigate={~p"/images"}
|
||||
>
|
||||
image
|
||||
</.link>
|
||||
</div>
|
||||
<div class="p-4 grow">
|
||||
<.link
|
||||
class="text-ctp-pink text-sm hover:border-b-2 border-ctp-pink"
|
||||
navigate={~p"/gists"}
|
||||
>
|
||||
gists
|
||||
</.link>
|
||||
</div>
|
||||
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
|
||||
<li class="text-[0.8125rem] leading-6">
|
||||
<%= @current_user.email %>
|
||||
|
|
|
|||
81
lib/zoeyscomputer_web/components/searchable_dropdown.ex
Normal file
81
lib/zoeyscomputer_web/components/searchable_dropdown.ex
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
defmodule ZoeyscomputerWeb.SearchableDropdown do
|
||||
use Phoenix.Component
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
attr :id, :string, required: true
|
||||
attr :options, :list, required: true
|
||||
attr :selected, :string, default: nil
|
||||
attr :class, :string, default: nil
|
||||
attr :name, :string, required: true
|
||||
attr :form, :any, required: true
|
||||
|
||||
def searchable_dropdown(assigns) do
|
||||
~H"""
|
||||
<div class={["relative w-full", @class]} id={"#{@id}-container"}>
|
||||
<div class="relative" phx-click-away={JS.hide(to: "##{@id}-dropdown", transition: "fade-out")}>
|
||||
<input type="hidden" name={@name} value={@selected} id={"#{@id}-input"} />
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center justify-between rounded-md border border-ctp-surface0 bg-ctp-base px-3 py-2 text-sm text-ctp-text shadow-sm hover:bg-ctp-surface0 focus:outline-none focus:ring-2 focus:ring-ctp-lavender transition-colors duration-200"
|
||||
phx-click={
|
||||
JS.toggle(to: "##{@id}-dropdown", in: "fade-in", out: "fade-out")
|
||||
|> JS.focus(to: "##{@id}-search")
|
||||
}
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="block truncate">
|
||||
<%= @selected || "Select an option..." %>
|
||||
</span>
|
||||
<svg
|
||||
class="h-5 w-5 text-ctp-overlay0 transform transition-transform duration-200"
|
||||
class={"#{if @selected, do: "rotate-180", else: ""}"}
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
id={"#{@id}-dropdown"}
|
||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md border border-ctp-surface0 bg-ctp-base shadow-lg hidden transition-all duration-200 ease-in-out"
|
||||
phx-hook="DropdownAnimation"
|
||||
>
|
||||
<div class="p-2">
|
||||
<input
|
||||
type="text"
|
||||
id={"#{@id}-search"}
|
||||
placeholder="Search..."
|
||||
class="w-full rounded-md border border-ctp-surface0 bg-ctp-mantle px-3 py-2 text-sm text-ctp-text placeholder-ctp-overlay0 focus:outline-none focus:ring-2 focus:ring-ctp-lavender transition-colors duration-200"
|
||||
phx-hook="SearchableDropdown"
|
||||
data-dropdown-id={@id}
|
||||
data-form-id={@form.id}
|
||||
/>
|
||||
</div>
|
||||
<ul class="max-h-48 overflow-y-auto py-1" role="listbox" id={"#{@id}-options"}>
|
||||
<li :for={option <- @options} class="transition-colors duration-150 ease-in-out">
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm hover:bg-ctp-surface0 focus:bg-ctp-surface0 focus:outline-none cursor-pointer transition-colors duration-150 ease-in-out"
|
||||
phx-click={
|
||||
JS.push("select_language", value: %{language: option}, target: "##{@form.id}")
|
||||
|> JS.hide(to: "##{@id}-dropdown", transition: "fade-out")
|
||||
}
|
||||
phx-target={"##{@form.id}"}
|
||||
role="option"
|
||||
data-option={option}
|
||||
>
|
||||
<%= option %>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue