ongoing UI work; fixes; TV callback loop problems again/still

This commit is contained in:
Tim
2024-03-09 19:17:06 -04:00
parent 1743a7462d
commit 4362d12f0f
14 changed files with 416 additions and 307 deletions

View File

@@ -1,5 +1,5 @@
<template>
<component :is="component" :builder="builder"/>
<component :is="component" :order="order" :builder="builder"/>
</template>
<script setup>
@@ -8,10 +8,9 @@ import DCABuilder from "@/components/chart/DCABuilder.vue";
import LimitBuilder from "@/components/chart/LimitBuilder.vue";
import MarketBuilder from "@/components/chart/MarketBuilder.vue";
const props = defineProps(['builder'])
const props = defineProps(['order','builder'])
const component = computed(()=>{
console.log('builder component', props.builder)
switch (props.builder.component) {
case 'MarketBuilder':
return MarketBuilder

View File

@@ -1,34 +1,39 @@
<template>
<v-sheet dense class="d-flex align-content-stretch" style="overflow-y: hidden">
<div :style="lightColorStyle" style="width:0.5em">&nbsp;</div>
<row-bar :color="color">
<color-band :color="color"/>
<div :style="faintColorStyle" style="width: 100%" class="justify-start align-content-start">
<v-text-field type="number" v-model="os.amount" min="0" step="1" variant="outlined" density="compact" hide-details
<v-text-field type="number" inputmode="numeric" pattern="[0-9]*\.?[0-9]*" v-model="os.amount" variant="outlined" density="compact" hide-details
min="0" :step="os.amount?Math.floor(os.amount/1000):1"
style="max-width:24em; display: inline-grid"
class="pb-2"
:color="color"
class="py-2" :color="color"
:label="os.amountIsTokenA ? 'Amount':('Value in '+os.quote.s)"
>
<template v-slot:prepend>
<v-btn :text="(os.buy ? 'Buy ' : 'Sell ') + os.base.s" variant="text" :color="color" @click="os.buy=!os.buy"/>
<v-btn :text="(os.buy ? 'Buy ' : 'Sell ') + os.base.s" variant="tonal" :color="color" @click="os.buy=!os.buy"/>
</template>
<template v-slot:append>
<template v-slot:append-inner>
<v-btn :text="os.amountToken.s+(os.amountIsTokenA?'':' worth')" :color="color" variant="text" @click="os.amountIsTokenA=!os.amountIsTokenA"/>
</template>
</v-text-field>
<template v-for="b in builders(order)">
<builder-factory :builder="b" :color="faintColor"/>
<div>
<span class="ma-3">Add condition:</span>
<!-- <v-btn variant="flat" prepend-icon="mdi-clock-outline" @click="build('DCABuilder')">DCA</v-btn>-->
<v-btn :color="color" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
<!-- <v-btn variant="flat" prepend-icon="mdi-vector-line">Line</v-btn>-->
<!--
mdi-ray-start-end
mdi-vector-polyline
-->
</div>
<builder-factory :order="order" :builder="b" :color="color"/>
</template>
<div class="my-3">
<span :style="colorStyle" class="ma-3">Add condition:</span>
<!-- <v-btn variant="flat" prepend-icon="mdi-clock-outline" @click="build('DCABuilder')">DCA</v-btn>-->
<v-btn :color="color" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
<!-- <v-btn variant="flat" prepend-icon="mdi-vector-line">Line</v-btn>-->
<!--
mdi-ray-start-end
mdi-vector-polyline
-->
</div>
<div class="d-flex justify-start">
<v-btn variant="tonal" :color="color" class="ma-6">Place Order</v-btn>
<v-btn variant="text" class="ma-6">Cancel</v-btn>
</div>
</div>
</v-sheet>
</row-bar>
</template>
<script setup>
@@ -37,7 +42,10 @@ import BuilderFactory from "@/components/chart/BuilderFactory.vue";
import {newBuilder, useChartOrderStore} from "@/orderbuild.js";
import {useOrderStore} from "@/store/store.js";
import {computed} from "vue";
import Color from "color";
import {lightenColor, lightenColor2} from "@/misc.js";
import {useTheme} from "vuetify";
import RowBar from "@/components/chart/RowBar.vue";
import ColorBand from "@/components/chart/ColorBand.vue";
const props = defineProps(['order'])
const co = useChartOrderStore()
@@ -49,14 +57,15 @@ function build(order, component, options={}) {
function builders(order) {
let result = order.builders
console.log('builders', result)
return result.length > 0 ? result : [newBuilder('MarketBuilder')]
}
const color = computed(()=>os.buy?'green':'red')
const lightColor = computed(() => new Color(color.value).hsl().lightness(85).string())
const faintColor = computed(() => new Color(color.value).hsl().lightness(97).string())
const colorStyle = computed(() => { return {'background-color': color.value} })
const theme = useTheme().current
const color = computed(()=>os.buy?theme.value.colors.success:theme.value.colors.error)
const lightColor = computed(() => lightenColor(color.value))
const faintColor = computed(() => lightenColor2(color.value))
const colorStyle = computed(() => { return {'color': color.value} })
const bgColorStyle = computed(() => { return {'background-color': color.value} })
const lightColorStyle = computed(() => { return {'background-color': lightColor.value} })
const faintColorStyle = computed(() => { return {'background-color': faintColor.value} })
@@ -74,4 +83,4 @@ const maxAmount = computed(()=>{
<style scoped lang="scss">
</style>
</style>

View File

@@ -0,0 +1,24 @@
<template>
<div :style="computedStyle" class="mr-1">&nbsp;</div>
</template>
<script setup>
import {computed} from "vue";
const props = defineProps(['color'])
const computedStyle = computed(() => {
console.log('computing bar color')
return {
'background-color': props.color,
'border-color': props.color,
'width': '.67em',
'border-top-width': '1px',
'border-bottom-width': '1px',
}
})
</script>
<style scoped lang="scss">
</style>

View File

@@ -1,13 +1,15 @@
<template>
<!-- todo extract builder-panel --> <!-- :builder="builder" color-tag="limit" -->
<v-sheet dense :color="faintColor" class="d-flex align-content-stretch" style="overflow-y: hidden">
<div :style="colorStyle" style="width:1em">&nbsp;</div>
<div style="min-width: 3em; font-size: larger" class="align-self-start ml-2 pt-3">{{ rungs === 1 ? 'Limit' : 'Ladder' }}</div>
<row-bar :color="builder.color">
<color-band :color="builder.color"/>
<div style="min-width: 3em; font-size: larger" :style="colorStyle"
class="align-self-start ml-2 pt-3">{{ rungs === 1 ? 'Limit' : 'Ladder' }}</div>
<div>
<v-text-field type="number" v-model="rungs"
density="compact" hide-details class="mx-1 my-2" variant="outlined"
label="Rungs"
:color="color" :base-color="color" min="1"
:disabled="!lineAPrice"
style="width: 4.5em;"
/>
</div>
@@ -49,7 +51,7 @@
</v-list>
</v-menu>
</div>
</v-sheet>
</row-bar>
<!-- <v-col>-->
<!-- <v-btn v-if="!co.drawing" prepend-icon="mdi-ray-vertex" @click="draw" variant="tonal">Draw</v-btn>-->
@@ -59,18 +61,25 @@
<script setup>
import {computed} from "vue";
import {chart, draggingShapeIds} from "@/charts/chart.js";
import {chart} from "@/charts/chart.js";
import {useChartOrderStore} from "@/orderbuild.js";
import Color from "color";
import {HLine} from "@/charts/shape.js";
import {builderDefaults} from "@/misc.js";
import {builderDefaults, lightenColor2} from "@/misc.js";
import {useTheme} from "vuetify";
import {useOrderStore} from "@/store/store.js";
import RowBar from "@/components/chart/RowBar.vue";
import ColorBand from "@/components/chart/ColorBand.vue";
const os = useOrderStore()
const co = useChartOrderStore()
const props = defineProps(['builder'])
const theme = useTheme().current
const props = defineProps(['order', 'builder'])
// Fields must be defined in order to be reactive
builderDefaults(props.builder, {
allocation: 1.0,
start: null, // todo
end: null, // todo
priceA: null,
@@ -116,15 +125,16 @@ const lineB = new HLine(
deleteBuilder
)
const adjustInteriorLine = (line) => {
console.log('interior line onModel')
props.builder.color = line.color
adjustShapes()
}
let interiorLines = []
function createInteriorLine(price) {
const line = new HLine({price: price, color: props.builder.color},
function (line) {
props.builder.color = line.color // todo adjust for saturation
},
deleteBuilder)
const line = new HLine({price: price, color: props.builder.color}, adjustInteriorLine, deleteBuilder)
line.onPoints = function (points) {
const delta = points[0].price - this.model.price
if (delta !== 0) {
@@ -147,16 +157,16 @@ function removeInteriorLine() {
const color = computed({
get() {
get() {return props.builder.color},
set(v) {
const maxLightness = 60
const c = new Color(props.builder.color).hsl()
return c.saturation <= maxLightness ? props.builder.color : c.lightness(maxLightness).string()
},
set(v) {props.builder.color = v}
const c = new Color(v).hsl()
props.builder.color = c.saturation <= maxLightness ? v : c.lightness(maxLightness).string()
}
})
const faintColor = computed(() => new Color(color.value).hsl().lightness(97).string())
const faintColor = computed(() => lightenColor2(color.value))
const colorStyle = computed(() => {
return {'background-color': color.value}
return {'color': color.value}
})
@@ -253,17 +263,35 @@ const weights = computed(() => {
const colors = computed( ()=> {
const c = new Color(props.builder.color).rgb()
const color = props.builder.color !== null ? props.builder.color
: os.buy ? theme.value.colors.success : theme.value.colors.error
const c = new Color(color).rgb()
const ws = weights.value;
const max = Math.max(...ws)
return ws.map((w)=>c.alpha(w/max).string())
const ns = ws.map((w)=>w/max) // set largest weight to 100%
const adj = ns.map((w)=>c.alpha(Math.pow(w,0.67))) // https://en.wikipedia.org/wiki/Stevens's_power_law
return adj.map((a)=>a.string())
})
function allocationText(weight) {
const alloc = props.builder.allocation
if (alloc===null) return ''
const w = weight * alloc
const a = props.order.amount * w
const t = os.amountToken
return `${(w*100).toFixed(1)}% ${a.toLocaleString('fullwide')} ${t.s}`
}
function adjustShapes() {
// this is where all the lines are created or adjusted
console.log('adjustShapes()')
const limits = prices.value
// console.log('limits', limits)
const colorStrings = colors.value
const lps = weights.value.map((w)=>{return {text:allocationText(w), textcolor:color.value}})
console.log('things', colorStrings, lps)
if( limits.length === 0 ) {
lineA.delete()
lineB.delete()
@@ -276,7 +304,7 @@ function adjustShapes() {
// SINGLE LINE
//
if (!lineA.beingDragged())
lineA.setModel({price:limits[0], color: colors.value[0]})
lineA.setModel({price:limits[0], color: colorStrings[0]}, lps[0])
lineB.delete()
if (interiorLines.length) {
for( const line of interiorLines )
@@ -289,9 +317,11 @@ function adjustShapes() {
// PRICE RANGE
//
if (!lineA.beingDragged())
lineA.setModel({price:limits[0], color: colorStrings[0]})
if (!lineB.beingDragged())
lineB.setModel({price:limits[limits.length-1], color: colorStrings[colorStrings.length-1]})
lineA.setModel({price:limits[0], color: colorStrings[0]}, lps[0])
if (!lineB.beingDragged()) {
const last = colorStrings.length - 1
lineB.setModel({price: limits[last], color: colorStrings[last]}, lps[last])
}
const numInterior = Math.max(0,limits.length-2);
// trim excess interior lines
@@ -304,7 +334,7 @@ function adjustShapes() {
if (i === interiorLines.length)
createInteriorLine(limit)
else if (!interiorLines[i].beingDragged())
interiorLines[i].setModel({price:limit, color: colorStrings[1+i]})
interiorLines[i].setModel({price:limit, color: colorStrings[1+i]}, lps[1+i])
}
}
@@ -312,10 +342,6 @@ function adjustShapes() {
function deleteShapes() {
// if (priceRangeId !== null)
// deleteShapeId(priceRangeId)
// for (const id of lineShapeIds)
// deleteShapeId(id)
lineA.delete()
lineB.delete()
for (const line of interiorLines)
@@ -325,7 +351,7 @@ function deleteShapes() {
function deleteBuilder() {
co.removeBuilder(props.builder)
props.order.builders = props.order.builders.filter((b)=>b!==props.builder)
deleteShapes()
}

View File

@@ -1,5 +1,5 @@
<template>
<v-sheet dense class="d-flex align-content-stretch" style="overflow-y: hidden">
<v-sheet dense class="d-flex align-content-stretch" style="overflow-y: hidden; background-color: inherit !important;">
<div class="bg-grey-lighten-1" style="width:1em">&nbsp;</div>
<div style="min-width: 3em; font-size: larger" class="align-self-start ma-3">Market Order</div>
<div>
@@ -22,7 +22,7 @@ import {useOrderStore} from "@/store/store.js";
const os = useOrderStore()
const co = useChartOrderStore()
const props = defineProps(['builder'])
const props = defineProps(['order', 'builder'])
// Fields must be defined in order to be reactive
builderDefaults(props.builder, {slippage: 0.10,})
@@ -33,8 +33,8 @@ builderDefaults(props.builder, {slippage: 0.10,})
:deep(.v-slider.v-input--vertical > .v-input__control) {
min-height: 5em !important;
}
:deep(.v-slider.no-slider-bg .v-slider-track__fill) {
background-color: inherit !important;
}
//:deep(.v-slider.no-slider-bg .v-slider-track__fill) {
// background-color: inherit !important;
//}
</style>

View File

@@ -0,0 +1,30 @@
<!--
A "RowBar" is used for chart order and builder areas. It is a flexbox that stretches vertically to hold its contents,
which are layed out in flex columns. -->
<template>
<v-sheet dense :color="faintColor" class="d-flex align-content-stretch" style="overflow-y: hidden">
<slot/>
</v-sheet>
</template>
<script setup>
import {computed} from "vue";
import {lightenColor2} from "@/misc.js";
const props = defineProps(['color'])
const faintColor = computed(() => lightenColor2(props.color))
const computedStyle = computed(() => {
console.log('computing bar color')
return {
'background-color': props.color,
'border-color': props.color,
'width': '.67em',
'border-top-width': '1px',
'border-bottom-width': '1px',
}
})
</script>
<style scoped lang="scss">
</style>