Skip to content
Brok UI

Loading…

No results

Map Blocks

Open source

Interactive MapLibre maps — analytics overview, logistics network, geographic heatmap and live delivery tracking.

Preview

resources/views/blocks/maps-analytics.blade.php Blade
@props([
    'heading' => 'Live visitors',
    'subheading' => 'Real-time traffic across the globe',
    // Current concurrent visitors and trend delta (positive => success).
    'visitors' => 12480,
    'trend' => '+8.2%',
    // [longitude, latitude] of the analytics hub the arcs flow out from.
    'hub' => [-98.5, 39.8],
    // Top countries: label, visitor count, share % (0–100), [lng, lat], busy flag.
    'countries' => null,
    // Device split percentages (should total ~100).
    'devices' => null,
])

@php
    $countries ??= [
        ['label' => __('United States'), 'count' => 4820, 'share' => 39, 'coords' => [-95.7, 37.1], 'busy' => true],
        ['label' => __('United Kingdom'), 'count' => 1960, 'share' => 16, 'coords' => [-1.5, 52.4], 'busy' => true],
        ['label' => __('Germany'), 'count' => 1430, 'share' => 11, 'coords' => [10.4, 51.2], 'busy' => false],
        ['label' => __('Brazil'), 'count' => 1180, 'share' => 9, 'coords' => [-51.9, -14.2], 'busy' => false],
        ['label' => __('Japan'), 'count' => 980, 'share' => 8, 'coords' => [138.3, 36.2], 'busy' => false],
        ['label' => __('India'), 'count' => 870, 'share' => 7, 'coords' => [78.9, 22.6], 'busy' => false],
        ['label' => __('Australia'), 'count' => 640, 'share' => 5, 'coords' => [133.8, -25.3], 'busy' => false],
    ];

    $devices ??= [
        ['label' => __('Desktop'), 'value' => 58, 'tone' => 'default'],
        ['label' => __('Mobile'), 'value' => 34, 'tone' => 'success'],
        ['label' => __('Tablet'), 'value' => 8, 'tone' => 'warning'],
    ];

    $trendVariant = str_starts_with(trim((string) $trend), '-') ? 'destructive' : 'success';
@endphp

<x-ui.card {{ $attributes->merge(['class' => 'w-full']) }}>
    <x-ui.card.header>
        <div class="flex flex-wrap items-start justify-between gap-4">
            <div class="space-y-2">
                <div class="flex items-center gap-2">
                    <x-ui.card.title>{{ __($heading) }}</x-ui.card.title>
                    <x-ui.badge variant="secondary" class="gap-2">
                        <span class="relative flex size-2">
                            <span class="absolute inline-flex size-2 motion-safe:animate-ping rounded-full bg-success opacity-75" aria-hidden="true"></span>
                            <span class="relative inline-flex size-2 rounded-full bg-success"></span>
                        </span>
                        {{ __('Live') }}
                    </x-ui.badge>
                </div>
                <x-ui.card.description>{{ __($subheading) }}</x-ui.card.description>
            </div>

            <div class="text-end">
                <p class="text-3xl font-bold tracking-tight tabular-nums text-foreground">{{ number_format($visitors) }}</p>
                <div class="mt-2 flex items-center justify-end gap-2">
                    @if ($trend)
                        <x-ui.badge :variant="$trendVariant">{{ $trend }}</x-ui.badge>
                    @endif
                    <span class="text-xs text-muted-foreground">{{ __('vs. last hour') }}</span>
                </div>
            </div>
        </div>
    </x-ui.card.header>

    <x-ui.card.content>
        <div class="grid gap-6 lg:grid-cols-5">
            {{-- Map: world globe with a hub and arcs out to top countries. --}}
            <div class="lg:col-span-3">
                <x-ui.map
                    projection="globe"
                    :center="$hub"
                    :zoom="1.1"
                    height="420px"
                    :label="__('Live visitors by country')"
                >
                    <x-ui.map.controls position="top-right" show-zoom />

                    <x-ui.map.marker :lng="$hub[0]" :lat="$hub[1]" color="primary" pulse :label="__('Analytics hub')" />

                    @foreach ($countries as $country)
                        <x-ui.map.arc
                            :from="$hub"
                            :to="$country['coords']"
                            :color="$country['busy'] ? 'success' : 'primary'"
                            :width="$country['busy'] ? 3 : 2"
                            :curve="0.3"
                        />

                        @if ($country['busy'])
                            <x-ui.map.marker
                                :lng="$country['coords'][0]"
                                :lat="$country['coords'][1]"
                                color="success"
                                pulse
                                :label="$country['label']"
                            />
                        @endif
                    @endforeach
                </x-ui.map>
            </div>

            {{-- Breakdown: top countries + device split. --}}
            <div class="lg:col-span-2">
                <div>
                    <h3 class="text-sm font-semibold text-foreground">{{ __('Top countries') }}</h3>
                    <div class="mt-4 space-y-4">
                        @foreach ($countries as $country)
                            <div class="space-y-2">
                                <div class="flex items-center justify-between gap-4 text-sm">
                                    <span class="font-medium text-foreground">{{ $country['label'] }}</span>
                                    <span class="tabular-nums text-muted-foreground">
                                        {{ number_format($country['count']) }}
                                        <span class="ms-2 text-foreground">{{ $country['share'] }}%</span>
                                    </span>
                                </div>
                                <x-ui.progress :value="$country['share']" :label="$country['label']" />
                            </div>
                        @endforeach
                    </div>
                </div>

                <x-ui.separator class="my-6" />

                <div>
                    <h3 class="text-sm font-semibold text-foreground">{{ __('Devices') }}</h3>
                    <div class="mt-4 space-y-4">
                        @foreach ($devices as $device)
                            <div class="space-y-2">
                                <div class="flex items-center justify-between gap-4 text-sm">
                                    <span class="font-medium text-foreground">{{ $device['label'] }}</span>
                                    <span class="tabular-nums text-foreground">{{ $device['value'] }}%</span>
                                </div>
                                <x-ui.progress :value="$device['value']" :tone="$device['tone']" :label="$device['label']" />
                            </div>
                        @endforeach
                    </div>
                </div>
            </div>
        </div>
    </x-ui.card.content>
