-
-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Select: Rework? #235
Comments
Hey @lukeed, I agree with your feelings on the I'd like to address it here in bits, potentially as a separate implementation that wouldn't depend on Melt, but I haven't had the time to figure out the ideal approach that checks the boxes:
Looking at other headless libraries like Radix, it appears they only accept string values which certainly simplifies things, but puts more onto the user should they want to bind to a non primitive value. I don't think this is a terrible idea for the shadcn-svelte implementation, but as you mentioned it deviates from the rest of the components. I'll need to think on this one for a bit - but a possible middle-ground for the time being could be adding something to the docs with this snippet to demonstrate creating reusable selects with limited value types. |
Agreed. Although numbers should be allowed because the user can I've updated my original code snippet w/ my latest draft which includes generic I personally have a different UI primitive for multiple selection, so I don't have it worked into here although it certainly could be. The only part that really changes is how one displays the selected value(s). |
It's worth noting that wrapping values with the Selected type is a great idea. While it may not be apparent with strings, for nested objects, it eliminates the need to map again and again to return the chosen value.
while using a context store to save these data for another component I assume that Svelte 5 will remove many complications, which might make things more reactive and neglect that bridge. Finally, I would like to thank you for sharing my thoughts |
@Ashaheen92 Look how much more you have to type. And you'd have to repeat that per Select usage. If you were able to bind to a |
@lukeed, you're absolutely right, and I acknowledge that you have more expertise than I do. I'm still in the learning process, but based on my considerations, using this approach seems to be the best practice. There are situations where you have no control over the data value you need or the label you want to display. I'm not trying to argue with you; I just want to share my perspective based on the specific use case. In my opinion, it's often more beneficial to enhance the existing solution rather than completely changing it. For instance, introducing a function named toSelected could facilitate handling various value types throughout the entire component. I appreciate your concern regarding my comment. Thank you for engaging me in this discussion. |
Alright, I've had some time to think about this one a bit more. How would you all feel about requiring the Here's how it may work: type SelectProps<T, U> = {
items: T[];
toValue: (item: T) => U;
toLabel: (item: T) => string;
// ... other props
} This way you can pass any arbitrary array of data, which could be an array of primitive values, objects, arrays, or whatever. You just need to let us know how to convert those into values/labels. Then we could use a Map to link the value with its label, so you could programmatically update the <script lang="ts">
import { Select } from 'bits-ui'
const myItems = [
{ id: 123, age: 28, name: { first: 'hunter', last: 'johnston' }, address: '123 main st.' },
{ id: 345, age: 32, name: { first: 'mike', last: 'smith' }, address: '331 bird st.' },
// ...
]
</script>
<Select.Root items={myItems} toValue={(item) => item.id} toLabel={(item) => item.name.first + ' ' + item.name.last}>
<Select.Trigger />
<Select.Content let:items>
{#each items as item}
<Select.Item {item} />
{/each}
</Select.Content>
</Select.Root> or <script lang="ts">
import { Select } from 'bits-ui'
const myItems = ['apple', 'orange', 'mango']
</script>
<Select.Root items={myItems} toValue={(item) => item} toLabel={(item) => item}>
<Select.Trigger />
<Select.Content let:items>
{#each items as item}
<Select.Item {item} />
{/each}
</Select.Content>
</Select.Root> This would mean that |
Yes, It is better to represent the
In addition, I propose consistently displaying the label, eliminating the need for duplication when providing
Consider:
Furthermore, I advocate for adhering to standardized attributes and markdown naming for HTML elements, aligning with MDN select. For instance, preferring These are suggestions I offer for your consideration. Should you find them worthwhile, I am eager to provide a PR to implement them. |
The problem with using the If a user had never opened the select or conditionally rendered some (imagine a virtual list of sorts), and you had something else that programmatically updated the value, there would be no mapping and it would fall on its face. The label -> value mapping has to happen at the root otherwise there are a number of issues that arise. We exhausted this with Melt before saying screw it and going to the |
Closing as addressed in |
The
selected
approach is awkward, tbh. In practice, it prevents users from binding to a real value. Instead, theyre forced to construct aselected
and then useonSelectedChange
to manually update the original value + the selected{value,label}
IMO something like the below should be offered as default implementation. Would PR but it breaks away from the standard export signature of the rest of the components; eg
export { Root as Select }
would likely be replaced byexport { Below as Select }
which then means the<X.Root/> === <X>
aliasing pattern is broken.Either the
Select.Content
or theSelect.Item
loop could be slotted w/ the above parts as default markupThe text was updated successfully, but these errors were encountered: