Skip to content
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

Nested submenus #12548

Merged
merged 13 commits into from
Mar 20, 2025
Merged

Nested submenus #12548

merged 13 commits into from
Mar 20, 2025

Conversation

vitvakatu
Copy link
Contributor

@vitvakatu vitvakatu commented Mar 18, 2025

Pull Request Description

Closes #12182

To test, you can provide widget configuration with nested items, like the following:

@x (Widget.Single_Choice values=[Choice.Option "'option1'",  Choice.Option "this one is submenu" [Choice.Option "'item 1'", Choice.Option "'item 2'"]])
some_func x:(Text|Number) = x + 'x'
nested-dropdown-menus.mp4

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    TypeScript,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • Unit tests have been written where possible.
  • If meaningful changes were made to logic or tests affecting Enso Cloud integration in the libraries,
    or the Snowflake database integration, a run of the Extra Tests has been scheduled.
    • If applicable, it is suggested to paste a link to a successful run of the Extra Tests.

Copy link

github-actions bot commented Mar 18, 2025

🧪 Storybook is successfully deployed!

📊 Dashboard:

@github-actions github-actions bot added the -libs-API-change-Base Marks a PR that changes the public API of Standard.Base label Mar 18, 2025
Copy link
Member

@AdRiley AdRiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the libs code change

Comment on lines 55 to 66
defineExpose({
isTargetOutside,
})

export interface Submenu {
entries: ComputedRef<Entry[]>
relativeTo: HTMLElement
}

const submenu = ref<Submenu | null>(null)
const submenuEntries = computed(() => submenu.value?.entries ?? [])
const submenuRef = useTemplateRef('submenuRef')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose a bit of reordering here, for better reading: types at the top (after props maybe), then refs, then rest and expose at end (the last is not a rule per se, but a place I usually look for exposes).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did some reordering, trying to keep related parts together.

<Teleport v-if="props.rootElement" :to="props.rootElement">
<div ref="dropdownElement" :style="floatingStyles" class="SelectionSubmenu widgetOutOfLayout">
<SizeTransition height :duration="100">
<DropdownWidget
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized we have DropdownWidget and DropdownMenu which, of course, don't use DropdownWidget at all.

I think the difference should be documented. Or these two could be merged? But the latter shall be rather a separate task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah… We have a couple of really confusing names here:

components/FloatingSelectionMenu.vue
components/GraphEditor/widgets/WidgetSelection
components/GraphEditor/widgets/WidgetSelection/SelectionSubmenu.vue
components/GraphEditor/widgets/WidgetSelection.vue
components/SelectionDropdown.vue
components/SelectionDropdownText.vue
components/SelectionMenu.vue
components/DropdownMenu.vue
components/SelectionDropdown.vue
components/SelectionDropdownText.vue
components/widgets/DropdownWidget.vue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure how to address it properly at the moment, we might need to do some renamings/refactorings, but I think it is out of scope of this PR. I will create an issue instead.

Comment on lines 69 to 81

/**
* Recursively find the nearest <li> element among ancestors of the event target.
* It is used to guarantee a stable target no matter the exact location of the click.
*/
function findTargetLiElement(target: EventTarget | null): HTMLElement | null {
if (!(target instanceof HTMLElement)) return null
let targetLiElement = target
while (!(targetLiElement instanceof HTMLLIElement)) {
targetLiElement = targetLiElement.parentElement as HTMLElement
}
return targetLiElement
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just use event.currentTarget on li element?

If not, add explanation somewhere, why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yep, it will work.

Comment on lines 241 to 258
export function singleChoiceConfiguration(config: SingleChoice): OneOfFunctionCalls {
const possibleChoices = config.values.flatMap(flattenChoice)
return {
kind: 'OneOfFunctionCalls',
possibleFunctions: new Map(
config.values.map((value) => [value.value, functionCallConfiguration(value.parameters)]),
possibleChoices.map((choice) => [choice.value, functionCallConfiguration(choice.parameters)]),
),
}
}

/** A configuration for the inner widget of a multiple-choice selection widget. */
export function multipleChoiceConfiguration(config: MultipleChoice): SomeOfFunctionCalls {
const possibleChoices = config.values.flatMap(flattenChoice)
return {
kind: 'SomeOfFunctionCalls',
possibleFunctions: new Map(
config.values.map((value) => [value.value, functionCallConfiguration(value.parameters)]),
possibleChoices.map((choice) => [choice.value, functionCallConfiguration(choice.parameters)]),
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be supernice to have these method tested.

If they are (I couldn't find), then extend them with case with nested choices.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some tests, although the implementation is very straightforward and I doubt anything will break here, but it can break in the usage place instead, and it is far more complicated to test.

@vitvakatu vitvakatu added the CI: Ready to merge This PR is eligible for automatic merge label Mar 20, 2025
@mergify mergify bot merged commit edfe8ed into develop Mar 20, 2025
75 of 76 checks passed
@mergify mergify bot deleted the wip/vitvakatu/nested-submenus branch March 20, 2025 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
-gui -libs-API-change-Base Marks a PR that changes the public API of Standard.Base CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Menu Widget Add Nesting
4 participants