agentsmith
agentsmith
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
I tried that originally, but I'll try again. Either way I'm about to just move on. It's mostly working. The app won't break because the value will be clamped even if the user can type in values outside of the min/max if they know how.
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
That looks like it should work. I think it's a bug/restriction in the shadcn-solid component. It does work as expect if I use that and a regular input. But the issue is still there if I use the shadcn-solid components.
import { createSignal } from 'solid-js'
import {
TextField,
TextFieldLabel,
TextFieldDescription,
TextFieldRoot,
} from '@/components/ui/textfield'

import type { Component } from 'solid-js'
import type { JSX } from 'solid-js/jsx-runtime'

interface NumberInputProps {
id: string
label: JSX.Element
info?: JSX.Element
class?: string
min?: number
max?: number
value?: number | (() => number)
onInput?: (value: number) => void
}

export const NumberInput: Component<NumberInputProps> = (props) => {
const [inputValue, setInputValue] = createSignal(
typeof props.value === 'function' ? props.value() : props.value,
{ equals: () => false }
)

// Clamping function to enforce min and max constraints
const clampValue = (value: number): number => {
let clamped = value
if (props.min !== undefined) {
clamped = Math.max(props.min, clamped)
}
if (props.max !== undefined) {
clamped = Math.min(props.max, clamped)
}
return clamped
}

const handleInput = (e: Event) => {
console.log('handleInput')
const target = e.target as HTMLInputElement
const val = Number(target.value)
const clamped = clampValue(val)
setInputValue(clamped) // Update local state to clamped value
props.onInput?.(clamped) // Update external signal
}

return (
<TextFieldRoot
class={props.class ?? 'w-24'}
defaultValue={inputValue()?.toString()}
>
<TextFieldLabel for={props.id}>{props.label}</TextFieldLabel>
<TextField
id={props.id}
type="number"
min={props.min}
max={props.max}
value={inputValue()}
onInput={handleInput}
/>
{props.info && <TextFieldDescription>{props.info}</TextFieldDescription>}
</TextFieldRoot>
// using this works as expected.
// <input
// id={props.id}
// type="number"
// min={props.min}
// max={props.max}
// value={inputValue()}
// onInput={handleInput}
// />
)
}

export default NumberInput
import { createSignal } from 'solid-js'
import {
TextField,
TextFieldLabel,
TextFieldDescription,
TextFieldRoot,
} from '@/components/ui/textfield'

import type { Component } from 'solid-js'
import type { JSX } from 'solid-js/jsx-runtime'

interface NumberInputProps {
id: string
label: JSX.Element
info?: JSX.Element
class?: string
min?: number
max?: number
value?: number | (() => number)
onInput?: (value: number) => void
}

export const NumberInput: Component<NumberInputProps> = (props) => {
const [inputValue, setInputValue] = createSignal(
typeof props.value === 'function' ? props.value() : props.value,
{ equals: () => false }
)

// Clamping function to enforce min and max constraints
const clampValue = (value: number): number => {
let clamped = value
if (props.min !== undefined) {
clamped = Math.max(props.min, clamped)
}
if (props.max !== undefined) {
clamped = Math.min(props.max, clamped)
}
return clamped
}

const handleInput = (e: Event) => {
console.log('handleInput')
const target = e.target as HTMLInputElement
const val = Number(target.value)
const clamped = clampValue(val)
setInputValue(clamped) // Update local state to clamped value
props.onInput?.(clamped) // Update external signal
}

return (
<TextFieldRoot
class={props.class ?? 'w-24'}
defaultValue={inputValue()?.toString()}
>
<TextFieldLabel for={props.id}>{props.label}</TextFieldLabel>
<TextField
id={props.id}
type="number"
min={props.min}
max={props.max}
value={inputValue()}
onInput={handleInput}
/>
{props.info && <TextFieldDescription>{props.info}</TextFieldDescription>}
</TextFieldRoot>
// using this works as expected.
// <input
// id={props.id}
// type="number"
// min={props.min}
// max={props.max}
// value={inputValue()}
// onInput={handleInput}
// />
)
}

