feat(ui): create ViewComponents library to enable building class and view files components

- replace some helper components and forms with class components in the ui
- create viewcomponents
service and load the component function to be used in views
This commit is contained in:
Yassine Doghri 2021-08-19 14:00:14 +00:00
parent fcecbe1c68
commit 94872f2338
41 changed files with 1323 additions and 692 deletions

View File

@ -46,6 +46,7 @@ class Autoload extends AutoloadConfig
'Config' => APPPATH . 'Config',
'ActivityPub' => APPPATH . 'Libraries/ActivityPub',
'Analytics' => APPPATH . 'Libraries/Analytics',
'ViewComponents' => APPPATH . 'Libraries/ViewComponents',
];
/**
@ -84,5 +85,5 @@ class Autoload extends AutoloadConfig
* ```
* @var array<int, string>
*/
public $files = [];
public $files = [APPPATH . 'Libraries/ViewComponents/Helpers/view_components_helper.php'];
}

View File

@ -110,7 +110,9 @@ if (! function_exists('button')) {
CODE_SAMPLE;
}
}
// ------------------------------------------------------------------------
if (! function_exists('icon_button')) {
/**
* Icon Button component
@ -145,6 +147,7 @@ if (! function_exists('icon_button')) {
}
}
// ------------------------------------------------------------------------
if (! function_exists('hint_tooltip')) {
/**
* Hint component
@ -167,7 +170,9 @@ if (! function_exists('hint_tooltip')) {
return $tooltip . '">' . icon('question') . '</span>';
}
}
// ------------------------------------------------------------------------
if (! function_exists('data_table')) {
/**
* Data table component
@ -223,7 +228,9 @@ if (! function_exists('data_table')) {
'</div>';
}
}
// ------------------------------------------------------------------------
if (! function_exists('publication_pill')) {
/**
* Publication pill component
@ -250,7 +257,9 @@ if (! function_exists('publication_pill')) {
'</span>';
}
}
// ------------------------------------------------------------------------
if (! function_exists('publication_button')) {
/**
* Publication button component
@ -508,27 +517,5 @@ if (! function_exists('relative_time')) {
CODE_SAMPLE;
}
}
// ------------------------------------------------------------------------
if (! function_exists('xml_editor')) {
/**
* XML Editor field
*
* @param array<string, mixed> $customData
* @param array<string, mixed> $extra
*/
function xml_editor(array $customData = [], string $value = '', array $extra = []): string
{
$defaultData = [
'slot' => 'textarea',
'rows' => 5,
];
$data = array_merge($defaultData, $customData);
$textarea = form_textarea($data, $value, $extra);
return <<<CODE_SAMPLE
<xml-editor>{$textarea}</time-ago>
CODE_SAMPLE;
}
}
// ------------------------------------------------------------------------

View File

