Как сохранить кнопки всегда внизу модала Ion Sheet?Html

Программисты Html
Ответить
Anonymous
 Как сохранить кнопки всегда внизу модала Ion Sheet?

Сообщение Anonymous »

Я использую модальное окно ion с точками останова, поэтому у него есть дескриптор, и я могу изменить его размер.



Хорошее состояние #1
Хорошее состояние #2




Здесь, на этой картинке, вы можете увидеть правильную вещь:
А также, когда я нажимаю «Отправить», все правильно с недопустимыми сообщениями:



Изображение


Изображение




Но проблема в том, что когда я поднимаю ящик так, что он занимает весь экран, кнопка находится не внизу, как показано здесь.



Плохое состояние
Измерения, которые я использую





Изображение


Изображение




Я хочу, чтобы кнопка была внизу, независимо от размера ящика, но я не могу этого сделать.
m-add-project.vue

Код: Выделить всё








{{ $t('projectModal.addNewProject') }}









{{ $t('projectModal.projectName')
}}


{{ $t('projectModal.category')
}}



{{ $t('projectModal.selectDate')
}}




{{ err.$message }}





{{ $t('projectModal.budgetUSD')
}}





{{ err.$message }}












{{ $t('projectModal.cancel') }}




{{ $t('projectModal.createProject') }}













import { ref, watch, computed } from 'vue'
import { required, minLength, maxLength, minValue, helpers } from '@vuelidate/validators'
import useVuelidate from '@vuelidate/core'
import XIcon from '@/plugins/app@projects/components/add-new-project/assets/x-icon.vue'
import DatePicker from 'primevue/datepicker'
import BudgetIcon from '@/plugins/app@projects/components/add-new-project/assets/budget-icon.vue'
import InputNumber from 'primevue/inputnumber'
import { useProjectsManagement } from '@/plugins/app@projects/composables/projects-management.composable'
import AInviteSelect from '@/plugins/app@projects/components/add-new-project/components/a-invite-select.vue'
import AUploadIcon from '@/plugins/app@projects/components/add-new-project/components/a-upload-icon.vue'
import { projectCategories } from '@/plugins/app@projects/composables/projects-management.composable'
import type { Project } from '@/plugins/app@projects/types/project.types'
import { getGlobalProperties } from '@wezeo/plugins'
import { useIsMobile } from '@/plugins/app/_composables/is-mobile.composable'

const emit = defineEmits(['closeModal', 'recalc-modal'])
const { $gp } = getGlobalProperties()
const uploadedFileName = ref('')
const { createProject } = useProjectsManagement()
const { isMobile } = useIsMobile()

const getInitialValues = () =>  ({
name: '',
category: null,
dueDate: null,
budget: null,
members: [],
uploadedImageUrl: ''
})

const fields = ref(getInitialValues())

