Wie bleiben die Knöpfe immer am unteren Rand des Ion Sheet Modal?CSS

CSS verstehen
Anonymous
 Wie bleiben die Knöpfe immer am unteren Rand des Ion Sheet Modal?

Post by Anonymous »

Ich verwende Ion Modal mit Haltepunkten, damit es ein Handle hat und ich die Größe ändern kann.



Guter Zustand #1
Guter Zustand #2




Hier in diesem Bild sehen Sie das Richtige:
Und auch wenn ich auf „Senden“ klicke, ist es mit den ungültigen Nachrichten korrekt:



Image


Image




Aber das Problem ist, wenn ich die Schublade hochrolle, sodass sie den gesamten Bildschirm einnimmt, befindet sich die Schaltfläche nicht unten, wie hier zu sehen ist.



Bad State
Dimensionen, die ich verwende





Image


Image




Ich möchte, dass die Schaltfläche unten ist, egal wie groß die Schublade ist, und das schaffe ich nicht.
m-add-project.vue

Code: Select all








{{ $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-responsive-modal:

Code: Select all














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;
}
}

Und das ist das Beispiel dafür in meinem Code:

Code: Select all



Okay, was ich möchte und brauche, ist, dass unabhängig von der Größe oder Größe des Mobiltelefons und der Schublade der Knopf immer unten sein sollte, auch beim iPhone SE oder iPhone 12 Pro.
Einfach zu reproduzierender Code:

Code: Select all






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 }
})
}



Guter Zustand #1
Guter Zustand #2





Image


Image




Ich möchte also, dass die Schaltflächen immer ganz unten sind, egal die Blattgröße, wenn sie 0,25 0,5 ,75 beträgt.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post