dan-lee / timescape

A flexible, headless date and time input library for JavaScript. Provides tools for building fully customizable date and time input fields, with support for libraries like React, Preact, Vue, Svelte and Solid.
https://timescape.daniellehr.de
MIT License
159 stars 2 forks source link

Allow partial input #42

Closed dan-lee closed 1 week ago

dan-lee commented 3 weeks ago

Addresses #41

This adds a new option disallowPartial to allow partial input dates and set it as default. Only when it's completely filled out, options.date returns a Date instance; otherwise it's undefined.

The Delete key clears a selected input field. Backspace returns character by character in a segment.

Preview here: https://feat-allow-partial-input.timescape.pages.dev/?path=%2Fstory%2Froot-timescape--partial

cloudflare-workers-and-pages[bot] commented 3 weeks ago

Deploying timescape with  Cloudflare Pages  Cloudflare Pages

Latest commit: a5f57c2
Status: ✅  Deploy successful!
Preview URL: https://404e3618.timescape.pages.dev
Branch Preview URL: https://feat-allow-partial-input.timescape.pages.dev

View logs

junwen-k commented 3 weeks ago

Hey @dan-lee, thank you so much for the prompt response!

It seems the updated behavior currently only clears segments with delete. Ideally, pressing backspace should also clear individual segments, similar to native inputs. This could help make the interaction more consistent.

Other than this, the current behavior aligns well with native HTML date inputs, which is great. However, I wonder if partial input should be the default behavior, as it seems more user-friendly and closer to standard date input handling (if we’re using native HTML input as our reference).

If so, perhaps the prop could be renamed in a negative form, like disallowPartial rather than allowPartial, to indicate opting out of partial input behavior rather than enabling it.

Thanks again for considering this!

As a bonus note, initially I compared the behavior with React Aria, where pressing backspace or delete removes characters one by one within a segment, moving the cursor back to the previous segment once a segment is empty.

https://github.com/user-attachments/assets/6b1748c2-bbf8-49af-b749-16def572536a

After double-checking with native HTML inputs and MUI, though, they seem to align with your current behavior, so I think we’re good here! 😊

dan-lee commented 3 weeks ago

I agree with your points! I will implement this; will stir up some things though :) We probably need to provide default placeholders when this is the new behavior. I am thinking of just providing yyyy, etc. Can be overwritten with <input placeholder="....">.

About backspace/delete: I think delete could clear the entire segment (like it is right now) and backspace could remove character by character in a segment, like ARIA. Wdyt?

junwen-k commented 3 weeks ago

I agree with your points! I will implement this; will stir up some things though :) We probably need to provide default placeholders when this is the new behavior. I am thinking of just providing yyyy, etc. Can be overwritten with <input placeholder="....">.

About backspace/delete: I think delete could clear the entire segment (like it is right now) and backspace could remove character by character in a segment, like ARIA. Wdyt?

I agree that having default placeholders similar to native html input. E.g, yyyy for year input, mm for month etc will be very helpful.

About backspace/delete: I think delete could clear the entire segment (like it is right now) and backspace could remove character by character in a segment, like ARIA. Wdyt?

Regarding to this, I've did some comparisons with other libraries to check their handling of backspace and delete, which might be useful as a reference:

Library GitHub Stars Backspace Behavior Delete Behavior
Ant Design 92.4k Edits text directly without segments Edits text directly without segments
Chakra UI 37.8k No date field component available No date field component available
Mantine 26.7k No date field component available No date field component available
BlueprintJS 20.7k Edits text directly without segments Edits text directly without segments
React Aria 12.9k Removes character-by-character in segment Removes character-by-character in segment
RSuite 8.3k Clears entire segment Moves cursor to the back of the input
MUI-X 4.5k Clears entire segment Clears entire segment

as of 2024 Nov 4th

I think having backspace remove characters one by one within a segment feels very nice from a UX perspective. When the highlighted segment is cleared, pressing backspace should move the highlighted segment back to the previous one, which would align well with typical user expectations.

Thank you!

dan-lee commented 3 weeks ago

Wow, that's great! Thanks for the effort. This is a beautiful summary. Also for the great feedback, really appreciated 🙏 I wish OSS was always this fun and engaging :)

For what it's worth, React Aria seems to remove character by character using the delete and backspace keys. I still think it's better UX to have those separated, as you suggested.

junwen-k commented 3 weeks ago

Wow, that's great! Thanks for the effort. This is a beautiful summary. Also for the great feedback, really appreciated 🙏 I wish OSS was always this fun and engaging :)