</x-ui.card>

Installation

terminal
php artisan ui:add blocks/maps-analytics

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/maps-analytics writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.0.4
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/maps-analytics.blade.php

Registry dependencies

card badge progress separator map

Package dependencies

composer: jml/brok:^0.2

npm: maplibre-gl

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

maps-analytics.md
# Brok UI block: Analytics Map (`maps-analytics`)

Real-time web-analytics overview: a live visitor count with trend, a globe of traffic arcs from a hub to top countries, and a country + device breakdown.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/maps-analytics
```

## Render it

```blade
<x-blocks.maps-analytics />
```

## Props

- `heading` (string, default `Live visitors`) — Card title shown above the live badge.
- `subheading` (string, default `Real-time traffic across the globe`) — Supporting copy under the heading.
- `visitors` (int, default `12480`) — Current concurrent visitor count, formatted with thousands separators.
- `trend` (string, default `+8.2%`) — Delta label vs. the last hour; a leading – renders the destructive badge variant.
- `hub` (array, default `[-98.5, 39.8]`) — [longitude, latitude] of the analytics hub the arcs originate from.
- `countries` (mixed|null, default `null`) — Top countries: label, count, share percentage, [lng, lat] coordinates, and a busy flag; null falls back to a sample list.
- `devices` (mixed|null, default `null`) — Device split: label, percentage value, and progress tone; null falls back to a sample list.

## Use when

- Showing real-time web-analytics traffic as a rotating globe with arcs from a hub to top visitor countries.
- Pairing the map with a live visitor count, trend badge, and a country/device breakdown panel.

## Avoid when

- Needing a flat US route map for logistics lanes or a single delivery route instead of a global analytics view; use maps-logistics or maps-delivery-tracker.

## Rules

- Render the installed block with `<x-blocks.maps-analytics />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/maps-analytics
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/maps-analytics.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Showing real-time web-analytics traffic as a rotating globe with arcs from a hub to top visitor countries.
  • Pairing the map with a live visitor count, trend badge, and a country/device breakdown panel.

Avoid when

  • Needing a flat US route map for logistics lanes or a single delivery route instead of a global analytics view; use maps-logistics or maps-delivery-tracker.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
header with live badge and visitor count globe map with hub marker and country arcs top countries list with progress bars device split list with progress bars

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.maps-analytics />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/maps-analytics.blade.php Blade
@props([
    'heading' => 'Live visitors',
    'subheading' => 'Real-time traffic across the globe',
    // Current concurrent visitors and trend delta (positive => success).
    'visitors' => 12480,
    'trend' => '+8.2%',
    // [longitude, latitude] of the analytics hub the arcs flow out from.
    'hub' => [-98.5, 39.8],
    // Top countries: label, visitor count, share % (0–100), [lng, lat], busy flag.
    'countries' => null,
    // Device split percentages (should total ~100).
    'devices' => null,
])

