refactor: listing component improvements
This commit is contained in:
249
src/components/ListingComponent/BalanceCard.vue
Normal file
249
src/components/ListingComponent/BalanceCard.vue
Normal file
@@ -0,0 +1,249 @@
|
||||
<script setup lang="ts">
|
||||
import type { ValidDeposit } from "@/model/ValidDeposit";
|
||||
import { ref, watch, onMounted, computed } from "vue";
|
||||
import { debounce } from "@/utils/debounce";
|
||||
import { decimalCount } from "@/utils/decimalCount";
|
||||
import { useFloating, arrow, offset, flip, shift } from "@floating-ui/vue";
|
||||
|
||||
const props = defineProps<{
|
||||
validDeposits: ValidDeposit[];
|
||||
activeLockAmount: number;
|
||||
selectedToken: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
withdraw: [amount: string];
|
||||
}>();
|
||||
|
||||
const withdrawAmount = ref<string>("");
|
||||
const withdrawButtonOpacity = ref<number>(0.6);
|
||||
const withdrawButtonCursor = ref<string>("not-allowed");
|
||||
const isCollapsibleOpen = ref<boolean>(false);
|
||||
const validDecimals = ref<boolean>(true);
|
||||
const validWithdrawAmount = ref<boolean>(true);
|
||||
const enableConfirmButton = ref<boolean>(false);
|
||||
const showInfoTooltip = ref<boolean>(false);
|
||||
const floatingArrow = ref(null);
|
||||
|
||||
const reference = ref<HTMLElement | null>(null);
|
||||
const floating = ref<HTMLElement | null>(null);
|
||||
const infoText = ref<HTMLElement | null>(null);
|
||||
|
||||
const remaining = computed(() => {
|
||||
if (props.validDeposits.length > 0) {
|
||||
const deposit = props.validDeposits[0];
|
||||
return deposit ? deposit.remaining : 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
const handleInputEvent = (event: any): void => {
|
||||
const { value } = event.target;
|
||||
|
||||
if (decimalCount(String(value)) > 2) {
|
||||
validDecimals.value = false;
|
||||
enableConfirmButton.value = false;
|
||||
return;
|
||||
}
|
||||
validDecimals.value = true;
|
||||
|
||||
if (value > remaining.value) {
|
||||
validWithdrawAmount.value = false;
|
||||
enableConfirmButton.value = false;
|
||||
return;
|
||||
}
|
||||
validWithdrawAmount.value = true;
|
||||
enableConfirmButton.value = true;
|
||||
};
|
||||
|
||||
const callWithdraw = () => {
|
||||
if (enableConfirmButton.value && withdrawAmount.value) {
|
||||
emit("withdraw", withdrawAmount.value);
|
||||
// Reset form after withdraw
|
||||
withdrawAmount.value = "";
|
||||
isCollapsibleOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openWithdrawForm = () => {
|
||||
isCollapsibleOpen.value = true;
|
||||
};
|
||||
|
||||
const cancelWithdraw = () => {
|
||||
isCollapsibleOpen.value = false;
|
||||
withdrawAmount.value = "";
|
||||
validDecimals.value = true;
|
||||
validWithdrawAmount.value = true;
|
||||
enableConfirmButton.value = false;
|
||||
};
|
||||
|
||||
watch(enableConfirmButton, (): void => {
|
||||
if (!enableConfirmButton.value) {
|
||||
withdrawButtonOpacity.value = 0.7;
|
||||
withdrawButtonCursor.value = "not-allowed";
|
||||
} else {
|
||||
withdrawButtonOpacity.value = 1;
|
||||
withdrawButtonCursor.value = "pointer";
|
||||
}
|
||||
});
|
||||
|
||||
watch(withdrawAmount, (): void => {
|
||||
if (!withdrawAmount.value || !enableConfirmButton.value) {
|
||||
withdrawButtonOpacity.value = 0.7;
|
||||
withdrawButtonCursor.value = "not-allowed";
|
||||
} else {
|
||||
withdrawButtonOpacity.value = 1;
|
||||
withdrawButtonCursor.value = "pointer";
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
useFloating(reference, floating, {
|
||||
placement: "right",
|
||||
middleware: [
|
||||
offset(10),
|
||||
flip(),
|
||||
shift(),
|
||||
arrow({ element: floatingArrow }),
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full bg-white p-4 sm:p-6 rounded-lg">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-sm leading-5 font-medium text-gray-600">
|
||||
Saldo disponível
|
||||
</p>
|
||||
<p class="text-xl leading-7 font-semibold text-gray-900">
|
||||
{{ remaining }} {{ selectedToken }}
|
||||
</p>
|
||||
<div class="flex gap-2 w-32 sm:w-56" v-if="activeLockAmount != 0">
|
||||
<span class="text-xs font-normal text-gray-400" ref="infoText">
|
||||
{{ `com ${activeLockAmount.toFixed(2)} ${selectedToken} em lock` }}
|
||||
</span>
|
||||
<div
|
||||
class="absolute mt-[2px] md-view"
|
||||
:style="{ left: `${(infoText?.clientWidth ?? 108) + 4}px` }"
|
||||
>
|
||||
<img
|
||||
alt="info image"
|
||||
src="@/assets/info.svg?url"
|
||||
aria-describedby="tooltip"
|
||||
ref="reference"
|
||||
@mouseover="showInfoTooltip = true"
|
||||
@mouseout="showInfoTooltip = false"
|
||||
/>
|
||||
<div
|
||||
role="tooltip"
|
||||
ref="floating"
|
||||
class="w-56 z-50 tooltip md-view"
|
||||
v-if="showInfoTooltip"
|
||||
>
|
||||
Valor "em lock" significa que a quantia está aguardando
|
||||
confirmação de compra e só estará disponível para saque caso a
|
||||
transação expire.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="!isCollapsibleOpen" class="flex justify-end items-center">
|
||||
<div
|
||||
class="flex gap-2 cursor-pointer items-center justify-self-center border-2 p-2 border-amber-300 rounded-md"
|
||||
@click="openWithdrawForm"
|
||||
>
|
||||
<img
|
||||
alt="Withdraw image"
|
||||
src="@/assets/withdraw.svg?url"
|
||||
class="w-3 h-3 sm:w-4 sm:h-4"
|
||||
/>
|
||||
<span class="last-release-info">Sacar</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-5">
|
||||
<div v-show="isCollapsibleOpen" class="py-2 w-100">
|
||||
<p class="text-sm leading-5 font-medium">Valor do saque</p>
|
||||
<input
|
||||
type="number"
|
||||
@input="debounce(handleInputEvent, 500)($event)"
|
||||
placeholder="0"
|
||||
class="text-2xl text-gray-900 w-full outline-none"
|
||||
v-model="withdrawAmount"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center" v-if="!validDecimals">
|
||||
<span class="text-red-500 font-normal text-sm">
|
||||
Por favor utilize no máximo 2 casas decimais
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-center" v-else-if="!validWithdrawAmount">
|
||||
<span class="text-red-500 font-normal text-sm">
|
||||
Saldo insuficiente
|
||||
</span>
|
||||
</div>
|
||||
<hr v-show="isCollapsibleOpen" class="pb-3" />
|
||||
<div
|
||||
v-show="isCollapsibleOpen"
|
||||
class="flex justify-between items-center"
|
||||
>
|
||||
<h1
|
||||
@click="cancelWithdraw"
|
||||
class="text-black font-medium cursor-pointer"
|
||||
>
|
||||
Cancelar
|
||||
</h1>
|
||||
|
||||
<div
|
||||
class="withdraw-button flex gap-2 items-center justify-self-center border-2 p-2 border-amber-300 rounded-md"
|
||||
@click="callWithdraw"
|
||||
>
|
||||
<img
|
||||
alt="Withdraw image"
|
||||
src="@/assets/withdraw.svg?url"
|
||||
class="w-3 h-3 sm:w-4 sm:h-4"
|
||||
/>
|
||||
<span class="last-release-info">Sacar</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
@apply text-gray-900;
|
||||
}
|
||||
|
||||
.last-release-info {
|
||||
@apply font-medium text-xs sm:text-sm text-gray-900 justify-self-center;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
@apply bg-white text-gray-900 font-medium text-xs md:text-base px-3 py-2 rounded border-2 border-emerald-500 left-5 top-[-3rem];
|
||||
}
|
||||
|
||||
.withdraw-button {
|
||||
opacity: v-bind(withdrawButtonOpacity);
|
||||
cursor: v-bind(withdrawButtonCursor);
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.md-view {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user