Commit abb7fbac authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat: add DropdownMenu component + remove global audio player in admin

parent d60498c1
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
import {
  VmAudio,
  VmCaptions,
  VmClickToPlay,
  VmControl,
  VmControls,
  VmCurrentTime,
  VmDefaultControls,
  VmDefaultSettings,
  VmDefaultUi,
  VmEndTime,
  VmFile,
  VmIcon,
  VmIconLibrary,
  VmLoadingScreen,
  VmMenu,
  VmMenuItem,
  VmMenuRadio,
  VmMenuRadioGroup,
  VmMuteControl,
  VmPlaybackControl,
  VmPlayer,
  VmScrubberControl,
  VmSettings,
  VmSettingsControl,
  VmSkeleton,
  VmSlider,
  VmSubmenu,
  VmTime,
  VmTimeProgress,
  VmTooltip,
  VmUi,
  VmVolumeControl,
} from "@vime/core";
import "@vime/core/themes/default.css";
import "@vime/core/themes/light.css";
import "./modules/play-episode-button";

// Register Castopod's icons library
const library: HTMLVmIconLibraryElement | null = document.querySelector(
  'vm-icon-library[name="castopod-icons"]'
);
if (library) {
  library.resolver = (iconName) => `/assets/icons/${iconName}.svg`;
}

// Vime elements for audio player
customElements.define("vm-player", VmPlayer);
customElements.define("vm-file", VmFile);
customElements.define("vm-audio", VmAudio);
customElements.define("vm-ui", VmUi);
customElements.define("vm-default-ui", VmDefaultUi);
customElements.define("vm-click-to-play", VmClickToPlay);
customElements.define("vm-captions", VmCaptions);
customElements.define("vm-loading-screen", VmLoadingScreen);
customElements.define("vm-default-controls", VmDefaultControls);
customElements.define("vm-default-settings", VmDefaultSettings);
customElements.define("vm-controls", VmControls);
customElements.define("vm-playback-control", VmPlaybackControl);
customElements.define("vm-volume-control", VmVolumeControl);
customElements.define("vm-scrubber-control", VmScrubberControl);
customElements.define("vm-current-time", VmCurrentTime);
customElements.define("vm-end-time", VmEndTime);
customElements.define("vm-settings-control", VmSettingsControl);
customElements.define("vm-time-progress", VmTimeProgress);
customElements.define("vm-control", VmControl);
customElements.define("vm-icon", VmIcon);
customElements.define("vm-icon-library", VmIconLibrary);
customElements.define("vm-tooltip", VmTooltip);
customElements.define("vm-mute-control", VmMuteControl);
customElements.define("vm-slider", VmSlider);
customElements.define("vm-time", VmTime);
customElements.define("vm-menu", VmMenu);
customElements.define("vm-menu-item", VmMenuItem);
customElements.define("vm-submenu", VmSubmenu);
customElements.define("vm-menu-radio-group", VmMenuRadioGroup);
customElements.define("vm-menu-radio", VmMenuRadio);
customElements.define("vm-settings", VmSettings);
customElements.define("vm-skeleton", VmSkeleton);
+51 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace App\Views\Components;

use Exception;
use ViewComponents\Component;

class DropdownMenu extends Component
{
    public string $id = '';

    public array $items = [];

    public function setItems(string $value): void
    {
        $this->items = json_decode(html_entity_decode($value), true);
    }

    public function render(): string
    {
        if ($this->items === []) {
            throw new Exception('Dropdown menu has no items');
        }

        $menuItems = '';
        foreach ($this->items as $item) {
            switch ($item['type']) {
                case 'link':
                    $menuItems .= anchor($item['uri'], $item['title'], [
                        'class' => 'px-4 py-1 hover:bg-gray-100' . (array_key_exists('class', $item) ? ' ' . $item['class'] : ''),
                    ]);
                    break;
                case 'separator':
                    $menuItems .= '<hr class="my-2 border border-gray-100">';
                    break;
                default:
                    break;
            }
        }

        return <<<HTML
            <nav id="{$this->id}"
                class="absolute z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border-black rounded-lg border-3"
                aria-labelledby="{$this->labeledBy}"
                data-dropdown="menu"
                data-dropdown-placement="bottom-end">{$menuItems}</nav>
        HTML;
    }
}
+22 −24
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
    <?= service('vite')
        ->asset('js/admin.ts', 'js') ?>
    <?= service('vite')
        ->asset('js/audio-player.ts', 'js') ?>
        ->asset('js/admin-audio-player.ts', 'js') ?>
