simeydotme / svelte-range-slider-pips

Multi-Thumb, Accessible, Beautiful Range Slider with Pips
https://simeydotme.github.io/svelte-range-slider-pips/
Mozilla Public License 2.0
446 stars 41 forks source link

[bug] Svelte 5 Range handles are not moving when dragged. #130

Closed serferdinand2 closed 5 months ago

serferdinand2 commented 6 months ago

Describe the bug The slider handles are not moving even tho the value is being read and the handler recognizes the value EDIT: It looks like this is, in general, an issue with bind: values = {...} and it is not limited to range property. As soon as you bind the values the slider handles will stop moving. To Reproduce

Steps to reproduce the behavior:

  1. npmi svelte-range-slider-pips components into svelte 5 project
  2. 
    let {min, max} = $props();
    // old export let min

let values = $state([0, 8]); // old $: values = [0,8] <RangeSlider float range pips hoverable {id} {min} {max} all="label" bind:values on:stop={someHandler} />


3. Run in Vite 

**Screenshots**
As seen from the picture below, even tho the handle was moved and the value was read, the actual handle element stayed in place.

![Screenshot 2024-01-24 at 19 59 39](https://github.com/simeydotme/svelte-range-slider-pips/assets/93290291/3842a5d2-83b4-4398-a5d5-b70ece3688eb)
![Screenshot 2024-01-24 at 20 00 37](https://github.com/simeydotme/svelte-range-slider-pips/assets/93290291/6a0d3c02-984c-4f5a-b0f0-fd665536ad87)

**Device/Environtment**

Svelte 5 with typescript and sveltekit 2

**Additional context**
I have noticed that this component is on svelte 3.0.0. It would be great to create an alpha version of svelte-range-slider-pips that is accommodating the new runes system. If not, it should be crosscompatible with at least Svelte 4.  
simeydotme commented 6 months ago

thanks @serferdinand2 for the detailed explainer! I have a question, though, your last line indicates the issue exists in svelte4?

Also I note you've written Sveltekit2 ... is this a SSR issue? Does the same thing happen when SSR is off? Perhaps the new runes are not compatible with binding to an array?

serferdinand2 commented 6 months ago

@simeydotme

I have made sure that the component is rendered in the browser with {#if browser} module. If not, an error occurs:

ReferenceError: document is not defined
    at pureText (/Users/***/Work/***/svelte_components/node_modules/svelte-range-slider-pips/src/RangeSlider.svelte:396:17)
    ...

So yes, I made sure that it is CSR.

As for the comment on Svelte 4, different issues might occur. The bundler support has changed. I am not 100% sure how exactly this might affect the slider. I did not dig into its code but I think it might be the reason for the #125 issue.

https://svelte.dev/docs/v4-migration-guide#browser-conditions-for-bundlers

I'd love to try and experimentally upgrade this component to Svelte 5 when I find some time.

simeydotme commented 6 months ago

Ah that was my bad, I fixed it with 2.3.1, apologies on that

simeydotme commented 6 months ago

Regarding Svelte 5;

Every few weeks I sit down and think "today is the day"... I install Svelte in a fresh branch and look at it and get mad.

There's no clear way, and no good tutorials, on how to build and bundle the Svelte components from the new Sveltekit library project into a UMD plain js.

I haven't upgraded from Svelte 3 because that was the last time the rollup bundler was able to convert the Svelte components into a .js file.

I feel quite disappointed, because I don't really want to isolate this component to Svelte. I've used it in my own Vue projects and jQuery projects. I don't want to lose that. can't seem to get the "custom element" bundle working.

If you have any information, or know any good resources on this I'd really appreciate it

I've learned a lot since I first started this project so I would love to rebuild it in Svelte 4/5 , maybe even with TypeScript 😬 .. but yeh I just can't get over that problem of locking it in to Svelte only.

simeydotme commented 6 months ago

ok, I have something working. 😅

watch this space

serferdinand2 commented 6 months ago

Sorry, long weekend.

I am glad if you found something that works.

Usually you can control SSR with onMount, {browser}, +page.js/ts and other.

simeydotme commented 6 months ago

hey @serferdinand2 do you mind to try with the beta version I just published? https://www.npmjs.com/package/svelte-range-slider-pips/v/3.0.0-beta.3 see if that is working?

I updated to svelte4 and added types and other small improvements. Haven't updated readme/docs yet ... need to make sure I didn't break anything first

serferdinand2 commented 6 months ago

I've installed the beta version, unfortunately, the issue with handles moving is persisting on Svelte 5 with Sveltekit 2

It looks like the value is updated but the nub is not moving still..

serferdinand2 commented 6 months ago

It also looks like if you pass the value like

<RangeSlider 
    {min}
    {max}
    value = {2}
    />

The numb is moved to the position indicated by the value. I hope this helps!

simeydotme commented 6 months ago

thanks @serferdinand2 , I'll look into it!

simeydotme commented 6 months ago

ok, I think it's to do with how Svelte5 Runes have compiled with the spring store;

$.untrack(() => {
  const trimmedAlignedValues = trimRange(values().map((v) => alignValueToStep(v, min(), max(), step(), precision())));

  if (!(values().length === trimmedAlignedValues.length) || !values().every((element, index) => coerceFloat(element, precision()) === trimmedAlignedValues[index])) {
    values(trimmedAlignedValues);
  }

  if ($.get(valueLength) !== values().length) {
    $.set(springPositions, spring(values().map((v) => valueAsPercent(v, min(), max())), springValues()));
  } else {
    $.get(springPositions).set(values().map((v) => valueAsPercent(v, min(), max())));
  }

  $.set(valueLength, values().length);
});

This is the compiled svelte5 (internal) rune code, and it seems to be not tracking anything inside this reactive chunk ($.untrack) when the values[] array changes .. I guess something about the way my reactive block code is done just doesn't convert well;

$: {
  // trim the range so it remains as a min/max (only 2 handles)
  // and also align the handles to the steps
  const trimmedAlignedValues = trimRange(
    values.map((v) => alignValueToStep(v, min, max, step, precision))
  );
  if (
    !(values.length === trimmedAlignedValues.length) ||
    !values.every(
      (element, index) =>
        coerceFloat(element, precision) === trimmedAlignedValues[index]
    )
  ) {
    values = trimmedAlignedValues;
  }

  // check if the valueLength (length of values[]) has changed,
  // because if so we need to re-seed the spring function with the
  // new values array.
  if (valueLength !== values.length) {
    // set the initial spring values when the slider initialises,
    // or when values array length has changed
    springPositions = spring(
      values.map((v) => valueAsPercent(v, min, max)),
      springValues
    );
  } else {
    // update the value of the spring function for animated handles
    // whenever the values has updated
    springPositions.set(values.map((v) => valueAsPercent(v, min, max)));
  }
  // set the valueLength for the next check
  valueLength = values.length;
}

None of this executes (I've run the debugger), but the specifically problematic code for this issue is; springPositions.set(values.map((v) => valueAsPercent(v, min, max)));

I might be able to modify the code execution and explicitly set the spring store when modifying the values array... however I feel there must be more deeper problems than this.

It might be that Svelte5 just simply breaks this component and we'll need to re-write it in Svelte5.

What's funny, though, is that in my Svelte4 branch I added the prop value as an option which still works, as it's not defined in the untrack parameters?

serferdinand2 commented 6 months ago

Oh so it is Svelte compiler doing this.

If the issue is with the spring library, it might not even work to convert it to Svelte 5.

I didn't see any issues regarding spring in svelte that are newer tho :/

simeydotme commented 6 months ago

no, it's not the Spring library .. its the way the "untrack" super-helper (no idea what to call that) is interpreting my code as something that doesn't need to be reactive... even though it's in a freaking reactive block. Seems the logic they are using is not fully walking my syntax tree

dummdidumm commented 6 months ago

The problem is on Svelte's side. The problem is the combination of passing in $state (which is fine-grained, compared to the old let which is not) in combination with the transformation of $: which only checks that the property as a whole, not its properties, have changed, in order to rerun. https://github.com/sveltejs/svelte/pull/10543 will fix this.

simeydotme commented 5 months ago

The problem is on Svelte's side. The problem is the combination of passing in $state (which is fine-grained, compared to the old let which is not) in combination with the transformation of $: which only checks that the property as a whole, not its properties, have changed, in order to rerun. sveltejs/svelte#10543 will fix this.

Thank you so much for following up on this! 🙏 Let me know if anything I can do to help

serferdinand2 commented 5 months ago

Looks like it works now after the latest svelte update!

simeydotme commented 5 months ago

amaze!