This commit is contained in:
Georges KABBOUCHI 2021-07-26 21:33:52 +03:00
parent 5d00313260
commit e22a0cc177
15 changed files with 255 additions and 91 deletions

29
components/Backdrop.vue Normal file
View File

@ -0,0 +1,29 @@
<template>
<transition
enter-active-class="duration-300 ease-out"
enter-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="duration-200 ease-in"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="show"
class="fixed inset-0 z-10 bg-primary-black bg-opacity-20"
v-on="$listeners"
></div>
</transition>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
export default defineComponent({
props: {
show: { type: Boolean, default: false },
},
setup() {
return {}
},
})
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="relative flex items-center w-full">
<input
class="w-full pr-4 form-input"
class="w-full pr-4 rounded-[6px] border border-grey-dark border-opacity-[0.15]"
type="text"
v-bind="$attrs"
:class="{ 'pl-9 py-1': dense, 'pl-12': !dense }"

View File

@ -0,0 +1,59 @@
<template>
<div class="flex flex-col flex-shrink-0 w-full">
<input
autocomplete="off"
class="w-full pl-8 pr-8 rounded-[6px] border border-grey-dark border-opacity-[0.15]"
type="text"
inputmode="decimal"
:value="value"
v-bind="$attrs"
v-on="inputListeners"
/>
<div class="h-0">
<transition
enter-active-class="duration-75 ease-out"
enter-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="duration-75 ease-in"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<p v-if="touched && showError && error" class="mt-1 text-red-600 text-11">{{ error }}</p>
</transition>
</div>
</div>
</template>
<script>
import { defineComponent, watch, ref } from '@nuxtjs/composition-api'
import { useInputListeners } from '@/composables/useInputListeners'
import { usePattern } from '@/composables/usePattern'
export default defineComponent({
inheritAttrs: false,
props: {
value: { type: String, default: '' },
showError: { type: Boolean, default: false },
error: { type: String, default: null },
},
setup(props, context) {
const { amountPattern } = usePattern()
const amountFilter = (value) => amountPattern.test(value)
const { inputListeners } = useInputListeners(props, context, amountFilter)
const touched = ref(false)
const stopTouchedWatcher = watch(
() => props.value,
() => {
touched.value = true
stopTouchedWatcher()
}
)
return { inputListeners, touched }
},
})
</script>

View File

@ -1,6 +1,6 @@
<template>
<div
class="absolute inset-y-0 right-0 z-10 flex flex-col w-full overflow-hidden transform shadow-xs xxl:translate-x-0 dark:shadow-none xxl-transform-none xxl:relative dark:bg-dark-500 bg-background"
class="absolute inset-y-0 right-0 z-10 flex flex-col w-full overflow-hidden transform shadow bg-white"
style="max-width: clamp(var(--min-width-app), var(--width-sidebar-context), 100%)"
:class="{
'translate-x-0 duration-300': isOpen,

View File

@ -1,5 +1,5 @@
<template>
<div class="flex flex-col dark:bg-dark-500 bg-background">
<div class="flex flex-col bg-white">
<slot />
</div>
</template>

View File

@ -1,26 +1,26 @@
<template>
<div
class="flex flex-shrink-0 w-full border-b items border-grey-light dark:border-dark-600 h-navbar"
style="min-height: var(--height-navbar)"
>
<div class="flex items-center w-full mx-auto" style="max-width: 296px">
<div class="flex-shrink-0 w-full mx-auto max-w-[385px] p-4">
<div class="flex justify-between items-center w-full">
<button
class="flex items-center justify-center py-2 mt-1 mr-5 text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none dark:text-light dark:text-opacity-84"
class="flex items-center justify-center text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none"
@click="back"
>
<SVGArrowLeft
class="transition-transform duration-75 ease-out transform group-hover:-translate-x-1"
/>
</button>
<div class="w-full mt-1 font-semibold text-14"><slot /></div>
<button
class="flex items-center justify-center text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none"
@click="close"
>
<SVGClose class="w-3 h-3" />
</button>
</div>
<div>
<div class="w-full my-5 text-center text-primary-gray font-semibold text-lg"><slot /></div>
</div>
<!-- <button
v-if="!!close"
class="flex items-center justify-center p-2 mt-1 text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none"
@click="close"
>
<SVGClose />
</button> -->
</div>
</template>
@ -36,8 +36,8 @@ export default defineComponent({
SVGArrowLeft,
},
setup() {
const { back } = useSidebar()
return { back }
const { back, close } = useSidebar()
return { back, close }
},
})
</script>

View File

@ -3,7 +3,7 @@
<SidebarContextHeader><slot name="title" /></SidebarContextHeader>
<div class="overflow-y-scroll scrollbar-hover">
<div class="mx-auto" style="max-width: 296px">
<div class="mx-auto max-w-[385px]">
<div class="py-2 sm:py-4">
<slot />
</div>