</head>

<body class="relative bg-pine-50 holy-grail-grid">
@@ -40,28 +40,26 @@
            data-dropdown="button"
            data-dropdown-target="my-account-dropdown-menu"
            aria-haspopup="true"
            aria-expanded="false">
                <?= icon('account-circle', 'text-2xl opacity-60 mr-2') ?>
                <?= user()
                    ->username ?>
                <?= icon('caret-down', 'ml-auto text-2xl') ?>
        </button>
        <nav
            id="my-account-dropdown-menu"
            class="absolute z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border-black rounded border-[3px]"
            aria-labelledby="my-accountDropdown"
            data-dropdown="menu"
            data-dropdown-placement="bottom-end">
                <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
                        'my-account',
                    ) ?>"><?= lang('AdminNavigation.account.my-account') ?></a>
                <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
                        'change-password',
                    ) ?>"><?= lang('AdminNavigation.account.change-password') ?></a>
                <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
                        'logout',
                    ) ?>"><?= lang('AdminNavigation.account.logout') ?></a>
        </nav>
            aria-expanded="false"><?= icon('account-circle', 'text-2xl opacity-60 mr-2') . user()->username . icon('caret-down', 'ml-auto text-2xl') ?></button>
        <DropdownMenu id="my-account-dropdown-menu" labeledBy="my-account-dropdown" items="<?= esc(json_encode([
            [
                'type' => 'link',
                'title' => lang('AdminNavigation.account.my-account'),
                'uri' => route_to('my-account'),
            ],
            [
                'type' => 'link',
                'title' => lang('AdminNavigation.account.change-password'),
                'uri' => route_to('change-password'),
            ],
            [
                'type' => 'separator',
            ],
            [
                'type' => 'link',
                'title' => lang('AdminNavigation.account.logout'),
                'uri' => route_to('logout'),
            ], ])) ?>" />
    </header>
    <aside id="admin-sidebar" class="sticky z-50 flex flex-col text-white transition duration-200 ease-in-out transform -translate-x-full border-r top-10 border-pine-900 bg-pine-800 holy-grail__sidebar md:translate-x-0">
        <?php if (isset($podcast) && isset($episode)): ?>
@@ -80,7 +78,7 @@
        </footer>
    </aside>
    <main class="relative holy-grail__main">
        <header class="z-40 flex items-center bg-white border-b sticky-header-outer border-pine-100">
        <header class="z-40 flex items-center px-4 bg-white border-b md:px-12 sticky-header-outer border-pine-100">
            <div class="container flex flex-col justify-end mx-auto -mt-4 sticky-header-inner">
                <?= render_breadcrumb('text-gray-800 text-xs items-center flex') ?>
                <div class="flex justify-between py-1">
