ongoing UI work; fixes; TV callback loop problems again/still
This commit is contained in:
@@ -42,7 +42,7 @@ export function initWidget(el) {
|
||||
autosize: true,
|
||||
// symbol: 'AAPL',
|
||||
symbol: 'UNIv3:WETH/USDC', // use this for ohlc
|
||||
interval: '1D',
|
||||
interval: '15',
|
||||
container: el,
|
||||
// datafeed: new Datafeeds.UDFCompatibleDatafeed("https://demo-feed-data.tradingview.com"),
|
||||
datafeed: datafeed, // use this for ohlc
|
||||
@@ -205,12 +205,15 @@ function handleCrosshairMovement(point) {
|
||||
|
||||
|
||||
let drawingEventQueue = []
|
||||
let eventLock = false // this is set to true before events are processed, in order to avoid any loops
|
||||
|
||||
|
||||
function handleDrawingEvent(id, event) {
|
||||
if (eventLock) return
|
||||
// it's important to decouple our actions from the TV thread. we must wait until the TV loop is completed
|
||||
// before interacting with the chart
|
||||
if (drawingEventQueue.length===0)
|
||||
setTimeout(drawingEventWorker,0)
|
||||
setTimeout(drawingEventWorker,1)
|
||||
drawingEventQueue.push([id,event])
|
||||
}
|
||||
|
||||
@@ -224,54 +227,60 @@ function drawingEventWorker() {
|
||||
|
||||
function doHandleDrawingEvent(id, event) {
|
||||
// console.log('drawing event', arguments)
|
||||
const shape = event === 'remove' ? null : chart.getShapeById(id);
|
||||
if (event === 'create') {
|
||||
const co = useChartOrderStore();
|
||||
const callbacks = co.drawingCallbacks
|
||||
console.log('drawing callbacks', callbacks)
|
||||
if (callbacks !== null) {
|
||||
shapeCallbacks[id] = callbacks
|
||||
co.drawing = false
|
||||
const points = shape.getPoints()
|
||||
const props = shape.getProperties()
|
||||
invokeCallbacks(shapeCallbacks[id], 'onCreate', id, points, props)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onPoints', id, points)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onProps', id, props)
|
||||
}
|
||||
} else if (event === 'points_changed') {
|
||||
if (id in shapeCallbacks) {
|
||||
const points = shape.getPoints()
|
||||
// console.log('points', points)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onPoints', id, points)
|
||||
}
|
||||
} else if (event === 'properties_changed') {
|
||||
if (id in shapeCallbacks) {
|
||||
const props = shape.getProperties()
|
||||
// console.log('props', props)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onProps', id, props)
|
||||
}
|
||||
} else if (event === 'move') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onMove', id)
|
||||
}
|
||||
} else if (event === 'remove') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onDelete', id)
|
||||
}
|
||||
} else if (event === 'click') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onClick', id)
|
||||
}
|
||||
} else if (event === 'hide') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onHide', id)
|
||||
}
|
||||
} else if (event === 'show') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onShow', id)
|
||||
}
|
||||
} else
|
||||
console.log('unknown drawing event', event)
|
||||
eventLock = true
|
||||
try {
|
||||
const shape = event === 'remove' ? null : chart.getShapeById(id);
|
||||
if (event === 'create') {
|
||||
const co = useChartOrderStore();
|
||||
const callbacks = co.drawingCallbacks
|
||||
console.log('drawing callbacks', callbacks)
|
||||
if (callbacks !== null) {
|
||||
shapeCallbacks[id] = callbacks
|
||||
co.drawing = false
|
||||
const points = shape.getPoints()
|
||||
const props = shape.getProperties()
|
||||
invokeCallbacks(shapeCallbacks[id], 'onCreate', id, points, props)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onPoints', id, points)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onProps', id, props)
|
||||
}
|
||||
} else if (event === 'points_changed') {
|
||||
if (id in shapeCallbacks) {
|
||||
const points = shape.getPoints()
|
||||
// console.log('points', points)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onPoints', id, points)
|
||||
}
|
||||
} else if (event === 'properties_changed') {
|
||||
if (id in shapeCallbacks) {
|
||||
const props = shape.getProperties()
|
||||
// console.log('props', props)
|
||||
invokeCallbacks(shapeCallbacks[id], 'onProps', id, props)
|
||||
}
|
||||
} else if (event === 'move') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onMove', id)
|
||||
}
|
||||
} else if (event === 'remove') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onDelete', id)
|
||||
}
|
||||
} else if (event === 'click') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onClick', id)
|
||||
}
|
||||
} else if (event === 'hide') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onHide', id)
|
||||
}
|
||||
} else if (event === 'show') {
|
||||
if (id in shapeCallbacks) {
|
||||
invokeCallbacks(shapeCallbacks[id], 'onShow', id)
|
||||
}
|
||||
} else
|
||||
console.log('unknown drawing event', event)
|
||||
}
|
||||
finally {
|
||||
eventLock = false
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteShapeId(id) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// noinspection JSPotentiallyInvalidUsageOfThis
|
||||
|
||||
import {invokeCallback} from "@/common.js";
|
||||
import {invokeCallback, mixin} from "@/common.js";
|
||||
import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape} from "@/charts/chart.js";
|
||||
|
||||
|
||||
@@ -11,19 +11,21 @@ import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape
|
||||
// shape.points
|
||||
// > [{time:17228394, price:42638.83}]
|
||||
//
|
||||
// shape.model is a vue ref({}) which stores the shape-specific control points, as defined by the shape subclass
|
||||
// use the shape.model.* fields in components to get reactive effects with the chart.
|
||||
// shape.model stores the shape-specific control points, as defined by the shape subclass, and may be a vue ref.
|
||||
// Use the shape.model.* fields in components to get reactive effects with the chart, or pass an onModel callback
|
||||
// to the constructor
|
||||
//
|
||||
|
||||
|
||||
/*
|
||||
The `name` field must match the user-facing name in the TradingView toolbar UI
|
||||
TradingView drawing tool codes:
|
||||
https://www.tradingview.com/charting-library-docs/latest/api/modules/Charting_Library/#supportedlinetools
|
||||
text anchored_text note anchored_note signpost double_curve arc icon emoji sticker arrow_up arrow_down arrow_left arrow_right price_label price_note arrow_marker flag vertical_line horizontal_line cross_line horizontal_ray trend_line info_line trend_angle arrow ray extended parallel_channel disjoint_angle flat_bottom anchored_vwap pitchfork schiff_pitchfork_modified schiff_pitchfork balloon comment inside_pitchfork pitchfan gannbox gannbox_square gannbox_fixed gannbox_fan fib_retracement fib_trend_ext fib_speed_resist_fan fib_timezone fib_trend_time fib_circles fib_spiral fib_speed_resist_arcs fib_channel xabcd_pattern cypher_pattern abcd_pattern callout triangle_pattern 3divers_pattern head_and_shoulders fib_wedge elliott_impulse_wave elliott_triangle_wave elliott_triple_combo elliott_correction elliott_double_combo cyclic_lines time_cycles sine_line long_position short_position forecast date_range price_range date_and_price_range bars_pattern ghost_feed projection rectangle rotated_rectangle circle ellipse triangle polyline path curve cursor dot arrow_cursor eraser measure zoom brush highlighter regression_trend fixed_range_volume_profile
|
||||
*/
|
||||
|
||||
export const ShapeType = {
|
||||
// this "enum" is a record of the TradingView keystrings
|
||||
|
||||
/*
|
||||
The `name` field must match the user-facing name in the TradingView toolbar UI
|
||||
TradingView drawing tool codes:
|
||||
https://www.tradingview.com/charting-library-docs/latest/api/modules/Charting_Library/#supportedlinetools
|
||||
text anchored_text note anchored_note signpost double_curve arc icon emoji sticker arrow_up arrow_down arrow_left arrow_right price_label price_note arrow_marker flag vertical_line horizontal_line cross_line horizontal_ray trend_line info_line trend_angle arrow ray extended parallel_channel disjoint_angle flat_bottom anchored_vwap pitchfork schiff_pitchfork_modified schiff_pitchfork balloon comment inside_pitchfork pitchfan gannbox gannbox_square gannbox_fixed gannbox_fan fib_retracement fib_trend_ext fib_speed_resist_fan fib_timezone fib_trend_time fib_circles fib_spiral fib_speed_resist_arcs fib_channel xabcd_pattern cypher_pattern abcd_pattern callout triangle_pattern 3divers_pattern head_and_shoulders fib_wedge elliott_impulse_wave elliott_triangle_wave elliott_triple_combo elliott_correction elliott_double_combo cyclic_lines time_cycles sine_line long_position short_position forecast date_range price_range date_and_price_range bars_pattern ghost_feed projection rectangle rotated_rectangle circle ellipse triangle polyline path curve cursor dot arrow_cursor eraser measure zoom brush highlighter regression_trend fixed_range_volume_profile
|
||||
*/
|
||||
Segment: {name: 'Trend Line', code: 'trend_line'},
|
||||
Ray: {name: 'Ray', code: 'ray'},
|
||||
Line: {name: 'Extended Line', code: 'extended'},
|
||||
@@ -47,8 +49,6 @@ class Shape {
|
||||
if (onDelete !== null )
|
||||
this.onDelete = onDelete
|
||||
this.lock = 0 // used to prevent callbacks when we are the ones forcing the chart change
|
||||
this.pointsLock = 0
|
||||
this.propsLock = 0
|
||||
this.create()
|
||||
}
|
||||
|
||||
@@ -93,11 +93,20 @@ class Shape {
|
||||
}
|
||||
|
||||
|
||||
setModel(model) {
|
||||
setModel(model, props=null) {
|
||||
for( const [k,v] of Object.entries(model))
|
||||
this.model[k] = v
|
||||
this.setPoints(this.pointsFromModel());
|
||||
this.setPropsIfDirty(this.propsFromModel())
|
||||
let p
|
||||
const mp = this.propsFromModel();
|
||||
if (props===null) {
|
||||
p = mp
|
||||
if (p===null)
|
||||
return
|
||||
}
|
||||
else if (mp !== null)
|
||||
p = mixin(props, mp)
|
||||
this.setPropsIfDirty(p)
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +117,6 @@ class Shape {
|
||||
else {
|
||||
const s = this.tvShape();
|
||||
if (!dragging) {
|
||||
this.pointsLock++
|
||||
s.setPoints(points)
|
||||
}
|
||||
else {
|
||||
@@ -126,7 +134,6 @@ class Shape {
|
||||
|
||||
setProps(props) {
|
||||
if(this.id) {
|
||||
this.propsLock++
|
||||
this.tvShape().setProperties(props)
|
||||
}
|
||||
}
|
||||
@@ -206,6 +213,8 @@ function dirtyProps(propsA, propsB) {
|
||||
|
||||
|
||||
class ShapeTVCallbacks {
|
||||
// These methods are called by TradingView and provide some default handling before invoking our own Shape callbacks
|
||||
|
||||
constructor(shape) {
|
||||
this.shape = shape
|
||||
}
|
||||
@@ -220,21 +229,14 @@ class ShapeTVCallbacks {
|
||||
|
||||
onPoints(shapeId, points) {
|
||||
this.shape.points = points
|
||||
if( this.shape.pointsLock ) {
|
||||
this.shape.pointsLock--
|
||||
return
|
||||
}
|
||||
this.shape.onPoints(points)
|
||||
this.shape.pointsToModel()
|
||||
this.shape.onModel(this.shape.model)
|
||||
}
|
||||
|
||||
onProps(shapeId, props) {
|
||||
console.log('props', shapeId, props)
|
||||
this.shape.props = props
|
||||
if( this.shape.propsLock ) {
|
||||
this.shape.propsLock--
|
||||
return
|
||||
}
|
||||
this.shape.onProps(props)
|
||||
this.shape.propsToModel()
|
||||
this.shape.onModel(this.shape.model)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"> </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>
|
||||
|
||||
24
src/components/chart/ColorBand.vue
Normal file
24
src/components/chart/ColorBand.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div :style="computedStyle" class="mr-1"> </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>
|
||||
@@ -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"> </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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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"> </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>
|
||||
|
||||
|
||||
30
src/components/chart/RowBar.vue
Normal file
30
src/components/chart/RowBar.vue
Normal 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>
|
||||
@@ -24,3 +24,4 @@ registerPlugins(app)
|
||||
|
||||
app.mount('#app')
|
||||
detectChain()
|
||||
window.$vuetify = app
|
||||
|
||||
11
src/misc.js
11
src/misc.js
@@ -1,6 +1,7 @@
|
||||
import {FixedNumber} from "ethers";
|
||||
import {usePrefStore, useStore} from "@/store/store.js";
|
||||
import {token} from "@/blockchain/token.js";
|
||||
import Color from "color";
|
||||
|
||||
export class SingletonCoroutine {
|
||||
constructor(f, delay=10, retry=true) {
|
||||
@@ -137,4 +138,12 @@ export function builderDefaults(builder, defaults) {
|
||||
|
||||
export function uuid() {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
}
|
||||
|
||||
export function lightenColor(color, lightness=85) {
|
||||
return new Color(color).hsl().lightness(lightness).string()
|
||||
}
|
||||
|
||||
export function lightenColor2(color) {
|
||||
return lightenColor(color,98)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export function newBuilder( component, options = {}) {
|
||||
}
|
||||
|
||||
// Orders hold an amount and builders
|
||||
// noinspection JSUnusedLocalSymbols
|
||||
const Order = {
|
||||
id: uuid(),
|
||||
amount: 0,
|
||||
|
||||
@@ -20,11 +20,8 @@ export default createVuetify({
|
||||
colors: {
|
||||
background: '#fdfffe',
|
||||
surface: '#fdfffe',
|
||||
// primary: '#1A6CAB',
|
||||
primary: '#00CC33',
|
||||
// secondary: '#59B8FF',
|
||||
success: '#00CC33',
|
||||
// info: '#fba92c',
|
||||
warning: '#ffcc00',
|
||||
error: '#CC0033',
|
||||
"on-background": black,
|
||||
|
||||
Reference in New Issue
Block a user