You've already forked AstralRinth
forked from didirus/AstralRinth
* feat: start on tax compliance * feat: avarala1099 composable * fix: shouldShow should be managed on the page itself * refactor: move show logic to revenue page * feat: security practices rather than info * feat: withdraw page lock * fix: empty modal bug & lint issues * feat: hide behind feature flag * Use standard admonition components, make casing consistent * modal title * lint * feat: withdrawal check * feat: tax cap on withdrawals warning * feat: start on revenue page overhaul * feat: segment generation for bar * feat: tooltips and links * fix: tooltip border * feat: finish initial layout, start on withdraw modal * feat: start on withdrawal limit stage * feat: shade support for primary colors * feat: start on withdraw details stage * fix: convert swatches to hex * feat: payout method/region dropdown temporarily using multiselect * feat: fix modal open issues and use teleport dropdowns * feat: hide transactions section if there are no transactions * refactor: NavStack surfaces * feat: new dropdown component * feat: remove teleport dropdown modal in favour of new combobox component * fix: lint * refactor: dashboard sidebar layout * feat: cleanup * fix: niche bugs * fix: ComboBox styling * feat: first part of qa * feat: animate flash rather than tooltip * fix: lint * feat: qa border gradient * fix: seg hover flashes * feat: i18n * feat: i18n and final QA * fix: lint * feat: QA * fix: lint * fix: merge conflicts * fix: intl * fix: blue hover * fix: transfers page * feat: surface variables & gradients * feat: text vars * fix: lint * fix: intl * feat: stages * fix: lint * feat: region selection * feat: method selection btns * fix: flex col on transactions * feat: hook up method selection to ctx * feat: muralpay kyc stage info * wip: muralpay integration * Basic Mural Pay API bindings * Fix clippy * use dotenvy in muralpay example * Refactor payout creation code * wip: muralpay payout requests * Mural Pay payouts work * Fix clippy * feat: progress * fix: broken tax form stage logic * polish: tax form stage and method selection stage layout * add mural pay fees API * Work on payout fee API * Fees API for more payment methods * Fix CI * polish: muralpay qa * refactor: clean up combobox component * polish: change from critical -> warning admonition in MuralpayDetailsStage * Temporarily disable Venmo and PayPal methods from frontend * polish: clean up transaction component & page * polish: navbar qa, text color-contrast in chips type buttonstyled, mb on rev/index.vue page * fix: incorrectly using available balance as tax form withdraw limit after tax forms submitted * wip: counterparties * Start on counterparties and payment methods API * polish: combobox component * polish: fix broken scroll logic using a composable & web:fix * fix: lint * polish: various QA fixes * feat: hook up with backend (wip) * feat: draft muralpay rails dynamic logic * polish: modify rails to support backend changes * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * feat: fees & methods endpoint hookup * chore: remove duplicates fix * polish: qa changes + figma match * Add countries to muralpay fiat methods * Compile fix * Add exchange rate info to fees endpoint * Add fees to premium Tremendous options * polish: i18n and better document type dropdown -> id input labels * feat: tremendous * fix: lint & i18n * feat: reintroduce tin mismatch logic to index.vue * polish: qa * fix: i18n * feat: remove teleport dropdown menu - combobox should be used * fix: lint * fix: jsdoc * feat: checkbox for reward program terms * Add delivery email field to Tremendous payouts * Add Tremendous product category to payout methods * Add bank details API to muralpay * Fix CI * Fix CI * polish: qa changes * feat: i18n pass * feat: deduplicate methods endpoint & fix i18n issues * chore: deduplicate i18n strings into common-messages.ts * fix: lint * fix: i18n * feat: estimates * polish: more QA * Remove prepaid visa, compute fees properly for Tremendous methods * Add more details to Tremendous errors * feat: withdraw endpoint impl & internals refactor * Add more details to Tremendous errors * feat: completion stage * Add fees to Mural * feat: transactions page match figma * fix: i18n * polish: QA changes * polish: qa * Payout history route and bank details * polish: autofill and requirements checks * fix: i18n + lint * fix: fiat rail fees * polish: move scroll fade stuff into NewModal rather than just CreatorWithdrawModal * feat: simplify action btn logic & tax form error * fix: tax -> Tax form * Re-add legacy PayPal/Venmo options for US * feat: mobile responsiveness fixes for modal * fix: responsiveness issues * feat: navstack responsiveness * fix: responsiveness * move the mural bank details route * fix: generated state cleanup & bank details input * fix: lint & i18n * Add utoipa support to payout endpoints * address some PR comments * polish: qa * add CORS to new utoipa routes * feat: legacy paypal/venmo stage * polish: reset amount on back qa * revert: navstack mr changes * polish: loading indicator on method selection stage * fix: paypal modal doesnt reopen after auth * fix: lint & i18n * fix: paypal flow * polish: qa changes * fix: gitignore * polish: qa fixes * fix: payouts_available in payouts.rs * fix: bug when limit is zero * polish: qa changes * fix: qa stuff & muralpay sub-division fix * Immediately approve mural payouts * Add currency support to Tremendous payouts * Currency forex * add forex to tremendous fee request * polish: qa & currency support for paypal tremendous * polish: fx qa * feat: demo mode flag * fix: i18n & padding issues * polish: qa changes * fix: ml * Add Mural balance to bank balance info * polish: show warning for paypal international USD withdrawals + more currencies * Add more Tremendous currencies support * fix: colors on balance bars * fix: empty states * fix: pl-8 mobile issue * fix: hide see all * Transaction payouts available use the correct date * Address my own review comment * Address PR comments * Change Mural withdrawal limit to 3k * fix: empty state + paypal warning * maybe fix tremendous gift cards * Change how Mural minimum withdrawals are calculated * Tweak min/max withdrawal values * fix: segment brightness * fix: min & max for muralpay & legacy paypal * Fix some icon issues * more issues * fix user menu * fix: remove + network --------- Signed-off-by: Calum H. <contact@cal.engineer> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: aecsocket <aecsocket@tutanota.com> Co-authored-by: Alejandro González <me@alegon.dev>
368 lines
10 KiB
Vue
368 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
color?: 'standard' | 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'purple' | 'medal-promo'
|
|
size?: 'standard' | 'large' | 'small'
|
|
circular?: boolean
|
|
type?: 'standard' | 'outlined' | 'transparent' | 'highlight' | 'highlight-colored-text' | 'chip'
|
|
colorFill?: 'auto' | 'background' | 'text' | 'none'
|
|
hoverColorFill?: 'auto' | 'background' | 'text' | 'none'
|
|
highlightedStyle?: 'main-nav-primary' | 'main-nav-secondary'
|
|
highlighted?: boolean
|
|
}>(),
|
|
{
|
|
color: 'standard',
|
|
size: 'standard',
|
|
circular: false,
|
|
type: 'standard',
|
|
colorFill: 'auto',
|
|
hoverColorFill: 'auto',
|
|
highlightedStyle: 'main-nav-primary',
|
|
highlighted: false,
|
|
},
|
|
)
|
|
|
|
const highlightedColorVar = computed(() => {
|
|
switch (props.color) {
|
|
case 'brand':
|
|
return 'var(--color-brand-highlight)'
|
|
case 'red':
|
|
return 'var(--color-red-highlight)'
|
|
case 'orange':
|
|
return 'var(--color-orange-highlight)'
|
|
case 'green':
|
|
return 'var(--color-green-highlight)'
|
|
case 'medal-promo':
|
|
case 'blue':
|
|
return 'var(--color-blue-highlight)'
|
|
case 'purple':
|
|
return 'var(--color-purple-highlight)'
|
|
case 'standard':
|
|
default:
|
|
return null
|
|
}
|
|
})
|
|
|
|
const colorVar = computed(() => {
|
|
switch (props.color) {
|
|
case 'brand':
|
|
return 'var(--color-brand)'
|
|
case 'red':
|
|
return 'var(--color-red)'
|
|
case 'orange':
|
|
return 'var(--color-orange)'
|
|
case 'green':
|
|
return 'var(--color-green)'
|
|
case 'blue':
|
|
return 'var(--color-blue)'
|
|
case 'purple':
|
|
return 'var(--color-purple)'
|
|
case 'medal-promo':
|
|
return 'var(--medal-promotion-text-orange)'
|
|
case 'standard':
|
|
default:
|
|
return null
|
|
}
|
|
})
|
|
|
|
const height = computed(() => {
|
|
if (props.size === 'large') {
|
|
return '3rem'
|
|
} else if (props.size === 'small') {
|
|
return '1.5rem'
|
|
}
|
|
return '2.25rem'
|
|
})
|
|
|
|
const width = computed(() => {
|
|
if (props.size === 'large') {
|
|
return props.circular ? '3rem' : 'auto'
|
|
} else if (props.size === 'small') {
|
|
return props.circular ? '1.5rem' : 'auto'
|
|
}
|
|
return props.circular ? '2.25rem' : 'auto'
|
|
})
|
|
|
|
const paddingX = computed(() => {
|
|
let padding = props.circular ? '0.5rem' : '0.75rem'
|
|
if (props.size === 'large') {
|
|
padding = props.circular ? '0.75rem' : '1rem'
|
|
} else if (props.size === 'small') {
|
|
padding = props.circular ? '0.125rem' : '0.5rem'
|
|
}
|
|
return `calc(${padding} - 0.125rem)`
|
|
})
|
|
|
|
const paddingY = computed(() => {
|
|
if (props.size === 'large') {
|
|
return '0.75rem'
|
|
}
|
|
return '0.5rem'
|
|
})
|
|
|
|
const gap = computed(() => {
|
|
if (props.size === 'large') {
|
|
return '0.5rem'
|
|
} else if (props.size === 'small') {
|
|
return '0.25rem'
|
|
}
|
|
return '0.375rem'
|
|
})
|
|
|
|
const fontWeight = computed(() => {
|
|
if (props.size === 'large') {
|
|
return '800'
|
|
}
|
|
return '600'
|
|
})
|
|
|
|
const radius = computed(() => {
|
|
if (props.circular) {
|
|
return '99999px'
|
|
}
|
|
|
|
if (props.size === 'large') {
|
|
return '1rem'
|
|
} else if (props.size === 'small') {
|
|
return '0.5rem'
|
|
}
|
|
return '0.75rem'
|
|
})
|
|
|
|
const iconSize = computed(() => {
|
|
if (props.size === 'large') {
|
|
return '1.5rem'
|
|
} else if (props.size === 'small') {
|
|
return '1rem'
|
|
}
|
|
return '1.25rem'
|
|
})
|
|
|
|
function setColorFill(
|
|
colors: { bg: string; text: string },
|
|
fill: 'background' | 'text' | 'none',
|
|
): { bg: string; text: string } {
|
|
if (colorVar.value) {
|
|
if (fill === 'background') {
|
|
if (props.type === 'highlight' && highlightedColorVar.value) {
|
|
colors.bg = highlightedColorVar.value
|
|
colors.text = 'var(--color-contrast)'
|
|
} else if (props.type === 'highlight-colored-text' && highlightedColorVar.value) {
|
|
colors.bg = highlightedColorVar.value
|
|
colors.text = colorVar.value
|
|
} else {
|
|
colors.bg = colorVar.value
|
|
colors.text = 'var(--color-accent-contrast)'
|
|
}
|
|
} else if (fill === 'text') {
|
|
colors.text = colorVar.value
|
|
}
|
|
}
|
|
return colors
|
|
}
|
|
|
|
const colorVariables = computed(() => {
|
|
if (props.highlighted) {
|
|
const colors = {
|
|
bg:
|
|
props.highlightedStyle === 'main-nav-primary'
|
|
? 'var(--color-brand-highlight)'
|
|
: 'var(--color-button-bg)',
|
|
text: 'var(--color-contrast)',
|
|
icon:
|
|
props.type === 'chip'
|
|
? 'var(--color-contrast)'
|
|
: props.highlightedStyle === 'main-nav-primary'
|
|
? 'var(--color-brand)'
|
|
: 'var(--color-contrast)',
|
|
}
|
|
const hoverColors = JSON.parse(JSON.stringify(colors))
|
|
const boxShadow =
|
|
props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : 'none'
|
|
return `--_bg: ${colors.bg}; --_text: ${colors.text}; --_icon: ${colors.icon}; --_hover-bg: ${hoverColors.bg}; --_hover-text: ${hoverColors.text}; --_hover-icon: ${hoverColors.icon}; --_box-shadow: ${boxShadow};`
|
|
}
|
|
|
|
let colors = {
|
|
bg: 'var(--color-button-bg)',
|
|
text: 'var(--color-base)',
|
|
}
|
|
let hoverColors = JSON.parse(JSON.stringify(colors))
|
|
|
|
if (props.type === 'outlined') {
|
|
hoverColors.bg = 'transparent'
|
|
}
|
|
|
|
if (props.type === 'outlined' || props.type === 'transparent') {
|
|
colors.bg = 'transparent'
|
|
colors = setColorFill(colors, props.colorFill === 'auto' ? 'text' : props.colorFill)
|
|
hoverColors = setColorFill(
|
|
hoverColors,
|
|
props.hoverColorFill === 'auto' ? 'text' : props.hoverColorFill,
|
|
)
|
|
} else if (props.type === 'chip') {
|
|
// Chip type uses highlight-colored-text styling when colored
|
|
if (colorVar.value && highlightedColorVar.value) {
|
|
colors.bg = highlightedColorVar.value
|
|
colors.text = colorVar.value
|
|
hoverColors.bg = highlightedColorVar.value
|
|
hoverColors.text = colorVar.value
|
|
}
|
|
} else {
|
|
colors = setColorFill(colors, props.colorFill === 'auto' ? 'background' : props.colorFill)
|
|
hoverColors = setColorFill(
|
|
hoverColors,
|
|
props.hoverColorFill === 'auto' ? 'background' : props.hoverColorFill,
|
|
)
|
|
}
|
|
|
|
const boxShadow = props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : 'none'
|
|
return `--_bg: ${colors.bg}; --_text: ${colors.text}; --_hover-bg: ${hoverColors.bg}; --_hover-text: ${hoverColors.text}; --_box-shadow: ${boxShadow};`
|
|
})
|
|
|
|
const fontSize = computed(() => {
|
|
if (props.size === 'small') {
|
|
return 'text-sm'
|
|
}
|
|
return 'text-base'
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="btn-wrapper"
|
|
:class="[{ outline: type === 'outlined', chip: type === 'chip' }, fontSize]"
|
|
:style="`${colorVariables}--_height:${height};--_width:${width};--_radius: ${radius};--_padding-x:${paddingX};--_padding-y:${paddingY};--_gap:${gap};--_font-weight:${fontWeight};--_icon-size:${iconSize};`"
|
|
>
|
|
<slot />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.btn-wrapper {
|
|
display: contents;
|
|
}
|
|
|
|
/* Searches up to 4 children deep for valid button */
|
|
.btn-wrapper :deep(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper :slotted(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper :slotted(*) > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper :slotted(*) > *:first-child > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper
|
|
:slotted(*)
|
|
> *:first-child
|
|
> *:first-child
|
|
> :is(button, a, .button-like):first-child {
|
|
@apply flex cursor-pointer flex-row items-center justify-center border-solid border-2 border-transparent bg-[--_bg] text-[--_text] h-[--_height] min-w-[--_width] rounded-[--_radius] px-[--_padding-x] py-[--_padding-y] gap-[--_gap] font-[--_font-weight] whitespace-nowrap;
|
|
box-shadow: var(--_box-shadow, inset 0 0 0 transparent);
|
|
transition:
|
|
scale 0.125s ease-in-out,
|
|
background-color 0.25s ease-in-out,
|
|
color 0.25s ease-in-out,
|
|
filter 0.25s ease-in-out;
|
|
|
|
svg:first-child {
|
|
color: var(--_icon, var(--_text));
|
|
transition: color 0.25s ease-in-out;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
&[disabled],
|
|
&[disabled='true'],
|
|
&.disabled,
|
|
&.looks-disabled {
|
|
@apply opacity-50;
|
|
}
|
|
|
|
&[disabled],
|
|
&[disabled='true'],
|
|
&.disabled {
|
|
@apply cursor-not-allowed;
|
|
}
|
|
|
|
&:not([disabled]):not([disabled='true']):not(.disabled) {
|
|
@apply hover:brightness-[--hover-brightness] focus-visible:brightness-[--hover-brightness] hover:bg-[--_hover-bg] hover:text-[--_hover-text] focus-visible:bg-[--_hover-bg] focus-visible:text-[--_hover-text];
|
|
|
|
&:hover svg:first-child,
|
|
&:focus-visible svg:first-child {
|
|
color: var(--_hover-icon, var(--_hover-text));
|
|
}
|
|
}
|
|
}
|
|
|
|
.btn-wrapper:not(.chip) :deep(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper:not(.chip) :slotted(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper:not(.chip) :slotted(*) > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper:not(.chip) :slotted(*) > *:first-child > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper:not(.chip)
|
|
:slotted(*)
|
|
> *:first-child
|
|
> *:first-child
|
|
> :is(button, a, .button-like):first-child {
|
|
&:not([disabled]):not([disabled='true']):not(.disabled) {
|
|
@apply active:scale-95;
|
|
}
|
|
}
|
|
|
|
.btn-wrapper.outline :deep(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper.outline :slotted(:is(button, a, .button-like):first-child),
|
|
.btn-wrapper.outline :slotted(*) > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper.outline :slotted(*) > *:first-child > :is(button, a, .button-like):first-child,
|
|
.btn-wrapper.outline
|
|
:slotted(*)
|
|
> *:first-child
|
|
> *:first-child
|
|
> :is(button, a, .button-like):first-child {
|
|
@apply border-current;
|
|
}
|
|
|
|
/*noinspection CssUnresolvedCustomProperty*/
|
|
.btn-wrapper :deep(:is(button, a, .button-like):first-child) > svg:first-child,
|
|
.btn-wrapper :slotted(:is(button, a, .button-like):first-child) > svg:first-child,
|
|
.btn-wrapper :slotted(*) > :is(button, a, .button-like):first-child > svg:first-child,
|
|
.btn-wrapper
|
|
:slotted(*)
|
|
> *:first-child
|
|
> :is(button, a, .button-like):first-child
|
|
> svg:first-child,
|
|
.btn-wrapper
|
|
:slotted(*)
|
|
> *:first-child
|
|
> *:first-child
|
|
> :is(button, a, .button-like):first-child
|
|
> svg:first-child {
|
|
min-width: var(--_icon-size, 1rem);
|
|
min-height: var(--_icon-size, 1rem);
|
|
}
|
|
|
|
.joined-buttons {
|
|
display: flex;
|
|
gap: 1px;
|
|
|
|
> .btn-wrapper:not(:first-child) {
|
|
:deep(:is(button, a, .button-like):first-child),
|
|
:slotted(:is(button, a, .button-like):first-child),
|
|
:slotted(*) > :is(button, a, .button-like):first-child,
|
|
:slotted(*) > *:first-child > :is(button, a, .button-like):first-child,
|
|
:slotted(*) > *:first-child > *:first-child > :is(button, a, .button-like):first-child {
|
|
border-top-left-radius: 0;
|
|
border-bottom-left-radius: 0;
|
|
}
|
|
}
|
|
|
|
> :not(:last-child) {
|
|
:deep(:is(button, a, .button-like):first-child),
|
|
:slotted(:is(button, a, .button-like):first-child),
|
|
:slotted(*) > :is(button, a, .button-like):first-child,
|
|
:slotted(*) > *:first-child > :is(button, a, .button-like):first-child,
|
|
:slotted(*) > *:first-child > *:first-child > :is(button, a, .button-like):first-child {
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* guys, I know this is nuts, I know */
|
|
</style>
|