Navigation
Pages
Home
Docs
Components
Dashboards
Blocks
Typeset
Create
Getting Started
Введение
Установка
Навыки
интерфейс командной строки
dev.py
JavaScript
Журнал изменений
llms.txt
Utilities
Прокрутка с исчезновением
Уценка
мерцание
Resources
Государственное устройство
Диаграмма
Набор текста
События и действия
Состояние клиента Var
Тематика
Charts
Гистограмма
Диаграмма площади
Линейный график
Components
Аватар
Аккордеон
Вкладки
Вложение
Вход
Выбирать
Выключатель
Группа ввода
Группа кнопок
Диалог
Значок
Карта
Кнопка
Маркер
Меню
Поле
Пузырь
Рамка
Сепаратор
Сообщение
Спиннер
Стол
Текстовая область
Типография
Флажок
Blocks
Дашборды-Дашборды
Дашборды
Filter Blocks
Area Charts
Filled line charts for showing trends over time.
Actual costs vs. potential costs
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.
$540,690
Actual costs
$422,300
Potential costs
-21.9%
Potential savings (%)
$118,390
Potential savings ($)
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Actual costs": 42340, "Potential costs": 32330},
{"date": "Feb 23", "Actual costs": 50120, "Potential costs": 40100},
{"date": "Mar 23", "Actual costs": 45190, "Potential costs": 38240},
{"date": "Apr 23", "Actual costs": 56420, "Potential costs": 31200},
{"date": "May 23", "Actual costs": 40420, "Potential costs": 34900},
{"date": "Jun 23", "Actual costs": 47010, "Potential costs": 36800},
{"date": "Jul 23", "Actual costs": 47490, "Potential costs": 34560},
{"date": "Aug 23", "Actual costs": 39610, "Potential costs": 31260},
{"date": "Sep 23", "Actual costs": 45860, "Potential costs": 29240},
{"date": "Oct 23", "Actual costs": 50910, "Potential costs": 31220},
{"date": "Nov 23", "Actual costs": 49190, "Potential costs": 33020},
{"date": "Dec 23", "Actual costs": 55190, "Potential costs": 36090},
]
summary = [
("Actual costs", "$540,690", "chart-1"),
("Potential costs", "$422,300", "chart-2"),
("Potential savings (%)", "-21.9%", None),
("Potential savings ($)", "$118,390", None),
]
def _gradient(id_: str, color: str) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(stop_color=f"var(--{color})", offset="5%", stop_opacity=0.2),
rx.el.svg.stop(stop_color=f"var(--{color})", offset="95%", stop_opacity=0.2),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _area(data_key: str, color: str) -> rx.Component:
return rx.recharts.area(
data_key=data_key,
fill=f"url(#{data_key.replace(' ', '_')})",
stroke=f"var(--{color})",
stroke_width=2,
is_animation_active=False,
active_dot={"fill": f"var(--{color})", "stroke": f"var(--{color})"},
dot=False,
)
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.area_chart(
rx.el.svg.defs(
_gradient("Actual_costs", "chart-1"),
_gradient("Potential_costs", "chart-2"),
),
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
rx.recharts.y_axis(
width=40,
tick_line=False,
axis_line=False,
hide=not show_y_axis,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
_area("Actual costs", "chart-1"),
_area("Potential costs", "chart-2"),
data=data,
width="100%",
height=288,
)
def area_chart_01():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Actual costs vs. potential costs",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.ul(
*[
rx.el.li(
rx.el.div(
rx.el.span(
class_name=f"w-1 h-6 shrink-0 rounded-sm bg-{color}"
)
if color
else None,
rx.el.p(total, class_name="text-lg font-semibold"),
class_name="flex items-center gap-2",
),
rx.el.p(
category,
class_name="text-sm text-muted-foreground "
+ ("pl-3" if color else ""),
),
)
for category, total, color in summary
],
class_name="grid grid-cols-2 lg:grid-cols-4 gap-6 mb-8",
),
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Follower metrics
More power? Upgrade to get more insights.
- Organic3,273
- Sponsored120
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Organic": 232, "Sponsored": 0},
{"date": "Feb 23", "Organic": 241, "Sponsored": 0},
{"date": "Mar 23", "Organic": 291, "Sponsored": 0},
{"date": "Apr 23", "Organic": 101, "Sponsored": 0},
{"date": "May 23", "Organic": 318, "Sponsored": 0},
{"date": "Jun 23", "Organic": 205, "Sponsored": 0},
{"date": "Jul 23", "Organic": 372, "Sponsored": 0},
{"date": "Aug 23", "Organic": 341, "Sponsored": 0},
{"date": "Sep 23", "Organic": 387, "Sponsored": 120},
{"date": "Oct 23", "Organic": 220, "Sponsored": 0},
{"date": "Nov 23", "Organic": 372, "Sponsored": 0},
{"date": "Dec 23", "Organic": 321, "Sponsored": 0},
]
summary = [
("Organic", "3,273", "chart-1"),
("Sponsored", "120", "chart-2"),
]
def _gradient(id_: str, color: str) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(stop_color=f"var(--{color})", offset="5%", stop_opacity=0.2),
rx.el.svg.stop(stop_color=f"var(--{color})", offset="95%", stop_opacity=0.2),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _area(data_key: str, color: str) -> rx.Component:
return rx.recharts.area(
data_key=data_key,
fill=f"url(#{data_key})",
stroke=f"var(--{color})",
stroke_width=2,
is_animation_active=False,
dot=False,
active_dot={"fill": f"var(--{color})", "stroke": f"var(--{color})"},
)
def _chart(show_y_axis: bool, start_end_only: bool = False) -> rx.Component:
return rx.recharts.area_chart(
rx.el.svg.defs(
_gradient("Organic", "chart-1"),
_gradient("Sponsored", "chart-2"),
),
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick_count=2 if start_end_only else None,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
rx.recharts.y_axis(
width=30,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
_area("Organic", "chart-1"),
_area("Sponsored", "chart-2"),
data=data,
width="100%",
height=220,
)
def area_chart_02():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Follower metrics",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"More power? Upgrade to get more insights.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.div(
_chart(show_y_axis=False, start_end_only=True), class_name="sm:hidden"
),
rx.el.div(
_chart(show_y_axis=True, start_end_only=False),
class_name="hidden sm:block",
),
rx.el.ul(
*[
rx.el.li(
rx.el.div(
rx.el.span(class_name=f"h-0.5 w-3 bg-{color}"),
rx.el.span(label, class_name="text-sm"),
class_name="flex items-center gap-2",
),
rx.el.span(value, class_name="text-sm font-medium"),
class_name="flex items-center justify-between py-2 border-b border-input last:border-0",
)
for label, value, color in summary
],
class_name="mt-4 w-full",
),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Bar Charts
Categorical comparisons across groups.
Общая статистика
Колесо.ру
Текущий год
import reflex as rx
from reflex.experimental import ClientStateVar
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
from components.ui.checkbox import checkbox
from components.ui.field import field
ShowComparison = ClientStateVar.create("show_comparison", False)
data = [
{"date": "Jan 23", "Текущий год": 68560, "Прошлый год": 28560},
{"date": "Feb 23", "Текущий год": 70320, "Прошлый год": 30320},
{"date": "Mar 23", "Текущий год": 80233, "Прошлый год": 70233},
{"date": "Apr 23", "Текущий год": 55123, "Прошлый год": 45123},
{"date": "May 23", "Текущий год": 56000, "Прошлый год": 80600},
{"date": "Jun 23", "Текущий год": 100000, "Прошлый год": 85390},
{"date": "Jul 23", "Текущий год": 85390, "Прошлый год": 45340},
{"date": "Aug 23", "Текущий год": 80100, "Прошлый год": 70120},
{"date": "Sep 23", "Текущий год": 75090, "Прошлый год": 69450},
{"date": "Oct 23", "Текущий год": 71080, "Прошлый год": 63345},
{"date": "Nov 23", "Текущий год": 61210, "Прошлый год": 100330},
{"date": "Dec 23", "Текущий год": 60143, "Прошлый год": 45321},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=55,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="Текущий год",
fill="var(--chart-1)",
is_animation_active=False,
max_bar_size=40,
),
rx.cond(
ShowComparison.value,
rx.recharts.bar(
data_key="Прошлый год",
fill="var(--chart-2)",
is_animation_active=False,
max_bar_size=40,
),
),
data=data,
width="100%",
height=250,
)
def bar_chart_01():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Общая статистика",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Колесо.ру",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.cond(
ShowComparison.value,
rx.el.div(
rx.el.div(class_name="w-3 h-3 bg-chart-2"),
"Предыдущий год",
class_name="text-sm flex flex-row gap-x-2 items-center",
),
),
rx.el.div(
rx.el.div(class_name="w-3 h-3 bg-chart-1"),
"Текущий год",
class_name="text-sm flex flex-row gap-x-2 items-center",
),
class_name="flex flex-row gap-x-2 justify-end items-center",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
rx.el.div(
field.root(
checkbox.root(
checkbox.indicator(),
id="terms-checkbox-basic",
on_click=ShowComparison.set_value(~ShowComparison.value),
),
field.label(
"Тот же период за прошлый год",
html_for="terms-checkbox-basic",
),
orientation="horizontal",
),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Обзор статистики
Общие данные.
Текущий год
$0.8M
+16%Прошлый год
$0.7M
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Текущий год": 68560, "Прошлый год": 28560},
{"date": "Feb 23", "Текущий год": 70320, "Прошлый год": 30320},
{"date": "Mar 23", "Текущий год": 80233, "Прошлый год": 70233},
{"date": "Apr 23", "Текущий год": 55123, "Прошлый год": 45123},
{"date": "May 23", "Текущий год": 56000, "Прошлый год": 80600},
{"date": "Jun 23", "Текущий год": 100000, "Прошлый год": 85390},
{"date": "Jul 23", "Текущий год": 85390, "Прошлый год": 45340},
{"date": "Aug 23", "Текущий год": 80100, "Прошлый год": 70120},
{"date": "Sep 23", "Текущий год": 75090, "Прошлый год": 69450},
{"date": "Oct 23", "Текущий год": 71080, "Прошлый год": 63345},
{"date": "Nov 23", "Текущий год": 61210, "Прошлый год": 100330},
{"date": "Dec 23", "Текущий год": 60143, "Прошлый год": 45321},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=50,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="Прошлый год",
fill="var(--chart-2)",
is_animation_active=False,
),
rx.recharts.bar(
data_key="Текущий год",
fill="var(--chart-1)",
is_animation_active=False,
),
data=data,
width="100%",
height=300,
)
def bar_chart_02():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Обзор статистики",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Общие данные.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.ul(
rx.el.li(
rx.el.div(
rx.el.div(class_name="w-3 h-3 rounded-[3px] bg-chart-1"),
rx.el.p("Текущий год", class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
rx.el.div(
rx.el.p("$0.8M", class_name="mt-0.5 text-base font-semibold"),
rx.el.span(
"+16%",
class_name="rounded px-1.5 py-0.5 text-xs font-medium bg-muted text-muted-foreground",
),
class_name="flex items-center gap-1.5",
),
),
rx.el.li(
rx.el.div(
rx.el.div(class_name="w-3 h-3 rounded-[3px] bg-chart-2"),
rx.el.p("Прошлый год", class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
rx.el.p("$0.7M", class_name="mt-0.5 text-base font-semibold"),
),
class_name="flex gap-10",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-4",
)
Average BPM
134bpm
import reflex as rx
from reflex.experimental import ClientStateVar
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Running": 167, "Cycling": 145},
{"date": "Feb 23", "Running": 125, "Cycling": 110},
{"date": "Mar 23", "Running": 156, "Cycling": 149},
{"date": "Apr 23", "Running": 165, "Cycling": 112},
{"date": "May 23", "Running": 153, "Cycling": 138},
{"date": "Jun 23", "Running": 124, "Cycling": 145},
{"date": "Jul 23", "Running": 164, "Cycling": 134},
{"date": "Aug 23", "Running": 123, "Cycling": 110},
{"date": "Sep 23", "Running": 132, "Cycling": 113},
{"date": "Oct 23", "Running": 124, "Cycling": 129},
{"date": "Nov 23", "Running": 149, "Cycling": 101},
{"date": "Dec 23", "Running": 129, "Cycling": 109},
]
_running_avg = round(sum(d["Running"] for d in data) / len(data))
_cycling_avg = round(sum(d["Cycling"] for d in data) / len(data))
_overall_avg = round((_running_avg + _cycling_avg) / 2)
ShowRunning = ClientStateVar.create("show_running", True)
ShowCycling = ClientStateVar.create("show_cycling", True)
AvgBPM = ClientStateVar.create("avg_bpm", _overall_avg)
def _legend_item(label: str, color: str, is_active: ClientStateVar) -> rx.Component:
return rx.el.button(
rx.el.div(
rx.el.span(class_name=f"size-3 rounded-[3px] {color}"),
rx.el.p(label, class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
on_click=is_active.set_value(~is_active.value),
class_name=rx.cond(
is_active.value,
"text-left opacity-100 cursor-pointer",
"text-left opacity-40 cursor-pointer",
),
)
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=30,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="Running",
fill="var(--chart-2)",
is_animation_active=False,
class_name=rx.cond(ShowRunning.value, "opacity-100", "opacity-50").to(str),
),
rx.recharts.bar(
data_key="Cycling",
fill="var(--chart-3)",
is_animation_active=False,
class_name=rx.cond(ShowCycling.value, "opacity-100", "opacity-50").to(str),
),
data=data,
width="100%",
height=300,
)
def bar_chart_03():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Average BPM",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
rx.cond(
ShowRunning.value & ShowCycling.value,
str(_overall_avg),
rx.cond(
ShowRunning.value,
str(_running_avg),
rx.cond(
ShowCycling.value,
str(_cycling_avg),
"0",
),
),
),
"bpm",
class_name="text-3xl font-bold",
),
),
rx.el.ul(
_legend_item("Running", "bg-chart-2", ShowRunning),
_legend_item("Cycling", "bg-chart-3", ShowCycling),
class_name="flex gap-10 items-center justify-end",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([2, 3], "square")
+ " w-full flex flex-col gap-6",
)
Temperature
Zurich — 24 hour readout
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip_content
data = [
{"hour": "00:00", "temperature": 12.8},
{"hour": "01:00", "temperature": 12.4},
{"hour": "02:00", "temperature": 12.2},
{"hour": "03:00", "temperature": 11.9},
{"hour": "04:00", "temperature": 11.7},
{"hour": "05:00", "temperature": 11.5},
{"hour": "06:00", "temperature": 11.3},
{"hour": "07:00", "temperature": 11.2},
{"hour": "08:00", "temperature": 11.5},
{"hour": "09:00", "temperature": 12.0},
{"hour": "10:00", "temperature": 13.0},
{"hour": "11:00", "temperature": 14.2},
{"hour": "12:00", "temperature": 15.5},
{"hour": "13:00", "temperature": 16.8},
{"hour": "14:00", "temperature": 17.5},
{"hour": "15:00", "temperature": 18.1},
{"hour": "16:00", "temperature": 18.2},
{"hour": "17:00", "temperature": 17.8},
{"hour": "18:00", "temperature": 17.2},
{"hour": "19:00", "temperature": 16.5},
{"hour": "20:00", "temperature": 15.8},
{"hour": "21:00", "temperature": 14.9},
{"hour": "22:00", "temperature": 14.2},
{"hour": "23:00", "temperature": 13.5},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="hour",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
height=50,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
label={
"value": "24H Temperature Readout (Zurich)",
"position": "insideBottom",
"style": {
"fill": "var(--muted-foreground)",
"fontSize": "12px",
},
},
),
rx.recharts.y_axis(
width=20,
tick_line=False,
axis_line=False,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="temperature",
fill="var(--chart-1)",
is_animation_active=False,
),
data=data,
width="100%",
height=320,
)
def bar_chart_04():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Temperature",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Zurich — 24 hour readout",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1], "square") + " w-full flex flex-col gap-6",
)
KPI Cards
Compact metric summaries for dashboards.
Прирост активных пользвователей+1.3%
996
Звонков за месяц+9.1%
1,672
Динамика пользователей-4.8%
5.1%
Изменение+2.7%
82.4%
import reflex as rx
from components.ui.card import card
data = [
{
"name": "Прирост активных пользвователей",
"stat": "996",
"change": "+1.3%",
"color": "bg-chart-1",
},
{
"name": "Звонков за месяц",
"stat": "1,672",
"change": "+9.1%",
"color": "bg-chart-2",
},
{
"name": "Динамика пользователей",
"stat": "5.1%",
"change": "-4.8%",
"color": "bg-chart-3",
},
{
"name": "Изменение",
"stat": "82.4%",
"change": "+2.7%",
"color": "bg-chart-4",
},
]
def _kpi_card(name: str, stat: str, change: str, color: str) -> rx.Component:
is_positive = change.startswith("+")
return card.root(
rx.el.div(
rx.el.div(class_name=f"w-1 shrink-0 rounded {color}"),
rx.el.div(
rx.el.span(name, class_name="truncate text-sm text-muted-foreground"),
rx.el.span(
change,
class_name="text-sm font-medium "
+ ("text-emerald-600" if is_positive else "text-red-600"),
),
class_name="flex w-full items-center justify-between gap-3 truncate",
),
class_name="flex gap-3 px-4",
),
rx.el.div(
rx.el.p(stat, class_name="text-3xl font-semibold"),
class_name="mt-2 pl-4",
),
size="sm",
class_name="w-full border border-input/80 rounded-2xl !ring-0",
)
def kpi_card_01():
return rx.el.dl(
*[_kpi_card(**item) for item in data],
class_name="w-full grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4",
)
Пользователей за месяц
42512-month avg
Количество звонков
85812-month avg
Изменение
2.9%12-month avg
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
from components.ui.card import card
data = [
{"date": "Jan 23", "users": 234, "sessions": 1432, "churn": 5.2},
{"date": "Feb 23", "users": 431, "sessions": 1032, "churn": 4.3},
{"date": "Mar 23", "users": 543, "sessions": 1089, "churn": 5.1},
{"date": "Apr 23", "users": 489, "sessions": 988, "churn": 5.4},
{"date": "May 23", "users": 391, "sessions": 642, "churn": 5.5},
{"date": "Jun 23", "users": 582, "sessions": 786, "churn": 4.8},
{"date": "Jul 23", "users": 482, "sessions": 673, "churn": 4.5},
{"date": "Aug 23", "users": 389, "sessions": 761, "churn": 0},
{"date": "Sep 23", "users": 521, "sessions": 793, "churn": 0},
{"date": "Oct 23", "users": 434, "sessions": 543, "churn": 0},
{"date": "Nov 23", "users": 332, "sessions": 678, "churn": 0},
{"date": "Dec 23", "users": 275, "sessions": 873, "churn": 0},
]
categories = [
{
"name": "Пользователей за месяц",
"key": "users",
"color": 1,
"avg": round(sum(d["users"] for d in data) / len(data)),
"suffix": "",
},
{
"name": "Количество звонков",
"key": "sessions",
"color": 2,
"avg": round(sum(d["sessions"] for d in data) / len(data)),
"suffix": "",
},
{
"name": "Изменение",
"key": "churn",
"color": 3,
"avg": round(sum(d["churn"] for d in data) / len(data), 1),
"suffix": "%",
},
]
def _gradient(id_: str, color: int) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(
stop_color=f"var(--chart-{color})", offset="5%", stop_opacity=0.2
),
rx.el.svg.stop(
stop_color=f"var(--chart-{color})", offset="95%", stop_opacity=0.2
),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _mini_chart(key: str, color: int) -> rx.Component:
return rx.recharts.area_chart(
chart_tooltip(label="hide"),
rx.el.svg.defs(_gradient(key, color)),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick={
"fill": "var(--foreground)",
"fontSize": 10,
},
),
rx.recharts.y_axis(hide=True),
rx.recharts.area(
data_key=key,
fill=f"url(#{key})",
stroke=f"var(--chart-{color})",
stroke_width=2,
dot=False,
is_animation_active=False,
active_dot={"fill": "var(--chart-1)", "stroke": f"var(--chart-{color})"},
),
data=data,
width="100%",
height=100,
)
def _kpi_mini_chart(
name: str, key: str, color: int, avg: float, suffix: str
) -> rx.Component:
return card.root(
rx.el.div(
rx.el.p(name, class_name="text-sm text-muted-foreground"),
rx.el.div(
rx.el.span(
f"{avg:,}{suffix}",
class_name="text-xl font-semibold",
),
rx.el.span(
"12-month avg",
class_name="text-sm text-muted-foreground",
),
class_name="mt-1 flex items-baseline justify-between gap-2",
),
_mini_chart(key, color),
),
size="sm",
class_name=chart_tooltip_content([color], "square")
+ " w-full px-4 pt-4 pb-0 border border-input/80 rounded-2xl !ring-0",
)
def kpi_card_02():
return rx.el.dl(
*[_kpi_mini_chart(**item) for item in categories],
class_name="w-full grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",
)