export default NumberInput
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
I've identified the issue and it's a reactive thing, but I don't know how to fix it yet. I can change the input value and when I do so it invokes the handle input which applies the clamp. This results in the underlying signal sticking to the value I expect. However, because the clamp results in the value being the same it doesn't trigger an update to re-render the input with the correct value. This seems like a fairly common use-case when using inputs. Is there a way to deal with this in solid?
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
Alright, I don't think this is a real issue that would happen, but I wasn't seeing that yesterday. Still an oddity though.
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
No description
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
No description
14 replies
SSolidJS
Created by agentsmith on 10/31/2024 in #support
Reactivity with shadcn-solid textfield
This is the implementation using the NumberInput. The trials and futurePoints inputs fail on the -1 test, but the other two work as expected.
import { createEffect, createSignal } from 'solid-js'
// import { Info } from 'lucide-solid'
import { Button } from '@/components/ui/button'
import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card'
// import { Checkbox, CheckboxControl, CheckboxLabel } from '@/components/ui/checkbox'
// import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { NumberInput } from '@/components/ui/NumberInput'
import MonteCarloChart from '@/components/charts/MonteCarlo'
import { setMonteCarloData, simulations } from '@/libs/monteCarlo'

import type { Component } from 'solid-js'
import type { TradeMetrics } from '@/libs/stats'

interface CardProps {
data: TradeMetrics | null
}

export const MonteCarloCard: Component<CardProps> = (props) => {
const [trials, setTrials] = createSignal(100)
const [futurePoints, setFuturePoints] = createSignal(100)
const [highRunsCnt, setHighRunsCnt] = createSignal(0)
const [lowRunsCnt, setLowRunsCnt] = createSignal(0)
// const [staticPoints, setStaticPoints] = createSignal(false)

const handleMonteCarloData = () => {
let simulationResult = simulations(
props.data?.netProfit ?? [],
trials(),
futurePoints(),
props.data?.startingEquity
)
// the data is already sorted by the last number in the array
// so we can just slice the array to remove the high and low points
// Slice off the high points (remove from the end)
const removeHigh = highRunsCnt()
// const highRemoved = simulationResult.slice(0, removeHigh)
simulationResult = simulationResult.slice(removeHigh)

// Slice off the low points (remove from the start)
const removeLow = lowRunsCnt()
// const lowRemoved = simulationResult.slice(simulationResult.length - removeLow)
simulationResult = simulationResult.slice(0, simulationResult.length - removeLow)
setMonteCarloData(() => simulationResult)
}

createEffect(() => {
handleMonteCarloData()
})

return (
<Card>
<CardHeader>
<CardTitle>Monte Carlo</CardTitle>
<div class="flex flex-wrap flex-row justify-end gap-2">
<NumberInput
id="trials"
label="Trials"
min={1}
value={trials}
onInput={setTrials}
/>
<NumberInput
id="futurePoints"
label="Future Points"
min={1}
value={futurePoints}
onInput={setFuturePoints}
/>
<NumberInput
class="w-36"
id="removeHighRuns"
label="Remove Best Trials"
min={0}
value={highRunsCnt}
onInput={setHighRunsCnt}
/>
<NumberInput
class="w-36"
id="removeLowRuns"
label="Remove Worst Trials"
min={0}
value={lowRunsCnt}
onInput={setLowRunsCnt}
/>
<Button
class="self-end"
style={{ 'margin-bottom': '0.3rem' }}
variant="default"
onClick={() => handleMonteCarloData()}
>
Rerun
</Button>
</div>
</CardHeader>
<CardContent>
<MonteCarloChart
data={props.data}
// staticPoints={staticPoints()}
/>
</CardContent>
</Card>
)
}

export default MonteCarloCard
import { createEffect, createSignal } from 'solid-js'
// import { Info } from 'lucide-solid'
import { Button } from '@/components/ui/button'
import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card'
// import { Checkbox, CheckboxControl, CheckboxLabel } from '@/components/ui/checkbox'
// import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { NumberInput } from '@/components/ui/NumberInput'
import MonteCarloChart from '@/components/charts/MonteCarlo'
import { setMonteCarloData, simulations } from '@/libs/monteCarlo'

