Skip to content

Commit

Permalink
rebuid grid on widget resize
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita-Polyakov committed Mar 7, 2024
1 parent 7aafeb6 commit b06a6b3
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 106 deletions.
71 changes: 41 additions & 30 deletions public/env.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"BASE_API_URL": "",
"API_KEYS": {
"moonpay": "pk_test_4ASGxHKGpLPE6sdQq1V3QjtpUFSpWLk",
"x1ex": "sprkwdgt-WYL6QBNC",
"nftStorage": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweDBmMzgwOTMyQTNDODM3ZDNiN2JEYzBBNTc0NmNkMDlBRGIyNUZGMzQiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTY0MjU4OTQ2ODA4MSwibmFtZSI6Im5mdC1zdG9yYWdlLWRldiJ9.hkvzea9ltcriXXHKoYd3F2Iu1Y8X5H-zunAQboC_3vw",
"etherscan": "YBR7IWEBEXFICT8M7GRV77NBX4AXJ9T53H",
"googleApi": "AIzaSyAzj7JxB-j8pJixtt6JSqLPhG0y02CGYOU",
"googleClientId": "498393666682-9eeiioee0a2sgb1671e9qir645f9n6cv.apps.googleusercontent.com"
"moonpay": "pk_live_LEyw9bsfK0n0v3cdHUaZS1z0qYdDRDu6",
"x1ex": "sprkwdgt-WUQBA5U2",
"etherscan": "XBS98SSP8J5FNS7EKQHMHRU94Z2YG7HFFK",
"nftStorage": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweDBmMzgwOTMyQTNDODM3ZDNiN2JEYzBBNTc0NmNkMDlBRGIyNUZGMzQiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTY0NTU0MDQ4MTQ5OSwibmFtZSI6Im5mdC1zdG9yYWdlLXByb2QifQ.CnW1bq8eWEYQxA6cggGMeAqCTozCJZ6s2oL_bssqnmc",
"googleApi": "AIzaSyCazdRheGfy7_bwVi0m5UgUYjz7l0c54nM",
"googleClientId": "728106973134-pfmcagr0t4lchepajcp7llp7f1t09lf0.apps.googleusercontent.com"
},
"FEATURE_FLAGS": {
"moonpay": true,
Expand All @@ -15,44 +15,55 @@
"soraCard": false,
"orderBook": true
},
"FAUCET_URL": "https://faucet.dev.sora2.tachi.soramitsu.co.jp/",
"SUBQUERY_ENDPOINT": "https://api.subquery.network/sq/sora-xor/sora-prod",
"SUBSQUID_ENDPOINT": "",
"DEFAULT_NETWORKS": [
{
"chain": "SORA-dev Testnet #1",
"name": "SORA",
"address": "wss://ws.framenode-1.r0.dev.sora2.soramitsu.co.jp",
"chain": "SORA",
"name": "SORA Parliament Ministry of Finance #1",
"address": "wss://ws.mof.sora.org",
"location": "GB"
},
{
"chain": "SORA-dev Testnet #2",
"name": "SORA",
"address": "wss://ws.framenode-2.r0.dev.sora2.soramitsu.co.jp",
"chain": "SORA",
"name": "SORA Parliament Ministry of Finance #2",
"address": "wss://mof2.sora.org",
"location": "SG"
},
{
"chain": "SORA-dev Testnet #3",
"name": "SORA",
"address": "wss://ws.framenode-3.r0.dev.sora2.soramitsu.co.jp"
"chain": "SORA",
"name": "SORA Parliament Ministry of Finance #3",
"address": "wss://mof3.sora.org",
"location": "DE"
},
{
"chain": "SORA",
"name": "OnFinality",
"address": "wss://sora.api.onfinality.io/public-ws",
"location": "JP"
},
{
"chain": "SORA",
"name": "Soramitsu",
"address": "wss://ws.framenode-0.a1.sora2.soramitsu.co.jp",
"location": "IE"
}
],
"SUBQUERY_ENDPOINT": "https://api.subquery.network/sq/sora-xor/sora-dev",
"SUBSQUID_ENDPOINT": "https://squid.subsquid.io/sora-dev/v/v1/graphql",
"NETWORK_TYPE": "Dev",
"CHAIN_GENESIS_HASH": "",
"EVM_NETWORKS_IDS": [97, 1001],
"NETWORK_TYPE": "Prod",
"CHAIN_GENESIS_HASH": "0x7e4e32d0feafd4f9c9414b0be86373f9a1efa904809b683453a9af6856d38ad5",
"SUB_NETWORKS": {
"Rococo": "wss://ws.relaychain-node-1.r1.dev.sora2.soramitsu.co.jp",
"RococoSora": "wss://ws.parachain-collator-1.c1.dev.sora2.soramitsu.co.jp",
"Kusama": "wss://kusama-rpc.polkadot.io",
"KusamaSora": "wss://ws.parachain-collator-1.c1.sora2.soramitsu.co.jp",
"Polkadot": "wss://rpc.polkadot.io"
"Kusama": "wss://kusama-rpc.dwellir.com",
"KusamaSora": "wss://ws.parachain-collator-2.c2.sora2.soramitsu.co.jp",
"Polkadot": "wss://polkadot-rpc.dwellir.com",
"PolkadotSora": "wss://ws.parachain-collator-3.pc3.sora2.soramitsu.co.jp"
},
"EVM_NETWORKS_IDS": [56, 8217],
"ETH_BRIDGE": {
"evmNetwork": 11155111,
"evmNetwork": 1,
"address": {
"XOR": "0x7F62CCd5566c64cfb785f73B3c19653D93e5414c",
"VAL": "0xe2C58207Cc6dF5565044eccffdf7aeb2DAe89647",
"OTHER": "0x401c6A23a44f72151D90878DF0aa86E77fBde0e2"
"XOR": "0xc08edf13be9b9cc584c5da8004ce7e6be63c1316",
"VAL": "0xd1eeb2f30016fffd746233ee12c486e7ca8efef1",
"OTHER": "0x313416870A4da6F12505a550B67bB73c8E21D5d3"
}
}
}
3 changes: 2 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ import AppHeader from '@/components/App/Header/AppHeader.vue';
import AppMenu from '@/components/App/Menu/AppMenu.vue';
import NodeErrorMixin from '@/components/mixins/NodeErrorMixin';
import SoraLogo from '@/components/shared/Logo/Sora.vue';
import { PageNames, Components, Language, BreakpointClass, Breakpoint } from '@/consts';
import { PageNames, Components, Language } from '@/consts';
import { BreakpointClass, Breakpoint } from '@/consts/layout';
import { getLocale } from '@/lang';
import router, { goTo, lazyComponent } from '@/router';
import { action, getter, mutation, state } from '@/store/decorators';
Expand Down
3 changes: 2 additions & 1 deletion src/components/App/Header/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import { Component, Mixins, Prop } from 'vue-property-decorator';
import WalletConnectMixin from '../../../components/mixins/WalletConnectMixin';
import PolkaswapLogo from '../../../components/shared/Logo/Polkaswap.vue';
import { PageNames, Components, BreakpointClass } from '../../../consts';
import { PageNames, Components } from '../../../consts';
import { BreakpointClass } from '../../../consts/layout';
import { lazyComponent, goTo } from '../../../router';
import { state, getter } from '../../../store/decorators';
Expand Down
8 changes: 7 additions & 1 deletion src/components/pages/Swap/Widget/Form.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<template>
<base-widget v-loading="parentLoading" class="swap-widget" :title="t('exchange.Swap')" primary-title>
<base-widget
class="swap-widget"
:title="t('exchange.Swap')"
primary-title
v-loading="parentLoading"
v-on="$listeners"
>
<template #filters>
<swap-status-action-badge>
<template #label>{{ t('marketText') }}:</template>
Expand Down
50 changes: 47 additions & 3 deletions src/components/shared/Widget/Base.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<template>
<s-card :class="['base-widget', { delimeter, full }]" border-radius="small" shadow="always" size="big" primary>
<s-card
ref="container"
border-radius="small"
shadow="always"
size="big"
primary
:class="['base-widget', { delimeter, full }]"
>
<template #header v-if="hasHeader">
<div class="base-widget-block base-widget-header">
<div :class="['base-widget-block', 'base-widget-title', { primary: primaryTitle }]">
Expand All @@ -21,14 +28,16 @@
</div>
</template>