const rules = {
name: {
required: helpers.withMessage($gp.$t('validation.required'), required),
minLength: helpers.withMessage(
({ $params }) => $gp.$t('validation.minLength', { min: $params.min }),
minLength(5)
),
maxLength: helpers.withMessage(
({ $params }) => $gp.$t('validation.maxLength', { max: $params.max }),
maxLength(10)
)
},
category: {
required: helpers.withMessage($gp.$t('validation.required'), required)
},
dueDate: {
required: helpers.withMessage($gp.$t('validation.required'), required),
notInFuture: helpers.withMessage($gp.$t('validation.notInFuture'), value => !value || value 
projectCategories.map(option => ({
...option,
value: $gp.$t(option.value)
}))
)

function emitClose() {
emit('closeModal')
resetValues()
}

function resetValues() {
fields.value = getInitialValues()
v$.value.$reset()
}

function saveProject(newProject: Project) {
createProject(newProject)
$gp.$toast.success('Project was successfully created!', 'bottom', 3000)
}

function createProjectObject(): Project {
return {
title: fields.value.name,
category: fields.value.category || '',
date: fields.value.dueDate ? formatDateForProject(fields.value.dueDate) : '',
budget: `$${Number(fields.value.budget).toLocaleString('de-DE')}`,
members: fields.value.members.map((member: any) => member.name),
status: 'started',
completedTasks: 0,
totalTasks: 0,
icon: fields.value.uploadedImageUrl || null
}
}

async function confirmAction(): Promise {
try {
await $gp.$alert.confirm('Do you want to create this project?')
return true
} catch (e) {
return false
}
}

async function submitForm() {
const isValid = await v$.value.$validate()
if (!isValid) return

const confirmed = await confirmAction()
if (!confirmed) return

const project = createProjectObject()
saveProject(project)
emitClose()
}

function formatDateForProject(date: Date): string {
const day = date.getDate().toString().padStart(2, '0');
const monthIndex = date.getMonth();
const year = date.getFullYear();
const months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
const monthName = months[monthIndex];
return `${day} ${monthName} ${year}`;
}

watch(
() => v$.value.$errors.map(e => e.$message).join(','),
async () => {
emit('recalc-modal')
}
)



.w-alert-modal .ion-page {
border: 1px solid var(--ion-color-neutral-grey);
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
}



.project-datepicker:deep(.p-inputtext::placeholder) {
color: var(--p-slate-500);
}

:deep(.p-datepicker) {
border-radius: 8px;
}

.project-budget:deep(.p-inputtext::placeholder) {
color: var(--p-slate-500);
}

.project-datepicker :deep(.p-datepicker-input-icon-container .p-datepicker-input-icon) {
width: 16px;
height: 19px;
min-width: 16px;
min-height: 19px;
max-width: 16px;
max-height: 19px;
}

:deep(.p-inputtext) {
font-size: 14px;
line-height: 21px;
}

:deep(.w-input-wrapper.custom-border-grey) {
border: 1px solid var(--ion-color-neutral-grey);
}

:deep(.p-inputtext) {
border: 1px solid var(--ion-color-neutral-grey);
}

:deep(.p-inputtext) {
border: 1px solid var(--ion-color-neutral-grey);
box-shadow: none;
}

m-Response-Modal:

Код: Выделить всё














import { ref, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { useIsMobile } from '@/plugins/app/_composables/is-mobile.composable'

const isOpen = ref(false)
const { isMobile } = useIsMobile()
const preMeasureRef = ref(null)
const measuredHeight = ref(650)
const computedBreakpoints = ref([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1])
const computedInitialBreakpoint = ref(0.5)
const modalRef = ref(null)
let resizeObserver
const isAvailable = ref(true)

onMounted(() => {
if (isMobile.value && preMeasureRef.value) {
resizeObserver = new ResizeObserver(() => {
const contentHeight = preMeasureRef.value?.offsetHeight || 650
measuredHeight.value = contentHeight
})
resizeObserver.observe(preMeasureRef.value)
}
})

onBeforeUnmount(() => {
if (resizeObserver && preMeasureRef.value) {
resizeObserver.unobserve(preMeasureRef.value)
}
})

async function openModal() {
await nextTick()
await recalcModal()
isOpen.value = true
}

function closeModal() {
isAvailable.value = false
isOpen.value = false;
modalRef.value = null;
setTimeout(() => {
isAvailable.value = true
}, 1)
}

async function recalcModal(validateActive: boolean = false) {
let contentHeight = preMeasureRef.value?.offsetHeight || 650
if (validateActive) {
contentHeight = contentHeight + 64
}
measuredHeight.value = contentHeight
const vh = window.innerHeight;
let fraction = Math.min(contentHeight / vh, 1);
let breakpoints = [...computedBreakpoints.value, fraction];
breakpoints = Array.from(new Set(breakpoints)).sort((a, b) => a - b);
computedBreakpoints.value = breakpoints;
computedInitialBreakpoint.value = fraction;

await nextTick();
await nextAnimationFrame();
if (isMobile.value && modalRef.value) {
modalRef.value.$el.setCurrentBreakpoint(fraction)
}
}

function nextAnimationFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
}

defineExpose({ openModal, closeModal, recalcModal })



@media (min-width: 640px) {
ion-modal.add-project-modal {
--height: auto;
}
}

И это пример в моем коде: Хорошо, я хочу и нуждаюсь в том, чтобы независимо от размера или размера мобильного телефона и ящика кнопка всегда была внизу, даже на iPhone SE или iPhone 12 Pro.
Легко воспроизвести код:

Код: Выделить всё






This progression is locked


You need to complete level {{ modalData.previousLevel }} before accessing this.


Cancel


Level up








import { IonButton, IonContent, IonIcon, IonModal } from '@ionic/vue'
import { informationCircleOutline } from 'ionicons/icons'
import { useRouter } from 'vue-router'

const props = defineProps()

const emit = defineEmits()

const router = useRouter()

const handleLevelUp = () => {
emit('dismiss')
router.push({
path: `/warm-up-info-screen/${props.modalData.skillId}/${props.modalData.previousProgressionId}`,
query: { fromSkillId: props.modalData.skillId }
})
}



Хорошее состояние №1
Хорошее состояние №2





Изображение


Изображение




Поэтому я хочу, чтобы кнопки всегда находились в самый нижний, независимо от размера листа, если он равен 0,25 0,5 0,75.

Подробнее здесь: https://stackoverflow.com/questions/796 ... heet-modal
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Html»