Skip to content

Commit

Permalink
Add popover & rename examples to playground
Browse files Browse the repository at this point in the history
  • Loading branch information
xinjie-zhang committed Jan 1, 2023
1 parent 41a772a commit 36c5fbf
Show file tree
Hide file tree
Showing 24 changed files with 362 additions and 8 deletions.
141 changes: 141 additions & 0 deletions components/popover.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<template x-component:hui="popover">
<slot></slot>
<script>
const _ = $vui._
return {
_isOpen: false,
open() {
this._isOpen = true
this.$dispatch('popover-close-others', { el: this.$el })
},
close(el) {
this._isOpen = false
if (!el) return
el = el || this.$findOne(':popover-button')
if (document.activeElement.isSameNode(el)) return
setTimeout(() => el.focus())
},
get isOpen() { return this._isOpen },
toggle() {
if (this.isOpen)
this.close()
else
this.open()
},
onMounted() {
let elGroup = this.$closest(':popover-group')
if (elGroup) {
elGroup.addEventListener('popover-close-others', ({ detail }) => {
if (detail.el.isSameNode(this.$el)) return
this.close(false)
})
}
}
}
</script>
</template>
<template x-component:hui="popover-overlay" x-show="$api && $api.context.isOpen">
<slot></slot>
<script>
return {
get context() { return this.$of(':popover') }
}
</script>
</template>
<template x-component:hui.unwrap="popover-button" @click="$api.onToggle()" @keydown.enter.stop.prevent="$api.onToggle()"
@keydown.space.stop.prevent="$api.onToggle()" @keyup.space.stop.prevent="" @keydown.tab="$api.onTabDown($event)"
@keyup.tab="$api.onTabUp($event)">
<button>
<slot></slot>
</button>
<script>
return {
get context() { return this.$of(':popover') },
onToggle() {
this.context.toggle()
},
onTabDown(e) {
if (!e.shiftKey && this.context.isOpen) {
let elPanel = this.context.$findOne(':popover-panel')
let firstFocusableEl = this.$focus.within(elPanel).getFirst()

if (firstFocusableEl) {
e.preventDefault()
e.stopPropagation()
this.$focus.focus(firstFocusableEl)
}
}
},
onTabUp(e) {
if (this.context.isOpen) {
// Check if the last focused element was "after" this one
let lastEl = this.$focus.previouslyFocused()
if (!lastEl) return
let elPanel = this.context.$findOne(':popover-panel')
if (
// Make sure the last focused wasn't part of this popover.
(!this.$el.contains(lastEl) && !elPanel.contains(lastEl))
// Also make sure it appeared "after" this button in the DOM.
&& (lastEl && (this.$el.compareDocumentPosition(lastEl) & Node.DOCUMENT_POSITION_FOLLOWING))
) {
e.preventDefault()
e.stopPropagation()
this.$focus.within(elPanel).last()
}
}
}
}
</script>
</template>
<template x-component:hui="popover-panel" x-show="$api && $api.context.isOpen"
x-effect="$api && $api.context.isOpen && $prop('focus') && $focus.first()"
@mousedown.window="$api && $api.onMouseDown($event)" @keydown.tab="$api.onTab($event)">
<slot></slot>
<script>
return {
get context() { return this.$of(':popover') },
onMouseDown(e) {
let context = this.context
if (!context.isOpen) return
let apiTarget = $vui.$api(e.target)
let elPanel = apiTarget.$closest(':popover-panel')
if (elPanel === this.$el) return
let elButton = apiTarget.$closest(':popover-button')
if (elButton && elButton === context.$findOne(':popover-button')) return

if (!this.$focus.focusable(e.target)) {
context.close()
}
},
onTab(e) {
let elButton = this.context.$findOne(':popover-button')
if (e.shiftKey && this.$focus.isFirst(e.target)) {
e.preventDefault()
e.stopPropagation()
this.$prop('focus') ? this.context.close() : elButton.focus()
} else if (!e.shiftKey && this.$focus.isLast(e.target)) {
e.preventDefault()
e.stopPropagation()

// Get the next panel button:
let els = this.$focus.within(document).all()
let buttonIdx = els.indexOf(elButton)
let nextEls = els
.splice(buttonIdx + 1) // Elements after button
.filter(el => !this.$el.contains(el)) // Ignore items in panel

nextEls[0].focus()
this.$prop('focus') && this.context.close()
}
}
}
</script>
</template>
<template x-component:hui="popover-group">
<slot></slot>
<script>
return {

}
</script>
</template>
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<template x-if="!item.hidden">
<a :href="'#' + item.path">
<li x-text="item.name"
class="flex items-center whitespace-nowrap rounded py-2 px-6 font-medium transition-colors duration-200 hover:bg-gray-700 hover:text-white lg:py-2 lg:px-4 lg:text-sm text-white">
class="flex items-center whitespace-nowrap rounded py-1 px-6 font-medium transition-colors duration-200 hover:bg-gray-700 hover:text-white lg:py-1 lg:px-4 lg:text-sm text-white">
</li>
</a>
</template>
Expand Down
18 changes: 11 additions & 7 deletions examples/dev.html → playground/dev.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@
get isHome() { return this.$route.page === 'home' },
routes: [
{ name: 'Home', path: '/index', page: 'home' },
{ name: 'Menu Basic', path: '/menu/basic', page: 'menu/basic' },
{ name: 'Menu', path: '/menu/basic', page: 'menu/basic' },
{ name: 'Menu Multiple Elements', path: '/menu/multiple-elements', page: 'menu/multiple-elements' },
{ name: 'Menu with Popper', path: '/menu/with-popper', page: 'menu/with-popper' },
{ name: 'Menu with Transition', path: '/menu/with-transition', page: 'menu/with-transition' },
{ name: 'Menu with Transition and Popper', path: '/menu/with-transition-and-popper', page: 'menu/with-transition-and-popper' },

{ name: 'Switch Basic', path: '/switch/basic', page: 'switch/basic' },
{ name: 'Tabs Basic', path: '/tabs/basic', page: 'tabs/basic' },
{ name: 'Combobox Basic', path: '/combobox/basic', page: 'combobox/basic' },
{ name: 'Listbox Basic', path: '/listbox/basic', page: 'listbox/basic' },
{ name: 'Popover', path: '/popover/basic', page: 'popover/basic' },

{ name: 'Switch', path: '/switch/basic', page: 'switch/basic' },
{ name: 'Tabs', path: '/tabs/basic', page: 'tabs/basic' },
{ name: 'Combobox', path: '/combobox/basic', page: 'combobox/basic' },
{ name: 'Listbox', path: '/listbox/basic', page: 'listbox/basic' },
{ name: 'Listbox Multi', path: '/listbox/multi', page: 'listbox/multi' },
{ name: 'Radio Group Basic', path: '/radio-group/basic', page: 'radio-group/basic' },
{ name: 'Radio Group', path: '/radio-group/basic', page: 'radio-group/basic' },
{ name: 'Something Wrong!', path: '(.*)', page: 'home', hidden: true }
]
}
Expand All @@ -45,7 +49,7 @@
class="h-full w-full font-sans text-gray-900 antialiased flex flex-col" x-data="root">
<app-header class="flex-none h-16 px-1 py-3 flex"></app-header>
<div class="flex-1 flex">
<app-side-menu class="w-64 flex-none" x-show="!isHome"></app-side-menu>
<app-side-menu class="w-74 flex-none" x-show="!isHome"></app-side-menu>
<div class="flex-1 h-full flex flex-col overflow-hidden">
<app-router-view class="flex-1"></app-router-view>
<app-code-viewer x-show="!isHome" class="flex-none h-64 overflow-y-auto bg-gray-700 m-1 rounded-md"
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
73 changes: 73 additions & 0 deletions playground/pages/menu/with-transition-and-popper.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script src="https://unpkg.com/@popperjs/core@2"></script>
<template x-component:page="menu-with-transition-and-popper" x-import="hui:menu" x-data="{
trigger: null,
container: null,
initPopper(){
if (!this.trigger || !this.container) return
Popper.createPopper(this.trigger,this.container, {
placement: 'bottom-end',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
})
}
}">
<div class="flex h-full w-screen justify-center bg-gray-50 p-12">
<div class="mt-64 inline-block text-left">
<hui-menu id="id-menu">
<span class="inline-flex rounded-md shadow-sm">
<hui-menu-button x-init="trigger=$el;initPopper()"
class="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<span>Options</span>
<svg class="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd" />
</svg>
</hui-menu-button>
</span>