@ -141,38 +141,12 @@ if (! function_exists('form_label')) {
//--------------------------------------------------------------------
if (! function_exists('form_multiselect')) {
/**
* Multi-select menu
*
* @param array<string, string> $options
* @param string[] $selected
* @param array<string, string> $customExtra
*/
function form_multiselect(
string $name = '',
array $options = [],
array $selected = [],
array $customExtra = []
): string {
$defaultExtra = [
'data-class' => $customExtra['class'],
'multiple' => 'multiple',
];
$extra = array_merge($defaultExtra, $customExtra);
return form_dropdown($name, $options, $selected, $extra);
}
}
//--------------------------------------------------------------------
if (! function_exists('form_dropdown')) {
/**
* Drop-down Menu (based on html select tag)
*
* @param array<string, mixed> $options
* @param string[] $selected
* @param array<string|int> $selected
* @param array<string, mixed> $customExtra
*/
function form_dropdown(
@ -236,81 +210,3 @@ if (! function_exists('form_dropdown')) {
return $form . "</select>\n";
}
}
//--------------------------------------------------------------------
if (! function_exists('form_editor')) {
/**
* Markdown editor
*
* @param array<string, mixed> $data
* @param array<string, mixed>|string $extra
*/
function form_markdown_editor(array $data = [], string $value = '', string | array $extra = ''): string
{
$editorClass = 'w-full flex flex-col bg-white border border-gray-500 focus-within:ring-1 focus-within:ring-blue-600';
if (array_key_exists('class', $data) && $data['class'] !== '') {
$editorClass .= ' ' . $data['class'];
unset($data['class']);
}
$data['class'] = 'border-none outline-none focus:border-none focus:outline-none w-full h-full';
return '<div class="' . $editorClass . '">' .
'<header class="sticky top-0 z-20 flex flex-wrap justify-between bg-white border-b border-gray-500">' .
'<markdown-write-preview for="' . $data['id'] . '" class="relative inline-flex h-8">' .
'<button type="button" slot="write" class="px-2 font-semibold focus:outline-none focus:ring-inset focus:ring-2 focus:ring-pine-600">' . lang(
'Common.forms.editor.write'
) . '</button>' .
'<button type="button" slot="preview" class="px-2 focus:outline-none focus:ring-inset focus:ring-2 focus:ring-pine-600">' . lang(
'Common.forms.editor.preview'
) . '</button>' .
'</markdown-write-preview>' .
'<markdown-toolbar for="' . $data['id'] . '" class="flex gap-4 px-2 py-1">' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-header class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'heading'
) . '</md-header>' .
'<md-bold class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'bold'
) . '</md-bold>' .
'<md-italic class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'italic'
) . '</md-italic>' .
'</div>' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-unordered-list class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'list-unordered'
) . '</md-unordered-list>' .
'<md-ordered-list class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'list-ordered'
) . '</md-ordered-list>' .
'</div>' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-quote class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'quote'
) . '</md-quote>' .
'<md-link class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'link'
) . '</md-link>' .
'<md-image class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'image-add'
) . '</md-image>' .
'</div>' .
'</markdown-toolbar>' .
'</header>' .
'<div class="relative">' .
form_textarea($data, $value, $extra) .
'<markdown-preview for="' . $data['id'] . '" class="absolute top-0 left-0 hidden w-full h-full p-2 overflow-y-auto prose bg-gray-50" showClass="bg-white"></markdown-preview>' .
'</div>' .
'<footer class="flex px-2 py-1 bg-gray-100 border-t">' .
'<a href="https://commonmark.org/help/" class="inline-flex items-center text-xs font-semibold text-gray-500 hover:text-gray-700" target="_blank" rel="noopener noreferrer">' . icon(
'markdown',
'mr-1 text-lg text-gray-400'
) . lang('Common.forms.editor.help') . '</a>' .
'</footer>' .
'</div>';
}
}
// ------------------------------------------------------------------------

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace ViewComponents;
class Component implements ComponentInterface
{
/**
* @var array<string, string>
*/
protected array $attributes = [
'class' => '',
];
/**
* @param array<string, mixed> $properties
* @param array<string, string> $attributes
*/
public function __construct(
protected array $properties,
array $attributes
) {
// overwrite default properties if set
foreach ($properties as $key => $value) {
$this->{$key} = $value;
}
$this->attributes = array_merge($this->attributes, $attributes);
}
public function render(): string
{
return static::class . ': RENDER METHOD NOT IMPLEMENTED';
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace ViewComponents;
interface ComponentInterface
{
public function render(): string;
}

View File

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace ViewComponents;
use ViewComponents\Config\ViewComponents;
use ViewComponents\Exceptions\ComponentNotFoundException;
class ComponentLoader
{
protected ViewComponents $config;
protected string $name;
/**
* @var array<string, mixed>
*/
protected array $properties = [];
/**
* @var array<string, string>
*/
protected array $attributes = [];
public function __construct()
{
$this->config = config('ViewComponents');
}
public function __get(string $property): mixed
{
if (property_exists($this, $property)) {
return $this->{$property};
}
}
// @phpstan-ignore-next-line
public function __set(string $property, mixed $value)
{
if (property_exists($this, $property)) {
$this->{$property} = $value;
}
return $this;
}
/**
* @throws ComponentNotFoundException
*/
public function load(): string
{
// first, check if there exists a component class to load in class components path
if (file_exists("{$this->config->classComponentsPath}/{$this->name}.php")) {
return $this->loadComponentClass();
}
// check for the existence of a view file if no component class has been found
// component view files are camel case
$camelCaseName = strtolower(preg_replace('~(?<!^)(?<!\/)[A-Z]~', '_$0', $this->name) ?? '');
if (file_exists("{$this->config->componentsViewPath}/{$camelCaseName}.php")) {
return $this->loadComponentView($camelCaseName);
}
throw new ComponentNotFoundException("Could not find component \"{$this->name}\"");
}
private function loadComponentClass(): string
{
$classComponentsNamespace = $this->config->classComponentsNamespace;
$namespacedName = str_replace('/', '\\', $this->name);
$componentClassNamespace = "{$classComponentsNamespace}\\{$namespacedName}";
$component = new $componentClassNamespace($this->properties, $this->attributes);
return $component->render();
}
private function loadComponentView(string $name): string
{
$viewData = [...$this->properties, ...$this->attributes];
return view("components/{$name}", $viewData);
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace ViewComponents\Config;
use CodeIgniter\Config\BaseService;
use ViewComponents\ComponentLoader;
/**
* Services Configuration file.
*
* Services are simply other classes/libraries that the system uses to do its job. This is used by CodeIgniter to allow
* the core of the framework to be swapped out easily without affecting the usage within the rest of your application.
*
* This file holds any application-specific services, or service overrides that you might need. An example has been
* included with the general method format you should use for your service methods. For more examples, see the core
* Services file at system/Config/Services.php.
*/
class Services extends BaseService
{
public static function viewcomponents(bool $getShared = true): ComponentLoader
{
if ($getShared) {
return self::getSharedInstance('viewcomponents');
}
return new ComponentLoader();
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace ViewComponents\Config;
use CodeIgniter\Config\BaseConfig;
class ViewComponents extends BaseConfig
{
public string $classComponentsNamespace = APP_NAMESPACE . '\View\Components';
public string $classComponentsPath = APPPATH . 'View/Components';
public string $componentsViewPath = APPPATH . 'Views/components';
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace ViewComponents\Exceptions;
use CodeIgniter\Exceptions\ExceptionInterface;
use RuntimeException;
class ComponentNotFoundException extends RuntimeException implements ExceptionInterface
{
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
if (! function_exists('component')) {
/**
* Loads the specified class or view file component in the parameters
*
* @param array<string, array<string, mixed>> $properties
* @param array<string, array<string, mixed>> $attributes
*/
function component(string $name, array $properties = [], array $attributes = []): string
{
$componentLoader = service('viewcomponents');
$componentLoader->name = $name;
$componentLoader->properties = $properties;
$componentLoader->attributes = $attributes;
return $componentLoader->load();
}
}

View File

@ -60,6 +60,10 @@ export class XMLEditor extends LitElement {
border: 1px solid #6b7280;
background-color: #ffffff;
}
.cm-editor.cm-focused {
outline: 2px solid transparent;
box-shadow: 0 0 0 1px #2563eb;
}
`;
render(): TemplateResult<1> {

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\View\Components;
use ViewComponents\Component;
class Button extends Component
{
protected string $label = '';
protected string $uri = '';
protected string $variant = 'default';
protected string $size = 'base';
protected string $iconLeft = '';
protected string $iconRight = '';
protected bool $isSquared = false;
public function render(): string
{
$baseClass =
'inline-flex items-center font-semibold shadow-xs rounded-full focus:outline-none focus:ring';
$variantClass = [
'default' => 'text-black bg-gray-300 hover:bg-gray-400',
'primary' => 'text-white bg-pine-700 hover:bg-pine-800',
'secondary' => 'text-white bg-gray-700 hover:bg-gray-800',
'accent' => 'text-white bg-rose-600 hover:bg-rose-800',
'success' => 'text-white bg-green-600 hover:bg-green-700',
'danger' => 'text-white bg-red-600 hover:bg-red-700',
'warning' => 'text-black bg-yellow-500 hover:bg-yellow-600',
'info' => 'text-white bg-blue-500 hover:bg-blue-600',
];
$sizeClass = [
'small' => 'text-xs md:text-sm',
'base' => 'text-sm md:text-base',
'large' => 'text-lg md:text-xl',
];
$basePaddings = [
'small' => 'px-2 md:px-3 md:py-1',
'base' => 'px-3 py-1 md:px-4 md:py-2',
'large' => 'px-3 py-2 md:px-5',
];
$squaredPaddings = [
'small' => 'p-1',
'base' => 'p-2',
'large' => 'p-3',
];
$buttonClass =
$baseClass .
' ' .
($this->isSquared
? $squaredPaddings[$this->size]
: $basePaddings[$this->size]) .
' ' .
$sizeClass[$this->size] .
' ' .
$variantClass[$this->variant];
if (array_key_exists('class', $this->attributes)) {
$buttonClass .= ' ' . $this->attributes['class'];
unset($this->attributes['class']);
}
if ($this->iconLeft !== '') {
$this->label = icon($this->iconLeft, 'mr-2') . $this->label;
}
if ($this->iconRight !== '') {
$this->label .= icon($this->iconRight, 'ml-2');
}
if ($this->uri !== '') {
return anchor($this->uri, $this->label, array_merge([
'class' => $buttonClass,
], $this->attributes));
}
$defaultButtonAttributes = [
'type' => 'button',
];
$attributes = stringify_attributes(array_merge($defaultButtonAttributes, $this->attributes));
return <<<CODE_SAMPLE
<button class="{$buttonClass}" {$attributes}>{$this->label}</button>
CODE_SAMPLE;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class Input extends Component
{
public function render(): string
{
return '';
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class Label extends Component
{
/**
* @var array<string, string>
*/
protected array $attributes = [
'for' => '',
'name' => '',
'value' => '',
'class' => '',
];
protected string $text = '';
protected string $hint = '';
protected bool $isOptional = false;
public function render(): string
{
$labelClass = $this->attributes['class'];
unset($this->attributes['class']);
$attributes = stringify_attributes($this->attributes);
$optionalText = $this->isOptional ? '<small class="ml-1 lowercase">(' .
lang('Common.optional') .
')</small>' : '';
$hint = $this->hint !== '' ? hint_tooltip($this->hint, 'ml-1') : '';
return <<<CODE_SAMPLE
<label class="{$labelClass}" {$attributes}>{$this->text}{$optionalText}{$hint}</label>
CODE_SAMPLE;
}
}

View File

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class MarkdownEditor extends Component
{
protected string $content = '';
public function render(): string
{
$editorClass = 'w-full flex flex-col bg-white border border-gray-500 focus-within:ring-1 focus-within:ring-blue-600';
if ($this->attributes['class'] !== '') {
$editorClass .= ' ' . $this->attributes['class'];
unset($this->attributes['class']);
}
$this->attributes['class'] = 'border-none outline-none focus:border-none focus:outline-none w-full h-full';
return '<div class="' . $editorClass . '">' .
'<header class="sticky top-0 z-20 flex flex-wrap justify-between bg-white border-b border-gray-500">' .
'<markdown-write-preview for="' . $this->attributes['id'] . '" class="relative inline-flex h-8">' .
'<button type="button" slot="write" class="px-2 font-semibold focus:outline-none focus:ring-inset focus:ring-2 focus:ring-pine-600">' . lang(
'Common.forms.editor.write'
) . '</button>' .
'<button type="button" slot="preview" class="px-2 focus:outline-none focus:ring-inset focus:ring-2 focus:ring-pine-600">' . lang(
'Common.forms.editor.preview'
) . '</button>' .
'</markdown-write-preview>' .
'<markdown-toolbar for="' . $this->attributes['id'] . '" class="flex gap-4 px-2 py-1">' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-header class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'heading'
) . '</md-header>' .
'<md-bold class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'bold'
) . '</md-bold>' .
'<md-italic class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'italic'
) . '</md-italic>' .
'</div>' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-unordered-list class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'list-unordered'
) . '</md-unordered-list>' .
'<md-ordered-list class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'list-ordered'
) . '</md-ordered-list>' .
'</div>' .
'<div class="inline-flex text-2xl gap-x-1">' .
'<md-quote class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'quote'
) . '</md-quote>' .
'<md-link class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'link'
) . '</md-link>' .
'<md-image class="opacity-50 hover:opacity-100 focus:outline-none focus:ring-2 focus:opacity-100 focus:ring-pine-600">' . icon(
'image-add'
) . '</md-image>' .
'</div>' .
'</markdown-toolbar>' .
'</header>' .
'<div class="relative">' .
form_textarea($this->attributes, $this->content) .
'<markdown-preview for="' . $this->attributes['id'] . '" class="absolute top-0 left-0 hidden w-full h-full p-2 overflow-y-auto prose bg-gray-50" showClass="bg-white"></markdown-preview>' .
'</div>' .
'<footer class="flex px-2 py-1 bg-gray-100 border-t">' .
'<a href="https://commonmark.org/help/" class="inline-flex items-center text-xs font-semibold text-gray-500 hover:text-gray-700" target="_blank" rel="noopener noreferrer">' . icon(
'markdown',
'mr-1 text-lg text-gray-400'
) . lang('Common.forms.editor.help') . '</a>' .
'</footer>' .
'</div>';
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class MultiSelect extends Component
{
/**
* @var array<string, string>
*/
protected array $options = [];
/**
* @var string[]
*/
protected array $selected = [];
public function render(): string
{
$defaultAttributes = [
'data-class' => $this->attributes['class'],
'multiple' => 'multiple',
];
$extra = array_merge($defaultAttributes, $this->attributes);
return form_dropdown($this->attributes['name'], $this->options, $this->selected, $extra);
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class MultiSelect extends Component
{
/**
* @var array<string, string>
*/
protected array $options = [];
/**
* @var string[]
*/
protected array $selected = [];
public function render(): string
{
$defaultAttributes = [
'data-class' => $this->attributes['class'],
'multiple' => 'multiple',
];
$extra = array_merge($defaultAttributes, $this->attributes);
return form_dropdown($this->attributes['name'], $this->options, $this->selected, $extra);
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
/**
* Form Checkbox Switch
*
* Abstracts form_label to stylize it as a switch toggle
*/
class Toggler extends Component
{
/**
* @var array<string, string>
*/
protected array $attributes = [
'id' => '',
'name' => '',
'value' => '',
'class' => '',
];
protected string $label = '';
protected string $hint = '';
protected bool $checked = false;
public function render(): string
{
$wrapperClass = $this->attributes['class'];
unset($this->attributes['class']);
$this->attributes['class'] = 'form-switch';
$checkbox = form_checkbox($this->attributes, $this->attributes['value'], $this->checked);
$hint = $this->hint !== '' ? hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1') : '';
return <<<CODE_SAMPLE
<label class="relative inline-flex items-center {$wrapperClass}">
{$checkbox}
<span class="form-switch-slider"></span>
<span class="ml-2">{$this->label}{$hint}</span>
</label>
CODE_SAMPLE;
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\View\Components\Forms;
use ViewComponents\Component;
class XMLEditor extends Component
{
protected string $content = '';
/**
* @var array<string, string>
*/
protected array $attributes = [
'slot' => 'textarea',
'rows' => '5',
'class' => 'textarea',
];
public function render(): string
{
$textarea = form_textarea($this->attributes, $this->content);
return <<<CODE_SAMPLE
<xml-editor>{$textarea}</time-ago>
CODE_SAMPLE;
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\View\Components;
use ViewComponents\Component;
class Icon extends Component
{
public string $glyph = '';
public function render(): string
{
$svgContents = file_get_contents('assets/icons/' . $this->glyph . '.svg');
if ($svgContents) {
if ($this->attributes['class'] !== '') {
$svgContents = str_replace('<svg', '<svg class="' . $this->attributes['class'] . '"', $svgContents);
}
return $svgContents;
}
return '□';
}
}

View File

@ -16,7 +16,7 @@
]) ?>
<?= csrf_field() ?>
<?= form_label(lang('Contributor.form.user'), 'user') ?>
<?= component('Forms/Label', ['text' => lang('Contributor.form.user')], ['for' => 'user']) ?>
<?= form_dropdown('user', $userOptions, [old('user', '')], [
'id' => 'user',
'class' => 'form-select mb-4',
@ -24,7 +24,7 @@
'placeholder' => lang('Contributor.form.user_placeholder')
]) ?>
<?= form_label(lang('Contributor.form.role'), 'role') ?>
<?= component('Forms/Label', ['text' => lang('Contributor.form.role')], ['for' => 'role']) ?>
<?= form_dropdown('role', $roleOptions, [old('role', '')], [
'id' => 'role',
'class' => 'form-select mb-4',

View File

@ -16,7 +16,7 @@
]) ?>
<?= csrf_field() ?>
<?= form_label(lang('Contributor.form.role'), 'role') ?>
<?= component('Forms/Label', ['text' => lang('Contributor.form.role')], ['for' => 'role']) ?>
<?= form_dropdown('role', $roleOptions, [old('role', $contributorGroupId)], [
'id' => 'role',
'class' => 'form-select mb-4',

View File

@ -28,11 +28,10 @@
lang('Episode.form.info_section_subtitle'),
) ?>
<?= form_label(
lang('Episode.form.audio_file'),
'audio_file',
[],
lang('Episode.form.audio_file_hint'),
<?= component(
'Forms/Label',
['text' => lang('Episode.form.audio_file'), 'hint' => lang('Episode.form.audio_file_hint')],
['for' => 'audio_file'],
) ?>
<?= form_input([
'id' => 'audio_file',
@ -43,12 +42,14 @@
'accept' => '.mp3,.m4a',
]) ?>
<?= form_label(
lang('Episode.form.image'),
'image',
[],
lang('Episode.form.image_hint'),
true,
<?= component(
'Forms/Label',
[
'text' => lang('Episode.form.image'),
'hint' => lang('Episode.form.image_hint'),
'isOptional' => true
],
['for' => 'image'],
) ?>
<?= form_input([
'id' => 'image',
@ -61,11 +62,11 @@
'Common.forms.image_size_hint',
) ?></small>
<?= form_label(
lang('Episode.form.title'),
'title',
[],
lang('Episode.form.title_hint'),
<?= component(
'Forms/Label',
['text' => lang('Episode.form.title'),
'hint' => lang('Episode.form.title_hint')],
['for' => 'title'],
) ?>
<?= form_input([
'id' => 'title',
@ -76,10 +77,10 @@
'data-slugify' => 'title',
]) ?>
<?= form_label(
lang('Episode.form.permalink'),
'slug',
[],
<?= component(
'Forms/Label',
['text' => lang('Episode.form.permalink')],
['for' => 'slug']
) ?>
<permalink-edit class="inline-flex items-center mb-4 text-xs" edit-label="<?= lang('Common.edit') ?>" copy-label="<?= lang('Common.copy') ?>" copied-label="<?= lang('Common.copied') ?>">
<span slot="domain"><?= base_url('/@'. $podcast->handle . '/episodes' ) . '/' ?></span>
@ -96,7 +97,7 @@
<div class="flex flex-col mb-4 gap-x-2 gap-y-4 md:flex-row">
<div class="flex flex-col flex-1">
<?= form_label(lang('Episode.form.season_number'), 'season_number') ?>
<?= component('Forms/Label', ['text' => lang('Episode.form.season_number')], ['for' => 'season_number']) ?>
<?= form_input([
'id' => 'season_number',
'name' => 'season_number',
@ -106,7 +107,7 @@
]) ?>
</div>
<div class="flex flex-col flex-1">
<?= form_label(lang('Episode.form.episode_number'), 'episode_number') ?>
<?= component('Forms/Label', ['text' => lang('Episode.form.episode_number')], ['for' => 'episode_number']) ?>
<?= form_input([
'id' => 'episode_number',
'name' => 'episode_number',
@ -203,36 +204,45 @@
) ?>
<div class="mb-4">
<?= form_label(lang('Episode.form.description'), 'description') ?>
<?= form_markdown_editor(
<?= component('Forms/Label', ['text' => lang('Episode.form.description')], ['for' => 'description']) ?>
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('description', '', false),
],
[
'id' => 'description',
'name' => 'description',
'required' => 'required',
],
old('description', '', false),
) ?>
</div>
<div class="mb-4">
<?= form_label(
lang('Episode.form.description_footer'),
'description_footer',
[],
lang('Episode.form.description_footer_hint'),
true
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.description_footer'),
'hint' => lang('Episode.form.description_footer_hint'),
'isOptional' => true
],
[
'for' => 'description_footer'
],
) ?>
<?= form_markdown_editor(
<?= component(
'Forms/MarkdownEditor',
[
'content' => old(
'description_footer',
$podcast->episode_description_footer_markdown ?? '',
false,
),
],
[
'id' => 'description_footer',
'name' => 'description_footer',
'rows' => 6
],
old(
'description_footer',
$podcast->episode_description_footer_markdown ?? '',
false,
),
) ?>
</div>
@ -243,12 +253,13 @@
lang('Episode.form.location_section_subtitle'),
) ?>
<?= form_label(
lang('Episode.form.location_name'),
'location_name',
[],
lang('Episode.form.location_name_hint'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.location_name'),
'hint' => lang('Episode.form.location_name_hint'),
'isOptional' => true
],
['for' => 'location_name'],
) ?>
<?= form_input([
'id' => 'location_name',
@ -290,12 +301,12 @@
<div class="py-2 tab-panels">
<section id="transcript-file-upload" class="flex items-center tab-panel">
<?= form_label(
lang('Episode.form.transcript_file'),
'transcript_file',
['class' => 'sr-only'],
lang('Episode.form.transcript_file'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.transcript_file'),
'isOptional' => true
],
['for' => 'transcript_file', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'transcript_file',
@ -306,12 +317,12 @@
]) ?>
</section>
<section id="transcript-file-remote-url" class="tab-panel">
<?= form_label(
lang('Episode.form.transcript_file_remote_url'),
'transcript_file_remote_url',
['class' => 'sr-only'],
lang('Episode.form.transcript_file_remote_url'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.transcript_file_remote_url'),
'isOptional' => true
],
['for' => 'transcript_file_remote_url', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'transcript_file_remote_url',
@ -353,12 +364,12 @@
<div class="py-2 tab-panels">
<section id="chapters-file-upload" class="flex items-center tab-panel">
<?= form_label(
lang('Episode.form.chapters_file'),
'chapters_file',
['class' => 'sr-only'],
lang('Episode.form.chapters_file'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.chapters_file'),
'isOptional' => true
],
['for' => 'chapters_file', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'chapters_file',
@ -369,12 +380,15 @@
]) ?>
</section>
<section id="chapters-file-remote-url" class="tab-panel">
<?= form_label(
lang('Episode.form.chapters_file_remote_url'),
'chapters_file_remote_url',
['class' => 'sr-only'],
lang('Episode.form.chapters_file_remote_url'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.chapters_file_remote_url'),
'isOptional' => true
],
[
'for' => 'chapters_file_remote_url',
'class' => 'sr-only'
],
) ?>
<?= form_input([
'id' => 'chapters_file_remote_url',
@ -395,27 +409,37 @@
lang('Episode.form.advanced_section_title'),
lang('Episode.form.advanced_section_subtitle'),
) ?>
<?= form_label(
lang('Episode.form.custom_rss'),
'custom_rss',
[],
lang('Episode.form.custom_rss_hint'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.custom_rss'),
'hint' => lang('Episode.form.custom_rss_hint'),
'isOptional' => true
],
['for' => 'custom_rss']
) ?>
<?= component('Forms/XMLEditor',
[
'content' => old('custom_rss', '')
],
[
'id' => 'custom_rss',
'name' => 'custom_rss',
]
) ?>
<?= xml_editor([
'id' => 'custom_rss',
'name' => 'custom_rss',
'class' => 'form-textarea',
'value' => old('custom_rss'),
]) ?>
<?= form_section_close() ?>
<?= form_switch(
lang('Episode.form.block') .
hint_tooltip(lang('Episode.form.block_hint'), 'ml-1'),
['id' => 'block', 'name' => 'block'],
'yes',
old('block', false),
<?= component(
'Forms/Toggler',
[
'label' => lang('Episode.form.block'),
'hint' => lang('Episode.form.block_hint')
],
[
'id' => 'block',
'name' => 'block',
'value' => 'yes',
'checked' => old('block', false),
]
) ?>
<?= button(

View File

@ -18,8 +18,8 @@
<?= csrf_field() ?>
<div class="inline-flex w-full p-2 mb-4 text-sm font-semibold text-yellow-800 bg-red-100 border border-red-300 rounded" role="alert">
<?= icon('alert', 'mr-2 text-lg flex-shrink-0') .
lang('Episode.form.warning') ?>
<?= icon('alert', 'mr-2 text-lg flex-shrink-0') .
lang('Episode.form.warning') ?>
</div>
<?= form_section(
@ -35,11 +35,11 @@
/>',
) ?>
<?= form_label(
lang('Episode.form.audio_file'),
'audio_file',
[],
lang('Episode.form.audio_file_hint'),
<?= component(
'Forms/Label',
['text' =>
lang('Episode.form.audio_file'), 'hint' => lang('Episode.form.audio_file_hint'),],
['for' => 'audio_file'],
) ?>
<?= form_input([
'id' => 'audio_file',
@ -49,12 +49,12 @@
'accept' => '.mp3,.m4a',
]) ?>
<?= form_label(
lang('Episode.form.image'),
'image',
[],
lang('Episode.form.image_hint'),
true,
<?= component(
'Forms/Label',
['text' =>
lang('Episode.form.image'), 'hint' => lang('Episode.form.image_hint'), 'isOptional' => true,],
['for' =>
'image',]
) ?>
<?= form_input([
@ -68,11 +68,12 @@
'Common.forms.image_size_hint',
) ?></small>
<?= form_label(
lang('Episode.form.title'),
'title',
[],
lang('Episode.form.title_hint'),
<?= component(
'Forms/Label',
['text' =>
lang('Episode.form.title'), 'hint' => lang('Episode.form.title_hint'),],
['for' =>
'title',]
) ?>
<?= form_input([
'id' => 'title',
@ -83,27 +84,29 @@
'data-slugify' => 'title',
]) ?>
<?= form_label(
lang('Episode.form.permalink'),
'slug',
[],
<?= component(
'Forms/Label',
[
'text' => lang('Episode.form.permalink')
],
['for' => 'slug',]
) ?>
<permalink-edit class="inline-flex items-center mb-4 text-xs" edit-label="<?= lang('Common.edit') ?>" copy-label="<?= lang('Common.copy') ?>" copied-label="<?= lang('Common.copied') ?>">
<span slot="domain"><?= base_url('/@'. $podcast->handle . '/episodes' ) . '/' ?></span>
<span slot="domain"><?= base_url('/@' . $podcast->handle . '/episodes') . '/' ?></span>
<?= form_input([
'id' => 'slug',
'name' => 'slug',
'class' => 'form-input flex-1 w-0 text-xs',
'value' => old('slug', $episode->slug),
'required' => 'required',
'data-slugify' => 'slug',
'slot' => 'slug-input'
'id' => 'slug',
'name' => 'slug',
'class' => 'form-input flex-1 w-0 text-xs',
'value' => old('slug', $episode->slug),
'required' => 'required',
'data-slugify' => 'slug',
'slot' => 'slug-input'
]) ?>
</permalink-edit>
<div class="flex flex-col mb-4 gap-x-2 gap-y-4 md:flex-row">
<div class="flex flex-col flex-1">
<?= form_label(lang('Episode.form.season_number'), 'season_number') ?>
<?= component('Forms/Label', ['text' => lang('Episode.form.season_number')], ['for' => 'season_number']) ?>
<?= form_input([
'id' => 'season_number',
'name' => 'season_number',
@ -113,7 +116,7 @@
]) ?>
</div>
<div class="flex flex-col flex-1">
<?= form_label(lang('Episode.form.episode_number'), 'episode_number') ?>
<?= component('Forms/Label', ['text' => lang('Episode.form.episode_number')], ['for' => 'episode_number']) ?>
<?= form_input([
'id' => 'episode_number',
'name' => 'episode_number',
@ -125,83 +128,83 @@
</div>
<?= form_fieldset('', ['class' => 'flex mb-4 gap-1']) ?>
<legend>
<legend>
<?= lang('Episode.form.type.label') .
hint_tooltip(lang('Episode.form.type.hint'), 'ml-1') ?>
</legend>
<?= form_radio(
['id' => 'full', 'name' => 'type', 'class' => 'form-radio-btn'],
'full',
old('type') ? old('type') === 'full' : $episode->type === 'full',
) ?>
<label for="full" class="inline-flex items-center">
<?= lang('Episode.form.type.full') ?>
</label>
<?= form_radio(
['id' => 'trailer', 'name' => 'type', 'class' => 'form-radio-btn'],
'trailer',
old('type') ? old('type') === 'trailer' : $episode->type === 'trailer',
) ?>
<label for="trailer" class="inline-flex items-center">
<?= lang('Episode.form.type.trailer') ?>
</label>
<?= form_radio(
['id' => 'bonus', 'name' => 'type', 'class' => 'form-radio-btn'],
'bonus',
old('type') ? old('type') === 'bonus' : $episode->type === 'bonus',
) ?>
<label for="bonus" class="inline-flex items-center">
<?= lang('Episode.form.type.bonus') ?>
</label>
</legend>
<?= form_radio(
['id' => 'full', 'name' => 'type', 'class' => 'form-radio-btn'],
'full',
old('type') ? old('type') === 'full' : $episode->type === 'full',
) ?>
<label for="full" class="inline-flex items-center">
<?= lang('Episode.form.type.full') ?>
</label>
<?= form_radio(
['id' => 'trailer', 'name' => 'type', 'class' => 'form-radio-btn'],
'trailer',
old('type') ? old('type') === 'trailer' : $episode->type === 'trailer',
) ?>
<label for="trailer" class="inline-flex items-center">
<?= lang('Episode.form.type.trailer') ?>
</label>
<?= form_radio(
['id' => 'bonus', 'name' => 'type', 'class' => 'form-radio-btn'],
'bonus',
old('type') ? old('type') === 'bonus' : $episode->type === 'bonus',
) ?>
<label for="bonus" class="inline-flex items-center">
<?= lang('Episode.form.type.bonus') ?>
</label>
<?= form_fieldset_close() ?>
<?= form_fieldset('', ['class' => 'mb-6']) ?>
<legend>
<legend>
<?= lang('Episode.form.parental_advisory.label') .
hint_tooltip(lang('Episode.form.parental_advisory.hint'), 'ml-1') ?>
</legend>
<?= form_radio(
[
'id' => 'undefined',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'undefined',
old('parental_advisory')
? old('parental_advisory') === 'undefined'
: $episode->parental_advisory === null,
) ?>
<label for="undefined"><?= lang(
'Episode.form.parental_advisory.undefined',
) ?></label>
<?= form_radio(
[
'id' => 'clean',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'clean',
old('parental_advisory')
? old('parental_advisory') === 'clean'
: $episode->parental_advisory === 'clean',
) ?>
<label for="clean"><?= lang(
'Episode.form.parental_advisory.clean',
) ?></label>
<?= form_radio(
[
'id' => 'explicit',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'explicit',
old('parental_advisory')
? old('parental_advisory') === 'explicit'
: $episode->parental_advisory === 'explicit',
) ?>
<label for="explicit"><?= lang(
'Episode.form.parental_advisory.explicit',
) ?></label>
</legend>
<?= form_radio(
[
'id' => 'undefined',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'undefined',
old('parental_advisory')
? old('parental_advisory') === 'undefined'
: $episode->parental_advisory === null,
) ?>
<label for="undefined"><?= lang(
'Episode.form.parental_advisory.undefined',
) ?></label>
<?= form_radio(
[
'id' => 'clean',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'clean',
old('parental_advisory')
? old('parental_advisory') === 'clean'
: $episode->parental_advisory === 'clean',
) ?>
<label for="clean"><?= lang(
'Episode.form.parental_advisory.clean',
) ?></label>
<?= form_radio(
[
'id' => 'explicit',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'explicit',
old('parental_advisory')
? old('parental_advisory') === 'explicit'
: $episode->parental_advisory === 'explicit',
) ?>
<label for="explicit"><?= lang(
'Episode.form.parental_advisory.explicit',
) ?></label>
<?= form_fieldset_close() ?>
<?= form_section_close() ?>
@ -213,36 +216,43 @@
) ?>
<div class="mb-4">
<?= form_label(lang('Episode.form.description'), 'description') ?>
<?= form_markdown_editor(
<?= component('Forms/Label', ['text' => lang('Episode.form.description')], ['for' => 'description']) ?>
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('description', $episode->description_markdown, false),
],
[
'id' => 'description',
'name' => 'description',
'required' => 'required',
],
old('description', $episode->description_markdown, false),
) ?>
</div>
<div class="mb-4">
<?= form_label(
lang('Episode.form.description_footer'),
'description_footer',
[],
lang('Episode.form.description_footer_hint'),
true
<?= component('Forms/Label',
[
'text' => lang('Episode.form.description_footer'),
'hint' => lang('Episode.form.description_footer_hint'),
'isOptional' => true
],
['for' => 'description_footer'],
) ?>
<?= form_markdown_editor(
<?= component(
'Forms/MarkdownEditor',
[
'content' => old(
'description_footer',
$podcast->episode_description_footer_markdown ?? '',
false,
),
],
[
'id' => 'description_footer',
'name' => 'description_footer',
'rows' => 6
],
old(
'description_footer',
$podcast->episode_description_footer_markdown ?? '',
false,
),
) ?>
</div>
@ -253,12 +263,13 @@
lang('Episode.form.location_section_subtitle'),
) ?>
<?= form_label(
lang('Episode.form.location_name'),
'location_name',
[],
lang('Episode.form.location_name_hint'),
true,
<?= component('Forms/Label',
[
'text' => lang('Episode.form.location_name'),
'hint' => lang('Episode.form.location_name_hint'),
'isOptional' => true
],
['for' => 'location_name']
) ?>
<?= form_input([
'id' => 'location_name',
@ -273,34 +284,34 @@
lang('Episode.form.additional_files_section_title'),
lang('Episode.form.additional_files_section_subtitle', [
'podcastNamespaceLink' =>
'“<a href="https://github.com/Podcastindex-org/podcast-namespace" target="_blank" rel="noreferrer noopener" style="text-decoration: underline;">podcast namespace</a>”',
'“<a href="https://github.com/Podcastindex-org/podcast-namespace" target="_blank" rel="noreferrer noopener" style="text-decoration: underline;">podcast namespace</a>”',
]),
) ?>
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
<legend><?= lang('Episode.form.transcript') .
'<small class="ml-1 lowercase">(' .
lang('Common.optional') .
')</small>' .
hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?></legend>
<div class="mb-4 form-input-tabs">
<input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= $episode->transcript_file_remote_url
? ''
: 'checked' ?> />
<label for="transcript-file-upload-choice"><?= lang(
'Common.forms.upload_file',
) ?></label>
<legend><?= lang('Episode.form.transcript') .
'<small class="ml-1 lowercase">(' .
lang('Common.optional') .
')</small>' .
hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?></legend>
<div class="mb-4 form-input-tabs">
<input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= $episode->transcript_file_remote_url
? ''
: 'checked' ?> />
<label for="transcript-file-upload-choice"><?= lang(
'Common.forms.upload_file',
) ?></label>
<input type="radio" name="transcript-choice" id="transcript-file-remote-url-choice" aria-controls="transcript-file-remote-url-choice" value="remote-url" <?= $episode->transcript_file_remote_url
? 'checked'
: '' ?> />
<label for="transcript-file-remote-url-choice"><?= lang(
'Common.forms.remote_url',
) ?></label>
<input type="radio" name="transcript-choice" id="transcript-file-remote-url-choice" aria-controls="transcript-file-remote-url-choice" value="remote-url" <?= $episode->transcript_file_remote_url
? 'checked'
: '' ?> />
<label for="transcript-file-remote-url-choice"><?= lang(
'Common.forms.remote_url',
) ?></label>
<div class="py-2 tab-panels">
<section id="transcript-file-upload" class="flex items-center tab-panel">
<?php if ($episode->transcript_file): ?>
<div class="py-2 tab-panels">
<section id="transcript-file-upload" class="flex items-center tab-panel">
<?php if ($episode->transcript_file) : ?>
<div class="flex justify-between">
<?= anchor(
$episode->transcript_file_url,
@ -321,7 +332,7 @@
icon('delete-bin', 'mx-auto'),
[
'class' =>
'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900',
'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang(
@ -331,12 +342,12 @@
) ?>
</div>
<?php endif; ?>
<?= form_label(
lang('Episode.form.transcript_file'),
'transcript_file',
['class' => 'sr-only'],
lang('Episode.form.transcript_file'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.transcript_file'),
'isOptional' => true
],
['for' => 'transcript_file', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'transcript_file',
@ -345,14 +356,14 @@
'type' => 'file',
'accept' => '.txt,.html,.srt,.json',
]) ?>
</section>
<section id="transcript-file-remote-url" class="tab-panel">
<?= form_label(
lang('Episode.form.transcript_file_remote_url'),
'transcript_file_remote_url',
['class' => 'sr-only'],
lang('Episode.form.transcript_file_remote_url'),
true,
</section>
<section id="transcript-file-remote-url" class="tab-panel">
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.transcript_file_remote_url'),
'isOptional' => true
],
['for' => 'transcript_file_remote_url', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'transcript_file_remote_url',
@ -365,35 +376,35 @@
$episode->transcript_file_remote_url,
),
]) ?>
</section>
</div>
</section>
</div>
</div>
<?= form_fieldset_close() ?>
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
<legend><?= lang('Episode.form.chapters') .
'<small class="ml-1 lowercase">(' .
lang('Common.optional') .
')</small>' .
hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?></legend>
<div class="mb-4 form-input-tabs">
<input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= $episode->chapters_file_remote_url
? ''
: 'checked' ?> />
<label for="chapters-file-upload-choice"><?= lang(
'Common.forms.upload_file',
) ?></label>
<legend><?= lang('Episode.form.chapters') .
'<small class="ml-1 lowercase">(' .
lang('Common.optional') .
')</small>' .
hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?></legend>
<div class="mb-4 form-input-tabs">
<input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= $episode->chapters_file_remote_url
? ''
: 'checked' ?> />
<label for="chapters-file-upload-choice"><?= lang(
'Common.forms.upload_file',
) ?></label>
<input type="radio" name="chapters-choice" id="chapters-file-remote-url-choice" aria-controls="chapters-file-remote-url-choice" value="remote-url" <?= $episode->chapters_file_remote_url
? 'checked'
: '' ?> />
<label for="chapters-file-remote-url-choice"><?= lang(
'Common.forms.remote_url',
) ?></label>
<input type="radio" name="chapters-choice" id="chapters-file-remote-url-choice" aria-controls="chapters-file-remote-url-choice" value="remote-url" <?= $episode->chapters_file_remote_url
? 'checked'
: '' ?> />
<label for="chapters-file-remote-url-choice"><?= lang(
'Common.forms.remote_url',
) ?></label>
<div class="py-2 tab-panels">
<section id="chapters-file-upload" class="flex items-center tab-panel">
<?php if ($episode->chapters_file): ?>
<div class="py-2 tab-panels">
<section id="chapters-file-upload" class="flex items-center tab-panel">
<?php if ($episode->chapters_file) : ?>
<div class="flex justify-between">
<?= anchor(
$episode->chapters_file_url,
@ -413,7 +424,7 @@
icon('delete-bin', 'mx-auto'),
[
'class' =>
'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900',
'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900',
'data-toggle' => 'tooltip',
'data-placement' => 'bottom',
'title' => lang(
@ -423,12 +434,12 @@
) ?>
</div>
<?php endif; ?>
<?= form_label(
lang('Episode.form.chapters_file'),
'chapters_file',
['class' => 'sr-only'],
lang('Episode.form.chapters_file'),
true,
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.chapters_file'),
'isOptional' => true
],
['for' => 'chapters_file', 'class' => 'sr-only'],
) ?>
<?= form_input([
'id' => 'chapters_file',
@ -437,14 +448,17 @@
'type' => 'file',
'accept' => '.json',
]) ?>
</section>
<section id="chapters-file-remote-url" class="tab-panel">
<?= form_label(
lang('Episode.form.chapters_file_remote_url'),
'chapters_file_remote_url',
['class' => 'sr-only'],
lang('Episode.form.chapters_file_remote_url'),
true,
</section>
<section id="chapters-file-remote-url" class="tab-panel">
<?= component( 'Forms/Label',
[
'text' => lang('Episode.form.chapters_file_remote_url'),
'isOptional' => true
],
[
'for' => 'chapters_file_remote_url',
'class' => 'sr-only'
],
) ?>
<?= form_input([
'id' => 'chapters_file_remote_url',
@ -457,9 +471,9 @@
$episode->chapters_file_remote_url,
),
]) ?>
</section>
</div>
</section>
</div>
</div>
<?= form_fieldset_close() ?>
<?= form_section_close() ?>
@ -468,27 +482,39 @@
lang('Episode.form.advanced_section_title'),
lang('Episode.form.advanced_section_subtitle'),
) ?>
<?= form_label(
lang('Episode.form.custom_rss'),
'custom_rss',
[],
lang('Episode.form.custom_rss_hint'),
true,
<?= component('Forms/Label',
[
'text' => lang('Episode.form.custom_rss'),
'hint' => lang('Episode.form.custom_rss_hint'),
'isOptional' => true,
],
[
'for' => 'custom_rss',
]
) ?>
<?= component('Forms/XMLEditor',
[
'content' => old('custom_rss', $episode->custom_rss_string)
],
[
'id' => 'custom_rss',
'name' => 'custom_rss',
]
) ?>
<?= xml_editor([
'id' => 'custom_rss',
'name' => 'custom_rss',
'class' => 'form-textarea',
'value' => old('custom_rss', $episode->custom_rss_string),
]) ?>
<?= form_section_close() ?>
<?= form_switch(
lang('Episode.form.block') .
hint_tooltip(lang('Episode.form.block_hint'), 'ml-1'),
['id' => 'block', 'name' => 'block'],
'yes',
old('block', $episode->is_blocked),
<?= component(
'Forms/Toggler',
[
'label' => lang('Episode.form.block'),
'hint' => lang('Episode.form.block_hint')
],
[
'id' => 'block',
'name' => 'block',
'value' => 'yes',
'checked' => old('block', $episode->is_blocked),
]
) ?>
<?= button(

View File

@ -10,7 +10,7 @@
<?= $this->section('content') ?>
<?= form_label(lang('Episode.embeddable_player.label'), 'label') ?>
<p><?= lang('Episode.embeddable_player.label') ?></p>
<div class="flex w-full mt-6 mb-6">
<?php foreach ($themes as $themeKey => $theme): ?>

View File

@ -60,8 +60,8 @@
($person->information_url === null
? ''
: "<a href=\"{$person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" .
$person->information_url .
'</a>') .
$person->information_url .
'</a>') .
'</div></div>';
},
],
@ -95,40 +95,36 @@
lang('Person.episode_form.add_section_subtitle'),
) ?>
<?= form_label(
lang('Person.episode_form.persons'),
'persons',
[],
lang('Person.episode_form.persons_hint'),
<?= component(
'Forms/Label',
['text' => lang('Person.episode_form.persons'), 'hint' => lang('Person.episode_form.persons_hint')],
['for' => 'persons'],
) ?>
<?= form_multiselect('persons[]', $personOptions, old('persons', []), [
<?= component('Forms/MultiSelect', ['options' => $personOptions, 'selected' => old('persons', [])], [
'id' => 'persons',
'class' => 'form-select mb-4',
'name' => 'persons[]',
'class' => 'mb-4',
'required' => 'required',
]) ?>
<?= form_label(
lang('Person.episode_form.roles'),
'roles',
[],
lang('Person.episode_form.roles_hint'),
true,
<?= component(
'Forms/Label',
['text' => lang('Person.episode_form.roles'), 'hint' => lang('Person.episode_form.roles_hint'), 'isOptional' => true],
['for' => 'roles'],
) ?>
<?= form_multiselect(
'roles[]',
$taxonomyOptions,
old('roles', []),
['id' => 'roles', 'class' => 'form-select mb-4'],
) ?>
<?= component('Forms/MultiSelect', ['options' => $taxonomyOptions, 'selected' => old('roles', [])], [
'id' => 'roles',
'name' => 'roles[]',
'class' => 'mb-4',
]) ?>
<?= form_section_close() ?>
<?= button(
lang('Person.episode_form.submit_add'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
) ?>
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -8,7 +8,6 @@
<?= lang('Episode.publish_edit') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= anchor(
@ -33,14 +32,11 @@
<small class="max-w-md mb-2 text-gray-600"><?= lang('Episode.publish_form.post_hint') ?></small>
<div class="mb-8 overflow-hidden bg-white shadow-md rounded-xl">
<div class="flex px-4 py-3">
<img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor
->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex flex-col min-w-0">
<p class="flex items-baseline min-w-0">
<span class="mr-2 font-semibold truncate"><?= $podcast->actor
->display_name ?></span>
<span class="text-sm text-gray-500 truncate">@<?= $podcast
->actor->username ?></span>
<span class="mr-2 font-semibold truncate"><?= $podcast->actor->display_name ?></span>
<span class="text-sm text-gray-500 truncate">@<?= $podcast->actor->username ?></span>
</p>
<?= relative_time($post->published_at, 'text-xs text-gray-500') ?>
</div>
@ -85,17 +81,17 @@
</div>
<footer class="flex justify-around px-6 py-3">
<span class="inline-flex items-center"><?= icon(
'chat',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
'chat',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
<span class="inline-flex items-center"><?= icon(
'repeat',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
'repeat',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
<span class="inline-flex items-center"><?= icon(
'heart',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
'heart',
'text-xl mr-1 text-gray-400',
) . '0' ?></span>
</footer>
</div>
@ -151,10 +147,10 @@
'data-input' => '',
]) ?>
<button class="p-3 border border-l-0 border-gray-500 bg-pine-100 focus:outline-none rounded-r-md hover:bg-pine-200 focus:ring" type="button" aria-label="<?= lang(
'Episode.publish_form.scheduled_publication_date_clear',
) ?>" title="<?= lang(
'Episode.publish_form.scheduled_publication_date_clear',
) ?>" data-clear=""><?= icon('close') ?></button>
'Episode.publish_form.scheduled_publication_date_clear',
) ?>" title="<?= lang(
'Episode.publish_form.scheduled_publication_date_clear',
) ?>" data-clear=""><?= icon('close') ?></button>
</div>
</div>
</div>

View File

@ -46,14 +46,17 @@
<div class="mb-4">
<?= form_label(lang('Page.form.content'), 'content') ?>
<?= form_markdown_editor(
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('content', '', false)
],
[
'id' => 'content',
'name' => 'content',
'required' => 'required',
'rows' => 20
],
old('content', '', false),
['rows' => '20']
) ?>
</div>

View File

@ -46,13 +46,16 @@
<div class="mb-4">
<?= form_label(lang('Page.form.content'), 'content') ?>
<?= form_markdown_editor(
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('content', $page->content_markdown, false),
],
[
'id' => 'content',
'name' => 'content',
'required' => 'required',
],
old('content', $page->content_markdown, false),
) ?>
</div>

View File

@ -82,13 +82,16 @@
<div class="mb-4">
<?= form_label(lang('Podcast.form.description'), 'description') ?>
<?= form_markdown_editor(
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('description', '', false),
],
[
'id' => 'description',
'name' => 'description',
'required' => 'required',
],
old('description', '', false),
) ?>
</div>
@ -122,16 +125,12 @@
'',
true,
) ?>
<?= form_multiselect(
'other_categories[]',
$categoryOptions,
[old('other_categories', '')],
[
'id' => 'other_categories',
'class' => 'mb-4',
'data-max-item-count' => '2',
],
) ?>
<?= component('Forms/MultiSelect', ['options' => $categoryOptions, 'selected' => old('other_categories', [])], [
'id' => 'other_categories',
'name' => 'other_categories[]',
'class' => 'mb-4',
'data-max-item-count' => '2',
]) ?>
<?= form_fieldset('', ['class' => 'mb-4']) ?>
<legend>
@ -340,12 +339,15 @@
lang('Podcast.form.custom_rss_hint'),
true,
) ?>
<?= xml_editor([
'id' => 'custom_rss',
'name' => 'custom_rss',
'class' => 'form-textarea',
'value' => old('custom_rss'),
]) ?>
<?= component('Forms/XMLEditor',
[
'content' => old('custom_rss', '')
],
[
'id' => 'custom_rss',
'name' => 'custom_rss',
]
) ?>
<?= form_section_close() ?>
<?= form_section(
@ -353,28 +355,46 @@
lang('Podcast.form.status_section_subtitle'),
) ?>
<?= form_switch(
lang('Podcast.form.block'),
['id' => 'block', 'name' => 'block'],
'yes',
old('block', false),
'mb-2',
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.lock'),
'hint' => lang('Podcast.form.lock_hint'),
],
[
'id' => 'lock',
'name' => 'lock',
'value' => 'yes',
'checked' => old('complete', true),
'class' => 'mb-2'
]
) ?>
<?= form_switch(
lang('Podcast.form.complete'),
['id' => 'complete', 'name' => 'complete'],
'yes',
old('complete', false),
'mb-2',
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.block'),
],
[
'id' => 'block',
'name' => 'block',
'value' => 'yes',
'checked' => old('block', false),
'class' => 'mb-2'
]
) ?>
<?= form_switch(
lang('Podcast.form.lock') .
hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'),
['id' => 'lock', 'name' => 'lock'],
'yes',
old('lock', true),
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.complete'),
],
[
'id' => 'complete',
'name' => 'complete',
'value' => 'yes',
'checked' => old('complete', false),
]
) ?>
<?= form_section_close() ?>

View File

@ -8,12 +8,14 @@
<?= lang('Podcast.edit') ?>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<?= form_open_multipart(route_to('podcast-edit', $podcast->id), [
'method' => 'post',
'class' => 'flex flex-col',
]) ?>
<?= csrf_field() ?>
<?= form_section(
@ -22,11 +24,8 @@
) ?>
<?= form_label(lang('Podcast.form.image'), 'image') ?>
<img
src="<?= $podcast->image->thumbnail_url ?>"
alt="<?= $podcast->title ?>"
class="object-cover w-32 h-32"
/>
<img src="<?= $podcast->image->thumbnail_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-32 h-32" />
<?= form_input([
'id' => 'image',
'name' => 'image',
@ -34,11 +33,14 @@
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
]) ?>
<small class="mb-4 text-gray-600"><?= lang(
'Common.forms.image_size_hint',
) ?></small>
'Common.forms.image_size_hint',
) ?></small>
<?= form_label(lang('Podcast.form.title'), 'title') ?>
<?= form_input([
'id' => 'title',
'name' => 'title',
@ -46,46 +48,52 @@
'value' => old('title', $podcast->title),
'required' => 'required',
]) ?>
<span class="mb-4 text-sm"><?= $podcast->link ?></span>
<?= form_fieldset('', ['class' => 'mb-4']) ?>
<legend><?= lang('Podcast.form.type.label') .
hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
</legend>
<?= form_radio(
['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'],
'episodic',
old('type') ? old('type') == 'episodic' : $podcast->type == 'episodic',
) ?>
<label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
<?= form_radio(
['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'],
'serial',
old('type') ? old('type') == 'serial' : $podcast->type == 'serial',
) ?>
<label for="serial"><?= lang('Podcast.form.type.serial') ?></label>
<legend><?= lang('Podcast.form.type.label') .
hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
</legend>
<?= form_radio(
['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'],
'episodic',
old('type') ? old('type') == 'episodic' : $podcast->type == 'episodic',
) ?>
<label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
<?= form_radio(
['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'],
'serial',
old('type') ? old('type') == 'serial' : $podcast->type == 'serial',
) ?>
<label for="serial"><?= lang('Podcast.form.type.serial') ?></label>
<?= form_fieldset_close() ?>
<div class="mb-4">
<?= form_label(lang('Podcast.form.description'), 'description') ?>
<?= form_markdown_editor([
<?= component(
'Forms/MarkdownEditor',
[
'content' => old('description', $podcast->description_markdown, false)
],
[
'id' => 'description',
'name' => 'description',
'required' => 'required',
],
old('description', $podcast->description_markdown, false)
) ?>
</div>
<?= form_section_close() ?>
<?= form_section(
lang('Podcast.form.classification_section_title'),
lang('Podcast.form.classification_section_subtitle'),
lang('Podcast.form.classification_section_title'),
lang('Podcast.form.classification_section_subtitle'),
) ?>
<?= form_label(lang('Podcast.form.language'), 'language') ?>
<?= form_dropdown(
'language',
$languageOptions,
@ -101,7 +109,7 @@
<?= form_dropdown(
'category',
$categoryOptions,
[old('category', (string) $podcast->category_id)],
[old('category', $podcast->category_id)],
[
'id' => 'category',
'class' => 'form-select mb-4',
@ -116,65 +124,63 @@
'',
true,
) ?>
<?= form_multiselect(
'other_categories[]',
$categoryOptions,
old('other_categories', $podcast->other_categories_ids),
[
'id' => 'other_categories',
'class' => 'mb-4',
'data-max-item-count' => '2',
],
) ?>
<?= component('Forms/MultiSelect', ['options' => $categoryOptions, 'selected' => old('other_categories', $podcast->other_categories_ids)], [
'id' => 'other_categories',
'name' => 'other_categories[]',
'class' => 'mb-4',
'data-max-item-count' => '2',
]) ?>
<?= form_fieldset('', ['class' => 'mb-4']) ?>
<legend><?= lang('Podcast.form.parental_advisory.label') .
hint_tooltip(lang('Podcast.form.parental_advisory.hint'), 'ml-1') ?>
</legend>
<?= form_radio(
[
'id' => 'undefined',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'undefined',
old('parental_advisory')
? old('parental_advisory') === 'undefined'
: $podcast->parental_advisory === null,
) ?>
<label for="undefined"><?= lang(
'Podcast.form.parental_advisory.undefined',
) ?></label>
<?= form_radio(
[
'id' => 'clean',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'clean',
old('parental_advisory')
? old('parental_advisory') === 'clean'
: $podcast->parental_advisory === 'clean',
) ?>
<label for="clean"><?= lang(
'Podcast.form.parental_advisory.clean',
) ?></label>
<?= form_radio(
[
'id' => 'explicit',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'explicit',
old('parental_advisory')
? old('parental_advisory') === 'explicit'
: $podcast->parental_advisory === 'explicit',
) ?>
<label for="explicit"><?= lang(
'Podcast.form.parental_advisory.explicit',
) ?></label>
<?= form_fieldset_close() ?>
<legend><?= lang('Podcast.form.parental_advisory.label') .
hint_tooltip(lang('Podcast.form.parental_advisory.hint'), 'ml-1') ?></legend>
<?= form_radio(
[
'id' => 'undefined',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'undefined',
old('parental_advisory')
? old('parental_advisory') === 'undefined'
: $podcast->parental_advisory === null,
) ?>
<label for="undefined"><?= lang(
'Podcast.form.parental_advisory.undefined',
) ?></label>
<?= form_radio(
[
'id' => 'clean',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'clean',
old('parental_advisory')
? old('parental_advisory') === 'clean'
: $podcast->parental_advisory === 'clean',
) ?>
<label for="clean"><?= lang(
'Podcast.form.parental_advisory.clean',
) ?></label>
<?= form_radio(
[
'id' => 'explicit',
'name' => 'parental_advisory',
'class' => 'form-radio-btn',
],
'explicit',
old('parental_advisory')
? old('parental_advisory') === 'explicit'
: $podcast->parental_advisory === 'explicit',
) ?>
<label for="explicit"><?= lang(
'Podcast.form.parental_advisory.explicit',
) ?></label>
<?= form_fieldset_close() ?>
<?= form_section_close() ?>
<?= form_section(
@ -188,6 +194,7 @@
[],
lang('Podcast.form.owner_name_hint'),
) ?>
<?= form_input([
'id' => 'owner_name',
'name' => 'owner_name',
@ -202,6 +209,7 @@
[],
lang('Podcast.form.owner_email_hint'),
) ?>
<?= form_input([
'id' => 'owner_email',
'name' => 'owner_email',
@ -218,6 +226,7 @@
lang('Podcast.form.publisher_hint'),
true,
) ?>
<?= form_input([
'id' => 'publisher',
'name' => 'publisher',
@ -226,6 +235,7 @@
]) ?>
<?= form_label(lang('Podcast.form.copyright'), 'copyright', [], '', true) ?>
<?= form_input([
'id' => 'copyright',
'name' => 'copyright',
@ -247,12 +257,14 @@
lang('Podcast.form.location_name_hint'),
true,
) ?>
<?= form_input([
'id' => 'location_name',
'name' => 'location_name',
'class' => 'form-input mb-4',
'value' => old('location_name', $podcast->location_name),
]) ?>
<?= form_section_close() ?>
<?= form_section(
@ -267,6 +279,7 @@
lang('Podcast.form.payment_pointer_hint'),
true,
) ?>
<?= form_input([
'id' => 'payment_pointer',
'name' => 'payment_pointer',
@ -285,41 +298,41 @@
true,
) ?>
<?= form_input([
'id' => 'partner_id',
'name' => 'partner_id',
'class' => 'form-input w-full',
'value' => old('partner_id', $podcast->partner_id),
]) ?>
'id' => 'partner_id',
'name' => 'partner_id',
'class' => 'form-input w-full',
'value' => old('partner_id', $podcast->partner_id),
]) ?>
</div>
<div class="flex flex-col flex-1">
<?= form_label(
lang('Podcast.form.partner_link_url'),
'partner_link_url',
[],
lang('Podcast.form.partner_link_url_hint'),
true,
) ?>
lang('Podcast.form.partner_link_url'),
'partner_link_url',
[],
lang('Podcast.form.partner_link_url_hint'),
true,
) ?>
<?= form_input([
'id' => 'partner_link_url',
'name' => 'partner_link_url',
'class' => 'form-input w-full',
'value' => old('partner_link_url', $podcast->partner_link_url),
]) ?>
'id' => 'partner_link_url',
'name' => 'partner_link_url',
'class' => 'form-input w-full',
'value' => old('partner_link_url', $podcast->partner_link_url),
]) ?>
</div>
<div class="flex flex-col flex-1">
<?= form_label(
lang('Podcast.form.partner_image_url'),
'partner_image_url',
[],
lang('Podcast.form.partner_image_url_hint'),
true,
) ?>
<?= form_input([
'id' => 'partner_image_url',
'name' => 'partner_image_url',
'class' => 'form-input w-full',
'value' => old('partner_image_url', $podcast->partner_image_url),
]) ?>
lang('Podcast.form.partner_image_url'),
'partner_image_url',
[],
lang('Podcast.form.partner_image_url_hint'),
true,
) ?>
<?= form_input([
'id' => 'partner_image_url',
'name' => 'partner_image_url',
'class' => 'form-input w-full',
'value' => old('partner_image_url', $podcast->partner_image_url),
]) ?>
</div>
</div>
<?= form_section_close() ?>
@ -328,6 +341,7 @@
lang('Podcast.form.advanced_section_title'),
lang('Podcast.form.advanced_section_subtitle'),
) ?>
<?= form_label(
lang('Podcast.form.custom_rss'),
'custom_rss',
@ -335,12 +349,17 @@
lang('Podcast.form.custom_rss_hint'),
true,
) ?>
<?= xml_editor([
'id' => 'custom_rss',
'name' => 'custom_rss',
'class' => 'form-textarea',
'value' => old('custom_rss', $podcast->custom_rss_string),
]) ?>
<?= component('Forms/XMLEditor',
[
'content' => old('custom_rss', $podcast->custom_rss_string)
],
[
'id' => 'custom_rss',
'name' => 'custom_rss',
]
) ?>
<?= form_section_close() ?>
<?= form_section(
@ -348,40 +367,62 @@
lang('Podcast.form.status_section_subtitle'),
) ?>
<?= form_switch(
lang('Podcast.form.block'),
['id' => 'block', 'name' => 'block'],
'yes',
old('block', $podcast->is_blocked),
'mb-2',
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.lock'),
'hint' => lang('Podcast.form.lock_hint'),
],
[
'id' => 'lock',
'name' => 'lock',
'value' => 'yes',
'checked' => old('complete', $podcast->is_locked),
'class' => 'mb-2'
]
) ?>
<?= form_switch(
lang('Podcast.form.complete'),
['id' => 'complete', 'name' => 'complete'],
'yes',
old('complete', $podcast->is_completed),
'mb-2',
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.block'),
],
[
'id' => 'block',
'name' => 'block',
'value' => 'yes',
'checked' => old('block', $podcast->is_blocked),
'class' => 'mb-2'
]
) ?>
<?= form_switch(
lang('Podcast.form.lock') .
hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'),
['id' => 'lock', 'name' => 'lock'],
'yes',
old('lock', $podcast->is_locked),
<?= component(
'Forms/Toggler',
[
'label' => lang('Podcast.form.complete'),
],
[
'id' => 'complete',
'name' => 'complete',
'value' => 'yes',
'checked' => old('complete', $podcast->is_completed),
]
) ?>
<?= form_section_close() ?>
<?= button(
lang('Podcast.form.submit_edit'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
<?= component(
'Button',
[
'label' => lang('Podcast.form.submit_edit'),
'variant' => 'primary',
],
[
'type' => 'submit',
'class' => 'self-end'
]
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -99,10 +99,12 @@
[],
lang('Person.podcast_form.persons_hint'),
) ?>
<?= form_multiselect('persons[]', $personOptions, old('persons', []), [
<?= component('Forms/MultiSelect', ['options' => $personOptions, 'selected' => old('persons', [])], [
'id' => 'persons',
'class' => 'form-select mb-4',
'name' => 'persons[]',
'class' => 'mb-4',
'required' => 'required',
'data-max-item-count' => '2',
]) ?>
<?= form_label(
@ -112,9 +114,10 @@
lang('Person.podcast_form.roles_hint'),
true,
) ?>
<?= form_multiselect('roles[]', $taxonomyOptions, old('roles', []), [
<?= component('Forms/MultiSelect', ['options' => $taxonomyOptions, 'selected' => old('roles', [])], [
'id' => 'roles',
'class' => 'form-select mb-4',
'name' => 'roles[]',
'class' => 'mb-4',
]) ?>
<?= form_section_close() ?>

View File

@ -103,34 +103,39 @@
'type' => 'text',
'placeholder' => lang("Platforms.description.{$platform->type}"),
]) ?>
<?= form_switch(
lang('Platforms.visible'),
<?= component(
'Forms/Toggler',
[
'label' => lang('Platforms.visible'),
],
[
'id' => $platform->slug . '_visible',
'name' => 'platforms[' . $platform->slug . '][visible]',
],
'yes',
old(
$platform->slug . '_visible',
$platform->is_visible ? $platform->is_visible : false,
),
'text-sm mb-1',
'value' => 'yes',
'checked' => old(
$platform->slug . '_visible',
$platform->is_visible ? $platform->is_visible : false,
),
'class' => 'text-sm mb-1'
]
) ?>
<?= form_switch(
lang('Platforms.on_embeddable_player'),
<?= component(
'Forms/Toggler',
[
'label' => lang('Platforms.on_embeddable_player'),
],
[
'id' => $platform->slug . '_on_embeddable_player',
'name' =>
'platforms[' . $platform->slug . '][on_embeddable_player]',
],
'yes',
old(
$platform->slug . '_on_embeddable_player',
$platform->is_on_embeddable_player
? $platform->is_on_embeddable_player
: false,
),
'text-sm',
'name' => 'platforms[' . $platform->slug . '][on_embeddable_player]',
'value' => 'yes',
'checked' => old(
$platform->slug . '_on_embeddable_player',
$platform->is_on_embeddable_player
? $platform->is_on_embeddable_player
: false,
),
'class' => 'text-sm'
]
) ?>
</div>
</div>

View File

@ -17,8 +17,9 @@
<?= csrf_field() ?>
<?= form_label(lang('User.form.roles'), 'roles') ?>
<?= form_multiselect('roles[]', $roleOptions, $user->roles, [
<?= component('Forms/MultiSelect', ['options' => $roleOptions, 'selected' => $user->roles], [
'id' => 'roles',
'name' => 'roles[]',
'class' => 'mb-4',
]) ?>

View File

View File

@ -1,4 +1,4 @@
<?= $this->include('podcast/_partials/comment_card_authenticated') ?>
<?= $this->include('podcast/_partials/comment_card_authenticated') ?>
<div class="-mt-2 overflow-hidden border-b border-l border-r post-replies rounded-b-xl">
<?= form_open(
route_to('comment-attempt-reply', $podcast->id, $episode->id, $comment->id),

View File

@ -38,7 +38,6 @@
'Podcast.episodes',
) ?></a>
</nav>
<section class="max-w-2xl px-6 py-8 mx-auto space-y-8">
<?php foreach ($posts as $post): ?>
<?php if ($post->reblog_of_id !== null): ?>

View File

@ -1,6 +1,7 @@
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\CodingStandard\Fixer\Naming\StandardizeHereNowDocKeywordFixer;
use Symplify\EasyCodingStandard\ValueObject\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
@ -20,6 +21,10 @@ return static function (ContainerConfigurator $containerConfigurator): void {
// skip specific generated files
__DIR__ . '/app/Language/*/PersonsTaxonomy.php',
StandardizeHereNowDocKeywordFixer::class => [
__DIR__ . '/app/Views/Components',
]
]);
$containerConfigurator->import(SetList::PSR_12);

View File

@ -35,3 +35,4 @@ parameters:
- app/Helpers
- app/Libraries/ActivityPub/Helpers
- app/Libraries/Analytics/Helpers
- app/Libraries/ViewComponents/Helpers

View File

@ -65,7 +65,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
],
]);
// Path to phpstan with extensions, that PHPSTan in Rector uses to determine types
// Path to phpstan with extensions, that PHPStan in Rector uses to determine types
$parameters->set(
Option::PHPSTAN_FOR_RECTOR_PATH,
__DIR__ . '/phpstan.neon',

View File

@ -4,6 +4,7 @@ module.exports = {
mode: "jit",
purge: [
"./app/Views/**/*.php",
"./app/View/Components/**/*.php",
"./app/Helpers/*.php",
"./app/Resources/**/*.ts",
],