import type { Component } from 'solid-js'
import type { TradeMetrics } from '@/libs/stats'

interface CardProps {
data: TradeMetrics | null
}

export const MonteCarloCard: Component<CardProps> = (props) => {
const [trials, setTrials] = createSignal(100)
const [futurePoints, setFuturePoints] = createSignal(100)
const [highRunsCnt, setHighRunsCnt] = createSignal(0)
const [lowRunsCnt, setLowRunsCnt] = createSignal(0)
// const [staticPoints, setStaticPoints] = createSignal(false)

const handleMonteCarloData = () => {
let simulationResult = simulations(
props.data?.netProfit ?? [],
trials(),
futurePoints(),
props.data?.startingEquity
)
// the data is already sorted by the last number in the array
// so we can just slice the array to remove the high and low points
// Slice off the high points (remove from the end)
const removeHigh = highRunsCnt()
// const highRemoved = simulationResult.slice(0, removeHigh)
simulationResult = simulationResult.slice(removeHigh)

// Slice off the low points (remove from the start)
const removeLow = lowRunsCnt()
// const lowRemoved = simulationResult.slice(simulationResult.length - removeLow)
simulationResult = simulationResult.slice(0, simulationResult.length - removeLow)
setMonteCarloData(() => simulationResult)
}

createEffect(() => {
handleMonteCarloData()
})

return (
<Card>
<CardHeader>
<CardTitle>Monte Carlo</CardTitle>
<div class="flex flex-wrap flex-row justify-end gap-2">
<NumberInput
id="trials"
label="Trials"
min={1}
value={trials}
onInput={setTrials}
/>
<NumberInput
id="futurePoints"
label="Future Points"
min={1}
value={futurePoints}
onInput={setFuturePoints}
/>
<NumberInput
class="w-36"
id="removeHighRuns"
label="Remove Best Trials"
min={0}
value={highRunsCnt}
onInput={setHighRunsCnt}
/>
<NumberInput
class="w-36"
id="removeLowRuns"
label="Remove Worst Trials"
min={0}
value={lowRunsCnt}
onInput={setLowRunsCnt}
/>
<Button
class="self-end"
style={{ 'margin-bottom': '0.3rem' }}
variant="default"
onClick={() => handleMonteCarloData()}
>
Rerun
</Button>
</div>
</CardHeader>
<CardContent>
<MonteCarloChart
data={props.data}
// staticPoints={staticPoints()}
/>
</CardContent>
</Card>
)
}

export default MonteCarloCard
14 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
Missed this message but I saw the Github post and commented back. That's what I was thinking, but wasn't sure.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
lol!
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
I'm not following. That came from the error that was a result of the DOM api call.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
The element is there in JS. The only thing I can figure is that it's in memory, but not yet in the DOM.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
It's the insert call specifically.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
Here in the minified plotly code. Ze._container.enter().insert("div", ":first-child").
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
Based on the error and the JS calls it looks like it's trying to insert an element in the DOM, but getting an error the the el reference is not present yet. The el is in JS at this point for sure, but maybe it's not rendered just yet. It should be because it's in the onMount function, but the error is related to accessing an element that not yet loaded in the DOM. This also explains why the await new Promise(resolve => setTimeout(resolve, 0)) resolves the issue becuase it needs just one more tick to be loaded.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
No description
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
You get an error building? Is that when it's linked? I haven't seen that yet.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
The babel error is because of the size of plotly. It is a peer dependency for the library. It's the application that gives that warning when running because of the size of plotly, but there's not much to be done about that. It's just a large library.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
I'm on windows using WSL which is just an unbuntu VM basically.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
I'm just about done with the basic of the project I'm working on. I could run without this, but I want to make it fancy.
105 replies
SSolidJS
Created by agentsmith on 10/4/2024 in #support
solid-plotly.js a new wrapper for Plotly.js
No description
105 replies