feat(plugins): add defaultValue for all field types

This commit is contained in:
Yassine Doghri 2024-12-19 12:33:57 +00:00
parent 00bd4c02ee
commit d3a98db6d0
41 changed files with 204 additions and 178 deletions

View File

@ -55,7 +55,7 @@ use RuntimeException;
* @property string $language_code
* @property int $category_id
* @property Category|null $category
* @property int[] $other_categories_ids
* @property string $other_categories_ids
* @property Category[] $other_categories
* @property string|null $parental_advisory
* @property string|null $publisher
@ -111,10 +111,7 @@ class Podcast extends Entity
*/
protected ?array $other_categories = null;
/**
* @var string[]|null
*/
protected ?array $other_categories_ids = null;
protected string $other_categories_ids = '';
/**
* @var Episode[]|null
@ -526,13 +523,10 @@ class Podcast extends Entity
return $this->other_categories;
}
/**
* @return int[]|string[]
*/
public function getOtherCategoriesIds(): array
public function getOtherCategoriesIds(): string
{
if ($this->other_categories_ids === null) {
$this->other_categories_ids = array_column($this->getOtherCategories(), 'id');
if ($this->other_categories_ids === '') {
$this->other_categories_ids = implode(',', array_column($this->getOtherCategories(), 'id'));
}
return $this->other_categories_ids;

View File

@ -136,6 +136,21 @@ export class XMLEditor extends LitElement {
overflow: hidden;
border: 3px solid hsl(var(--color-border-contrast));
background-color: hsl(var(--color-background-elevated));
transition-property:
color,
background-color,
border-color,
text-decoration-color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.cm-editor.cm-focused {
outline: 2px solid transparent;

View File

@ -1,6 +1,6 @@
@layer components {
.form-radio-btn {
@apply absolute right-4 top-4 border-contrast border-3 text-accent-base;
@apply absolute right-4 top-4 border-contrast border-3 text-accent-base transition;
&:focus {
@apply ring-accent;

View File

@ -9,7 +9,7 @@ use Override;
class Checkbox extends FormComponent
{
protected array $props = ['hint', 'helper', 'isChecked'];
protected array $props = ['hint', 'helper'];
protected array $casts = [
'isChecked' => 'boolean',
@ -19,8 +19,6 @@ class Checkbox extends FormComponent
protected string $helper = '';
protected bool $isChecked = false;
#[Override]
public function render(): string
{
@ -28,10 +26,10 @@ class Checkbox extends FormComponent
[
'id' => $this->id,
'name' => $this->name,
'class' => 'form-checkbox bg-elevated text-accent-base border-contrast border-3 w-6 h-6',
'class' => 'form-checkbox bg-elevated text-accent-base border-contrast border-3 focus:ring-accent w-6 h-6 transition',
],
'yes',
old($this->name) ? old($this->name) === 'yes' : $this->isChecked,
in_array($this->getValue(), ['yes', 'true', 'on', '1'], true),
);
$hint = $this->hint === '' ? '' : (new Hint([

View File

@ -18,20 +18,18 @@ class CodeEditor extends FormComponent
'class' => 'textarea',
];
protected string $content = '';
protected string $lang = '';
public function setContent(string $value): void
public function setValue(string $value): void
{
$this->content = htmlspecialchars_decode($value);
$this->value = htmlspecialchars_decode($value);
}
#[Override]
public function render(): string
{
$this->attributes['slot'] = 'textarea';
$textarea = form_textarea($this->attributes, $this->content);
$textarea = form_textarea($this->attributes, $this->getValue());
return <<<HTML
<code-editor lang="{$this->lang}">{$textarea}</code-editor>

View File

@ -19,14 +19,14 @@ class DatetimePicker extends FormComponent
'name' => $this->name,
'class' => 'rounded-l-lg border-0 border-rounded-r-none flex-1 focus:ring-0',
'data-input' => '',
], old($this->name, (string) $this->value));
], $this->getValue());
$clearLabel = lang(
'Episode.publish_form.scheduled_publication_date_clear',
);
$closeIcon = icon('close-fill');
$this->mergeClass('flex border-3 rounded-lg border-contrast focus-within:ring-accent');
$this->mergeClass('flex border-3 rounded-lg border-contrast focus-within:ring-accent transition');
return <<<HTML
<div {$this->getStringifiedAttributes()}>

View File

@ -12,6 +12,7 @@ abstract class FormComponent extends Component
'id',
'name',
'value',
'defaultValue',
'isRequired',
'isReadonly',
];
@ -25,10 +26,9 @@ abstract class FormComponent extends Component
protected string $name;
/**
* @var null|string|list<string>
*/
protected null|string|array $value = null;
protected string $value = '';
protected string $defaultValue = '';
protected bool $isRequired = false;
@ -60,4 +60,9 @@ abstract class FormComponent extends Component
$this->attributes['readonly'] = 'readonly';
}
}
protected function getValue(): string
{
return old($this->name, $this->value === '' ? $this->defaultValue : $this->value);
}
}

View File

@ -15,7 +15,7 @@ class Input extends FormComponent
#[Override]
public function render(): string
{
$this->mergeClass('w-full border-contrast rounded-lg focus:border-contrast border-3 focus-within:ring-accent');
$this->mergeClass('w-full border-contrast rounded-lg focus:border-contrast border-3 focus-within:ring-accent transition');
if ($this->type === 'file') {
$this->mergeClass('file:px-3 file:py-2 file:h-[40px] file:font-semibold file:text-skin-muted file:text-sm file:rounded-none file:border-none file:bg-highlight file:cursor-pointer');
@ -30,8 +30,7 @@ class Input extends FormComponent
}
$this->attributes['type'] = $this->type;
$this->attributes['value'] = $this->value;
return form_input($this->attributes, old($this->name, (string) $this->value));
return form_input($this->attributes, $this->getValue());
}
}

View File

@ -23,7 +23,7 @@ class MarkdownEditor extends FormComponent
#[Override]
public function render(): string
{
$this->mergeClass('w-full flex flex-col bg-elevated border-3 border-contrast rounded-lg overflow-hidden focus-within:ring-accent');
$this->mergeClass('w-full flex flex-col bg-elevated border-3 border-contrast rounded-lg overflow-hidden focus-within:ring-accent transition');
$wrapperClass = $this->attributes['class'];
$this->attributes['class'] = 'bg-elevated border-none focus:border-none focus:outline-none focus:ring-0 w-full h-full';
@ -31,7 +31,7 @@ class MarkdownEditor extends FormComponent
$textarea = form_textarea(
$this->attributes,
old($this->name, (string) $this->value)
$this->getValue()
);
$markdownIcon = (string) icon('markdown-fill', [
'class' => 'mr-1 text-lg opacity-40',

View File

@ -19,10 +19,10 @@ class PermalinkEditor extends FormComponent
#[Override]
public function render(): string
{
$this->mergeClass('flex-1 text-xs border-contrast rounded-lg focus:border-contrast border-3 focus-within:ring-accent');
$this->mergeClass('flex-1 text-xs border-contrast rounded-lg focus:border-contrast border-3 focus-within:ring-accent transition');
$this->attributes['slot'] = 'slug-input';
$input = form_input($this->attributes, old($this->name, (string) $this->value));
$input = form_input($this->attributes, $this->getValue());
$editLabel = lang('Common.edit');
$copyLabel = lang('Common.copy');

View File

@ -23,9 +23,9 @@ class Radio extends FormComponent
[
'id' => $this->value,
'name' => $this->name,
'class' => 'text-accent-base bg-elevated border-contrast border-3 w-6 h-6',
'class' => 'text-accent-base bg-elevated border-contrast border-3 focus:ring-accent w-6 h-6 transition',
],
$this->value,
$this->getValue(),
old($this->name) ? old($this->name) === $this->value : $this->isChecked,
);

View File

@ -44,7 +44,7 @@ class RadioButton extends FormComponent
$radioInput = form_radio(
$data,
$this->value,
$this->getValue(),
old($this->name) ? old($this->name) === $this->value : $this->isSelected,
);

View File

@ -30,17 +30,18 @@ class RadioGroup extends FormComponent
public function render(): string
{
$this->mergeClass('flex flex-col');
$options = '';
foreach ($this->options as $option) {
$options .= (new RadioButton([
$radioButtonData = [
'value' => $option['value'],
'name' => $this->name,
'slot' => $option['label'],
'description' => $option['description'] ?? '',
'isSelected' => var_export($this->value === null ? ($option['value'] === $this->options[array_key_first($this->options)]['value']) : ($this->value === $option['value']), true),
'isSelected' => var_export($this->getValue() === '' ? $option['value'] === $this->options[0]['value'] : $option['value'] === $this->getValue(), true),
'isRequired' => var_export($this->isRequired, true),
]))->render();
];
$options .= (new RadioButton($radioButtonData))->render();
}
$helperText = '';

View File

@ -8,7 +8,7 @@ use Override;
class Select extends FormComponent
{
protected array $props = ['options', 'defaultValue'];
protected array $props = ['options'];
protected array $casts = [
'options' => 'array',
@ -19,8 +19,6 @@ class Select extends FormComponent
*/
protected array $options = [];
protected string $defaultValue = '';
#[Override]
public function render(): string
{
@ -35,7 +33,7 @@ class Select extends FormComponent
$this->attributes = [...$defaultAttributes, ...$this->attributes];
$options = '';
$selected = $this->value ?? $this->defaultValue;
$selected = $this->getValue();
foreach ($this->options as $option) {
$options .= '<option ' . (array_key_exists('description', $option) ? 'data-label-description="' . $option['description'] . '" ' : '') . 'value="' . $option['value'] . '"' . ($option['value'] === $selected ? ' selected' : '') . '>' . $option['label'] . '</option>';
}

View File

@ -8,12 +8,10 @@ use Override;
class SelectMulti extends FormComponent
{
protected array $props = ['options', 'defaultValue'];
protected array $props = ['options'];
protected array $casts = [
'value' => 'array',
'options' => 'array',
'defaultValue' => 'array',
'options' => 'array',
];
/**
@ -21,11 +19,6 @@ class SelectMulti extends FormComponent
*/
protected array $options = [];
/**
* @var list<string>
*/
protected array $defaultValue = [];
#[Override]
public function render(): string
{
@ -43,9 +36,9 @@ class SelectMulti extends FormComponent
$this->attributes = [...$defaultAttributes, ...$this->attributes];
$options = '';
$selected = $this->value ?? $this->defaultValue;
$selected = explode(',', $this->getValue()) ?? [];
foreach ($this->options as $option) {
$options .= '<option ' . (array_key_exists('description', $option) ? 'data-label-description="' . $option['description'] . '" ' : '') . 'value="' . $option['value'] . '"' . (in_array($option['value'], $selected, true) ? ' selected' : '') . '>' . $option['label'] . '</option>';
$options .= '<option ' . (array_key_exists('description', $option) ? 'data-label-description="' . $option['description'] . '" ' : '') . 'value="' . $option['value'] . '"' . (in_array((string) $option['value'], $selected, true) ? ' selected' : '') . '>' . $option['label'] . '</option>';
}
$this->attributes['name'] = $this->name . '[]';

View File

@ -8,24 +8,19 @@ use Override;
class Textarea extends FormComponent
{
public function setValue(?string $value): void
public function setValue(string $value): void
{
if ($value) {
$this->value = htmlspecialchars_decode($value);
}
$this->value = htmlspecialchars_decode($value);
}
#[Override]
public function render(): string
{
$this->mergeClass('bg-elevated w-full rounded-lg border-3 border-contrast focus:border-contrast focus-within:ring-accent');
$this->mergeClass('bg-elevated w-full rounded-lg border-3 border-contrast focus:border-contrast focus-within:ring-accent transition');
$this->attributes['id'] = $this->id;
$textarea = form_textarea(
$this->attributes,
old($this->name, $this->value ?? '', false)
);
$textarea = form_textarea($this->attributes, $this->getValue());
return <<<HTML
{$textarea}

View File

@ -9,7 +9,7 @@ use Override;
class Toggler extends FormComponent
{
protected array $props = ['size', 'hint', 'helper', 'isChecked'];
protected array $props = ['size', 'hint', 'helper'];
protected array $casts = [
'isChecked' => 'boolean',
@ -33,7 +33,7 @@ class Toggler extends FormComponent
'class' => 'form-switch',
],
'yes',
old($this->name) ? old($this->name) === 'yes' : $this->isChecked
in_array($this->getValue(), ['yes', 'true', 'on', '1'], true),
);
$hint = $this->hint === '' ? '' : (new Hint([

View File

@ -101,16 +101,17 @@ each property being a field key and the value being a `Field` object.
A field is a form element:
| Property | Type | Note |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `type` | `checkbox` \| `datetime` \| `email` \| `group` \| `html` \| `markdown` \| `number` \| `radio-group` \| `rss` \| `select-multiple` \| `select` \| `text` \| `textarea` \| `toggler` \| `url` | Default is `text` |
| `label` (required) | `string` | Can be translated (see i18n) |
| `hint` | `string` | Can be translated (see i18n) |
| `helper` | `string` | Can be translated (see i18n) |
| `optional` | `boolean` | Default is `false` |
| `options` | `Options` | Required for `radio-group`, `select-multiple`, and `select` types. |
| `multiple` | `boolean` | Default is `false` |
| `fields` | `Array<string, Field>` | Required for `group` type |
| Property | Type | Note |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `type` | `checkbox` \| `datetime` \| `email` \| `group` \| `html` \| `markdown` \| `number` \| `radio-group` \| `rss` \| `select-multiple` \| `select` \| `text` \| `textarea` \| `toggler` \| `url` | Default is `text` |
| `label` (required) | `string` | Can be translated (see i18n) |
| `hint` | `string` | Can be translated (see i18n) |
| `helper` | `string` | Can be translated (see i18n) |
| `defaultValue` | `string` | You can specify multiple comma separated values for `select-multiple` |
| `optional` | `boolean` | Default is `false` |
| `options` | `Options` | Required for `radio-group`, `select-multiple`, and `select` types. |
| `multiple` | `boolean` | Default is `false` |
| `fields` | `Array<string, Field>` | Required for `group` type |
#### Options object

View File

@ -340,9 +340,10 @@ class PluginController extends BaseController
$value,
$this->request->getPost('client_timezone')
)->setTimezone(app_timezone()),
'markdown' => new Markdown($value),
'rss' => new RSS($value),
default => $value,
'markdown' => new Markdown($value),
'rss' => new RSS($value),
'comma-separated-string' => implode(',', $value),
default => $value,
};
}
}

View File

@ -52,13 +52,14 @@ class Plugins
];
public const FIELDS_CASTS = [
'checkbox' => 'bool',
'datetime' => 'datetime',
'markdown' => 'markdown',
'number' => 'int',
'rss' => 'rss',
'toggler' => 'bool',
'url' => 'uri',
'checkbox' => 'bool',
'datetime' => 'datetime',
'markdown' => 'markdown',
'number' => 'int',
'rss' => 'rss',
'toggler' => 'bool',
'url' => 'uri',
'select-multiple' => 'comma-separated-string',
];
/**

View File

@ -12,6 +12,7 @@ use Override;
* @property string $label
* @property string $hint
* @property string $helper
* @property string $defaultValue
* @property bool $optional
* @property Option[] $options
* @property bool $multiple
@ -20,15 +21,16 @@ use Override;
class Field extends ManifestObject
{
protected const VALIDATION_RULES = [
'type' => 'permit_empty|in_list[checkbox,datetime,email,group,html,markdown,number,radio-group,rss,select-multiple,select,text,textarea,toggler,url]',
'key' => 'required|alpha_dash',
'label' => 'required|string',
'hint' => 'permit_empty|string',
'helper' => 'permit_empty|string',
'optional' => 'permit_empty|is_boolean',
'options' => 'permit_empty|is_list',
'multiple' => 'permit_empty|is_boolean',
'fields' => 'permit_empty|is_list',
'type' => 'permit_empty|in_list[checkbox,datetime,email,group,html,markdown,number,radio-group,rss,select-multiple,select,text,textarea,toggler,url]',
'key' => 'required|alpha_dash',
'label' => 'required|string',
'hint' => 'permit_empty|string',
'helper' => 'permit_empty|string',
'defaultValue' => 'permit_empty|string',
'optional' => 'permit_empty|is_boolean',
'options' => 'permit_empty|is_list',
'multiple' => 'permit_empty|is_boolean',
'fields' => 'permit_empty|is_list',
];
protected const CASTS = [
@ -46,6 +48,8 @@ class Field extends ManifestObject
protected string $helper = '';
protected string $defaultValue = '';
protected bool $optional = false;
protected bool $multiple = false;

View File

@ -204,6 +204,9 @@
"optional": {
"type": "boolean"
},
"defaultValue": {
"type": "string"
},
"options": {
"type": "object",
"patternProperties": {

View File

@ -4,15 +4,17 @@
continue;
}
$isSectionActive = false;
$activeSection = '';
$activeItem = '';
foreach ($data['items'] as $item) {
$href = str_starts_with($item, '/') ? $item : route_to($item, $podcastId ?? null, $episodeId ?? null);
// TODO: use glob to show active section when current url starts with item
if (url_is($href)) {
$activeItem = $item;
$isSectionActive = true;
$activeSection = $section;
}
}
$isSectionActive = $section === $activeSection;
?>
<details <?= $isSectionActive ? 'open="open"' : '' ?> class="<?= $isSectionActive ? 'bg-navigation-active' : '' ?> [&[open]>summary::after]:rotate-90">
<summary class="inline-flex items-center w-full h-12 px-4 py-2 font-semibold after:w-5 after:h-5 after:transition-transform after:content-chevronRightIcon after:ml-2 after:opacity-60 after:text-white">
@ -22,7 +24,7 @@
]) ?>
<?= lang($langKey . '.' . $section) ?>
<?php if (array_key_exists('count', $data)): ?>
<a href="<?= route_to($data['count-route'], $podcastId ?? null, $episodeId ?? null) ?>" class="px-2 ml-2 text-xs font-normal rounded-full <?= $isSectionActive ? 'bg-navigation' : 'bg-navigation-active' ?>"><?= $data['count'] ?></a>
<a href="<?= route_to($data['count-route'], $podcastId ?? null, $episodeId ?? null) ?>" class="px-2 ml-2 text-xs font-normal rounded-full <?= $activeSection ? 'bg-navigation' : 'bg-navigation-active' ?>"><?= $data['count'] ?></a>
<?php endif; ?>
</div>
<?php if(array_key_exists('add-cta', $data)): ?>

View File

@ -16,7 +16,7 @@
'podcastTitle' => $podcast->title,
]) ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('Contributor.delete_form.understand', [
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('Contributor.delete_form.understand', [
'contributor' => $contributor->username,
'podcastTitle' => $podcast->title,
]) ?></x-Forms.Checkbox>

View File

@ -126,7 +126,7 @@
</x-Forms.Section>
<x-Forms.Section title="<?= lang('Episode.form.premium_title') ?>">
<x-Forms.Toggler class="mt-2" name="premium" isChecked="<?= $podcast->is_premium_by_default ? 'true' : 'false' ?>">
<x-Forms.Toggler class="mt-2" name="premium" defaultValue="<?= $podcast->is_premium_by_default ? 'yes' : '' ?>">
<?= lang('Episode.form.premium') ?></x-Forms.Toggler>
</x-Forms.Section>
@ -200,7 +200,7 @@
subtitle="<?= lang('Episode.form.advanced_section_subtitle') ?>"
>
<x-Forms.Toggler name="block" isChecked="false" hint="<?= esc(lang('Episode.form.block_hint')) ?>"><?= lang('Episode.form.block') ?></x-Forms.Toggler>
<x-Forms.Toggler name="block" hint="<?= esc(lang('Episode.form.block_hint')) ?>"><?= lang('Episode.form.block') ?></x-Forms.Toggler>
</x-Forms.Section>

View File

@ -11,7 +11,7 @@
<x-Alert variant="danger" class="font-semibold"><?= lang('Episode.delete_form.disclaimer') ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('Episode.delete_form.understand') ?></x-Forms.Checkbox>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('Episode.delete_form.understand') ?></x-Forms.Checkbox>
<div class="self-end mt-4">
<x-Button uri="<?= route_to('episode-view', $podcast->id, $episode->id) ?>"><?= lang('Common.cancel') ?></x-Button>

View File

@ -137,7 +137,7 @@
</x-Forms.Section>
<x-Forms.Section title="<?= lang('Episode.form.premium_title') ?>" >
<x-Forms.Toggler class="mt-2" name="premium" isChecked="<?= $episode->is_premium ? 'true' : 'false' ?>">
<x-Forms.Toggler class="mt-2" name="premium" value="<?= $episode->is_premium ? 'yes' : '' ?>">
<?= lang('Episode.form.premium') ?></x-Forms.Toggler>
</x-Forms.Section>
@ -274,7 +274,7 @@
subtitle="<?= lang('Episode.form.advanced_section_subtitle') ?>"
>
<x-Forms.Toggler id="block" name="block" isChecked="<?= $episode->is_blocked ? 'true' : 'false' ?>" hint="<?= esc(lang('Episode.form.block_hint')) ?>"><?= lang('Episode.form.block') ?></x-Forms.Toggler>
<x-Forms.Toggler id="block" name="block" value="<?= $episode->is_blocked ? 'yes' : '' ?>" hint="<?= esc(lang('Episode.form.block_hint')) ?>"><?= lang('Episode.form.block') ?></x-Forms.Toggler>
</x-Forms.Section>

View File

@ -26,7 +26,7 @@
label="<?= esc(lang('Person.episode_form.persons')) ?>"
hint="<?= esc(lang('Person.episode_form.persons_hint')) ?>"
options="<?= esc(json_encode($personOptions)) ?>"
defaultValue="<?= esc(json_encode(old('persons', []))) ?>"
defaultValue="<?= implode(',', (old('persons', []))) ?>"
isRequired="true"
/>
@ -37,7 +37,7 @@
label="<?= esc(lang('Person.episode_form.roles')) ?>"
hint="<?= esc(lang('Person.episode_form.roles_hint')) ?>"
options="<?= esc(json_encode($taxonomyOptions)) ?>"
defaultValue="<?= esc(json_encode(old('roles', []))) ?>"
defaultValue="<?= implode(',', (old('roles', []))) ?>"
/>
<x-Button variant="primary" type="submit" class="self-end"><?= lang('Person.episode_form.submit_add') ?></x-Button>

View File

@ -11,7 +11,7 @@
<x-Alert variant="danger" class="font-semibold"><?= lang('Episode.unpublish_form.disclaimer') ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('Episode.unpublish_form.understand') ?></x-Forms.Checkbox>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('Episode.unpublish_form.understand') ?></x-Forms.Checkbox>
<div class="self-end mt-4">
<x-Button uri="<?= route_to('episode-view', $podcast->id, $episode->id) ?>"><?= lang('Common.cancel') ?></x-Button>

View File

@ -4,7 +4,8 @@
name="<?= $name ?>"
hint="<?= $hint ?>"
helper="<?= $helper ?>"
isChecked="<?= $value ? 'true' : 'false' ?>"
value="<?= $value ? 'yes' : '' ?>"
defaultValue="<?= $defaultValue ?>"
><?= $label ?></x-Forms.Checkbox>
<?php break;
case 'toggler': ?>
@ -13,7 +14,8 @@ case 'toggler': ?>
name="<?= $name ?>"
hint="<?= $hint ?>"
helper="<?= $helper ?>"
isChecked="<?= $value ? 'true' : 'false' ?>"
value="<?= $value ? 'yes' : '' ?>"
defaultValue="<?= $defaultValue ?>"
><?= $label ?></x-Forms.Toggler>
<?php break;
case 'radio-group': ?>
@ -26,6 +28,7 @@ case 'radio-group': ?>
options="<?= $options ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'select': ?>
@ -39,6 +42,7 @@ case 'select': ?>
options="<?= $options ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'select-multiple': ?>
@ -51,7 +55,8 @@ case 'select-multiple': ?>
helper="<?= $helper ?>"
options="<?= $options ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= esc(json_encode($value)) ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'email': ?>
@ -65,6 +70,7 @@ case 'email': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'url': ?>
@ -79,6 +85,7 @@ case 'url': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'number': ?>
@ -92,6 +99,7 @@ case 'number': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'textarea': ?>
@ -104,6 +112,7 @@ case 'textarea': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'html': ?>
@ -116,7 +125,8 @@ case 'html': ?>
hint="<?= $hint ?>"
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
content="<?= htmlspecialchars($value) ?>"
value="<?= htmlspecialchars($value) ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'markdown': ?>
@ -129,6 +139,7 @@ case 'markdown': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'rss': ?>
@ -141,7 +152,8 @@ case 'rss': ?>
hint="<?= $hint ?>"
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
content="<?= htmlspecialchars($value) ?>"
value="<?= htmlspecialchars($value) ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
case 'datetime': ?>
@ -154,6 +166,7 @@ case 'datetime': ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php break;
default: ?>
@ -166,5 +179,6 @@ default: ?>
helper="<?= $helper ?>"
isRequired="<?= $optional ? 'false' : 'true' ?>"
value="<?= $value ?>"
defaultValue="<?= $defaultValue ?>"
/>
<?php endswitch; ?>

View File

@ -17,15 +17,16 @@
<legend class="absolute font-mono left-0 -top-px -ml-6 rounded-l-full rounded-r-none w-6 text-xs h-6 inline-flex items-center justify-center font-semibold border border-subtle bg-base"><span class="sr-only"><?= $field->getTranslated($plugin->getKey(), 'label') ?></span> <span data-field-array-number><?= $index + 1 ?></span></legend>
<?php foreach ($field->fields as $subfield): ?>
<?= view('plugins/_field', [
'class' => 'flex-1',
'type' => $subfield->type,
'name' => sprintf('%s[%s][%s]', $field->key, $index, $subfield->key),
'label' => $subfield->getTranslated($plugin->getKey(), 'label'),
'hint' => $subfield->getTranslated($plugin->getKey(), 'hint'),
'value' => $value[$subfield->key] ?? '',
'helper' => $subfield->getTranslated($plugin->getKey(), 'helper'),
'options' => esc(json_encode($subfield->getOptionsArray($plugin->getKey()))),
'optional' => $subfield->optional,
'class' => 'flex-1',
'type' => $subfield->type,
'name' => sprintf('%s[%s][%s]', $field->key, $index, $subfield->key),
'label' => $subfield->getTranslated($plugin->getKey(), 'label'),
'hint' => $subfield->getTranslated($plugin->getKey(), 'hint'),
'value' => $value[$subfield->key] ?? null,
'helper' => $subfield->getTranslated($plugin->getKey(), 'helper'),
'defaultValue' => esc($subfield->defaultValue),
'options' => esc(json_encode($subfield->getOptionsArray($plugin->getKey()))),
'optional' => $subfield->optional,
]) ?>
<?php endforeach; ?>
<x-IconButton variant="danger" glyph="delete-bin-fill" data-field-array-delete="<?= $index ?>" class="absolute right-0 top-0 -mt-4 -mr-4"><?= lang('Common.forms.fieldArray.remove') ?></x-IconButton>
@ -42,15 +43,16 @@
<div class="relative flex items-end" data-field-array-item="<?= $index ?>">
<span class="self-start mr-1 -ml-5 w-4 rtl text-sm before:content-['.']" data-field-array-number style="direction:rtl"><?= $index + 1 ?></span>
<?= view('plugins/_field', [
'class' => 'flex-1',
'type' => $field->type,
'name' => sprintf('%s[%s]', $field->key, $index),
'label' => $field->getTranslated($plugin->getKey(), 'label'),
'hint' => $field->getTranslated($plugin->getKey(), 'hint'),
'value' => $value,
'helper' => $field->getTranslated($plugin->getKey(), 'helper'),
'options' => esc(json_encode($field->getOptionsArray($plugin->getKey()))),
'optional' => $field->optional,
'class' => 'flex-1',
'type' => $field->type,
'name' => sprintf('%s[%s]', $field->key, $index),
'label' => $field->getTranslated($plugin->getKey(), 'label'),
'hint' => $field->getTranslated($plugin->getKey(), 'hint'),
'value' => $value,
'helper' => $field->getTranslated($plugin->getKey(), 'helper'),
'defaultValue' => esc($field->defaultValue),
'options' => esc(json_encode($field->getOptionsArray($plugin->getKey()))),
'optional' => $field->optional,
]) ?>
<x-IconButton variant="danger" glyph="delete-bin-fill" data-field-array-delete="<?= $index ?>" type="button" class="mb-2 ml-2"><?= lang('Common.forms.fieldArray.remove') ?></x-IconButton>
</div>
@ -65,29 +67,31 @@
<legend class="relative z-10 font-bold text-heading-foreground font-display before:w-full before:absolute before:h-1/2 before:left-0 before:bottom-0 before:rounded-full before:bg-heading-background before:z-[-10] tracking-wide text-base"><?= $field->getTranslated($plugin->getKey(), 'label') ?></legend>
<?php foreach ($field->fields as $subfield): ?>
<?= view('plugins/_field', [
'class' => 'flex-1',
'type' => $subfield->type,
'name' => sprintf('%s[%s]', $field->key, $subfield->key),
'label' => $subfield->getTranslated($plugin->getKey(), 'label'),
'hint' => $subfield->getTranslated($plugin->getKey(), 'hint'),
'value' => $value[$subfield->key] ?? '',
'helper' => $subfield->getTranslated($plugin->getKey(), 'helper'),
'options' => esc(json_encode($subfield->getOptionsArray($plugin->getKey()))),
'optional' => $subfield->optional,
'class' => 'flex-1',
'type' => $subfield->type,
'name' => sprintf('%s[%s]', $field->key, $subfield->key),
'label' => $subfield->getTranslated($plugin->getKey(), 'label'),
'hint' => $subfield->getTranslated($plugin->getKey(), 'hint'),
'value' => $value[$subfield->key] ?? null,
'helper' => $subfield->getTranslated($plugin->getKey(), 'helper'),
'defaultValue' => esc($subfield->defaultValue),
'options' => esc(json_encode($subfield->getOptionsArray($plugin->getKey()))),
'optional' => $subfield->optional,
]) ?>
<?php endforeach; ?>
</fieldset>
<?php else: ?>
<?= view('plugins/_field', [
'class' => '',
'type' => $field->type,
'name' => $field->key,
'label' => $field->getTranslated($plugin->getKey(), 'label'),
'hint' => $field->getTranslated($plugin->getKey(), 'hint'),
'value' => get_plugin_setting($plugin->getKey(), $field->key, $context),
'helper' => $field->getTranslated($plugin->getKey(), 'helper'),
'options' => esc(json_encode($field->getOptionsArray($plugin->getKey()))),
'optional' => $field->optional,
'class' => '',
'type' => $field->type,
'name' => $field->key,
'label' => $field->getTranslated($plugin->getKey(), 'label'),
'hint' => $field->getTranslated($plugin->getKey(), 'hint'),
'value' => get_plugin_setting($plugin->getKey(), $field->key, $context),
'helper' => $field->getTranslated($plugin->getKey(), 'helper'),
'defaultValue' => esc($field->defaultValue),
'options' => esc(json_encode($field->getOptionsArray($plugin->getKey()))),
'optional' => $field->optional,
]) ?>
<?php endif; ?>
<?php endforeach; ?>

View File

@ -58,7 +58,7 @@
name="<?= 'platforms[' . esc($platform->slug) . '][account_id]' ?>"
value="<?= esc($platform->account_id) ?>"
placeholder="<?= lang("Platforms.description.{$platform->type}") ?>" />
<x-Forms.Toggler size="small" class="mt-4 text-sm" id="<?= esc($platform->slug) . '_visible' ?>" name="<?= 'platforms[' . esc($platform->slug) . '][visible]'?>" isChecked="<?= old(esc($platform->slug) . '_visible', $platform->is_visible ? 'true' : 'false') ?>"><?= lang('Platforms.visible') ?></x-Forms.Toggler>
<x-Forms.Toggler size="small" class="mt-4 text-sm" id="<?= esc($platform->slug) . '_visible' ?>" name="<?= 'platforms[' . esc($platform->slug) . '][visible]'?>" value="<?= $platform->is_visible ? 'yes' : '' ?>"><?= lang('Platforms.visible') ?></x-Forms.Toggler>
</fieldset>
</div>
</article>

View File

@ -150,7 +150,7 @@
</x-Forms.Section>
<x-Forms.Section title="<?= lang('Podcast.form.premium') ?>">
<x-Forms.Toggler class="mt-2" name="premium_by_default" isChecked="false" hint="<?= esc(lang('Podcast.form.premium_by_default_hint')) ?>">
<x-Forms.Toggler class="mt-2" name="premium_by_default" hint="<?= esc(lang('Podcast.form.premium_by_default_hint')) ?>">
<?= lang('Podcast.form.premium_by_default') ?></x-Forms.Toggler>
</x-Forms.Section>
@ -168,13 +168,13 @@
<x-Forms.Section
title="<?= lang('Podcast.form.advanced_section_title') ?>" >
<x-Forms.Toggler class="mb-2" name="lock" isChecked="true" hint="<?= esc(lang('Podcast.form.lock_hint')) ?>">
<x-Forms.Toggler class="mb-2" name="lock" defaultValue="yes" hint="<?= esc(lang('Podcast.form.lock_hint')) ?>">
<?= lang('Podcast.form.lock') ?>
</x-Forms.Toggler>
<x-Forms.Toggler class="mb-2" name="block" isChecked="false" hint="<?= esc(lang('Podcast.form.block_hint')) ?>">
<x-Forms.Toggler class="mb-2" name="block" hint="<?= esc(lang('Podcast.form.block_hint')) ?>">
<?= lang('Podcast.form.block') ?>
</x-Forms.Toggler>
<x-Forms.Toggler name="complete" isChecked="false">
<x-Forms.Toggler name="complete">
<?= lang('Podcast.form.complete') ?>
</x-Forms.Toggler>

View File

@ -11,7 +11,7 @@
<x-Alert variant="danger" class="font-semibold"><?= lang('Podcast.delete_form.disclaimer') ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('Podcast.delete_form.understand') ?></x-Forms.Checkbox>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('Podcast.delete_form.understand') ?></x-Forms.Checkbox>
<div class="self-end mt-4">
<x-Button uri="<?= route_to('podcast-view', $podcast->id) ?>"><?= lang('Common.cancel') ?></x-Button>

View File

@ -96,13 +96,13 @@
value="<?= $podcast->category_id ?>"
options="<?= esc(json_encode($categoryOptions)) ?>"
isRequired="true" />
<x-Forms.Field
as="SelectMulti"
name="other_categories"
label="<?= esc(lang('Podcast.form.other_categories')) ?>"
data-max-item-count="2"
value="<?= esc(json_encode($podcast->other_categories_ids)) ?>"
value="<?= $podcast->other_categories_ids ?>"
options="<?= esc(json_encode($categoryOptions)) ?>" />
<x-Forms.RadioGroup
@ -183,7 +183,7 @@
</x-Forms.Section>
<x-Forms.Section title="<?= lang('Podcast.form.premium') ?>">
<x-Forms.Toggler class="mt-2" name="premium_by_default" isChecked="<?= $podcast->is_premium_by_default ? 'true' : 'false' ?>" hint="<?= esc(lang('Podcast.form.premium_by_default_hint')) ?>">
<x-Forms.Toggler class="mt-2" name="premium_by_default" value="<?= $podcast->is_premium_by_default ? 'yes' : '' ?>" hint="<?= esc(lang('Podcast.form.premium_by_default_hint')) ?>">
<?= lang('Podcast.form.premium_by_default') ?></x-Forms.Toggler>
</x-Forms.Section>
@ -211,13 +211,13 @@ hint="<?= esc(lang('Podcast.form.new_feed_url_hint')) ?>"
value="<?= esc($podcast->new_feed_url) ?>"
/>
<x-Forms.Toggler class="mb-2" name="lock" isChecked="<?= $podcast->is_locked ? 'true' : 'false' ?>" hint="<?= esc(lang('Podcast.form.lock_hint')) ?>">
<x-Forms.Toggler class="mb-2" name="lock" value="<?= $podcast->is_locked ? 'yes' : '' ?>" hint="<?= esc(lang('Podcast.form.lock_hint')) ?>">
<?= lang('Podcast.form.lock') ?>
</x-Forms.Toggler>
<x-Forms.Toggler class="mb-2" name="block" isChecked="<?= $podcast->is_blocked ? 'true' : 'false' ?>" hint="<?= esc(lang('Podcast.form.block_hint')) ?>">
<x-Forms.Toggler class="mb-2" name="block" value="<?= $podcast->is_blocked ? 'yes' : '' ?>" hint="<?= esc(lang('Podcast.form.block_hint')) ?>">
<?= lang('Podcast.form.block') ?>
</x-Forms.Toggler>
<x-Forms.Toggler name="complete" isChecked="<?= $podcast->is_completed ? 'true' : 'false' ?>">
<x-Forms.Toggler name="complete" value="<?= $podcast->is_completed ? 'yes' : '' ?>">
<?= lang('Podcast.form.complete') ?>
</x-Forms.Toggler>

View File

@ -26,7 +26,7 @@
label="<?= esc(lang('Person.podcast_form.persons')) ?>"
hint="<?= esc(lang('Person.podcast_form.persons_hint')) ?>"
options="<?= esc(json_encode($personOptions)) ?>"
defaultValue="<?= esc(json_encode(old('persons', []))) ?>"
defaultValue="<?= implode(',', old('persons', [])) ?>"
isRequired="true" />
<x-Forms.Field
@ -36,7 +36,7 @@
label="<?= esc(lang('Person.podcast_form.roles')) ?>"
hint="<?= esc(lang('Person.podcast_form.roles_hint')) ?>"
options="<?= esc(json_encode($taxonomyOptions)) ?>"
defaultValue="<?= esc(json_encode(old('roles', []))) ?>"
defaultValue="<?= implode(',', old('roles', [])) ?>"
/>
<x-Button variant="primary" class="self-end" type="submit"><?= lang('Person.podcast_form.submit_add') ?></x-Button>

View File

@ -73,9 +73,9 @@
title="<?= lang('Settings.housekeeping.title') ?>"
subtitle="<?= lang('Settings.housekeeping.subtitle') ?>" >
<x-Forms.Toggler name="reset_counts" size="small" isChecked="false" hint="<?= esc(lang('Settings.housekeeping.reset_counts_helper')) ?>"><?= lang('Settings.housekeeping.reset_counts') ?></x-Forms.Toggler>
<x-Forms.Toggler name="rename_episodes_files" size="small" isChecked="false" hint="<?= esc(lang('Settings.housekeeping.rename_episodes_files_hint')) ?>"><?= lang('Settings.housekeeping.rename_episodes_files') ?></x-Forms.Toggler>
<x-Forms.Toggler name="clear_cache" size="small" isChecked="false" hint="<?= esc(lang('Settings.housekeeping.clear_cache_helper')) ?>"><?= lang('Settings.housekeeping.clear_cache') ?></x-Forms.Toggler>
<x-Forms.Toggler name="reset_counts" size="small" hint="<?= esc(lang('Settings.housekeeping.reset_counts_helper')) ?>"><?= lang('Settings.housekeeping.reset_counts') ?></x-Forms.Toggler>
<x-Forms.Toggler name="rename_episodes_files" size="small" hint="<?= esc(lang('Settings.housekeeping.rename_episodes_files_hint')) ?>"><?= lang('Settings.housekeeping.rename_episodes_files') ?></x-Forms.Toggler>
<x-Forms.Toggler name="clear_cache" size="small" hint="<?= esc(lang('Settings.housekeeping.clear_cache_helper')) ?>"><?= lang('Settings.housekeeping.clear_cache') ?></x-Forms.Toggler>
<?php // @icon("home-gear-fill")?>
<x-Button variant="primary" type="submit" iconLeft="home-gear-fill"><?= lang('Settings.housekeeping.run') ?></x-Button>

View File

@ -13,7 +13,7 @@
'subscriber' => $subscription->email,
]) ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('Subscription.delete_form.understand') ?></x-Forms.Checkbox>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('Subscription.delete_form.understand') ?></x-Forms.Checkbox>
<div class="flex items-center self-end mt-4 gap-x-2">
<x-Button uri="<?= route_to('subscription-list', $podcast->id) ?>"><?= lang('Common.cancel') ?></x-Button>

View File

@ -15,7 +15,7 @@
'user' => $user->username,
]) ?></x-Alert>
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true" isChecked="false"><?= lang('User.delete_form.understand', [
<x-Forms.Checkbox class="mt-2" name="understand" isRequired="true"><?= lang('User.delete_form.understand', [
'user' => $user->username,
]) ?></x-Forms.Checkbox>

View File

@ -45,14 +45,14 @@ if ($userPodcasts !== []) {
$items[] = [
'type' => 'link',
'title' => <<<HTML
<div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
<span class="absolute top-0 right-1 w-2.5 h-2.5 bg-red-500 rounded-full border border-background-elevated {$unreadNotificationDotDisplayClass}"></span>
</div>
<span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div>
HTML
<div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
<span class="absolute top-0 right-1 w-2.5 h-2.5 bg-red-500 rounded-full border border-background-elevated {$unreadNotificationDotDisplayClass}"></span>
</div>
<span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div>
HTML
,
'uri' => route_to('notification-list', $userPodcast->id),
];

View File

@ -29,7 +29,7 @@
<!-- Remember me -->
<?php if (setting('Auth.sessionConfig')['allowRemembering']): ?>
<x-Forms.Checkbox name="remember" isChecked="<?= old('remember') ?>" size="small"><?= lang('Auth.rememberMe') ?></x-Forms.Checkbox>
<x-Forms.Checkbox name="remember" size="small"><?= lang('Auth.rememberMe') ?></x-Forms.Checkbox>
<?php endif; ?>
<x-Button variant="primary" type="submit" class="self-end"><?= lang('Auth.login') ?></x-Button>