<template x-teleport="body">
<hui-menu-items x-transition:enter="transition duration-500 ease-out"
x-transition:enter-start="transform scale-50 opacity-0"
x-transition:enter-end="transform scale-100 opacity-100"
x-transition:leave="transition duration-75 ease-out"
x-transition:leave-start="transform scale-100 opacity-100"
x-transition:leave-end="transform scale-95 opacity-0" x-init="container=$el;initPopper()"
class="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none">
<div class="px-4 py-3">
<p class="text-sm leading-5">Signed in as</p>
<p class="truncate text-sm font-medium leading-5 text-gray-900">[email protected]</p>
</div>

<div class="py-1">
<hui-menu-item><a :class="
['block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50']" href="#account-settings">Account
settings</a></hui-menu-item>
<hui-menu-item><a :class="
['block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50']" href="#support">Support</a></hui-menu-item>
<hui-menu-item disabled><a :class="
['block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50']" href="#new-feature">New feature
(soon)</a></hui-menu-item>
<hui-menu-item><a :class="
['block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50']" href="#license">License</a></hui-menu-item>
</div>
<div class="py-1">
<hui-menu-item><a :class="
['block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50']" href="#sign-out">Sign out</a></hui-menu-item>
</div>
</hui-menu-items>
</template>
</hui-menu>
</div>
</div>
</template>
56 changes: 56 additions & 0 deletions playground/pages/menu/with-transition.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<template x-component:page="menu-with-transition" x-import="hui:menu">
<div class="flex h-full w-full justify-center bg-gray-50 p-12">
<div class="relative inline-block text-left">
<hui-menu>
<span class="rounded-md shadow-sm">
<hui-menu-button
class="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<span>Options</span>
<svg class="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd" />
</svg>
</hui-menu-button>
</span>