+37 −36
Original line number Diff line number Diff line
@@ -74,44 +74,45 @@
            [
                'header' => lang('Episode.list.actions'),
                'cell' => function ($episode, $podcast) {
                    return '<button id="more-dropdown-<?= $episode->id ?>" type="button" class="inline-flex items-center p-1 outline-none focus:ring" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $episode->id ?>-menu" aria-haspopup="true" aria-expanded="false">' .
                    return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 outline-none focus:ring" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' .
                        icon('more') .
                        '</button>' .
                        '<nav id="more-dropdown-<?= $episode->id ?>-menu" class="flex flex-col py-2 text-black whitespace-no-wrap bg-white border rounded shadow" aria-labelledby="more-dropdown-<?= $episode->id ?>" data-dropdown="menu" data-dropdown-placement="bottom-start" data-dropdown-offset-x="0" data-dropdown-offset-y="-24">' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'episode-edit',
                            $podcast->id,
                            $episode->id,
                        ) . '">' . lang('Episode.edit') . '</a>' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'embeddable-player-add',
                            $podcast->id,
                            $episode->id,
                        ) . '">' . lang(
                            'Episode.embeddable_player.title',
                        ) . '</a>' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'episode-persons-manage',
                            $podcast->id,
                            $episode->id,
                        ) . '">' . lang('Person.persons') . '</a>' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'soundbites-edit',
                            $podcast->id,
                            $episode->id,
                        ) . '">' . lang('Episode.soundbites') . '</a>' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'episode',
                            $podcast->handle,
                            $episode->slug,
                        ) . '">' . lang('Episode.go_to_page') . '</a>' .
                        '<a class="px-4 py-1 hover:bg-gray-100" href="' . route_to(
                            'episode-delete',
                            $podcast->id,
                            $episode->id,
                        ) . '">' . lang('Episode.delete') . '</a>' .
                        '</nav>' .
                        '</div>';
                        '<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labeledBy="more-dropdown-' . $episode->id . '" items="' . esc(json_encode([
                            [
                                'type' => 'link',
                                'title' => lang('Episode.edit'),
                                'uri' => route_to('episode-edit', $podcast->id, $episode->id),
                            ],
                            [
                                'type' => 'link',
                                'title' => lang('Episode.embeddable_player.title'),
                                'uri' => route_to('embeddable-player-add', $podcast->id, $episode->id),
                            ],
                            [
                                'type' => 'link',
                                'title' => lang('Person.persons'),
                                'uri' => route_to('episode-persons-manage', $podcast->id, $episode->id),
                            ],
                            [
                                'type' => 'link',
                                'title' => lang('Episode.soundbites'),
                                'uri' => route_to('soundbites-edit', $podcast->id, $episode->id),
                            ],
                            [
                                'type' => 'link',
                                'title' => lang('Episode.go_to_page'),
                                'uri' => route_to('episode', $podcast->handle, $episode->slug),
                            ],
                            [
                                'type' => 'separator',
                            ],
                            [
                                'type' => 'link',
                                'title' => lang('Episode.delete'),
                                'uri' => route_to('episode-delete', $podcast->id, $episode->id),
                                'class' => 'font-semibold text-red-600',
                            ],
                        ])) . '" />';
                },
            ],
        ],
+36 −35
Original line number Diff line number Diff line
@@ -52,41 +52,42 @@
                        aria-haspopup="true"
                        aria-expanded="false"
                        ><?= icon('more') ?></button>
                    <nav
                        id="more-dropdown-<?= $episode->id ?>-menu"
                        class="z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border rounded shadow"
                        aria-labelledby="more-dropdown-<?= $episode->id ?>"
                        data-dropdown="menu"
                        data-dropdown-placement="bottom">
                            <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
    'episode-edit',
    $podcast->id,
    $episode->id,
) ?>"><?= lang('Episode.edit') ?></a>
                            <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
    'embeddable-player-add',
    $podcast->id,
    $episode->id,
) ?>"><?= lang(
    'Episode.embeddable_player.title',
) ?></a>
                            <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
    'episode-persons-manage',
    $podcast->id,
    $episode->id,
) ?>"><?= lang('Person.persons') ?></a>
                            <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
    'episode',
    $podcast->handle,
    $episode->slug,
) ?>"><?= lang('Episode.go_to_page') ?></a>
                            <hr class="my-2 border border-gray-100">
                            <a class="px-4 py-1 font-semibold text-red-600 hover:bg-gray-100" href="<?= route_to(
    'episode-delete',
    $podcast->id,
    $episode->id,
) ?>"><?= lang('Episode.delete') ?></a>
                    </nav>
                    <DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labeledBy="more-dropdown-<?= $episode->id ?>" items="<?= esc(json_encode([
                        [
                            'type' => 'link',
                            'title' => lang('Episode.edit'),
                            'uri' => route_to('episode-edit', $podcast->id, $episode->id),
                        ],
                        [
                            'type' => 'link',
                            'title' => lang('Episode.embeddable_player.title'),
                            'uri' => route_to('embeddable-player-add', $podcast->id, $episode->id),
                        ],
                        [
                            'type' => 'link',
                            'title' => lang('Person.persons'),
                            'uri' => route_to('episode-persons-manage', $podcast->id, $episode->id),
                        ],
                        [
                            'type' => 'link',
                            'title' => lang('Episode.soundbites'),
                            'uri' => route_to('soundbites-edit', $podcast->id, $episode->id),
                        ],
                        [
                            'type' => 'link',
                            'title' => lang('Episode.go_to_page'),
                            'uri' => route_to('episode', $podcast->handle, $episode->slug),
                        ],
                        [
                            'type' => 'separator',
                        ],
                        [
                            'type' => 'link',
                            'title' => lang('Episode.delete'),
                            'uri' => route_to('episode-delete', $podcast->id, $episode->id),
                            'class' => 'font-semibold text-red-600',
                        ],
                    ])) ?>" />
                </div>
            </article>
        <?php endforeach; ?>