For what it's worth, React Aria seems to remove character by character using the delete and backspace keys. I still think it's better UX to have those separated, as you suggested.

Ah, thanks for catching that—I’ve updated the table to reflect this accurately.

I also just thought of an idea: what if we allow users to control this behavior using props, perhaps something like

backspaceDeletionMode: 'segment' | 'character' // Defaults to `segment` to match native input behavior.
deleteDeletionMode: 'segment' | 'character' // Defaults to `segment` to match native input behavior.

This way, we could support both UX options. By default, it could clear the entire segment for both backspace and delete, and users could optionally opt in to the more granular behavior similar to React Aria.

Of course, adding this control might expand the API surface and increase maintenance, so it would need to be considered carefully. If we want to keep things simple, I am leaning towards following the behavior of React Aria if possible, otherwise native date input.

And on a side note, I really appreciate the work you’re doing here—there’s such a need for a headless date field library like this! I’ve actually been looking for something like this for a long time. Thank you for making it happen and for all the thoughtful engagement along the way. 🚀

changeset-bot[bot] commented 3 weeks ago

🦋 Changeset detected

Latest commit: a5f57c22438748ac4a167d0008197410ec5ddbbd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages | Name | Type | | --------- | ----- | | timescape | Minor | | demo | Patch |

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

dan-lee commented 3 weeks ago

@junwen-k can you check again now? :)

junwen-k commented 3 weeks ago

@dan-lee I took a closer look and did a comparison using this StackBlitz example.

Here are a few differences I noticed:

  1. React Aria’s year segment doesn’t consistently display 4 digits, while Timescape always shows a padded 4-digit year.

  2. React Aria triggers onChange on every keystroke rather than only on onBlur.

  3. There seems to be a minor issue where, if I enter a 2-digit year (like 12), switch to another segment, and then return to the year segment, I need to press backspace twice to clear the segment.

    https://github.com/user-attachments/assets/3a6c5e50-2061-4f1c-b788-e384d72817b7

  4. Seems like theres some issue with negative value in year segment input.

    image

Overall, it looks really good—these are just a few observations I came across. Thanks for the fantastic work and all your effort on this!

dan-lee commented 2 weeks ago

Thanks for your feedback @junwen-k, sorry took me some time :)

  1. React Aria’s year segment doesn’t consistently display 4 digits, while Timescape always shows a padded 4-digit year.

That's true, the library is a bit opinionated about how to render dates. I plan to add a new option, formatSegment or something, which gives back the user control. Then, there could be some default and maybe localized presets if there's a need.

Thinking of an API like:

formatSegment: (opts: {
  incomplete: boolean;
  value: Date;
  valueAsString: string;
  type: DateType;
  options: Options;
}) => string | number | undefined
  1. React Aria triggers onChange on every keystroke rather than only on onBlur.

I am a bit hesitant to do this, because intermediate steps are not full dates. Arrow up/down will trigger change and switching left/right between fields also does. Whenever a date is "complete" from the libraries POV the date change is happening.

  1. There seems to be a minor issue where, if I enter a 2-digit year (like 12), switch to another segment, and then return to the year segment, I need to press backspace twice to clear the segment.

Should be fixed :)

  1. Seems like theres some issue with negative value in year segment input.

For now just fixed it by rendering negative values without 0 padding. I think it's an edge case. Could be fixed in different ways by the proposal in bullet point 1.

junwen-k commented 1 week ago

Hi! I just realized that negative year values are actually possible, which I initially thought didn’t make sense.

In React Aria, when the year value is negative, it appends "BC." However, the underlying value doesn't seem to reflect BC and the only way to achieve this is by manually decrementing the year—typing a negative value directly doesn’t seem to work.

Here’s a demo of it:

https://github.com/user-attachments/assets/b5e3dade-0c50-4669-8fe0-a835240283ea

As for the native HTML input, I noticed that it cycles to 275760 when trying to go below the valid year range. Here’s what I observed:

https://github.com/user-attachments/assets/275d42ca-4b87-43e6-9a79-e0a4d462895f

Thank you for your patience and for the great work you've put into this! It’s been a while since my last reply, and I really appreciate you waiting for my response. Everything else looks pretty good, just wanted to share these observations.

dan-lee commented 1 week ago

Thanks for your feedback @junwen-k! I went ahead and merged it and will cut a release soon. Yeah, about the negative values, I think formatSegment can also solve this, but the problem will be similar in that the underlying date and BC will not correlate directly. Internally, it will still be a negative Date value. Imho there's probably no point in tweaking this edge case too much anyway.