Skip to content

Commit

Permalink
feat: make proposal sidebar resizable
Browse files Browse the repository at this point in the history
  • Loading branch information
wa0x6e committed Dec 12, 2024
1 parent ebd0b3b commit d7ebc56
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 145 deletions.
68 changes: 68 additions & 0 deletions apps/ui/src/components/Ui/ResizableHorizontal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts" setup>
const props = defineProps<{
width: number;
max?: number;
min?: number;
}>();
const containerEl = ref<HTMLElement | null>(null);
const sliderEl = ref<HTMLElement | null>(null);
const initialized = ref(false);
const sliderOriginalPositionX = ref(0);
const { x, y } = useDraggable(sliderEl, {
axis: 'x'
});
const containerWidth = computed(() => {
const width = getWidth(x.value);
if (props.max && width > props.max) return props.max;
if (props.min && width < props.min) return props.min;
return width;
});
function getWidth(newSliderPositionX: number): number {
const offset = sliderOriginalPositionX.value - newSliderPositionX;
return props.width + offset;
}
function initResizer() {
if (!sliderEl.value || !containerEl.value) return;
const position = sliderEl.value.getBoundingClientRect();
sliderOriginalPositionX.value = position.x;
x.value = position.x;
y.value = position.y;
initialized.value = true;
}
onMounted(() => {
initResizer();
});
</script>

<template>
<div ref="containerEl" :style="{ width: `${containerWidth}px` }">
<div ref="sliderEl" class="slider" />
<slot />
</div>
</template>

