diff --git a/explorer/assets/css/tooltip.css b/explorer/assets/css/tooltip.css
index 8ce7acdab..039017c72 100644
--- a/explorer/assets/css/tooltip.css
+++ b/explorer/assets/css/tooltip.css
@@ -8,16 +8,14 @@
.chart-tooltip-dot {
height: 10px;
width: 10px;
- top: -5px;
+ bottom: -5px;
border-radius: 100%;
- background-color: hsl(var(--foreground));
- opacity: 0.2;
+ background-color: hsl(var(--accent));
position: absolute;
transition: all 0.2s;
}
.chart-tooltip-dot:hover {
- opacity: 0.5;
transform: scale(1.2);
cursor: pointer;
}
@@ -26,7 +24,7 @@
min-height: 50px;
min-width: 250px;
padding: 20px;
- margin-top: 8px;
+ margin-bottom: 8px;
background-color: hsl(var(--card));
border-radius: 8px;
border-width: 1px;
@@ -49,6 +47,7 @@
.chart-tooltip-item {
display: flex;
justify-content: space-between;
+ gap: 5px;
width: 100%;
}
@@ -59,3 +58,4 @@
.chart-tooltip-item-value {
color: hsl(var(--foreground));
}
+
diff --git a/explorer/assets/vendor/charts/batch_size.js b/explorer/assets/vendor/charts/batch_size.js
index 91027dbee..9abbe0b5a 100644
--- a/explorer/assets/vendor/charts/batch_size.js
+++ b/explorer/assets/vendor/charts/batch_size.js
@@ -1,15 +1,12 @@
+import { yTickCallbackShowMinAndMaxValues } from "./helpers";
import { alignedTooltip } from "./tooltip";
export const batchSizeCustomOptions = (options, data) => {
// show only min and max values
- options.scales.y.ticks.callback = (_value, index, values) => {
- const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
- if (index === 0) return `${Math.min(...dataY)} proofs`;
- if (index === values.length - 1) {
- return `${Math.max(...dataY)} proofs`;
- }
- return "";
- };
+ options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(
+ data,
+ (val) => `${val} proofs`
+ );
// show age min, mean and max age in x axis
options.scales.x.ticks.callback = (_value, index, values) => {
@@ -23,9 +20,10 @@ export const batchSizeCustomOptions = (options, data) => {
options.plugins.tooltip.external = (context) =>
alignedTooltip(context, {
+ name: "batch-size",
title: "Batch size",
items: [
- { title: "Cost", id: "cost" },
+ { title: "Fee per proof", id: "cost" },
{ title: "Age", id: "age" },
{ title: "Merkle root", id: "merkle_root" },
{ title: "Block number", id: "block_number" },
@@ -40,8 +38,7 @@ export const batchSizeCustomOptions = (options, data) => {
onTooltipUpdate: (tooltipModel) => {
const dataset = tooltipModel.dataPoints[0].dataset;
const idx = tooltipModel.dataPoints[0].dataIndex;
-
- const cost = `${dataset.data[idx].y} USD`;
+ const amount_of_proofs = dataset.data[idx].y;
const age = dataset.age[idx];
const merkleRootHash = dataset.merkle_root[idx];
const merkle_root = `${merkleRootHash.slice(
@@ -49,7 +46,7 @@ export const batchSizeCustomOptions = (options, data) => {
6
)}...${merkleRootHash.slice(merkleRootHash.length - 4)}`;
const block_number = dataset.data[idx].x;
- const amount_of_proofs = dataset.amount_of_proofs[idx];
+ const cost = `${dataset.fee_per_proof[idx]} USD`;
return {
cost,
diff --git a/explorer/assets/vendor/charts/cost_per_proof.js b/explorer/assets/vendor/charts/cost_per_proof.js
index e38ab1039..e60438b13 100644
--- a/explorer/assets/vendor/charts/cost_per_proof.js
+++ b/explorer/assets/vendor/charts/cost_per_proof.js
@@ -1,15 +1,12 @@
+import { yTickCallbackShowMinAndMaxValues } from "./helpers";
import { alignedTooltip } from "./tooltip";
export const costPerProofCustomOptions = (options, data) => {
- // show only min and max values
- options.scales.y.ticks.callback = (_value, index, values) => {
- const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
- if (index === 0) return `${Math.min(...dataY)} USD`;
- if (index === values.length - 1) {
- return `${Math.max(...dataY)} USD`;
- }
- return "";
- };
+ // show only 0, min and max values
+ options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(
+ data,
+ (val) => `${val} USD`
+ );
// show age min, mean and max age in x axis
options.scales.x.ticks.callback = (_value, index, values) => {
@@ -23,9 +20,10 @@ export const costPerProofCustomOptions = (options, data) => {
options.plugins.tooltip.external = (context) =>
alignedTooltip(context, {
+ name: "cost-per-proof",
title: "Cost per proof",
items: [
- { title: "Cost", id: "cost" },
+ { title: "Fee per proof", id: "cost" },
{ title: "Age", id: "age" },
{ title: "Merkle root", id: "merkle_root" },
{ title: "Block number", id: "block_number" },
diff --git a/explorer/assets/vendor/charts/helpers.js b/explorer/assets/vendor/charts/helpers.js
new file mode 100644
index 000000000..ee73d0d2d
--- /dev/null
+++ b/explorer/assets/vendor/charts/helpers.js
@@ -0,0 +1,43 @@
+const findClosestIndex = (target, values) => {
+ let closestIndex = 0;
+ let smallestDiff = Math.abs(values[0] - target);
+ for (let i = 1; i < values.length; i++) {
+ const diff = Math.abs(values[i] - target);
+ if (diff < smallestDiff) {
+ closestIndex = i;
+ smallestDiff = diff;
+ }
+ }
+ return closestIndex;
+};
+
+/**
+ * A callback function to customize y-axis tick labels by showing only the zero, minimum and maximum data values.
+ *
+ * @param {Object} data - The chart data object containing datasets and their values.
+ * @param {Function} renderText - A function to format and render text for the tick labels.
+ * @returns {Function} - A callback function for Chart.js tick customization.
+ *
+ * The returned function compares the current tick index with the indices of the values closest
+ * to the minimum and maximum data points, and displays these values formatted using the
+ * `renderText` function.
+ *
+ * @example
+ * options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(data, (val) => `${val} USD`);
+ */
+export const yTickCallbackShowMinAndMaxValues =
+ (data, renderText) => (_value, index, values) => {
+ if (index === 0) return renderText(0);
+
+ const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
+ const sortedData = dataY.sort((a, b) => b - a);
+ const min = sortedData[0];
+ const max = sortedData[sortedData.length - 1];
+ const valsData = values.map((item) => item.value);
+ const idxClosestToMin = findClosestIndex(min, valsData);
+ const idxClosestToMax = findClosestIndex(max, valsData);
+
+ if (index == idxClosestToMin) return renderText(min);
+ if (index == idxClosestToMax) return renderText(max);
+ return "";
+ };
diff --git a/explorer/assets/vendor/charts/index.js b/explorer/assets/vendor/charts/index.js
index faa2155f7..911230632 100644
--- a/explorer/assets/vendor/charts/index.js
+++ b/explorer/assets/vendor/charts/index.js
@@ -21,7 +21,7 @@ const applyOptionsByChartId = (id, options, data) => {
export default {
mounted() {
this.initChart();
- window.addEventListener("theme-changed", this.reinitChart.bind(this));
+ window.addEventListener("resize", this.resizeChart.bind(this));
},
updated() {
@@ -31,9 +31,8 @@ export default {
destroyed() {
if (this.chart) {
this.chart.destroy();
+ window.removeEventListener("resize", this.resizeChart.bind(this));
}
-
- window.removeEventListener("theme-changed", this.reinitChart.bind(this));
},
initChart() {
@@ -51,6 +50,8 @@ export default {
data,
options,
});
+
+ this.resizeChart();
},
reinitChart() {
@@ -59,4 +60,10 @@ export default {
}
this.initChart();
},
+
+ resizeChart() {
+ if (this.chart) {
+ this.chart.resize();
+ }
+ },
};
diff --git a/explorer/assets/vendor/charts/tooltip.js b/explorer/assets/vendor/charts/tooltip.js
index 3ab1d723e..57309fe99 100644
--- a/explorer/assets/vendor/charts/tooltip.js
+++ b/explorer/assets/vendor/charts/tooltip.js
@@ -26,6 +26,7 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*
* @param {Object} context - The chart.js context, typically passed as `this` within chart hooks.
* @param {Object} params - An object containing configuration for the tooltip.
+ * @param {Object} params.name - A string that serves as an identifier for the tooltip.
* @param {string} params.title - The title text to display in the tooltip.
* @param {Array} params.items - An array of items (with ids) to be displayed inside the tooltip.
* @param {Array} params.onTooltipClick - A callback that receives `tooltipModel` and gets triggered when the tooltip is clicked.
@@ -36,6 +37,7 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*
* @example
* alignedTooltip(context, {
+ * name: "my-chart",
* title: "Tooltip Title",
* items: [{ title: "Cost", id: "cost_id" }, { title: "Timestamp", id: "timestamp_id" }],
* onTooltipClick: (tooltipModel) => {
@@ -58,15 +60,15 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*/
export const alignedTooltip = (
context,
- { title, items, onTooltipClick, onTooltipUpdate }
+ { name, title, items, onTooltipClick, onTooltipUpdate }
) => {
const tooltipModel = context.tooltip;
- let tooltipEl = document.getElementById("chartjs-tooltip");
+ let tooltipEl = document.getElementById(`chartjs-tooltip-${name}`);
if (!tooltipEl) {
tooltipEl = document.createElement("div");
tooltipEl.style = "transition: opacity 0.3s;";
tooltipEl.style = "transition: left 0.1s;";
- tooltipEl.id = "chartjs-tooltip";
+ tooltipEl.id = `chartjs-tooltip-${name}`;
tooltipEl.innerHTML = tooltipComponent({
title,
isTooltipClickable: !!onTooltipClick,
@@ -81,6 +83,10 @@ export const alignedTooltip = (
tooltipEl.style.opacity = 0;
tooltipEl.style.zIndex = -1;
};
+ // this is needed to maintain responsiveness
+ window.addEventListener("resize", () => {
+ tooltipEl.remove();
+ });
if (onTooltipClick)
tooltipEl.querySelector(".chart-tooltip-dot").onclick = () =>
onTooltipClick(tooltipModel);
@@ -91,12 +97,6 @@ export const alignedTooltip = (
tooltipEl.style.opacity = 0;
return;
}
- tooltipEl.classList.remove("above", "below", "no-transform");
- if (tooltipModel.yAlign) {
- tooltipEl.classList.add(tooltipModel.yAlign);
- } else {
- tooltipEl.classList.add("no-transform");
- }
const values = onTooltipUpdate(tooltipModel);
items.forEach((item) => {
@@ -115,5 +115,9 @@ export const alignedTooltip = (
tooltipModel.caretX +
"px";
tooltipEl.style.top =
- position.top + window.scrollY + tooltipModel.caretY + "px";
+ position.top -
+ tooltipEl.offsetHeight +
+ window.scrollY +
+ tooltipModel.caretY +
+ "px";
};
diff --git a/explorer/assets/vendor/dark_mode.js b/explorer/assets/vendor/dark_mode.js
index 9485082de..7c0ebc19b 100644
--- a/explorer/assets/vendor/dark_mode.js
+++ b/explorer/assets/vendor/dark_mode.js
@@ -41,8 +41,6 @@ const setupThemeToggle = () => {
.getElementById("theme-toggle")
.addEventListener("click", function () {
toggleVisibility(!isDark());
- // chart.js listens for this event to re-render the chart and update its colors
- window.dispatchEvent(new Event("theme-changed"));
});
};
diff --git a/explorer/assets/vendor/tooltip.js b/explorer/assets/vendor/tooltip.js
index 86bd1de22..80b29ba7a 100644
--- a/explorer/assets/vendor/tooltip.js
+++ b/explorer/assets/vendor/tooltip.js
@@ -41,7 +41,7 @@ class Tooltip {
setupFloatingUI() {
this.cleanup = autoUpdate(this.$parent, this.$tooltip, () => {
computePosition(this.$parent, this.$tooltip, {
- placement: "top",
+ placement: "bottom",
middleware: [offset(5), flip(), shift({ padding: 5 })]
}).then(({ x, y }) => {
Object.assign(this.$tooltip.style, {
diff --git a/explorer/lib/explorer/models/batches.ex b/explorer/lib/explorer/models/batches.ex
index 27045ad6e..b22dd7b68 100644
--- a/explorer/lib/explorer/models/batches.ex
+++ b/explorer/lib/explorer/models/batches.ex
@@ -139,6 +139,19 @@ defmodule Batches do
result -> result
end
end
+
+ def get_avg_fee_per_proof() do
+ query =
+ from(b in Batches,
+ where: b.is_verified == true,
+ select: avg(b.fee_per_proof)
+ )
+
+ case Explorer.Repo.one(query) do
+ nil -> 0
+ result -> result
+ end
+ end
def get_amount_of_verified_proofs() do
query = from(b in Batches,
diff --git a/explorer/lib/explorer_web/components/assets_cta.ex b/explorer/lib/explorer_web/components/assets_cta.ex
index 184885649..35cc452c6 100644
--- a/explorer/lib/explorer_web/components/assets_cta.ex
+++ b/explorer/lib/explorer_web/components/assets_cta.ex
@@ -25,7 +25,7 @@ defmodule AssetsCTAComponent do
View all active operators
- <.link navigate={~p"/restakes"} class="flex-1 flex flex-col justify-start gap-0.5 group">
+ <.link navigate={~p"/restaked"} class="flex-1 flex flex-col justify-start gap-0.5 group">
Total Restaked
diff --git a/explorer/lib/explorer_web/components/batches_table.ex b/explorer/lib/explorer_web/components/batches_table.ex
new file mode 100644
index 000000000..3fc72b306
--- /dev/null
+++ b/explorer/lib/explorer_web/components/batches_table.ex
@@ -0,0 +1,51 @@
+defmodule ExplorerWeb.BatchesTable do
+ use Phoenix.Component
+ use ExplorerWeb, :live_component
+
+ attr(:batches, :list, required: true)
+
+ def batches_table(assigns) do
+ ~H"""
+ <.table id="batches" rows={@batches}>
+ <:col :let={batch} label="Batch Hash" class="text-left">
+ <.link navigate={~p"/batches/#{batch.merkle_root}"}>
+
+ <%= Helpers.shorten_hash(batch.merkle_root, 6) %>
+ <.right_arrow />
+ <.tooltip>
+ <%= batch.merkle_root %>
+
+
+
+
+ <:col :let={batch} label="Status">
+ <.dynamic_badge_for_batcher status={Helpers.get_batch_status(batch)} />
+
+ <:col :let={batch} label="Age">
+
+ <%= batch.submission_timestamp |> Helpers.parse_timeago() %>
+
+
+ <:col :let={batch} label="Block Number">
+ <%= batch.submission_block_number |> Helpers.format_number() %>
+
+
+ <:col :let={batch} label="Fee per proof">
+ <%= case EthConverter.wei_to_usd(batch.fee_per_proof, 6) do %>
+ <% {:ok, usd} -> %>
+ <%= "#{usd} USD" %>
+ <% {:error, _} -> %>
+ <%= "N/A" %>
+ <% end %>
+ <.tooltip>
+ ~= <%= EthConverter.wei_to_eth(batch.fee_per_proof, 6) %> ETH
+
+
+
+ <:col :let={batch} label="Number of proofs">
+ <%= batch.amount_of_proofs |> Helpers.format_number() %>
+
+
+ """
+ end
+end
diff --git a/explorer/lib/explorer_web/components/charts.ex b/explorer/lib/explorer_web/components/charts.ex
index 6603a6ebb..a5c1f5aa7 100644
--- a/explorer/lib/explorer_web/components/charts.ex
+++ b/explorer/lib/explorer_web/components/charts.ex
@@ -15,7 +15,7 @@ defmodule ExplorerWeb.ChartComponents do
data-chart-type={@chart_type}
data-chart-data={@chart_data}
data-chart-options={@chart_options}
- style="height: 100%; width: 100%;"
+ class="!w-full !h-full"
>
@@ -23,9 +23,116 @@ defmodule ExplorerWeb.ChartComponents do
end
@doc """
- Renders a line chart with aligned style.
+ Renders a bar chart with aligned style.
## Examples
- <.line
+ <.bar_chart
+ id="exchanges"
+ points={%{x: [1, 2, 3, 4], y: ["01-01-2024", "01-02-2024", "01-03-2024", "01-04-2024"]},}
+ show_ticks={%{x: true, y: true}}
+ extra_data={%{merkle_roots: [0x1, 0x2, 0x3, 0x4]}}
+ />
+ !Note:
+ - id is used to reference the chart on javascript to apply custom styles, configurations, tooltip, that are possible only via javascript
+ - points: nil values are automatically ignored and not displayed
+ - extra_data: any other data you might want to retrieve via javascript later
+ """
+ attr(:id, :string, required: true)
+ attr(:points, :map, required: true)
+ attr(:extra_data, :map, default: %{})
+ attr(:show_ticks, :map, default: %{x: true, y: true})
+
+ def bar_chart(assigns) do
+ ~H"""
+ <.basic_chart
+ id={@id}
+ chart_type="bar"
+ chart_data={
+ Jason.encode!(%{
+ labels: @points,
+ datasets: [
+ Map.merge(
+ %{
+ data: @points,
+ fill: false,
+ tension: 0.1,
+ backgroundColor: "rgba(24, 255, 128, 0.3)",
+ hoverBackgroundColor: "rgba(24, 255, 128, 0.5)",
+ borderColor: "rgb(24, 255, 127)",
+ borderWidth: 1.5,
+ borderRadius: 4
+ },
+ @extra_data
+ )
+ ]
+ })
+ }
+ chart_options={
+ Jason.encode!(%{
+ animation: false,
+ responsive: false,
+ maintainAspectRatio: false,
+ interaction: %{
+ mode: "index",
+ intersect: false
+ },
+ plugins: %{
+ legend: %{
+ display: false
+ }
+ },
+ elements: %{
+ point: %{
+ pointStyle: false
+ }
+ },
+ scales: %{
+ x: %{
+ bounds: "data",
+ offset: false,
+ ticks: %{
+ display: @show_ticks.x,
+ autoSkip: false,
+ maxRotation: 0,
+ font: %{
+ weight: "700"
+ }
+ },
+ grid: %{
+ display: false
+ },
+ border: %{
+ display: false
+ }
+ },
+ y: %{
+ offset: false,
+ beginAtZero: true,
+ ticks: %{
+ display: @show_ticks.y,
+ autoSkip: false,
+ maxRotation: 0,
+ font: %{
+ weight: "700"
+ }
+ },
+ grid: %{
+ display: false
+ },
+ border: %{
+ display: false
+ }
+ }
+ }
+ })
+ }
+ />
+ """
+ end
+
+ @doc """
+ Renders a linear chart with aligned style.
+ ## Examples
+ <.line_chart
id="exchanges"
points={%{x: [1, 2, 3, 4], y: ["01-01-2024", "01-02-2024", "01-03-2024", "01-04-2024"]},}
show_ticks={%{x: true, y: true}}
@@ -59,6 +166,8 @@ defmodule ExplorerWeb.ChartComponents do
}
chart_options={
Jason.encode!(%{
+ animation: false,
+ responsive: false,
maintainAspectRatio: false,
interaction: %{
mode: "index",
diff --git a/explorer/lib/explorer_web/components/contracts.ex b/explorer/lib/explorer_web/components/contracts.ex
index 6f353d69a..48652bf21 100644
--- a/explorer/lib/explorer_web/components/contracts.ex
+++ b/explorer/lib/explorer_web/components/contracts.ex
@@ -1,17 +1,45 @@
defmodule ContractsComponent do
use ExplorerWeb, :live_component
- attr :class, :string, default: nil
+ attr(:class, :string, default: nil)
+ attr(:host, :string, default: nil)
@impl true
def mount(socket) do
+ addresses = Helpers.get_aligned_contracts_addresses()
+
{:ok,
assign(socket,
- service_manager_address:
- AlignedLayerServiceManager.get_aligned_layer_service_manager_address(),
- batcher_payment_service_address:
- BatcherPaymentServiceManager.get_batcher_payment_service_address(),
- network: System.get_env("ENVIRONMENT")
+ contracts: [
+ %{
+ contract_name: "AlignedServiceManager",
+ address: addresses["alignedLayerServiceManager"]
+ },
+ %{
+ contract_name: "BatcherPaymentService",
+ address: addresses["batcherPaymentService"]
+ },
+ %{
+ contract_name: "BlsApkRegistry",
+ address: addresses["blsApkRegistry"]
+ },
+ %{
+ contract_name: "IndexRegistry",
+ address: addresses["indexRegistry"]
+ },
+ %{
+ contract_name: "OperatorStateRetriever",
+ address: addresses["operatorStateRetriever"]
+ },
+ %{
+ contract_name: "RegistryCoordinator",
+ address: addresses["registryCoordinator"]
+ },
+ %{
+ contract_name: "StakeRegistry",
+ address: addresses["stakeRegistry"]
+ }
+ ]
)}
end
@@ -22,6 +50,7 @@ defmodule ContractsComponent do
<.card
inner_class="text-base leading-9 flex flex-wrap sm:flex-row overflow-x-auto gap-x-2"
title="Contract Addresses"
+ subtitle={"All Aligned contracts addresses on #{Helpers.get_current_network_from_host(@host)}"}
>
<.link
href="https://docs.alignedlayer.com/guides/6_contract_addresses"
@@ -29,32 +58,36 @@ defmodule ContractsComponent do
target="_blank"
rel="noopener noreferrer"
>
- View All <.icon name="hero-arrow-top-right-on-square-solid" class="size-3.5 mb-1" />
+ See more <.icon name="hero-arrow-top-right-on-square-solid" class="size-3.5 mb-1" />
-
- <.icon name="hero-cpu-chip" class="size-4 mb-0.5" /> Service Manager:
-
- <.a
- href={"#{Helpers.get_etherescan_url()}/address/#{@service_manager_address}"}
- class="hover:text-foreground/80"
- target="_blank"
- rel="noopener noreferrer"
- >
- <%= @service_manager_address %>
-
-
- <.icon name="hero-wallet" class="size-4 mb-0.5" /> Batcher Payment Service:
-
- <.a
- href={"#{Helpers.get_etherescan_url()}/address/#{@batcher_payment_service_address}"}
- class="hover:text-foreground/80"
- target="_blank"
- rel="noopener noreferrer"
- >
- <%= @batcher_payment_service_address %>
-
+
+ <%= for %{contract_name: contract_name, address: address} <- @contracts do %>
+ <.contract contract_name={contract_name} address={address} />
+ <% end %>
+
"""
end
+
+ attr(:contract_name, :string)
+ attr(:address, :string)
+
+ def contract(assigns) do
+ ~H"""
+
+
+ <%= @contract_name %>
+
+ <.a
+ href={"#{Helpers.get_etherescan_url()}/address/#{@address}"}
+ class="hover:text-foreground/80"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <%= @address %>
+
+
+ """
+ end
end
diff --git a/explorer/lib/explorer_web/components/core_components.ex b/explorer/lib/explorer_web/components/core_components.ex
index b77c5034b..e9b93e9c8 100644
--- a/explorer/lib/explorer_web/components/core_components.ex
+++ b/explorer/lib/explorer_web/components/core_components.ex
@@ -305,19 +305,23 @@ defmodule ExplorerWeb.CoreComponents do
"""
attr(:class, :string, default: nil)
attr(:title, :string, default: nil)
+ attr(:subtitle, :string, default: nil)
attr(:inner_class, :string, default: nil)
-
+ attr(:header_container_class, :string, default: nil)
slot(:inner_block, default: nil)
def card(assigns) do
~H"""
- <.card_background class={@class}>
-
- <%= @title %>
-
-
+ <.card_background class={classes(["px-10 py-8", @class])}>
+
+
+ <%= @title %>
+
+
<%= @subtitle %>
+
+
<%= render_slot(@inner_block) %>
-
+
"""
end
@@ -522,13 +526,26 @@ defmodule ExplorerWeb.CoreComponents do
classes([
"px-3 py-1 rounded-full font-semibold relative group",
case @variant do
- "accent" -> "color-accent text-accent-foreground bg-accent group-hover:bg-accent/80"
- "primary" -> "color-primary text-primary-foreground bg-primary group-hover:bg-primary/80"
- "secondary" -> "color-secondary text-secondary-foreground bg-secondary group-hover:bg-secondary/80"
- "destructive" -> "color-destructive text-destructive-foreground bg-destructive group-hover:bg-destructive/80"
- "foreground" -> "color-foreground text-background bg-foreground group-hover:bg-foreground/80"
- "card" -> "color-card text-card-foreground bg-card group-hover:bg-card/80"
- _ -> "color-accent text-accent-foreground bg-accent group-hover:bg-accent/80"
+ "accent" ->
+ "color-accent text-accent-foreground bg-accent group-hover:bg-accent/80"
+
+ "primary" ->
+ "color-primary text-primary-foreground bg-primary group-hover:bg-primary/80"
+
+ "secondary" ->
+ "color-secondary text-secondary-foreground bg-secondary group-hover:bg-secondary/80"
+
+ "destructive" ->
+ "color-destructive text-destructive-foreground bg-destructive group-hover:bg-destructive/80"
+
+ "foreground" ->
+ "color-foreground text-background bg-foreground group-hover:bg-foreground/80"
+
+ "card" ->
+ "color-card text-card-foreground bg-card group-hover:bg-card/80"
+
+ _ ->
+ "color-accent text-accent-foreground bg-accent group-hover:bg-accent/80"
end,
@class
])
@@ -541,13 +558,16 @@ defmodule ExplorerWeb.CoreComponents do
>
<%= for {value, on_click} <- @options do %>
-