Skip to content

Commit

Permalink
Merge pull request #528 from gobitfly/BIDS-2859/frontend-subscription…
Browse files Browse the repository at this point in the history
…-to-notifications-modal

(BIDS-2859) frontend - subscription to notifications modal
  • Loading branch information
thib-wien authored Jul 29, 2024
2 parents 4509a99 + 7a82bb3 commit 25be31c
Show file tree
Hide file tree
Showing 34 changed files with 1,223 additions and 215 deletions.
1 change: 1 addition & 0 deletions frontend/assets/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ a {
pointer-events: none;
}
}

.link {
color: var(--link-color);
cursor: pointer;
Expand Down
46 changes: 46 additions & 0 deletions frontend/components/bc/InputNumber.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script setup lang="ts">
import type { Nullable } from 'primevue/ts-helpers'
import InputNumber from 'primevue/inputnumber'
const props = defineProps<{
min: number,
max: number,
maxFractionDigits: number
}>()
const parentVmodel = defineModel<number>({ required: true })
const bridgedVmodel = usePrimitiveRefBridge<number, Nullable<number>>(parentVmodel, n => (isNaN(n) ? null : n), n => (n ?? NaN))
function sendValue (input: Nullable<number>) : void {
if (input === undefined || input === null || isNaN(input) || input < props.min || input > props.max) {
input = NaN
} else {
const stringifyied = String(input)
const comma = stringifyied.indexOf('.')
if (comma >= 0 && stringifyied.length - comma - 1 > props.maxFractionDigits) {
input = NaN
}
}
bridgedVmodel.pauseBridgeFromNowOn() // this allows us to output the value to the parent v-model without causing an injection of the value back into the InputNumber v-model (that would empty InputNumber at each key stroke if the input is invalid)
parentVmodel.value = input
bridgedVmodel.wakeupBridgeAtNextTick()
}
</script>

<template>
<InputNumber
v-model="bridgedVmodel"
:min="min"
:max="max"
:max-fraction-digits="maxFractionDigits"
locale="en-US"
class="why-the-hell-dont-they-fix-this-bug"
@input="input => { if (typeof input.value !== 'string') sendValue(input.value) }"
/>
</template>

<style scoped lang="scss">
.why-the-hell-dont-they-fix-this-bug {
:deep(input) { width: 100%; }
}
</style>
55 changes: 55 additions & 0 deletions frontend/components/bc/NetworkSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script setup lang="ts">
import type { MultiBarItem } from '~/types/multiBar'
import { IconNetwork } from '#components'
import { ChainInfo, ChainIDs } from '~/types/network'
const props = defineProps<{
readonlyNetworks?: ChainIDs[]
}>()
const { availableNetworks, isNetworkDisabled } = useNetworkStore()
/** If prop `:readonly-networks` is given:
* the networks in array `:readonly-networks` are shown to the user and they are unclickable,
* Otherwise, give a v-model. If the v-model is
* a ChainIDs: only one network can be selected by the user,
* an array of ChainIDs: several networks can be selected by the user */
const selection = defineModel<ChainIDs|ChainIDs[]>({ required: false })
let barSelection: Ref<string> | Ref<string[]>
if (props.readonlyNetworks) {
barSelection = ref<string[]>([])
} else if (Array.isArray(selection.value)) {
barSelection = useArrayRefBridge<ChainIDs, string>(selection as Ref<ChainIDs[]>)
} else {
barSelection = usePrimitiveRefBridge<ChainIDs, string>(selection as Ref<ChainIDs>)
}
const buttons = computed(() => {
const list: MultiBarItem[] = []
const source = props.readonlyNetworks || availableNetworks.value
for (const chainId of source) {
list.push({
component: IconNetwork,
componentProps: { chainId, harmonizePerceivedSize: true, colored: true },
componentClass: 'maximum',
value: String(chainId),
disabled: isNetworkDisabled(chainId),
tooltip: ChainInfo[chainId].name
})
}
return list
})
</script>

<template>
<BcToggleMultiBar v-if="Array.isArray(barSelection)" v-model="barSelection" :buttons="buttons" :readonly-mode="!!readonlyNetworks" />
<BcToggleSingleBar v-else v-model="barSelection" :buttons="buttons" layout="minimal" />
</template>

<style lang="scss">
.maximum {
width: 100%;
height: 100%;
}
</style>
3 changes: 1 addition & 2 deletions frontend/components/bc/SearchbarGeneral.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { SearchbarShape, type SearchbarColors, SearchbarPurpose, ResultType, type ResultSuggestion, pickHighestPriorityAmongBestMatchings } from '~/types/searchbar'
import { ChainInfo } from '~/types/network'
defineProps<{
barShape: SearchbarShape,
Expand All @@ -11,7 +10,7 @@ defineProps<{
async function redirectToRelevantPage (result : ResultSuggestion) {
let path : string
let q = ''
const networkPath = '/networks' + ChainInfo[result.chainId].path
const networkPath = '/networks/' + result.chainId
switch (result.type) {
case ResultType.Tokens :
Expand Down
1 change: 0 additions & 1 deletion frontend/components/bc/searchbar/SearchbarMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import {
} from '~/types/searchbar'
import { ChainIDs, ChainInfo } from '~/types/network'
import { API_PATH } from '~/types/customFetch'
import { useNetworkStore } from '~/stores/useNetworkStore'
const dropdownLayout = ref<SearchbarDropdownLayout>('narrow-dropdown')
Expand Down
8 changes: 5 additions & 3 deletions frontend/components/bc/table/BcTablePopoutEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ interface Props {
truncateText?: boolean,
}
defineProps<Props>()
</script>

<template>
<div class="bc-poput-edit" :class="{ 'truncate-text': truncateText }">
<slot name="content">
<span v-if="label" class="content">
<BcTooltip v-if="label" :hide="!truncateText" :fit-content="true" class="content" :text="label">
{{ label }}
</span>
</BcTooltip>
</slot>
<div class="icon">
<FontAwesomeIcon v-if="!noIcon" class="link" :icon="faEdit" @click="() => emit('onEdit')" />
</div>
</div>
</template>

<style lang="scss" scoped>
@use "~/assets/css/utils.scss";
Expand All @@ -37,6 +38,7 @@ defineProps<Props>()
.content {
@include utils.truncate-text;
user-select: none;
}
}
Expand Down
63 changes: 40 additions & 23 deletions frontend/components/bc/toggle/MultiBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,57 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { type MultiBarItem } from '~/types/multiBar'
interface Props {
icons: MultiBarItem[]
readonlyMode?: boolean,
buttons: MultiBarItem[]
}
const props = defineProps<Props>()
const selected = defineModel<string[]>({ required: true })
type ButtonStates = Record<string, boolean>
const inital: Record<string, boolean> = {}
const modelValues = ref<Record<string, boolean>>(props.icons.reduce((map, { value }) => {
map[value] = selected.value.includes(value)
return map
}, inital))
const selection = defineModel<string[]>({ required: true })
const buttonStates = useObjectRefBridge<string[], ButtonStates>(selection, receiveFromVModel, sendToVModel)
watch(modelValues, () => {
const list: string[] = []
Object.entries(modelValues.value).forEach(([key, value]) => {
function receiveFromVModel (data: string[]) : ButtonStates {
const states = props.buttons.reduce((map, { value }) => {
map[value] = data.includes(value)
return map
}, {} as ButtonStates)
return states
}
function sendToVModel (data: ButtonStates) : string[] {
const selection: string[] = []
Object.entries(data).forEach(([key, value]) => {
if (value) {
list.push(key)
selection.push(key)
}
})
selected.value = list
}, { deep: true })
return selection
}
// this line is independent of the bridge above (that addresses the on/off states), this line updates the component if the list of buttons comes late
watch(() => props.buttons, () => { buttonStates.value = receiveFromVModel(selection.value) })
const readonlyClass = computed(() => props.readonlyMode ? 'read-only' : '')
</script>

<template>
<div class="bc-togglebar">
<div class="bc-togglebar" :class="readonlyClass">
<BcToggleMultiBarButton
v-for="icon in props.icons"
:key="icon.value"
v-model="modelValues[icon.value]"
:class="icon.className"
:icon="icon.icon"
:tooltip="icon.tooltip"
v-for="button in props.buttons"
:key="button.value"
v-model="buttonStates[button.value]"
:class="button.className"
:icon="button.icon"
:tooltip="button.tooltip"
:disabled="button.disabled"
:readonly-class="readonlyClass"
>
<template #icon>
<slot :name="icon.value">
<component :is="icon.component" v-if="icon.component" />
<FontAwesomeIcon v-else-if="icon.icon" :icon="icon.icon" />
<slot :name="button.value">
<component :is="button.component" v-if="button.component" v-bind="button.componentProps" :class="button.componentClass" />
<FontAwesomeIcon v-else-if="button.icon" :icon="button.icon" />
</slot>
</template>
</BcToggleMultiBarButton>
Expand All @@ -57,5 +69,10 @@ watch(modelValues, () => {
background-color: var(--container-background);
border: solid 1px var(--container-border-color);
border-radius: var(--border-radius);
&.read-only {
padding: 0px;
border: none;
}
}
</style>
34 changes: 26 additions & 8 deletions frontend/components/bc/toggle/MultiBarButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import BcTooltip from '../BcTooltip.vue'
interface Props {
icon?: IconDefinition,
falseIcon?: IconDefinition,
disabled?:boolean,
tooltip?: string,
readonlyClass?: string
}
const props = defineProps<Props>()
Expand All @@ -21,14 +23,23 @@ const icon = computed(() => {
<template>
<BcTooltip :dont-open-permanently="true" :hover-delay="350">
<template #tooltip>
<div class="button-tooltip">
<div class="button-tooltip" :class="readonlyClass">
<div v-if="tooltip" class="individual">
{{ tooltip }}
</div>
<div>{{ selected ? $t('filter.enabled'): $t('filter.disabled') }}</div>
<div v-if="readonlyClass !== 'read-only'">
{{ disabled ? $t('common.unavailable') : (selected ? $t('filter.enabled'): $t('filter.disabled')) }}
</div>
</div>
</template>
<ToggleButton v-model="selected" class="bc-toggle" on-label="''" off-icon="''">
<ToggleButton
v-model="selected"
class="bc-toggle"
:class="readonlyClass"
on-label="''"
off-icon="''"
:disabled="disabled || readonlyClass === 'read-only'"
>
<template #icon="slotProps">
<slot name="icon" v-bind="slotProps">
<FontAwesomeIcon v-if="icon" :icon="icon" />
Expand All @@ -39,31 +50,38 @@ const icon = computed(() => {
</template>

<style lang="scss" scoped>
.button-tooltip{
.button-tooltip {
width: max-content;
text-align: left;
.individual{
.individual::not(.read-only) {
margin-bottom: var(--padding);
}
}
.bc-toggle {
min-width: 30px;
min-height: 30px;
&.p-button {
&.p-togglebutton {
width: 30px;
height: 30px;
padding: 2px;
border-style: none;
color: var(--container-color);
background-color: var(--container-border-color);
&:not(.p-highlight) {
&:not(.p-highlight),
&.read-only {
background-color: var(--container-background);
}
// this is needed as the primvevue ToggleButton adds a yes/no label if none is provided
:deep(.p-button-label) {
display: none;
}
&.p-disabled {
&:not(.read-only) {
opacity: 0.5;
}
cursor: default;
}
}
}
}
Expand Down
Loading

0 comments on commit 25be31c

Please sign in to comment.