View File

@ -1,83 +1,55 @@
<template>
<SidebarContextRootContainer>
<template #title>Supply SYMBOL</template>
<template #title>Supply {{ symbol }}</template>
<SidebarSectionValueWithIcon class="mt-6" label="Token Balance">
<template #icon><IconCurrency :currency="rootTokenKey"/></template>
<template #value>BALANCE</template>
<SidebarSectionValueWithIcon class="mt-2" label="Token Balance">
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value>{{ balance }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<hr />
<InputNumeric
v-model="amount"
:disabled="pending"
class="mt-6"
placeholder="Amount to supply"
:error="'ERROR'"
/>
<hr />
<div class="flex items-center justify-between mt-6">
<div class="font-semibold">Set Max</div>
<ToggleButton :checked="isMaxAmount" @change="toggle" />
<div class="bg-background mt-6 p-8">
<input-numeric v-model="amount" placeholder="Amount to supply" />
</div>
<SidebarContextHeading class="mt-6"
>Projected Debt Position
</SidebarContextHeading>
<hr />
<SidebarSectionStatus
class="mt-6"
:liquidation="maxLiquidation"
:status="status"
/>
<hr />
<!-- prettier-ignore -->
<SidebarSectionValueWithIcon class="mt-6" label="Liquidation Price (ETH)">
<template #value>{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }} / {{ formatUsd(liquidationMaxPrice) }}</template>
</SidebarSectionValueWithIcon>
<hr />
<div class="flex flex-shrink-0 mt-6">
<ButtonCTA
class="w-full"
:disabled="!isValid || pending"
:loading="pending"
@click="cast"
>Supply</ButtonCTA
>
</div>
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
</SidebarContextRootContainer>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
import { computed, defineComponent, ref } from '@nuxtjs/composition-api'
import InputNumeric from '~/components/common/input/InputNumeric.vue'
import { useAaveV2Position } from '~/composables/useAaveV2Position'
import { useFormatting } from '~/composables/useFormatting'
import { useToken } from '~/composables/useToken'
export default defineComponent({
components: { InputNumeric },
props: {
tokenKey: { type: String, required: true },
},
setup(props) {
const { status } = useAaveV2Position()
const { formatUsd, formatUsdMax, formatNumber, formatDecimal } = useFormatting()
const { status, displayPositions } = useAaveV2Position()
// const { formatUsd, formatUsdMax, formatNumber, formatDecimal } = useFormatting()
const amount = ref('')
const rootTokenKey = computed(() => 'eth')
const { getTokenByKey } = useToken()
const token = computed(() => getTokenByKey(props.tokenKey))
const symbol = computed(() => token.value?.symbol)
const balance = computed(() => "0")
return {
amount,
status,
formatUsd, formatUsdMax, formatNumber, formatDecimal,
rootTokenKey,
token,
symbol,
balance,
}
},
})

View File

@ -1,17 +1,19 @@
<template>
<div class="flex items-center justify-between flex-shrink-0">
<ValueDisplay class="mr-3" :label="label">
<slot name="value" />
</ValueDisplay>
<div class="flex flex-col items-center justify-center text-center flex-shrink-0">
<slot name="icon" />
<value-display class="mt-4" :label="label">
<slot name="value" />
</value-display>
</div>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
import ValueDisplay from './ValueDisplay.vue'
export default defineComponent({
components: { ValueDisplay },
props: {
label: { type: String, required: true },
},

View File

@ -1,13 +1,14 @@
<template>
<div class="flex flex-col items-start">
<ValueDisplayLabel class="flex mb-2"
>{{ label }} <Info v-if="tooltip" :text="tooltip" class="ml-1" />
</ValueDisplayLabel>
<div class="h-6 font-medium text-19">
<div class="flex flex-col items-center">
<div class="h-6 font-medium text-24">
<Spinner v-if="loading" class="w-5 h-5" />
<slot v-else name="default" />
</div>
<value-display-label class="flex mb-2 mt-3"
>{{ label }} <Info v-if="tooltip" :text="tooltip" class="ml-1" />
</value-display-label>
<transition
enter-active-class="duration-200 ease-out"
enter-class="opacity-0"
@ -27,8 +28,10 @@
<script>
import { defineComponent } from '@nuxtjs/composition-api'
import ValueDisplayLabel from './ValueDisplayLabel.vue'
export default defineComponent({
components: { ValueDisplayLabel },
props: {
label: { type: String, default: null },
loading: { type: Boolean, default: false },

View File

@ -0,0 +1,16 @@
import { computed } from "@nuxtjs/composition-api";
import { useSidebar } from "./useSidebar";
export function useBackdrop() {
const { isOpen: isSidbarOpen, close: closeSidbar } = useSidebar();
const isShown = computed(() => isSidbarOpen.value);
function close() {
if (isSidbarOpen.value) {
closeSidbar();
}
}
return { isShown, close };
}

View File

@ -0,0 +1,57 @@
import { computed } from '@vue/composition-api'
import { useBigNumber } from './useBigNumber'
const { gt } = useBigNumber()
function createEventListener(context, eventName, filter, params) {
return function (event) {
const input = event.target
if (filter) {
// filter input with input filter
if (filter(input.value)) {
input.oldValue = input.value
input.oldSelectionStart = input.selectionStart
input.oldSelectionEnd = input.selectionEnd
if (params?.max && gt(input.value, params.max)) {
input.value = params.max
}
context.emit(eventName, input.value)
} else if (Object.prototype.hasOwnProperty.call(input, 'oldValue')) {
input.value = input.oldValue
// FIXME: Jumps back to old value after moving cursor
if (input.oldSelectionStart !== null && input.oldSelectionEnd !== null) {
input.setSelectionRange(input.oldSelectionStart, input.oldSelectionEnd)
}
} else {
input.value = ''
}
} else {
context.emit(eventName, input.value)
}
}
}
export function useInputListeners(props, context, filter, params) {
if (!props || typeof props.value === 'undefined') {
throw new Error(
"No prop 'value' found. Make sure to define and add it to the input.\nAdd the prop like this:\nvalue: { type: String, required: false, default: '' }\n\nAnd the binding on the input like this:\n:value=\"value\""
)
}
const inputListeners = computed(() =>
Object.assign({}, context.listeners, {
change: createEventListener(context, 'change', filter, params),
input: createEventListener(context, 'input', filter, params),
keydown: createEventListener(context, 'keydown', filter, params),
keyup: createEventListener(context, 'keyup', filter, params),
mousedown: createEventListener(context, 'mousedown', filter, params),
mouseup: createEventListener(context, 'mouseup', filter, params),
select: createEventListener(context, 'select', filter, params),
contextmenu: createEventListener(context, 'contextmenu', filter, params),
dro: createEventListener(context, 'dro', filter, params),
})
)
return { inputListeners }
}

View File

@ -0,0 +1,7 @@
const amountPattern = /^\d*([,\\.]\d*)?$/;
const notAlphaNumPattern = /[^A-Za-z0-9\s]+/gi;
export function usePattern() {
return { amountPattern, notAlphaNumPattern };
}

View File

@ -15,6 +15,7 @@ import { useWeb3 } from "./useWeb3";
// import SidebarAaveV2Withdraw from '~/components/sidebar/context/aaveV2/SidebarAaveV2Withdraw.vue'
const sidebars = {
"/polygon/aave-v2": { component: null },
"/polygon/aave-v2#overview": { component: null },
"/polygon/aave-v2#supply": { component: SidebarAaveV2Supply },
"/polygon/aave-v2#borrow": { component: null },
@ -45,7 +46,7 @@ export function init() {
const hasDsaChanged = dsa !== oldDsa;
const [hash, params] = route.hash.split("?");
if (hasPathChanged){
router.push({ hash: null })
return

View File

@ -1,11 +1,16 @@
<template>
<div class="min-h-screen overflow-hidden font-sans antialiased text-primary-black">
<div
class="min-h-screen overflow-hidden font-sans antialiased text-primary-black"
>
<Navbar />
<div class="max-w-6xl mx-auto py-12 overflow-x-hidden ">
<div>
<Nuxt />
</div>
</div>
<Backdrop :show="isBackdropShown" @click="closeBackdrop" />
<SidebarContext class="grid-sidebar-context" />
<NotificationBar />
@ -13,12 +18,14 @@
</template>
<script>
import { defineComponent } from "@nuxtjs/composition-api";
import { defineComponent, watch } from "@nuxtjs/composition-api";
import MakerDAOIcon from '~/assets/icons/makerdao.svg?inline'
import CompoundIcon from '~/assets/icons/compound.svg?inline'
import AaveIcon from '~/assets/icons/aave.svg?inline'
import { useWeb3 } from '~/composables/useWeb3'
import { init as initSidebars } from '~/composables/useSidebar'
import { useBackdrop } from '@/composables/useBackdrop'
export default defineComponent({
components: {
MakerDAOIcon,
@ -27,6 +34,15 @@ export default defineComponent({
},
setup() {
const { active, activate, deactivate } = useWeb3();
const { isShown: isBackdropShown, close: closeBackdrop } = useBackdrop()
watch(isBackdropShown, () => {
if (isBackdropShown.value) {
document.body.classList.add('overflow-hidden')
} else {
document.body.classList.remove('overflow-hidden')
}
})
initSidebars();
@ -34,6 +50,8 @@ export default defineComponent({
active,
activate,
deactivate,
isBackdropShown,
closeBackdrop,
}
}