-
Notifications
You must be signed in to change notification settings - Fork 0
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
Single Select #1
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for talking me through my dumb questions on this. This looks really great to me and I like doing the recursion step in your utils so it is really easy to test. Might be worth changing your path to an array to avoid having to do the string splitting which would fail is someone has a /
in their ID. But otherwise this looks really solid to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat! Works really well - couple of suggestions but up to you.
Are you going to add a gha to run the tests and coverage?
package.json
Outdated
@@ -0,0 +1,44 @@ | |||
{ | |||
"name": "vue-nested-multiselect", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've been prefacing our npm packages with @reside-ic, and including repository, author, license, homepage so they show up nicely on npm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
src/App.vue
Outdated
@@ -0,0 +1,66 @@ | |||
<template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like this doesn't really belong in src if it's just for manual testing, and same for main.ts. Could they be pulled out into folder called something like demo
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
<vue-feather type="chevron-right" | ||
v-show="!option.open" | ||
class="icon"/> | ||
<vue-feather type="chevron-down" | ||
v-show="option.open" | ||
class="icon"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a bit more compact, but not too fussed
<vue-feather type="chevron-right" | |
v-show="!option.open" | |
class="icon"/> | |
<vue-feather type="chevron-down" | |
v-show="option.open" | |
class="icon"/> | |
<vue-feather :type="option.open ? 'chevron-down' : 'chevron-right'" | |
class="icon"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the reason i did this was because v-show will actually load both of them so if someone were to spam click, this performs slightly better, obviously not noticeably but thats why i tried to do it this way, happy to change it though! up to you really XD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok - leave it as it is if it behaves more nicely this way!
src/components/utils.ts
Outdated
@@ -0,0 +1,65 @@ | |||
import { FlatOption, Option } from "./types"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should types.ts
and utils.ts
live outside components
since they aren't... ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep can move them! done
src/components/DropdownItem.vue
Outdated
expand: { | ||
type: Function as PropType<(payload: MouseEvent) => void>, | ||
default: () => null | ||
}, | ||
collapse: { | ||
type: Function as PropType<(payload: MouseEvent) => void>, | ||
default: () => null | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why these are props? These are emitted events aren't they?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha i noticed this mistake when wrapping up the multiselect but was just gonna change it in the multiselect branch if no one noticed 😆 , done. This was from a previous commit where i was passing closures in as props and calling them, no idea why but decided to use emits now
src/components/utils.ts
Outdated
return JSON.stringify(array1) === JSON.stringify(array2); | ||
}; | ||
|
||
export const expandOptions = (array: FlatOption[], optionPath: string[]): FlatOption[] => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder if it would be a bit more Vue-y to modify the array in place rather than map to a whole new array. I "think" that should work if the FlatOption array was created as reactive
when initialised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe ref
would be fine - can never remember how it treats arrays, but I'm sure it can handle changing arrays!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ref was indeed fine XD
src/components/utils.ts
Outdated
} | ||
|
||
// if you are a child of a previously opened option then you should show | ||
const show = openOptionsPaths.some(openOpPath => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand why previously opened paths are relevant here. Shouldn't it just be modifying show to true for anything immediately under the selected path, and leaving everything else alone?
And would it make sense to take advantage of the fact that we know the options are in order, so that if we find the next sibling (or higher?) of the selected path we can stop changing things (and we don't need to change anything before the selected path)?
So, could you have something like this (for modifying in place)?
const expandOptions = (array: FlatOption[], optionPath: stirng[]) => {
let index = array.findIndex((fo: FlatOption) => arraysAreEqual(fo.path, optionPath)) + 1;
// loop until end of all options, or next sibling (or higher level option)
while (index < array.length && array[index].path.length > optionPath.length ) {
// only change immediate children
if (array[index].path.length === optionPath.length + 1) {
array[index].show = true;
}
index = index + 1;
}
}
Alternatively while loop could check that the path starts with the optionPath path (rather than just looking at path length).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i really like this idea of just mutating that slice of the array, vue seems to work well with just the ref so all good on that front too! the open paths are relevant because they keep track of what someone has opened before and we should open that. For example lets say something expands the options "parent" > "child" > "grandchild" and then they collapse parent. When they expand parent they should see grandchild too, this is how the current treeselect does it and I think it is actually quite a helpful functionality especially in deeper structures
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, and that's going to use the 'open' prop which is retained rather than 'show' which just determines whether it's being displayed right now? Don't think I'd quite spotted the distinction there, but yeah that makes total sense.
src/components/utils.ts
Outdated
}); | ||
}; | ||
|
||
export const collapseOptions = (array: FlatOption[], optionPath: string[]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do something similar here in terms of only examining/modifying the descendants of the optionsPath option..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep done!
@@ -0,0 +1,23 @@ | |||
# Logs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably don't need all of this I guess, but no harm!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yh this just came with vite XD thought i would just leave it in there
@@ -0,0 +1,9 @@ | |||
.dropdown-menu { | |||
--cui-dropdown-link-active-bg: #ebedef !important; | |||
--cui-dropdown-link-active-color: rbga(44, 56, 74, 0.95) !important; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably document how package client can override style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shall i leave this for another PR? i would like to add styling and configuration last once the single select, multiselect and search things are all done, ticket: https://mrc-ide.myjetbrains.com/youtrack/agiles/103-61/current?issue=mrc-4697
Yes I will be adding these once single select and multiselect components are done, i think these things are better once theres a decent base! |
…, emit modifiers, general clean up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Couple of tiny comments, but good to go.
overflow-wrap: break-word; | ||
text-align: left; | ||
} | ||
</style>../utils |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this ../utils
doing here?
|
||
while (index < array.length && array[index].path.length > optionPath.length) { | ||
const currentOption = array[index]; | ||
const show = openOptionsPaths.some(openOpPath => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this bit merits a comment maybe something like.
const show = openOptionsPaths.some(openOpPath => { | |
// Show options whose immediate parents are marked as open | |
const show = openOptionsPaths.some(openOpPath => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed this comment in #6, it is added in
Description
Don't be afraid! Most of the diff lines are
package-lock.json
and boilerplate. This PR adds the nested single select into this package. I have not updated the README, incorporated CodeCov or added a test workflow yet, these will happen once the multiselect is added but to see demo run:Feel free to change the data in the
App.vue
file to test out the different cases.To test run:
This project uses vite.
Guide
My general approach was to use an existing dropdown component and modify it so we can have nested options. The way this works is we first
flattenOptions
which will convertinto
The path keeps track of the directory like structure so we can find if something is a child of another by comparing strings. The show will control what options are shown in the menu so when we expand a collapsible item, all the shows of the children will flip to
true
. The collapse will scan for any path with the same prefix as the item being collapsed and collapse those.