Skip to content

Commit bc6bf23

Browse files
authored
feat: Wallet with panels (#164)
1 parent 413a5a9 commit bc6bf23

31 files changed

+869
-288
lines changed

frontend/assets/styles/ant-design.less

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
@primary-color: #1aab9b;
44
@modal-body-padding: 0px;
55
@dropdown-selected-color: @text-color;
6+
@border-width-base: 2px;
67

78
.anticon {
89
vertical-align: 0.125em !important;

frontend/components/MainFlow.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
:auction-transaction="selectedAuction"
4545
:is-connecting="isConnecting"
4646
:is-authorizing="isAuthorizing"
47-
:is-wallet-authorised="isWalletAuthorised"
47+
:is-wallet-authorized="isWalletAuthorized"
4848
:authorised-collaterals="authorisedCollaterals"
4949
:is-executing="isExecuting"
5050
:wallet-address="walletAddress"
@@ -63,7 +63,7 @@
6363
:is-authorizing="isAuthorizing"
6464
:is-depositing-or-withdrawing="isDepositingOrWithdrawing"
6565
:is-executing="isExecuting"
66-
:is-wallet-authorised="isWalletAuthorised"
66+
:is-wallet-authorized="isWalletAuthorized"
6767
:is-explanations-shown="isExplanationsShown"
6868
:authorised-collaterals="authorisedCollaterals"
6969
:wallet-address="walletAddress"
@@ -134,7 +134,7 @@ export default Vue.extend({
134134
type: Boolean,
135135
default: false,
136136
},
137-
isWalletAuthorised: {
137+
isWalletAuthorized: {
138138
type: Boolean,
139139
default: false,
140140
},

frontend/components/common/BaseButton.vue

+22-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<Button
33
:disabled="disabled || isLoading"
4-
:class="buttonStyles"
5-
class="overflow-hidden"
4+
:class="buttonClass"
5+
class="Button"
66
:type="type"
77
:html-type="htmlType"
88
@click="$emit('click')"
@@ -43,35 +43,43 @@ export default Vue.extend({
4343
},
4444
},
4545
computed: {
46-
buttonStyles() {
46+
buttonClass() {
4747
if (this.type === 'primary') {
48-
return 'Button-Primary';
48+
return 'Primary';
4949
}
5050
if (this.type === 'link') {
51-
return 'Button-Link';
51+
return 'Link';
5252
}
53-
return '';
53+
return 'Default';
5454
},
5555
},
5656
});
5757
</script>
5858

59-
<style>
60-
.Button-Primary,
61-
.dark .Button-Primary {
59+
<style scoped>
60+
.Button {
61+
@apply overflow-hidden;
62+
}
63+
64+
.Button.Default:enabled {
65+
@apply border-primary text-primary hover:text-white hover:bg-primary-light hover:border-primary-light;
66+
}
67+
68+
.Primary:enabled,
69+
.dark .Primary:enabled {
6270
@apply text-white bg-primary border-primary focus:bg-primary-light focus:border-primary-light hover:bg-primary-light hover:border-primary-light;
6371
}
6472
65-
.Button-Link,
66-
.dark .Button-Link {
67-
@apply inline-flex items-center p-0 h-auto;
73+
.Link,
74+
.dark .Link {
75+
@apply inline-flex items-center p-0 h-auto leading-4;
6876
}
6977
70-
.Button-Link span {
78+
.Link span {
7179
@apply text-primary underline overflow-auto transition-colors;
7280
}
7381
74-
.Button-Link:hover span {
82+
.Link:hover span {
7583
@apply text-primary-light no-underline;
7684
}
7785
</style>

frontend/components/common/BasePanel.stories.js

+15-10
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,43 @@ storiesOf('common/BasePanel', module)
1616
.add('Stack of panels', () => ({
1717
...common,
1818
template: `<div>
19-
<BasePanel v-bind="$data" currentState="incorrect"> Error content </BasePanel>
20-
<BasePanel v-bind="$data" currentState="notice"> Notice content </BasePanel>
21-
<BasePanel v-bind="$data" currentState="correct"> Correct content </BasePanel>
22-
<BasePanel v-bind="$data" currentState="inactive"> Inactive content </BasePanel>
19+
<BasePanel currentState="incorrect"><template #title>{{ incorrect }}</template>Error content</BasePanel>
20+
<BasePanel currentState="notice"><template #title>{{ notice }}</template>Notice content</BasePanel>
21+
<BasePanel currentState="correct"><template #title>{{ correct }}</template>Correct content</BasePanel>
22+
<BasePanel currentState="inactive"><template #title>{{ inactive }}</template>Inactive content</BasePanel>
2323
</div>`,
2424
}))
2525
.add('Inactive', () => ({
2626
...common,
27-
template: '<BasePanel v-bind="$data" currentState="inactive"> Inactive content </BasePanel>',
27+
template:
28+
'<BasePanel currentState="inactive"><template #title>{{ inactive }}</template>Inactive content</BasePanel>',
2829
}))
2930
.add('Incorrect', () => ({
3031
...common,
31-
template: '<BasePanel v-bind="$data" currentState="incorrect"> Incorrect content </BasePanel>',
32+
template:
33+
'<BasePanel currentState="incorrect"><template #title>{{ incorrect }}</template>Incorrect content</BasePanel>',
3234
}))
3335
.add('Correct', () => ({
3436
...common,
35-
template: '<BasePanel v-bind="$data" currentState="correct"> Correct content </BasePanel>',
37+
template:
38+
'<BasePanel currentState="correct"><template #title>{{ correct }}</template>Correct content</BasePanel>',
3639
}))
3740
.add('Notice', () => ({
3841
...common,
39-
template: '<BasePanel v-bind="$data" currentState="notice"> Notice content </BasePanel>',
42+
template:
43+
'<BasePanel currentState="notice"><template #title>{{ notice }}</template>Notice content</BasePanel>',
4044
}))
4145
.add('Very long notice', () => ({
4246
...common,
43-
template: `<BasePanel notice="${faker.lorem.sentence(20)}" currentState="notice">
47+
template: `<BasePanel currentState="notice">
48+
<template #title>${faker.lorem.sentence(20)}</template>
4449
Content: ${faker.lorem.sentence(50)}
4550
</BasePanel>`,
4651
}))
4752
.add('Notice with html', () => ({
4853
...common,
4954
template: `<BasePanel currentState="notice">
50-
<template #notice>Title with <i>italic</i> text</template>
55+
<template #title>Title with <i>italic</i> text</template>
5156
Content: ${faker.lorem.sentence(50)}
5257
</BasePanel>`,
5358
}));

frontend/components/common/BasePanel.vue

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
<template>
22
<div class="BasePanel">
3-
<button class="Title" :class="titleClass" @click="isExpanded = !isExpanded">
3+
<button class="Title" type="button" :class="titleClass" @click="isExpanded = !isExpanded">
44
<div class="Icon" :class="iconClass">
55
<Icon v-if="currentState === 'inactive'" type="close" />
66
<Icon v-else-if="currentState === 'incorrect'" type="warning" theme="filled" />
77
<Icon v-else-if="currentState === 'correct'" type="check" />
88
<Icon v-else-if="currentState === 'notice'" type="warning" theme="filled" />
99
</div>
10-
<slot :name="currentState">{{ $props[currentState] }}</slot>
10+
<slot name="title" />
1111
</button>
12-
<div v-show="isExpanded" class="Content">
13-
<slot />
14-
</div>
12+
<CollapseTransition>
13+
<div v-show="isExpanded" class="Content">
14+
<slot />
15+
</div>
16+
</CollapseTransition>
1517
</div>
1618
</template>
1719

1820
<script lang="ts">
1921
import Vue from 'vue';
2022
import { Icon } from 'ant-design-vue';
23+
import CollapseTransition from '@ivanv/vue-collapse-transition';
2124
2225
const STATES = [
2326
{
@@ -49,41 +52,32 @@ const STATES = [
4952
export default Vue.extend({
5053
components: {
5154
Icon,
55+
CollapseTransition,
5256
},
5357
props: {
5458
currentState: {
5559
type: String,
5660
required: true,
5761
validator: (value: string) => STATES.map(s => s.name).includes(value),
5862
},
59-
...STATES.reduce(
60-
(props, state) => ({
61-
...props,
62-
[state.name]: {
63-
type: String,
64-
default: '',
65-
},
66-
}),
67-
{}
68-
),
6963
},
7064
data() {
7165
return {
7266
isExpanded: false,
7367
};
7468
},
7569
computed: {
76-
titleClass() {
70+
titleClass(): string | undefined {
7771
return STATES.find(s => s.name === this.currentState)?.titleClass;
7872
},
79-
iconClass() {
73+
iconClass(): string | undefined {
8074
return STATES.find(s => s.name === this.currentState)?.iconClass;
8175
},
8276
},
8377
watch: {
8478
currentState: {
8579
immediate: true,
86-
handler(newState: string) {
80+
handler(newState: string): void {
8781
this.isExpanded = STATES.filter(s => s.isExpanded)
8882
.map(s => s.name)
8983
.includes(newState);
@@ -98,6 +92,15 @@ export default Vue.extend({
9892
@apply flex flex-col;
9993
@apply border-2 border-gray-300 dark:border-gray-600 dark:text-gray-200;
10094
}
95+
.BasePanel:first-child {
96+
@apply rounded-t;
97+
}
98+
.BasePanel:last-child {
99+
@apply rounded-b;
100+
}
101+
.BasePanel:only-child {
102+
@apply rounded;
103+
}
101104
.BasePanel + .BasePanel {
102105
margin-top: -2px;
103106
}
@@ -108,6 +111,6 @@ export default Vue.extend({
108111
@apply inline;
109112
}
110113
.Content {
111-
@apply px-2 py-1;
114+
@apply px-3 pt-1 pb-3;
112115
}
113116
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<template>
2+
<div class="BaseValueInput">
3+
<Tooltip :visible="!!errorMessage" placement="topLeft" :title="errorMessage">
4+
<Input
5+
v-model="inputText"
6+
class="Input"
7+
:class="{ Error: !!errorMessage }"
8+
:disabled="disabled"
9+
@focus="hideMaxValue"
10+
@blur="showMaxValueIfEmpty"
11+
/>
12+
<div class="Overlay right-2" :class="{ 'opacity-50': disabled }">
13+
<format-currency v-if="!inputValue && maxValue" :value="maxValue" />&nbsp;DAI
14+
</div>
15+
</Tooltip>
16+
</div>
17+
</template>
18+
19+
<script lang="ts">
20+
import Vue from 'vue';
21+
import BigNumber from 'bignumber.js';
22+
import { Input, Tooltip } from 'ant-design-vue';
23+
import FormatCurrency from '~/components/utils/FormatCurrency.vue';
24+
25+
export default Vue.extend({
26+
components: { FormatCurrency, Tooltip, Input },
27+
props: {
28+
inputValue: {
29+
type: Object as Vue.PropType<BigNumber> | undefined,
30+
default: undefined,
31+
},
32+
minValue: {
33+
type: Object as Vue.PropType<BigNumber>,
34+
default: undefined,
35+
},
36+
maxValue: {
37+
type: Object as Vue.PropType<BigNumber>,
38+
default: undefined,
39+
},
40+
validator: {
41+
type: Function as Vue.PropType<function>,
42+
default: () => {},
43+
},
44+
disabled: {
45+
type: Boolean,
46+
default: false,
47+
},
48+
},
49+
data() {
50+
return {
51+
inputText: '',
52+
};
53+
},
54+
computed: {
55+
isInputEmpty(): boolean {
56+
return this.inputText.trim() === '';
57+
},
58+
inputTextParsed(): BigNumber | undefined {
59+
if (this.isInputEmpty) {
60+
return undefined;
61+
}
62+
return new BigNumber(this.inputText.trim());
63+
},
64+
errorMessage(): string {
65+
try {
66+
if (this.validator) {
67+
this.validator(this.inputTextParsed, this.minValue, this.maxValue);
68+
}
69+
} catch (error) {
70+
return error.message;
71+
}
72+
return '';
73+
},
74+
},
75+
watch: {
76+
inputText(_newInputText: string, oldInputText: string) {
77+
if (this.errorMessage || !this.inputTextParsed) {
78+
this.$emit('update:inputValue', new BigNumber(NaN));
79+
return;
80+
}
81+
if (this.inputTextParsed.isNaN()) {
82+
this.inputText = oldInputText || '';
83+
return;
84+
}
85+
this.$emit('update:inputValue', this.inputTextParsed);
86+
},
87+
inputValue(newInputValue: BigNumber | undefined) {
88+
if (!newInputValue) {
89+
this.inputText = '';
90+
return;
91+
}
92+
if (newInputValue.isEqualTo(this.minValue) && !newInputValue.isZero()) {
93+
this.inputText = newInputValue.toFixed();
94+
}
95+
},
96+
},
97+
methods: {
98+
hideMaxValue(): void {
99+
if (!this.inputValue) {
100+
this.$emit('update:inputValue', new BigNumber(NaN));
101+
}
102+
},
103+
showMaxValueIfEmpty(): void {
104+
if (this.isInputEmpty) {
105+
this.$emit('update:inputValue', undefined);
106+
}
107+
},
108+
},
109+
});
110+
</script>
111+
112+
<style scoped>
113+
.BaseValueInput {
114+
@apply w-full relative dark:text-gray-300;
115+
}
116+
117+
.Input {
118+
@apply text-right pr-8;
119+
}
120+
121+
.Error {
122+
@apply border-red-500 hover:border-red-400;
123+
}
124+
125+
.Error:focus {
126+
@apply border-red-400;
127+
128+
box-shadow: 0 0 3px rgb(239, 68, 68);
129+
}
130+
131+
.Overlay {
132+
@apply absolute pointer-events-none h-full flex items-center;
133+
134+
top: 0.5px;
135+
}
136+
</style>

0 commit comments

Comments
 (0)