refactor(pokemon): reorganize edit form fields into tabs
Split form into Basic and Advance tabs to improve usability Add validation logic to switch tabs if required fields are missing
This commit is contained in:
@@ -98,6 +98,9 @@ const messages = {
|
||||
detailKicker: 'Pokédex Detail',
|
||||
editKicker: 'Pokédex Edit',
|
||||
editSubtitle: 'Maintain Pokemon profile, details, types, stats, specialities, and favourites.',
|
||||
editSections: 'Pokemon edit sections',
|
||||
editTabBasic: 'Basic',
|
||||
editTabAdvance: 'Advance',
|
||||
newTitle: 'New Pokemon',
|
||||
editTitle: 'Edit #{id} {name}',
|
||||
loadingList: 'Loading Pokemon list',
|
||||
@@ -529,6 +532,9 @@ const messages = {
|
||||
detailKicker: 'Pokédex Detail',
|
||||
editKicker: 'Pokédex Edit',
|
||||
editSubtitle: '维护 Pokemon 介绍、属性、六维、特长和喜欢的东西。',
|
||||
editSections: 'Pokemon 编辑分区',
|
||||
editTabBasic: '基础',
|
||||
editTabAdvance: '进阶',
|
||||
newTitle: '新增 Pokemon',
|
||||
editTitle: '编辑 #{id} {name}',
|
||||
loadingList: '正在加载 Pokemon 列表',
|
||||
|
||||
@@ -725,6 +725,69 @@ button:disabled,
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.modal-edit-form--tabbed {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.pokemon-edit-form {
|
||||
height: clamp(420px, calc(100dvh - 188px), 640px);
|
||||
min-height: 0;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.pokemon-edit-panel {
|
||||
min-height: 0;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
align-content: start;
|
||||
overflow-y: auto;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.pokemon-edit-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.pokemon-measurement-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pokemon-measurement-control {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface-soft);
|
||||
}
|
||||
|
||||
.pokemon-measurement-control > .field-label {
|
||||
min-height: 44px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pokemon-measurement-control > .field {
|
||||
min-width: 96px;
|
||||
flex: 1 1 110px;
|
||||
}
|
||||
|
||||
.pokemon-measurement-control > .pokemon-measurement-fields {
|
||||
min-width: 0;
|
||||
flex: 1 1 220px;
|
||||
grid-template-columns: repeat(2, minmax(72px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pokemon-measurement-fields,
|
||||
.pokemon-stats-fields {
|
||||
display: grid;
|
||||
@@ -3409,6 +3472,10 @@ button:disabled,
|
||||
.appearance-row__main {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.pokemon-measurement-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@@ -3436,6 +3503,7 @@ button:disabled,
|
||||
.toolbar,
|
||||
.entity-grid,
|
||||
.grid,
|
||||
.pokemon-edit-grid,
|
||||
.coming-soon-preview {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import Modal from '../components/Modal.vue';
|
||||
import PokemonStatsFields from '../components/PokemonStatsFields.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
import StatusMessage from '../components/StatusMessage.vue';
|
||||
import Tabs from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import TranslationFields from '../components/TranslationFields.vue';
|
||||
import { iconCancel, iconSave } from '../icons';
|
||||
@@ -36,6 +37,7 @@ const loading = ref(true);
|
||||
const busy = ref(false);
|
||||
const message = ref('');
|
||||
const creatingSelect = ref('');
|
||||
const activeEditTab = ref('basic');
|
||||
const heightUnit = ref<'imperial' | 'metric'>('imperial');
|
||||
const weightUnit = ref<'imperial' | 'metric'>('imperial');
|
||||
|
||||
@@ -77,6 +79,10 @@ const cancelTo = computed(() => (isEditing.value ? `/pokemon/${routeId.value}` :
|
||||
const selectedSkillDropRows = computed(() =>
|
||||
pokemonForm.value.skillItemDrops.filter((row) => pokemonForm.value.skillIds.includes(row.skillId) && skillSupportsItemDrop(row.skillId))
|
||||
);
|
||||
const editTabs = computed(() => [
|
||||
{ value: 'basic', label: t('pages.pokemon.editTabBasic') },
|
||||
{ value: 'advance', label: t('pages.pokemon.editTabAdvance') }
|
||||
]);
|
||||
const totalHeightInchesValue = computed(() => Math.round(pokemonForm.value.heightInches));
|
||||
const heightFeetValue = computed(() => Math.floor(totalHeightInchesValue.value / 12));
|
||||
const heightInchesValue = computed(() => totalHeightInchesValue.value - heightFeetValue.value * 12);
|
||||
@@ -164,6 +170,21 @@ function pokemonNameForSave() {
|
||||
return pokemonForm.value.translations[String(locale.value || '')]?.name ?? '';
|
||||
}
|
||||
|
||||
function pokemonIdForSave() {
|
||||
return Number(isEditing.value ? routeId.value : pokemonForm.value.id);
|
||||
}
|
||||
|
||||
function hasRequiredBasicFields() {
|
||||
const id = pokemonIdForSave();
|
||||
return Number.isInteger(id) && id > 0 && pokemonNameForSave().trim() !== '';
|
||||
}
|
||||
|
||||
async function showBasicFieldValidation() {
|
||||
activeEditTab.value = 'basic';
|
||||
await nextTick();
|
||||
document.querySelector<HTMLFormElement>('#pokemon-edit-form')?.reportValidity();
|
||||
}
|
||||
|
||||
function closeEditor() {
|
||||
void router.push(cancelTo.value);
|
||||
}
|
||||
@@ -241,12 +262,17 @@ async function createMultiOption(selectKey: string, type: ConfigType, name: stri
|
||||
}
|
||||
|
||||
async function savePokemon() {
|
||||
if (!hasRequiredBasicFields()) {
|
||||
await showBasicFieldValidation();
|
||||
return;
|
||||
}
|
||||
|
||||
busy.value = true;
|
||||
message.value = '';
|
||||
|
||||
try {
|
||||
const payload: PokemonPayload = {
|
||||
id: Number(isEditing.value ? routeId.value : pokemonForm.value.id),
|
||||
id: pokemonIdForSave(),
|
||||
name: pokemonNameForSave(),
|
||||
genus: pokemonForm.value.genus,
|
||||
details: pokemonForm.value.details,
|
||||
@@ -282,171 +308,186 @@ watch(() => pokemonForm.value.skillIds.slice(), syncSkillItemDrops);
|
||||
<Modal :title="pageTitle" :subtitle="t('pages.pokemon.editSubtitle')" :close-label="t('common.close')" size="wide" @close="closeEditor">
|
||||
<StatusMessage v-if="message" variant="danger">{{ message }}</StatusMessage>
|
||||
|
||||
<form v-if="!loading && options" id="pokemon-edit-form" class="modal-edit-form" @submit.prevent="savePokemon">
|
||||
<div class="field">
|
||||
<label for="pokemon-id">ID</label>
|
||||
<input id="pokemon-id" v-model="pokemonForm.id" :disabled="isEditing" min="1" required type="number" />
|
||||
</div>
|
||||
<form v-if="!loading && options" id="pokemon-edit-form" class="modal-edit-form modal-edit-form--tabbed pokemon-edit-form" @submit.prevent="savePokemon">
|
||||
<Tabs id="pokemon-edit-tabs" v-model="activeEditTab" :tabs="editTabs" :label="t('pages.pokemon.editSections')" />
|
||||
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-name"
|
||||
v-model:base-value="pokemonForm.name"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="name"
|
||||
:label="t('common.name')"
|
||||
:languages="languages"
|
||||
required
|
||||
/>
|
||||
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-genus"
|
||||
v-model:base-value="pokemonForm.genus"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="genus"
|
||||
:label="t('pages.pokemon.genus')"
|
||||
:languages="languages"
|
||||
/>
|
||||
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-details"
|
||||
v-model:base-value="pokemonForm.details"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="details"
|
||||
:label="t('pages.pokemon.details')"
|
||||
:languages="languages"
|
||||
multiline
|
||||
:rows="5"
|
||||
/>
|
||||
|
||||
<div class="field">
|
||||
<span id="pokemon-height-label" class="field-label">{{ t('pages.pokemon.height') }}</span>
|
||||
<div class="segmented" aria-labelledby="pokemon-height-label">
|
||||
<button :class="{ active: heightUnit === 'imperial' }" type="button" @click="heightUnit = 'imperial'">
|
||||
{{ t('pages.pokemon.heightImperial') }}
|
||||
</button>
|
||||
<button :class="{ active: heightUnit === 'metric' }" type="button" @click="heightUnit = 'metric'">
|
||||
{{ t('pages.pokemon.heightMetric') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="heightUnit === 'imperial'" class="pokemon-measurement-fields">
|
||||
<section v-if="activeEditTab === 'basic'" class="pokemon-edit-panel" role="tabpanel" :aria-label="t('pages.pokemon.editTabBasic')">
|
||||
<div class="pokemon-edit-grid">
|
||||
<div class="field">
|
||||
<label for="pokemon-height-feet">{{ t('pages.pokemon.feet') }}</label>
|
||||
<input id="pokemon-height-feet" :value="heightFeetValue" min="0" step="1" type="number" inputmode="numeric" @input="updateHeightFeet" />
|
||||
<label for="pokemon-id">ID</label>
|
||||
<input id="pokemon-id" v-model="pokemonForm.id" :disabled="isEditing" min="1" required type="number" />
|
||||
</div>
|
||||
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-name"
|
||||
v-model:base-value="pokemonForm.name"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="name"
|
||||
:label="t('common.name')"
|
||||
:languages="languages"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pokemon-edit-grid">
|
||||
<div class="field">
|
||||
<label for="pokemon-height-inches">{{ t('pages.pokemon.inches') }}</label>
|
||||
<input id="pokemon-height-inches" :value="heightInchesValue" min="0" step="1" type="number" inputmode="numeric" @input="updateHeightInches" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="field">
|
||||
<label for="pokemon-height-meters">{{ t('pages.pokemon.meters') }}</label>
|
||||
<input id="pokemon-height-meters" :value="heightMetersValue" min="0" step="0.01" type="number" inputmode="decimal" @input="updateHeightMeters" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<span id="pokemon-weight-label" class="field-label">{{ t('pages.pokemon.weight') }}</span>
|
||||
<div class="segmented" aria-labelledby="pokemon-weight-label">
|
||||
<button :class="{ active: weightUnit === 'imperial' }" type="button" @click="weightUnit = 'imperial'">
|
||||
{{ t('pages.pokemon.pounds') }}
|
||||
</button>
|
||||
<button :class="{ active: weightUnit === 'metric' }" type="button" @click="weightUnit = 'metric'">
|
||||
{{ t('pages.pokemon.kilograms') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="weightUnit === 'imperial'" class="field">
|
||||
<label for="pokemon-weight-pounds">{{ t('pages.pokemon.pounds') }}</label>
|
||||
<input id="pokemon-weight-pounds" :value="weightPoundsValue" min="0" step="0.1" type="number" inputmode="decimal" @input="updateWeightPounds" />
|
||||
</div>
|
||||
|
||||
<div v-else class="field">
|
||||
<label for="pokemon-weight-kg">{{ t('pages.pokemon.kilograms') }}</label>
|
||||
<input id="pokemon-weight-kg" :value="weightKgValue" min="0" step="0.01" type="number" inputmode="decimal" @input="updateWeightKg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-types">{{ t('pages.pokemon.types') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-types"
|
||||
v-model="pokemonForm.typeIds"
|
||||
:options="options.pokemonTypes"
|
||||
:max="2"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-types'"
|
||||
:placeholder="t('pages.pokemon.searchTypes')"
|
||||
@create="createMultiOption('pokemon-types', 'pokemon-types', $event, pokemonForm.typeIds, 2)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<span class="field-label">{{ t('pages.pokemon.statsTitle') }}</span>
|
||||
<PokemonStatsFields id-prefix="pokemon-stats" v-model="pokemonForm.stats" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-environment">{{ t('pages.pokemon.environment') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-environment"
|
||||
v-model="pokemonForm.environmentId"
|
||||
:options="options.environments"
|
||||
:multiple="false"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-environment'"
|
||||
:placeholder="t('common.select')"
|
||||
:search-placeholder="t('pages.pokemon.searchEnvironment')"
|
||||
@create="createSingleOption('pokemon-environment', 'environments', $event, (value) => (pokemonForm.environmentId = value))"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-skills">{{ t('pages.pokemon.skills') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-skills"
|
||||
v-model="pokemonForm.skillIds"
|
||||
:options="options.skills"
|
||||
:max="2"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-skills'"
|
||||
:placeholder="t('pages.pokemon.searchSkills')"
|
||||
@create="createMultiOption('pokemon-skills', 'skills', $event, pokemonForm.skillIds, 2)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-things">{{ t('pages.pokemon.favoriteThings') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-things"
|
||||
v-model="pokemonForm.favoriteThingIds"
|
||||
:options="options.favoriteThings"
|
||||
:max="6"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-things'"
|
||||
:placeholder="t('pages.pokemon.searchFavoriteThings')"
|
||||
@create="createMultiOption('pokemon-things', 'favorite-things', $event, pokemonForm.favoriteThingIds, 6)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedSkillDropRows.length" class="field">
|
||||
<span class="field-label">{{ t('pages.pokemon.skillDrops') }}</span>
|
||||
<div class="skill-drop-list">
|
||||
<div v-for="row in selectedSkillDropRows" :key="row.skillId" class="skill-drop-row">
|
||||
<label :for="`pokemon-skill-drops-${row.skillId}`">{{ skillDropLabel(row.skillId) }}</label>
|
||||
<label for="pokemon-environment">{{ t('pages.pokemon.environment') }}</label>
|
||||
<TagsSelect
|
||||
:id="`pokemon-skill-drops-${row.skillId}`"
|
||||
v-model="row.itemId"
|
||||
:options="itemOptions"
|
||||
id="pokemon-environment"
|
||||
v-model="pokemonForm.environmentId"
|
||||
:options="options.environments"
|
||||
:multiple="false"
|
||||
:placeholder="t('pages.pokemon.dropItem')"
|
||||
:search-placeholder="t('pages.pokemon.searchItems')"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-environment'"
|
||||
:placeholder="t('common.select')"
|
||||
:search-placeholder="t('pages.pokemon.searchEnvironment')"
|
||||
@create="createSingleOption('pokemon-environment', 'environments', $event, (value) => (pokemonForm.environmentId = value))"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-skills">{{ t('pages.pokemon.skills') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-skills"
|
||||
v-model="pokemonForm.skillIds"
|
||||
:options="options.skills"
|
||||
:max="2"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-skills'"
|
||||
:placeholder="t('pages.pokemon.searchSkills')"
|
||||
@create="createMultiOption('pokemon-skills', 'skills', $event, pokemonForm.skillIds, 2)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-things">{{ t('pages.pokemon.favoriteThings') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-things"
|
||||
v-model="pokemonForm.favoriteThingIds"
|
||||
:options="options.favoriteThings"
|
||||
:max="6"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-things'"
|
||||
:placeholder="t('pages.pokemon.searchFavoriteThings')"
|
||||
@create="createMultiOption('pokemon-things', 'favorite-things', $event, pokemonForm.favoriteThingIds, 6)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedSkillDropRows.length" class="field">
|
||||
<span class="field-label">{{ t('pages.pokemon.skillDrops') }}</span>
|
||||
<div class="skill-drop-list">
|
||||
<div v-for="row in selectedSkillDropRows" :key="row.skillId" class="skill-drop-row">
|
||||
<label :for="`pokemon-skill-drops-${row.skillId}`">{{ skillDropLabel(row.skillId) }}</label>
|
||||
<TagsSelect
|
||||
:id="`pokemon-skill-drops-${row.skillId}`"
|
||||
v-model="row.itemId"
|
||||
:options="itemOptions"
|
||||
:multiple="false"
|
||||
:placeholder="t('pages.pokemon.dropItem')"
|
||||
:search-placeholder="t('pages.pokemon.searchItems')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else class="pokemon-edit-panel" role="tabpanel" :aria-label="t('pages.pokemon.editTabAdvance')">
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-genus"
|
||||
v-model:base-value="pokemonForm.genus"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="genus"
|
||||
:label="t('pages.pokemon.genus')"
|
||||
:languages="languages"
|
||||
/>
|
||||
|
||||
<TranslationFields
|
||||
id-prefix="pokemon-details"
|
||||
v-model:base-value="pokemonForm.details"
|
||||
v-model:translations="pokemonForm.translations"
|
||||
field="details"
|
||||
:label="t('pages.pokemon.details')"
|
||||
:languages="languages"
|
||||
multiline
|
||||
:rows="5"
|
||||
/>
|
||||
|
||||
<div class="field">
|
||||
<span id="pokemon-measurements-label" class="field-label">{{ t('pages.pokemon.measurements') }}</span>
|
||||
<div class="pokemon-measurement-row" aria-labelledby="pokemon-measurements-label">
|
||||
<div class="pokemon-measurement-control">
|
||||
<span id="pokemon-height-label" class="field-label">{{ t('pages.pokemon.height') }}</span>
|
||||
<div class="segmented" aria-labelledby="pokemon-height-label">
|
||||
<button :class="{ active: heightUnit === 'imperial' }" type="button" @click="heightUnit = 'imperial'">
|
||||
{{ t('pages.pokemon.heightImperial') }}
|
||||
</button>
|
||||
<button :class="{ active: heightUnit === 'metric' }" type="button" @click="heightUnit = 'metric'">
|
||||
{{ t('pages.pokemon.heightMetric') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="heightUnit === 'imperial'" class="pokemon-measurement-fields">
|
||||
<div class="field">
|
||||
<label for="pokemon-height-feet">{{ t('pages.pokemon.feet') }}</label>
|
||||
<input id="pokemon-height-feet" :value="heightFeetValue" min="0" step="1" type="number" inputmode="numeric" @input="updateHeightFeet" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-height-inches">{{ t('pages.pokemon.inches') }}</label>
|
||||
<input id="pokemon-height-inches" :value="heightInchesValue" min="0" step="1" type="number" inputmode="numeric" @input="updateHeightInches" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="field">
|
||||
<label for="pokemon-height-meters">{{ t('pages.pokemon.meters') }}</label>
|
||||
<input id="pokemon-height-meters" :value="heightMetersValue" min="0" step="0.01" type="number" inputmode="decimal" @input="updateHeightMeters" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pokemon-measurement-control">
|
||||
<span id="pokemon-weight-label" class="field-label">{{ t('pages.pokemon.weight') }}</span>
|
||||
<div class="segmented" aria-labelledby="pokemon-weight-label">
|
||||
<button :class="{ active: weightUnit === 'imperial' }" type="button" @click="weightUnit = 'imperial'">
|
||||
{{ t('pages.pokemon.pounds') }}
|
||||
</button>
|
||||
<button :class="{ active: weightUnit === 'metric' }" type="button" @click="weightUnit = 'metric'">
|
||||
{{ t('pages.pokemon.kilograms') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="weightUnit === 'imperial'" class="field">
|
||||
<label for="pokemon-weight-pounds">{{ t('pages.pokemon.pounds') }}</label>
|
||||
<input id="pokemon-weight-pounds" :value="weightPoundsValue" min="0" step="0.1" type="number" inputmode="decimal" @input="updateWeightPounds" />
|
||||
</div>
|
||||
|
||||
<div v-else class="field">
|
||||
<label for="pokemon-weight-kg">{{ t('pages.pokemon.kilograms') }}</label>
|
||||
<input id="pokemon-weight-kg" :value="weightKgValue" min="0" step="0.01" type="number" inputmode="decimal" @input="updateWeightKg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="pokemon-types">{{ t('pages.pokemon.types') }}</label>
|
||||
<TagsSelect
|
||||
id="pokemon-types"
|
||||
v-model="pokemonForm.typeIds"
|
||||
:options="options.pokemonTypes"
|
||||
:max="2"
|
||||
allow-create
|
||||
:creating="creatingSelect === 'pokemon-types'"
|
||||
:placeholder="t('pages.pokemon.searchTypes')"
|
||||
@create="createMultiOption('pokemon-types', 'pokemon-types', $event, pokemonForm.typeIds, 2)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<span class="field-label">{{ t('pages.pokemon.statsTitle') }}</span>
|
||||
<PokemonStatsFields id-prefix="pokemon-stats" v-model="pokemonForm.stats" />
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<section v-else class="modal-edit-form skeleton-detail-section" aria-busy="true" :aria-label="t('pages.pokemon.loadingEdit')">
|
||||
|
||||
Reference in New Issue
Block a user