<hui-menu-items x-transition:enter="transition duration-500 ease-out"
x-transition:enter-start="transform scale-50 opacity-0" x-transition:enter-end="transform scale-100 opacity-100"
x-transition:leave="transition duration-75 ease-out"
x-transition:leave-start="transform scale-100 opacity-100" x-transition:leave-end="transform scale-95 opacity-0"
class="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none">
<div class="px-4 py-3">
<p class="text-sm leading-5">Signed in as</p>
<p class="truncate text-sm font-medium leading-5 text-gray-900">[email protected]</p>
</div>

<div class="py-1">
<app-menu-item href="#account-settings">Account settings</app-menu-item>
<app-menu-item href="#support">Support</app-menu-item>
<app-menu-item disabled href="#new-feature">New feature (soon)</app-menu-item>
<app-menu-item href="#license">License</app-menu-item>
</div>
<div class="py-1">

<app-menu-item href="#sign-out">Sign out</app-menu-item>
</div>
</hui-menu-items>
</hui-menu>
</div>
</div>
</template>

<template x-component:app.unwrap="menu-item">
<hui-menu-item>
<a :href="$prop('href')" :class="[
'flex justify-between w-full text-left px-4 py-2 text-sm leading-5',
active ? 'bg-indigo-500 text-white' : 'text-gray-700',
disabled ? 'cursor-not-allowed opacity-50' : '']">
<span :class="active ? 'font-bold' : '' ">
<slot></slot>
</span>
<kbd :class="['font-sans', active ? 'text-indigo-50' : '']">⌘K</kbd>
</a>
</hui-menu-item>
</template>
Loading

0 comments on commit 36c5fbf

Please sign in to comment.