<style scoped>
.slider {
@apply block fixed z-[99] w-[5px] h-full ml-[-2px];
&:before {
@apply h-full border-l block ml-[2px];
content: '';
}
&:hover {
@apply bg-skin-border cursor-col-resize;
}
}
</style>
294 changes: 149 additions & 145 deletions apps/ui/src/views/Proposal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -237,163 +237,167 @@ watchEffect(() => {
</UiScrollerHorizontal>
<router-view :proposal="proposal" />
</div>
<Affix
<UiResizableHorizontal
:width="340"
:max="400"
:min="340"
:class="[
'shrink-0 md:w-[340px] border-l-0 md:border-l',
'shrink-0 h-full',
{
'hidden md:block': route.name === 'space-proposal-votes',
'-mb-6': !withoutBottomPadding
'hidden md:block': route.name === 'space-proposal-votes'
}
]"
:top="72"
:bottom="64"
>
<div class="flex flex-col space-y-4 p-4">
<div
v-if="
!proposal.cancelled &&
['pending', 'active'].includes(proposal.state)
"
>
<h4 class="mb-2.5 eyebrow flex items-center space-x-2">
<template v-if="editMode">
<IH-cursor-click />
<span>Edit your vote</span>
</template>
<template v-else-if="currentVote">
<IH-check-circle />
<span>Your vote</span>
</template>
<template v-else>
<IH-cursor-click />
<span>Cast your vote</span>
</template>
</h4>
<div class="space-y-2">
<IndicatorVotingPower
v-if="web3.account && (!currentVote || editMode)"
v-slot="votingPowerProps"
:network-id="proposal.network"
:voting-power="votingPower"
class="mb-2 flex items-center"
@fetch-voting-power="handleFetchVotingPower"
>
<div
v-if="
votingPower?.error &&
votingPower.error.details === 'NOT_READY_YET' &&
['evmSlotValue', 'ozVotesStorageProof'].includes(
votingPower.error.source
)
"
<Affix :top="72" :bottom="64">
<div class="flex flex-col space-y-4 p-4">
<div
v-if="
!proposal.cancelled &&
['pending', 'active'].includes(proposal.state)
"
>
<h4 class="mb-2.5 eyebrow flex items-center space-x-2">
<template v-if="editMode">
<IH-cursor-click />
<span>Edit your vote</span>
</template>
<template v-else-if="currentVote">
<IH-check-circle />
<span>Your vote</span>
</template>
<template v-else>
<IH-cursor-click />
<span>Cast your vote</span>
</template>
</h4>
<div class="space-y-2">
<IndicatorVotingPower
v-if="web3.account && (!currentVote || editMode)"
v-slot="votingPowerProps"
:network-id="proposal.network"
:voting-power="votingPower"
class="mb-2 flex items-center"
@fetch-voting-power="handleFetchVotingPower"
>
<span class="inline-flex align-top h-[27px] items-center">
<IH-exclamation-circle class="mr-1" />
</span>
Please allow few minutes for the voting power to be collected
from Ethereum.
</div>
<div v-else class="flex gap-1.5 items-center">
<span class="shrink-0">Voting power:</span>
<button
type="button"
class="truncate"
@click="votingPowerProps.onClick"
>
<UiLoading
v-if="!votingPower || votingPower.status === 'loading'"
/>
<IH-exclamation
v-else-if="votingPower.status === 'error'"
class="inline-block text-rose-500"
/>
<span
v-else
class="text-skin-link"
v-text="getFormattedVotingPower(votingPower)"
/>
</button>
<a
<div
v-if="
votingPower?.status === 'success' &&
votingPower.totalVotingPower === BigInt(0)
votingPower?.error &&
votingPower.error.details === 'NOT_READY_YET' &&
['evmSlotValue', 'ozVotesStorageProof'].includes(
votingPower.error.source
)
"
:href="`${HELPDESK_URL}/en/articles/9566904-why-do-i-have-0-voting-power`"
target="_blank"
>
<IH-question-mark-circle />
</a>
</div>
</IndicatorVotingPower>
<ProposalVote
v-if="proposal"
:proposal="proposal"
:edit-mode="editMode"
@enter-edit-mode="editMode = true"
>
<ProposalVoteBasic
v-if="proposal.type === 'basic'"
:choices="proposal.choices"
@vote="handleVoteClick"
/>
<ProposalVoteSingleChoice
v-else-if="proposal.type === 'single-choice'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteApproval
v-else-if="proposal.type === 'approval'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteRankedChoice
v-else-if="proposal.type === 'ranked-choice'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteWeighted
v-else-if="['weighted', 'quadratic'].includes(proposal.type)"
<span class="inline-flex align-top h-[27px] items-center">
<IH-exclamation-circle class="mr-1" />
</span>
Please allow few minutes for the voting power to be
collected from Ethereum.
</div>
<div v-else class="flex gap-1.5 items-center">
<span class="shrink-0">Voting power:</span>
<button
type="button"
class="truncate"
@click="votingPowerProps.onClick"
>
<UiLoading
v-if="!votingPower || votingPower.status === 'loading'"
/>
<IH-exclamation
v-else-if="votingPower.status === 'error'"
class="inline-block text-rose-500"
/>
<span
v-else
class="text-skin-link"
v-text="getFormattedVotingPower(votingPower)"
/>
</button>
<a
v-if="
votingPower?.status === 'success' &&
votingPower.totalVotingPower === BigInt(0)
"
:href="`${HELPDESK_URL}/en/articles/9566904-why-do-i-have-0-voting-power`"
target="_blank"
>
<IH-question-mark-circle />
</a>
</div>
</IndicatorVotingPower>
<ProposalVote
v-if="proposal"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
</ProposalVote>
:edit-mode="editMode"
@enter-edit-mode="editMode = true"
>
<ProposalVoteBasic
v-if="proposal.type === 'basic'"
:choices="proposal.choices"
@vote="handleVoteClick"
/>
<ProposalVoteSingleChoice
v-else-if="proposal.type === 'single-choice'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteApproval
v-else-if="proposal.type === 'approval'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteRankedChoice
v-else-if="proposal.type === 'ranked-choice'"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteWeighted
v-else-if="
['weighted', 'quadratic'].includes(proposal.type)
"
:proposal="proposal"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
</ProposalVote>
</div>
</div>
<div v-if="!proposal.cancelled">
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-chart-square-bar />
Results
</h4>
<ProposalResults
with-details
:proposal="proposal"
:decimals="votingPowerDecimals"
/>
</div>
<div v-if="space.labels?.length && proposal.labels?.length">
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-tag />
Labels
</h4>
<ProposalLabels
:labels="proposal.labels"
:space="space"
with-link
/>
</div>
<div>
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-clock />
Timeline
</h4>
<ProposalTimeline :data="proposal" />
</div>
</div>
<div v-if="!proposal.cancelled">
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-chart-square-bar />
Results
</h4>
<ProposalResults
with-details
:proposal="proposal"
:decimals="votingPowerDecimals"
/>
</div>
<div v-if="space.labels?.length && proposal.labels?.length">
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-tag />
Labels
</h4>
<ProposalLabels
:labels="proposal.labels"
:space="space"
with-link
/>
</div>
<div>
<h4 class="mb-2.5 eyebrow flex items-center gap-2">
<IH-clock />
Timeline
</h4>
<ProposalTimeline :data="proposal" />
</div>
</div>
</Affix>
</Affix>
</UiResizableHorizontal>
</template>
<teleport to="#modal">
<ModalTerms
Expand Down

0 comments on commit d7ebc56

Please sign in to comment.