@php
    $countries ??= [
        ['label' => __('United States'), 'count' => 4820, 'share' => 39, 'coords' => [-95.7, 37.1], 'busy' => true],
        ['label' => __('United Kingdom'), 'count' => 1960, 'share' => 16, 'coords' => [-1.5, 52.4], 'busy' => true],
        ['label' => __('Germany'), 'count' => 1430, 'share' => 11, 'coords' => [10.4, 51.2], 'busy' => false],
        ['label' => __('Brazil'), 'count' => 1180, 'share' => 9, 'coords' => [-51.9, -14.2], 'busy' => false],
        ['label' => __('Japan'), 'count' => 980, 'share' => 8, 'coords' => [138.3, 36.2], 'busy' => false],
        ['label' => __('India'), 'count' => 870, 'share' => 7, 'coords' => [78.9, 22.6], 'busy' => false],
        ['label' => __('Australia'), 'count' => 640, 'share' => 5, 'coords' => [133.8, -25.3], 'busy' => false],
    ];

    $devices ??= [
        ['label' => __('Desktop'), 'value' => 58, 'tone' => 'default'],
        ['label' => __('Mobile'), 'value' => 34, 'tone' => 'success'],
        ['label' => __('Tablet'), 'value' => 8, 'tone' => 'warning'],
    ];

    $trendVariant = str_starts_with(trim((string) $trend), '-') ? 'destructive' : 'success';
@endphp

<x-ui.card {{ $attributes->merge(['class' => 'w-full']) }}>
    <x-ui.card.header>
        <div class="flex flex-wrap items-start justify-between gap-4">
            <div class="space-y-2">
                <div class="flex items-center gap-2">
                    <x-ui.card.title>{{ __($heading) }}</x-ui.card.title>
                    <x-ui.badge variant="secondary" class="gap-2">
                        <span class="relative flex size-2">
                            <span class="absolute inline-flex size-2 motion-safe:animate-ping rounded-full bg-success opacity-75" aria-hidden="true"></span>
                            <span class="relative inline-flex size-2 rounded-full bg-success"></span>
                        </span>
                        {{ __('Live') }}
                    </x-ui.badge>
                </div>
                <x-ui.card.description>{{ __($subheading) }}</x-ui.card.description>
            </div>

            <div class="text-end">
                <p class="text-3xl font-bold tracking-tight tabular-nums text-foreground">{{ number_format($visitors) }}</p>
                <div class="mt-2 flex items-center justify-end gap-2">
                    @if ($trend)
                        <x-ui.badge :variant="$trendVariant">{{ $trend }}</x-ui.badge>
                    @endif
                    <span class="text-xs text-muted-foreground">{{ __('vs. last hour') }}</span>
                </div>
            </div>
        </div>
    </x-ui.card.header>

    <x-ui.card.content>
        <div class="grid gap-6 lg:grid-cols-5">
            {{-- Map: world globe with a hub and arcs out to top countries. --}}
            <div class="lg:col-span-3">
                <x-ui.map
                    projection="globe"
                    :center="$hub"
                    :zoom="1.1"
                    height="420px"
                    :label="__('Live visitors by country')"
                >
                    <x-ui.map.controls position="top-right" show-zoom />

                    <x-ui.map.marker :lng="$hub[0]" :lat="$hub[1]" color="primary" pulse :label="__('Analytics hub')" />

                    @foreach ($countries as $country)
                        <x-ui.map.arc
                            :from="$hub"
                            :to="$country['coords']"
                            :color="$country['busy'] ? 'success' : 'primary'"
                            :width="$country['busy'] ? 3 : 2"
                            :curve="0.3"
                        />

                        @if ($country['busy'])
                            <x-ui.map.marker
                                :lng="$country['coords'][0]"
                                :lat="$country['coords'][1]"
                                color="success"
                                pulse
                                :label="$country['label']"
                            />
                        @endif
                    @endforeach
                </x-ui.map>
            </div>

            {{-- Breakdown: top countries + device split. --}}
            <div class="lg:col-span-2">
                <div>
                    <h3 class="text-sm font-semibold text-foreground">{{ __('Top countries') }}</h3>
                    <div class="mt-4 space-y-4">
                        @foreach ($countries as $country)
                            <div class="space-y-2">
                                <div class="flex items-center justify-between gap-4 text-sm">
                                    <span class="font-medium text-foreground">{{ $country['label'] }}</span>
                                    <span class="tabular-nums text-muted-foreground">
                                        {{ number_format($country['count']) }}
                                        <span class="ms-2 text-foreground">{{ $country['share'] }}%</span>
                                    </span>
                                </div>
                                <x-ui.progress :value="$country['share']" :label="$country['label']" />
                            </div>
                        @endforeach
                    </div>
                </div>

                <x-ui.separator class="my-6" />

                <div>
                    <h3 class="text-sm font-semibold text-foreground">{{ __('Devices') }}</h3>
                    <div class="mt-4 space-y-4">
                        @foreach ($devices as $device)
                            <div class="space-y-2">
                                <div class="flex items-center justify-between gap-4 text-sm">
                                    <span class="font-medium text-foreground">{{ $device['label'] }}</span>
                                    <span class="tabular-nums text-foreground">{{ $device['value'] }}%</span>
                                </div>
                                <x-ui.progress :value="$device['value']" :tone="$device['tone']" :label="$device['label']" />
                            </div>
                        @endforeach
                    </div>
                </div>
            </div>
        </div>
    </x-ui.card.content>
</x-ui.card>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.4
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0