<div :class="['base-widget-content', { extensive }]">
<div :class="['base-widget-content', { extensive }]" ref="content">
<slot />
</div>
</s-card>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue, Ref } from 'vue-property-decorator';
import { debouncedInputHandler } from '@/utils';
@Component
export default class BaseWidget extends Vue {
Expand All @@ -39,9 +48,39 @@ export default class BaseWidget extends Vue {
@Prop({ default: false, type: Boolean }) readonly extensive!: boolean;
@Prop({ default: false, type: Boolean }) readonly primaryTitle!: boolean;
@Ref('container') readonly container!: Vue;
@Ref('content') readonly content!: HTMLDivElement;
observer: ResizeObserver | null = null;
handleContentResize = debouncedInputHandler(this.onContentResize, 300, { leading: false });
get hasHeader(): boolean {
return !!this.title || !!this.$slots.title;
}
mounted(): void {
this.createContentObserver();
}
beforeDestroy(): void {
this.destroyContentObserver();
}
private createContentObserver(): void {
this.observer = new ResizeObserver(this.handleContentResize);
this.observer.observe(this.content);
}
private destroyContentObserver(): void {
this.observer?.disconnect();
this.observer = null;
}
onContentResize(): void {
const el = this.container.$el;
const { clientHeight: height, clientWidth: width } = el;
this.$emit('resize', { height, width });
}
}
</script>

Expand Down Expand Up @@ -76,6 +115,11 @@ $left: $inner-spacing-medium;
display: flex;
flex-flow: column nowrap;
align-items: normal;
overflow: unset;
&.full {
flex: 1;
}
&-block {
display: flex;
Expand Down
98 changes: 63 additions & 35 deletions src/components/shared/Widget/Grid/Grid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,68 @@
<div ref="gridWrapper" class="grid-wrapper">
<grid-layout
:layout.sync="layout"
:responsive-layouts="layouts"
:responsive-layouts="responsiveLayouts"
:cols="cols"
:breakpoints="breakpoints"
:row-height="rowHeight"
:is-draggable="draggable"
:is-resizable="resizable"
:vertical-compact="compact"
:margin="[margin, margin]"
:responsive="true"
:prevent-collision="true"
:use-css-transforms="true"
:vertical-compact="compact"
:use-css-transforms="false"
@layout-ready="onReady"
@layout-updated="onUpdate"
@breakpoint-changed="onBreakpointChanged"
>
<grid-item
v-for="item in layout"
:key="item.i"
:i="item.i"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:min-h="item.minH"
:min-w="item.minW"
class="grid-item"
>
<template v-if="ready">
<slot v-bind="item" />
<slot
v-bind="{
id: item.i,
resize: ($event) => onWidgetResize($event, item.i),
}"
/>
</template>
</grid-item>
</grid-layout>
</div>
</template>

<script lang="ts">
import cloneDeep from 'lodash/fp/cloneDeep';
import debounce from 'lodash.debounce';
import { GridLayout, GridItem } from 'vue-grid-layout';
import { Component, Prop, Ref, Vue, Watch } from 'vue-property-decorator';
import { Component, Prop, Ref, Vue } from 'vue-property-decorator';
import { Breakpoint } from '@/consts';
import { Breakpoint, BreakpointKey } from '@/consts/layout';
import type { Layout, LayoutConfiguration, ResponsiveLayouts } from '@/types/layout';
const DEFAULT_BREAKPOINT = 'lg';
const DEFAULT_BREAKPOINTS = {
lg: Breakpoint.HugeDesktop, // 2092
md: Breakpoint.LargeDesktop, // 1440
sm: Breakpoint.Desktop, // 1024
xs: Breakpoint.LargeMobile, // 528
xss: 0,
const DEFAULT_BREAKPOINTS: LayoutConfiguration = {
[BreakpointKey.lg]: Breakpoint.HugeDesktop, // 2092
[BreakpointKey.md]: Breakpoint.LargeDesktop, // 1440
[BreakpointKey.sm]: Breakpoint.Desktop, // 1024
[BreakpointKey.xs]: Breakpoint.LargeMobile, // 528
[BreakpointKey.xss]: 0,
};
const DEFAULT_COLS = {
lg: 24,
md: 12,
sm: 12,
xs: 4,
xss: 4,
const DEFAULT_COLS: LayoutConfiguration = {
[BreakpointKey.lg]: 24,
[BreakpointKey.md]: 12,
[BreakpointKey.sm]: 12,
[BreakpointKey.xs]: 4,
[BreakpointKey.xss]: 4,
};
@Component({
Expand All @@ -65,32 +73,56 @@ const DEFAULT_COLS = {
},
})
export default class WidgetsGrid extends Vue {
@Prop({ required: true, type: Object }) readonly layouts!: any;
@Prop({ required: true, type: Object }) readonly layouts!: ResponsiveLayouts;
@Prop({ default: 10, type: Number }) readonly rowHeight!: number;
@Prop({ default: 16, type: Number }) readonly margin!: number;
@Prop({ default: false, type: Boolean }) readonly compact!: boolean;
@Prop({ default: false, type: Boolean }) readonly draggable!: boolean;
@Prop({ default: false, type: Boolean }) readonly resizable!: boolean;
@Prop({ default: () => DEFAULT_COLS, type: Object }) readonly cols!: any;
@Prop({ default: () => DEFAULT_BREAKPOINTS, type: Object }) readonly breakpoints!: any;
@Prop({ default: false, type: Boolean }) readonly compact!: boolean;
@Prop({ default: () => DEFAULT_COLS, type: Object }) readonly cols!: LayoutConfiguration;
@Prop({ default: () => DEFAULT_BREAKPOINTS, type: Object }) readonly breakpoints!: LayoutConfiguration;
@Ref('gridWrapper') readonly gridWrapper!: HTMLDivElement;
ready = false;
onReady = debounce(this.setReady);
breakpoint = DEFAULT_BREAKPOINT;
layout = [];
breakpoint: BreakpointKey = BreakpointKey.lg;
layout: Layout = [];
get responsiveLayouts(): ResponsiveLayouts {
return Object.entries(this.layouts).reduce<ResponsiveLayouts>((acc, [key, layout]) => {
acc[key] = layout.map((widget) => ({
...widget,
minW: widget.minW ?? widget.w,
minH: widget.minH ?? widget.h,
}));
return acc;
}, {});
}
setReady(): void {
this.ready = true;
}
onUpdate(newLayout): void {
console.log('Updated layout: ', newLayout);
onBreakpointChanged(newBreakpoint: BreakpointKey): void {
this.breakpoint = newBreakpoint;
}
onBreakpointChanged(newBreakpoint, newLayout) {
console.log('BREAKPOINT CHANGED breakpoint=', newBreakpoint, ', layout: ', newLayout);
onWidgetResize(rect: DOMRectReadOnly, id: string): void {
const layout: Layout = cloneDeep(this.layout);
const widget = layout.find((item) => item.i === id);
if (!widget) return;
const { height } = rect;
// `height = h * (rowHeight + margin) - margin` - library calculates grid-item height (px)
// `h = (height + margin) / (rowHeight + margin)`
const calculatedH = Math.ceil((height + this.margin) / (this.rowHeight + this.margin));
const updatedH = Math.max(widget.minH, calculatedH);
// mutate local layout
widget.h = updatedH;
// update component layout
this.layout = layout;
}
}
</script>
Expand All @@ -100,10 +132,6 @@ export default class WidgetsGrid extends Vue {
.grid-item {
display: flex;
flex-flow: column nowrap;
.base-widget {
flex: 1;
}
}
}
</style>
Loading

0 comments on commit b06a6b3

Please sign in to comment.