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

feat(web-core): implement Fullscreenable component #7663

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

MathieuRA
Copy link
Member

@MathieuRA MathieuRA commented May 15, 2024

Do not squash, review by commits

GIF

Recording 2024-05-16 at 10 13 48

Checklist

  • Commit
    • Title follows commit conventions
    • Reference the relevant issue (Fixes #007, See xoa-support#42, See https://...)
    • If bug fix, add Introduced by
  • Changelog
    • If visible by XOA users, add changelog entry
    • Update "Packages to release" in CHANGELOG.unreleased.md
  • PR
    • If UI changes, add screenshots
    • If not finished or not tested, open as Draft

@OlivierFL OlivierFL requested a review from ByScripts May 21, 2024 14:35
Comment on lines 12 to 14
defineProps<{
back: () => void
}>()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd use events instead:

Suggested change
defineProps<{
back: () => void
}>()
const emit = defineEmits<{
close: []
}>()

Then replace @click="back" with @click="emit('close')" above.

Comment on lines 1 to 39
<template>
<Teleport to="body" :disabled="!isMobile">
<div v-if="isOpen && isMobile" class="mobile">
<FullscreenableHeader v-if="isMobile" :back="close"><slot name="header" /></FullscreenableHeader>
<slot />
</div>
<slot v-else />
</Teleport>
</template>

<script lang="ts" setup>
import FullscreenableHeader from '@core/components/fullscreenable/FullscreenableHeader.vue'
import { ref, inject } from 'vue'
import { useUiStore } from '@core/stores/ui.store'
import { storeToRefs } from 'pinia'
import { IK_FULLSCREENABLE_BEFORE_CLOSE } from '@core/utils/injection-keys.util'
import { noop } from '@vueuse/core'

const { isMobile } = storeToRefs(useUiStore())

const beforeClose = inject(IK_FULLSCREENABLE_BEFORE_CLOSE, noop)

const isOpen = ref(true)
const close = () => {
beforeClose()
isOpen.value = false
}
</script>

<style lang="postcss" scoped>
.mobile {
background-color: var(--background-color-secondary);
height: 100vh;
position: absolute;
top: 0;
width: 100%;
z-index: 10001;
}
</style>
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that this component should not be responsible of its state and thus, not to contain isOpen/close.

IMO, it should only be a wrapper to display an element in fullscreen on mobile, with a disabled prop to handle case where we don't want to teleport the content.

Maybe it should be named accordingly to that statement (for example MobileFullscreen. In my next comments, I will use this name for clarity, but it can be changed if we think about a better name)

Here is some suggestions (it's just a mock, there is missing imports, non-existant components etc.), the comments are here for explanation and can be removed:

Suggested change
<template>
<Teleport to="body" :disabled="!isMobile">
<div v-if="isOpen && isMobile" class="mobile">
<FullscreenableHeader v-if="isMobile" :back="close"><slot name="header" /></FullscreenableHeader>
<slot />
</div>
<slot v-else />
</Teleport>
</template>
<script lang="ts" setup>
import FullscreenableHeader from '@core/components/fullscreenable/FullscreenableHeader.vue'
import { ref, inject } from 'vue'
import { useUiStore } from '@core/stores/ui.store'
import { storeToRefs } from 'pinia'
import { IK_FULLSCREENABLE_BEFORE_CLOSE } from '@core/utils/injection-keys.util'
import { noop } from '@vueuse/core'
const { isMobile } = storeToRefs(useUiStore())
const beforeClose = inject(IK_FULLSCREENABLE_BEFORE_CLOSE, noop)
const isOpen = ref(true)
const close = () => {
beforeClose()
isOpen.value = false
}
</script>
<style lang="postcss" scoped>
.mobile {
background-color: var(--background-color-secondary);
height: 100vh;
position: absolute;
top: 0;
width: 100%;
z-index: 10001;
}
</style>
<template>
<!-- When disabled or viewed on desktop, display the slot as is -->
<slot v-if="uiStore.isDesktop || disabled">
<!-- In other cases, if this "panel" is `open`, let's display it in the body -->
<Teleport v-if="open" to="body">
<div class="mobile-fullscreen">
<!-- MobileHeadBar is a designed component in Figma -->
<!-- It's just used here as example and doesn't have to be created now -->
<MobileHeadBar>
<ButtonIcon :icon="faAngleLeft" @click="emit('close')" />
<slot name="header" />
</MobileHeadBar>
<slot />
</div>
</Teleport>
</template>
<script lang="ts" setup>
import { ref, inject } from 'vue'
import { useUiStore } from '@core/stores/ui.store'
import { storeToRefs } from 'pinia'
defineProps<{
disabled?: boolean // To display the slot as-is
open?: boolean // On mobile, to know if the panel is open
}>()
const emit = defineEmits<{
close: []
}>()
const uiStore = useUiStore()
</script>
<style lang="postcss" scoped>
.mobile-fullscreen {
background-color: var(--background-color-secondary);
position: fixed;
inset: 0;
z-index: 100;
}
</style>

See MenuList below for usage suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can't comment code which had not been modified so I'll comment here.

First, I don't think it should be the developer responsibility to remember to wrap each MenuList in the MobileFullscreen component each time it's used. This is likely to be error-prone and tedious.

IMO, MobileFullscreen should be integrated directly in components, whenever it's necessary.

In the case of MenuList I would replace:

<template>
  <slot :is-open="isOpen" :open="open" name="trigger" />
  <Teleport :disabled="!shouldTeleport" to="body">
    <ul v-if="!hasTrigger || isOpen" ref="menu" :class="{ horizontal, shadow }" class="menu-list" v-bind="$attrs">
      <slot />
    </ul>
  </Teleport>
</template>

With:

<template>
  <slot :is-open="isOpen" :open="open" name="trigger" />
  <!-- We want do disable the wrapper if there is no trigger -->
  <!-- Because No trigger = Menu displayed in the flow, not on action -->
  <MobileFullscreen
    :disabled="!hasTrigger"
    :open="isOpen /* On mobile, the wrapper is open if the menu is open */"
    @close="isOpen = false"
  >
    <!-- Keep the default behavior for desktop -->
    <ul
      v-if="!hasTrigger || isOpen"
      ref="menu"
      :class="{ horizontal, shadow, mobile: uiStore.isMobile }"
      class="menu-list"
      v-bind="$attrs"
    >
      <slot />
    </ul>
  </MobileFullscreen>
</template>

Copy link
Contributor

Choose a reason for hiding this comment

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

With this solution, the Menu can be build normally:

      <MenuList>
        <template #trigger="{ open }">
          <UiButton @click="open">Open</UiButton>
        </template>
        <MenuItem>Foo</MenuItem>
        <MenuItem>Bar</MenuItem>
        <MenuItem>
          Baz
          <template #submenu>
            <MenuItem>Bar 1.1</MenuItem>
            <MenuItem>
              Bar 1.2
              <template #submenu>
                <MenuItem>Bar 1.2.1</MenuItem>
                <MenuItem>Bar 1.2.2</MenuItem>
                <MenuItem>Bar 1.2.3</MenuItem>
              </template>
            </MenuItem>
            <MenuItem>Bar 1.3</MenuItem>
          </template>
        </MenuItem>
      </MenuList>

When clicking the trigger, the menu will then be displayed as a dropdown on desktop and fullscreen on mobile.

If we remove the #trigger template, the menu will be displayed as-is on desktop and mobile, but clicking on a menu item with submenu, then the submenu will be displayed as dropdown on desktop and fullscreen in mobile.

It should also handle multiple submenu correctly on mobile.

@MathieuRA MathieuRA force-pushed the core/fullscreenable branch 2 times, most recently from 15e9740 to bfedf66 Compare May 23, 2024 09:00
@OlivierFL OlivierFL requested a review from ByScripts June 3, 2024 08:43
MathieuRA and others added 3 commits June 3, 2024 15:27
Co-authored-by: OlivierFL <66562640+OlivierFL@users.noreply.github.com>
Co-authored-by: OlivierFL <66562640+OlivierFL@users.noreply.github.com>
…eFullscreen.vue

Co-authored-by: OlivierFL <66562640+OlivierFL@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants