Advanced web UI components for APEX framework applications. Build production-ready monitoring dashboards, interactive visualizations, and terminal-themed interfaces with Phoenix LiveView.
- 🎯 Advanced Chart Widgets: Multi-format chart support (line, bar, pie, area) with real-time updates
- 🖥️ Terminal Theme Components: Consistent green-on-dark terminal aesthetic
- ⚡ LiveView Components: Production-ready widgets and layouts optimized for real-time updates
- 🎮 3D Visualizations: Three.js integration for interactive cluster topology displays
- 📡 Real-time Updates: WebSocket-based live data streaming with configurable intervals
- 📱 Responsive Design: Mobile-friendly and adaptive layouts
- đź§© Modular Architecture: Mix-and-match component system with optional dependencies
Add apex_ui
to your list of dependencies in mix.exs
:
def deps do
[
{:apex_ui, "~> 0.0.1"}
]
end
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
import ApexUI.Components.Widgets.ChartWidget
def render(assigns) do
~H"""
<div class="grid grid-cols-2 gap-4 p-4">
<.live_component
module={ApexUI.Components.Widgets.ChartWidget}
id="system-metrics"
chart_type={:line}
data={@metrics_data}
title="System Metrics"
real_time={true}
height={400}
/>
<.live_component
module={ApexUI.Components.Widgets.ChartWidget}
id="process-distribution"
chart_type={:pie}
data={@process_data}
title="Process Distribution"
color_scheme={:multi}
/>
</div>
"""
end
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(1000, self(), :update_metrics)
end
socket =
socket
|> assign(:metrics_data, get_metrics_data())
|> assign(:process_data, get_process_data())
{:ok, socket}
end
def handle_info(:update_metrics, socket) do
socket =
socket
|> assign(:metrics_data, get_metrics_data())
|> assign(:process_data, get_process_data())
{:noreply, socket}
end
defp get_metrics_data do
# Your data fetching logic
[
%{x: 1, y: :rand.uniform(100)},
%{x: 2, y: :rand.uniform(100)},
%{x: 3, y: :rand.uniform(100)}
]
end
defp get_process_data do
[
%{value: 30, label: "GenServers"},
%{value: 20, label: "Supervisors"},
%{value: 15, label: "Tasks"}
]
end
end
Add to your assets/js/app.js
:
import {ApexUIHooks} from "../deps/apex_ui/assets/js/hooks"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: ApexUIHooks,
params: {_csrf_token: csrfToken}
})
For custom styling, add to your assets/css/app.css
:
@import "../deps/apex_ui/assets/css/apex_ui.css";
Multi-format chart component with real-time capabilities:
<.live_component
module={ApexUI.Components.Widgets.ChartWidget}
id="my-chart"
chart_type={:line} # :line, :bar, :pie, :area
data={@chart_data}
title="My Chart"
real_time={true}
color_scheme={:green} # :green, :blue, :multi
height={300}
show_grid={true}
show_legend={true}
/>
Supported Data Formats:
# Line/Bar/Area charts
[
%{x: 1, y: 10, timestamp: ~U[2024-01-01 12:00:00Z]},
%{x: 2, y: 15, timestamp: ~U[2024-01-01 12:01:00Z]}
]
# Pie charts
[
%{value: 30, label: "Category A"},
%{value: 20, label: "Category B"}
]
Real-time system monitoring with multiple metrics:
<.live_component
module={ApexUI.Components.Widgets.SystemMetricsWidget}
id="system-metrics"
data={@system_data}
refresh_interval={1000}
show_alerts={true}
/>
Interactive process management interface:
<.live_component
module={ApexUI.Components.Widgets.ProcessListWidget}
id="process-list"
processes={@processes}
filterable={true}
sortable={true}
actions={[:inspect, :kill, :trace]}
/>
Flexible terminal-themed panel system:
<.live_component
module={ApexUI.Components.Layout.TerminalPanelLayout}
id="dashboard-layout"
layout_type={:grid} # :grid, :split, :tabs
panels={@dashboard_panels}
/>
Status bar with real-time metrics:
<.live_component
module={ApexUI.Components.Terminal.TerminalStatusBar}
id="status-bar"
metrics={@status_metrics}
navigation_links={@nav_links}
/>
Complete system monitoring dashboard:
# In your router
live "/dashboard", ApexUI.Live.SystemDashboardLive, :index
3D cluster topology visualization:
# In your router
live "/cluster/viz", ApexUI.Live.ClusterVisualizationLive, :index
Arsenal command center interface:
# In your router (requires arsenal)
live "/arsenal", ApexUI.Live.ArsenalLive, :index
Add to your config/config.exs
:
config :apex_ui,
theme: :terminal_green, # :terminal_green, :terminal_blue, :dark
real_time_updates: true,
chart_defaults: %{
height: 300,
width: 400,
animated: true,
color_scheme: :green,
show_grid: true,
show_legend: true
},
terminal_defaults: %{
background: "bg-gray-900",
text_color: "text-green-400",
border_color: "border-green-500/30",
accent_color: "text-green-300"
}
Override default colors by setting CSS variables:
:root {
--apex-ui-primary: #10b981; /* Green */
--apex-ui-secondary: #064e3b; /* Dark green */
--apex-ui-background: #111827; /* Dark gray */
--apex-ui-surface: #1f2937; /* Medium gray */
--apex-ui-text: #d1fae5; /* Light green */
}
Include ApexUI components in your Tailwind config:
// tailwind.config.js
module.exports = {
content: [
"./lib/**/*.ex",
"./lib/**/*.heex",
"../deps/apex_ui/lib/**/*.ex"
],
theme: {
extend: {
colors: {
'apex-green': {
50: '#ecfdf5',
500: '#10b981',
900: '#064e3b'
}
}
}
}
}
ApexUI components automatically subscribe to real-time updates:
# In your LiveView
def mount(_params, _session, socket) do
if connected?(socket) do
# Subscribe to system metrics
Phoenix.PubSub.subscribe(MyApp.PubSub, "system_metrics")
# Subscribe to process updates
Phoenix.PubSub.subscribe(MyApp.PubSub, "process_updates")
end
{:ok, socket}
end
def handle_info({:system_metrics, data}, socket) do
{:noreply, assign(socket, :metrics_data, data)}
end
# From your application
Phoenix.PubSub.broadcast(MyApp.PubSub, "system_metrics", {
:system_metrics,
%{cpu: 45.2, memory: 67.8, timestamp: DateTime.utc_now()}
})
Extend ApexUI with custom JavaScript hooks:
// assets/js/custom_hooks.js
import {ApexUIHooks} from "../deps/apex_ui/assets/js/hooks"
const CustomHooks = {
...ApexUIHooks,
MyCustomWidget: {
mounted() {
// Custom widget logic
},
updated() {
// Handle updates
}
}
}
export default CustomHooks
For cluster visualization, ensure Three.js is available:
cd assets && npm install three
// assets/js/app.js
import * as THREE from 'three'
window.THREE = THREE
Create custom data adapters for your specific use case:
defmodule MyApp.MetricsAdapter do
@behaviour ApexUI.DataAdapter
def format_chart_data(raw_data, chart_type) do
case chart_type do
:line ->
Enum.map(raw_data, fn point ->
%{x: point.timestamp, y: point.value}
end)
:pie ->
Enum.map(raw_data, fn {label, value} ->
%{label: label, value: value}
end)
end
end
end
Extend existing components:
defmodule MyApp.CustomChartWidget do
use ApexUI.Components.Widgets.ChartWidget
# Override or extend behavior
def handle_event("custom_action", params, socket) do
# Custom event handling
{:noreply, socket}
end
end
defmodule MyAppWeb.MonitoringDashboard do
use MyAppWeb, :live_view
def render(assigns) do
~H"""
<div class="min-h-screen bg-gray-900 text-green-400">
<!-- Status Bar -->
<.live_component
module={ApexUI.Components.Terminal.TerminalStatusBar}
id="status-bar"
metrics={@status_metrics}
/>
<!-- Main Dashboard -->
<div class="grid grid-cols-12 gap-4 p-4">
<!-- System Metrics Chart -->
<div class="col-span-8">
<.live_component
module={ApexUI.Components.Widgets.ChartWidget}
id="system-chart"
chart_type={:line}
data={@system_metrics}
title="System Performance"
real_time={true}
height={400}
/>
</div>
<!-- Process Distribution -->
<div class="col-span-4">
<.live_component
module={ApexUI.Components.Widgets.ChartWidget}
id="process-pie"
chart_type={:pie}
data={@process_distribution}
title="Process Types"
color_scheme={:multi}
/>
</div>
<!-- Process List -->
<div class="col-span-12">
<.live_component
module={ApexUI.Components.Widgets.ProcessListWidget}
id="process-list"
processes={@processes}
filterable={true}
actions={[:inspect, :kill]}
/>
</div>
</div>
</div>
"""
end
end
defmodule MyApp.MetricsCollector do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def init(state) do
schedule_collection()
{:ok, state}
end
def handle_info(:collect_metrics, state) do
metrics = %{
cpu: get_cpu_usage(),
memory: get_memory_usage(),
process_count: get_process_count(),
timestamp: DateTime.utc_now()
}
Phoenix.PubSub.broadcast(MyApp.PubSub, "system_metrics", {
:system_metrics,
metrics
})
schedule_collection()
{:noreply, state}
end
defp schedule_collection do
Process.send_after(self(), :collect_metrics, 1000)
end
defp get_cpu_usage, do: :cpu_sup.avg1() / 256 * 100
defp get_memory_usage, do: :memsup.get_memory_data()
defp get_process_count, do: length(Process.list())
end
defmodule ApexUI.Components.Widgets.ChartWidgetTest do
use ExUnit.Case, async: true
import Phoenix.LiveViewTest
test "renders chart with data" do
data = [%{x: 1, y: 10}, %{x: 2, y: 20}]
html = render_component(ApexUI.Components.Widgets.ChartWidget, %{
id: "test-chart",
chart_type: :line,
data: data,
title: "Test Chart"
})
assert html =~ "Test Chart"
assert html =~ "(2 points)"
end
end
defmodule MyAppWeb.DashboardLiveTest do
use MyAppWeb.ConnCase
import Phoenix.LiveViewTest
test "displays real-time metrics", %{conn: conn} do
{:ok, view, html} = live(conn, "/dashboard")
assert html =~ "System Metrics"
# Simulate metric update
send(view.pid, {:system_metrics, %{cpu: 50.0, memory: 75.0}})
assert render(view) =~ "50.0"
end
end
- Limit data points: Use
max_data_points
to prevent memory issues - Batch updates: Collect multiple metrics before broadcasting
- Use Process.send_after: Avoid blocking operations in LiveView processes
- Debounce rapid updates: Implement client-side debouncing for high-frequency data
# Limit chart data to prevent memory growth
def handle_info({:metrics_update, new_data}, socket) do
current_data = socket.assigns.chart_data
updated_data =
(current_data ++ [new_data])
|> Enum.take(-100) # Keep only last 100 points
{:noreply, assign(socket, :chart_data, updated_data)}
end
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
MIT License - see LICENSE for details.